From 06adc57a39094a1932aed40f6a583609d583a193 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 08:38:59 +0200 Subject: [PATCH 001/155] Update to SDK 8.0 --- src/global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global.json b/src/global.json index 6fb33515b..60b4c0250 100644 --- a/src/global.json +++ b/src/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "6.0.408", + "version": "8.0.100", "rollForward": "latestMinor", "allowPrerelease": false } From d6eb7a1390917db0dbf5a297c8298a861960d53d Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 08:39:22 +0200 Subject: [PATCH 002/155] Removed DotNext.Reflection library --- .../DotNext.Benchmarks.csproj | 1 - .../Reflection/FieldReflectionBenchmark.cs | 43 - .../HashMethodReflectionBenchmark.cs | 38 - .../PropertyGetterReflectionBenchmark.cs | 56 -- .../StringMethodReflectionBenchmark.cs | 42 - .../Reflection/TryParseReflectionBenchmark.cs | 51 -- src/DotNext.Reflection/ArrayExtensions.cs | 35 - src/DotNext.Reflection/Assembly.cs | 4 - src/DotNext.Reflection/Disposable.cs | 53 -- .../DotNext.Reflection.csproj | 73 -- src/DotNext.Reflection/ExceptionMessages.cs | 72 -- .../ExceptionMessages.restext | 21 - src/DotNext.Reflection/FodyWeavers.xml | 3 - src/DotNext.Reflection/Function.cs | 603 ------------- src/DotNext.Reflection/Number.cs | 252 ------ src/DotNext.Reflection/Procedure.cs | 475 ---------- src/DotNext.Reflection/Record.cs | 39 - .../Reflection/AbstractDelegateException.cs | 10 - .../Reflection/BinaryOperator.cs | 205 ----- .../Reflection/BuiltinOperatorInfo.cs | 51 -- .../ConstraintViolationException.cs | 12 - .../Reflection/Constructor.cs | 400 --------- .../Reflection/DynamicInvoker.cs | 129 --- src/DotNext.Reflection/Reflection/Event.cs | 503 ----------- .../Reflection/ExtensionRegistry.cs | 124 --- src/DotNext.Reflection/Reflection/Field.cs | 582 ------------- .../Reflection/IConstructor.cs | 12 - src/DotNext.Reflection/Reflection/IEvent.cs | 54 -- src/DotNext.Reflection/Reflection/IField.cs | 43 - src/DotNext.Reflection/Reflection/IMember.cs | 684 --------------- src/DotNext.Reflection/Reflection/IMethod.cs | 23 - .../Reflection/IOperator.cs | 26 - .../Reflection/IProperty.cs | 49 -- src/DotNext.Reflection/Reflection/Indexer.cs | 514 ----------- .../Reflection/MemberAccessor.cs | 35 - .../Reflection/MemberCache.cs | 103 --- src/DotNext.Reflection/Reflection/Method.cs | 598 ------------- .../Reflection/MethodLookup.cs | 17 - .../Reflection/MissingAttributeException.cs | 27 - .../Reflection/MissingConstructorException.cs | 34 - .../Reflection/MissingEventException.cs | 34 - .../Reflection/MissingFieldException.cs | 33 - .../Reflection/MissingMethodException.cs | 55 -- .../Reflection/MissingOperatorException.cs | 25 - .../Reflection/MissingPropertyException.cs | 33 - src/DotNext.Reflection/Reflection/Operator.cs | 224 ----- .../Reflection/OperatorLookup.cs | 22 - src/DotNext.Reflection/Reflection/Property.cs | 483 ----------- src/DotNext.Reflection/Reflection/Ref.cs | 114 --- .../Reflector.DynamicFieldInvoker.cs | 78 -- .../Reflector.DynamicMethodInvoker.cs | 112 --- .../Reflection/Reflector.cs | 120 --- .../Reflection/Signature.cs | 159 ---- .../Reflection/Type.Attributes.cs | 47 - .../Reflection/Type.Ctors.cs | 716 --------------- .../Reflection/Type.Events.cs | 50 -- .../Reflection/Type.Fields.cs | 49 -- .../Reflection/Type.Indexers.cs | 53 -- .../Reflection/Type.Methods.cs | 814 ------------------ .../Reflection/Type.Operators.cs | 72 -- .../Reflection/Type.Properties.cs | 125 --- src/DotNext.Reflection/Reflection/Type.cs | 168 ---- .../Reflection/TypeExtensions.cs | 28 - .../Reflection/UnaryOperator.cs | 173 ---- .../Runtime/CompilerServices/Awaitable.cs | 102 --- .../Runtime/CompilerServices/Awaiter.cs | 122 --- .../Runtime/CompilerServices/Concept.cs | 55 -- .../CompilerServices/ConceptAttribute.cs | 9 - .../CompilerServices/ConstraintAttribute.cs | 19 - .../CompilerServices/NotifyCompletion.cs | 36 - .../CompilerServices/ReflectionUtils.cs | 138 --- src/DotNext.Tests/DelegateHelpersTests.cs | 22 - src/DotNext.Tests/DisposableTests.cs | 42 - src/DotNext.Tests/DotNext.Tests.csproj | 1 - src/DotNext.Tests/NumberConceptTests.cs | 47 - .../Reflection/DynamicInvocationTests.cs | 190 ---- .../Reflection/ExtensionRegistryTests.cs | 49 -- src/DotNext.Tests/Reflection/OperatorTests.cs | 54 -- .../Reflection/RecordConceptTests.cs | 21 - src/DotNext.Tests/Reflection/RefTests.cs | 13 - .../Reflection/ReflectorTests.cs | 138 --- .../Reflection/TypeExtensionsTests.cs | 10 - src/DotNext.Tests/Reflection/TypeTests.cs | 369 -------- .../CompilerServices/AwaitableTests.cs | 44 - src/DotNext.sln | 2 - 85 files changed, 11366 deletions(-) delete mode 100644 src/DotNext.Benchmarks/Reflection/FieldReflectionBenchmark.cs delete mode 100644 src/DotNext.Benchmarks/Reflection/HashMethodReflectionBenchmark.cs delete mode 100644 src/DotNext.Benchmarks/Reflection/PropertyGetterReflectionBenchmark.cs delete mode 100644 src/DotNext.Benchmarks/Reflection/StringMethodReflectionBenchmark.cs delete mode 100644 src/DotNext.Benchmarks/Reflection/TryParseReflectionBenchmark.cs delete mode 100644 src/DotNext.Reflection/ArrayExtensions.cs delete mode 100644 src/DotNext.Reflection/Assembly.cs delete mode 100644 src/DotNext.Reflection/Disposable.cs delete mode 100644 src/DotNext.Reflection/DotNext.Reflection.csproj delete mode 100644 src/DotNext.Reflection/ExceptionMessages.cs delete mode 100644 src/DotNext.Reflection/ExceptionMessages.restext delete mode 100644 src/DotNext.Reflection/FodyWeavers.xml delete mode 100644 src/DotNext.Reflection/Function.cs delete mode 100644 src/DotNext.Reflection/Number.cs delete mode 100644 src/DotNext.Reflection/Procedure.cs delete mode 100644 src/DotNext.Reflection/Record.cs delete mode 100644 src/DotNext.Reflection/Reflection/AbstractDelegateException.cs delete mode 100644 src/DotNext.Reflection/Reflection/BinaryOperator.cs delete mode 100644 src/DotNext.Reflection/Reflection/BuiltinOperatorInfo.cs delete mode 100644 src/DotNext.Reflection/Reflection/ConstraintViolationException.cs delete mode 100644 src/DotNext.Reflection/Reflection/Constructor.cs delete mode 100644 src/DotNext.Reflection/Reflection/DynamicInvoker.cs delete mode 100644 src/DotNext.Reflection/Reflection/Event.cs delete mode 100644 src/DotNext.Reflection/Reflection/ExtensionRegistry.cs delete mode 100644 src/DotNext.Reflection/Reflection/Field.cs delete mode 100644 src/DotNext.Reflection/Reflection/IConstructor.cs delete mode 100644 src/DotNext.Reflection/Reflection/IEvent.cs delete mode 100644 src/DotNext.Reflection/Reflection/IField.cs delete mode 100644 src/DotNext.Reflection/Reflection/IMember.cs delete mode 100644 src/DotNext.Reflection/Reflection/IMethod.cs delete mode 100644 src/DotNext.Reflection/Reflection/IOperator.cs delete mode 100644 src/DotNext.Reflection/Reflection/IProperty.cs delete mode 100644 src/DotNext.Reflection/Reflection/Indexer.cs delete mode 100644 src/DotNext.Reflection/Reflection/MemberAccessor.cs delete mode 100644 src/DotNext.Reflection/Reflection/MemberCache.cs delete mode 100644 src/DotNext.Reflection/Reflection/Method.cs delete mode 100644 src/DotNext.Reflection/Reflection/MethodLookup.cs delete mode 100644 src/DotNext.Reflection/Reflection/MissingAttributeException.cs delete mode 100644 src/DotNext.Reflection/Reflection/MissingConstructorException.cs delete mode 100644 src/DotNext.Reflection/Reflection/MissingEventException.cs delete mode 100644 src/DotNext.Reflection/Reflection/MissingFieldException.cs delete mode 100644 src/DotNext.Reflection/Reflection/MissingMethodException.cs delete mode 100644 src/DotNext.Reflection/Reflection/MissingOperatorException.cs delete mode 100644 src/DotNext.Reflection/Reflection/MissingPropertyException.cs delete mode 100644 src/DotNext.Reflection/Reflection/Operator.cs delete mode 100644 src/DotNext.Reflection/Reflection/OperatorLookup.cs delete mode 100644 src/DotNext.Reflection/Reflection/Property.cs delete mode 100644 src/DotNext.Reflection/Reflection/Ref.cs delete mode 100644 src/DotNext.Reflection/Reflection/Reflector.DynamicFieldInvoker.cs delete mode 100644 src/DotNext.Reflection/Reflection/Reflector.DynamicMethodInvoker.cs delete mode 100644 src/DotNext.Reflection/Reflection/Reflector.cs delete mode 100644 src/DotNext.Reflection/Reflection/Signature.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Attributes.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Ctors.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Events.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Fields.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Indexers.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Methods.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Operators.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.Properties.cs delete mode 100644 src/DotNext.Reflection/Reflection/Type.cs delete mode 100644 src/DotNext.Reflection/Reflection/TypeExtensions.cs delete mode 100644 src/DotNext.Reflection/Reflection/UnaryOperator.cs delete mode 100644 src/DotNext.Reflection/Runtime/CompilerServices/Awaitable.cs delete mode 100644 src/DotNext.Reflection/Runtime/CompilerServices/Awaiter.cs delete mode 100644 src/DotNext.Reflection/Runtime/CompilerServices/Concept.cs delete mode 100644 src/DotNext.Reflection/Runtime/CompilerServices/ConceptAttribute.cs delete mode 100644 src/DotNext.Reflection/Runtime/CompilerServices/ConstraintAttribute.cs delete mode 100644 src/DotNext.Reflection/Runtime/CompilerServices/NotifyCompletion.cs delete mode 100644 src/DotNext.Reflection/Runtime/CompilerServices/ReflectionUtils.cs delete mode 100644 src/DotNext.Tests/NumberConceptTests.cs delete mode 100644 src/DotNext.Tests/Reflection/DynamicInvocationTests.cs delete mode 100644 src/DotNext.Tests/Reflection/ExtensionRegistryTests.cs delete mode 100644 src/DotNext.Tests/Reflection/OperatorTests.cs delete mode 100644 src/DotNext.Tests/Reflection/RecordConceptTests.cs delete mode 100644 src/DotNext.Tests/Reflection/RefTests.cs delete mode 100644 src/DotNext.Tests/Reflection/ReflectorTests.cs delete mode 100644 src/DotNext.Tests/Reflection/TypeTests.cs delete mode 100644 src/DotNext.Tests/Runtime/CompilerServices/AwaitableTests.cs diff --git a/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj b/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj index 705fadb73..e8d586b82 100644 --- a/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj +++ b/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj @@ -48,7 +48,6 @@ - diff --git a/src/DotNext.Benchmarks/Reflection/FieldReflectionBenchmark.cs b/src/DotNext.Benchmarks/Reflection/FieldReflectionBenchmark.cs deleted file mode 100644 index a12f85796..000000000 --- a/src/DotNext.Benchmarks/Reflection/FieldReflectionBenchmark.cs +++ /dev/null @@ -1,43 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Order; -using System; -using System.Reflection; -using FastMember; - -namespace DotNext.Reflection; - -[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] -[Orderer(SummaryOrderPolicy.FastestToSlowest)] -public class FieldReflectionBenchmark -{ - public class MyType - { - public string Value; - } - - private static readonly FieldInfo ValueField = typeof(MyType).GetField(nameof(MyType.Value)); - private static readonly MyType Instance = new() { Value = "Hello, world!" }; - private static readonly DynamicInvoker DynamicFieldGetter = ValueField.Unreflect(BindingFlags.GetField, false); - private static readonly DynamicInvoker DynamicFieldGetterVolatile = ValueField.Unreflect(BindingFlags.GetField, true); - private static readonly ObjectAccessor FieldAccessor = ObjectAccessor.Create(Instance); - private static readonly MemberGetter StronglyTypedAccessor = ValueField.Unreflect(); - - [Benchmark] - public object GetFieldUsingDynamicInvoker() => DynamicFieldGetter(Instance, Span.Empty); - - [Benchmark] - public object GetFieldUsingDynamicInvokerVolatile() => DynamicFieldGetterVolatile(Instance, Span.Empty); - - [Benchmark] - public object GetFieldUsingFastMemberLibrary() => FieldAccessor[nameof(MyType.Value)]; - - [Benchmark] - public object GetFieldUsingTypedReflection() => StronglyTypedAccessor(Instance); - - [Benchmark] - public object GetFieldDirect() => Instance.Value; - - [Benchmark] - public object GetFieldUsingReflection() => ValueField.GetValue(Instance); -} \ No newline at end of file diff --git a/src/DotNext.Benchmarks/Reflection/HashMethodReflectionBenchmark.cs b/src/DotNext.Benchmarks/Reflection/HashMethodReflectionBenchmark.cs deleted file mode 100644 index fe06bece3..000000000 --- a/src/DotNext.Benchmarks/Reflection/HashMethodReflectionBenchmark.cs +++ /dev/null @@ -1,38 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Order; -using System; -using System.Reflection; -using System.Security.Cryptography; - -namespace DotNext.Reflection; - -[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] -[Orderer(SummaryOrderPolicy.Method)] -public class HashMethodReflectionBenchmark -{ - private static readonly Func ComputeHash = Type.Method.Require(nameof(MD5.ComputeHash)); - private static readonly Function ComputeHashSpecial = Type.RequireMethod<(byte[], int, int), byte[]>(nameof(MD5.ComputeHash)); - private static readonly MethodInfo ComputeHashReflected = typeof(MD5).GetMethod(nameof(MD5.ComputeHash), new[] { typeof(byte[]), typeof(int), typeof(int) }, Array.Empty()); - - private readonly byte[] data = new byte[1024 * 1024]; //1KB - private readonly MD5 hash = MD5.Create(); - - public HashMethodReflectionBenchmark() - { - //generate random data - Random.Shared.NextBytes(data); - } - - [Benchmark] - public byte[] WithoutReflection() => hash.ComputeHash(data, 0, data.Length); - - [Benchmark] - public byte[] WithTypedReflection() => ComputeHash(hash, data, 0, data.Length); - - [Benchmark] - public byte[] WithTypedReflectionSpecial() => ComputeHashSpecial(hash, (data, 0, data.Length)); - - [Benchmark] - public object WithReflection() => ComputeHashReflected.Invoke(hash, new object[] { data, 0, data.Length }); -} \ No newline at end of file diff --git a/src/DotNext.Benchmarks/Reflection/PropertyGetterReflectionBenchmark.cs b/src/DotNext.Benchmarks/Reflection/PropertyGetterReflectionBenchmark.cs deleted file mode 100644 index 76aa5958c..000000000 --- a/src/DotNext.Benchmarks/Reflection/PropertyGetterReflectionBenchmark.cs +++ /dev/null @@ -1,56 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Order; -using FastMember; -using System; -using System.Reflection; - -namespace DotNext.Reflection; - -[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] -[Orderer(SummaryOrderPolicy.Declared)] -public class PropertyGetterReflectionBenchmark -{ - private sealed class IndexOfCalculator - { - private readonly char character; - private readonly int startIndex; - private readonly string source; - - internal IndexOfCalculator(string source, char ch, int startIndex) - { - this.source = source; - character = ch; - this.startIndex = startIndex; - } - - public int IndexOf => source.IndexOf(character, startIndex); - } - - private static readonly IndexOfCalculator IndexOfCalc = new("Hello, world!", 'd', 0); - private static readonly ObjectAccessor Accessor = ObjectAccessor.Create(IndexOfCalc); - private static readonly MethodInfo ReflectedGetter = IndexOfCalc.GetType().GetProperty(nameof(IndexOfCalculator.IndexOf)).GetMethod; - private static readonly MemberGetter StaticallyReflected = Type.Property.RequireGetter(nameof(IndexOfCalculator.IndexOf)); - - private static readonly Function UntypedReflected = ReflectedGetter.Unreflect>(); - - private static readonly DynamicInvoker DynamicAccessor = ReflectedGetter.Unreflect(); - - [Benchmark(Description = "Direct call", Baseline = true)] - public int NoReflection() => IndexOfCalc.IndexOf; - - [Benchmark(Description = "Reflection with DotNext using delegate type MemberGetter")] - public int UseFastTypedReflection() => StaticallyReflected(IndexOfCalc); - - [Benchmark(Description = "Reflection with DotNext using DynamicInvoker")] - public object UseDynamicInvoker() => DynamicAccessor(IndexOfCalc, Array.Empty()); - - [Benchmark(Description = "Reflection with DotNext using delegate type Function")] - public object UseFastUntypedReflection() => UntypedReflected(IndexOfCalc, new ValueTuple()); - - [Benchmark(Description = "ObjectAccess class from FastMember library")] - public object UseObjectAccessor() => Accessor[nameof(IndexOfCalculator.IndexOf)]; - - [Benchmark(Description = ".NET reflection")] - public object UseReflection() => ReflectedGetter.Invoke(IndexOfCalc, Array.Empty()); -} \ No newline at end of file diff --git a/src/DotNext.Benchmarks/Reflection/StringMethodReflectionBenchmark.cs b/src/DotNext.Benchmarks/Reflection/StringMethodReflectionBenchmark.cs deleted file mode 100644 index 63f537ad2..000000000 --- a/src/DotNext.Benchmarks/Reflection/StringMethodReflectionBenchmark.cs +++ /dev/null @@ -1,42 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Order; -using System; -using System.Reflection; - -namespace DotNext.Reflection; - -[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] -[Orderer(SummaryOrderPolicy.Method)] -public class StringMethodReflectionBenchmark -{ - private static readonly Func IndexOf = Type.Method.Require(nameof(string.IndexOf)); - private static readonly Function IndexOfSpecial = Type.RequireMethod<(char, int), int>(nameof(string.IndexOf)); - - private static readonly MethodInfo IndexOfReflected = typeof(string).GetMethod(nameof(string.IndexOf), new[] { typeof(char), typeof(int) }, Array.Empty()); - - private static readonly Function IndexOfSpecialUntyped = IndexOfReflected.Unreflect>(); - - private static readonly DynamicInvoker IndexOfDynamicInvoker = IndexOfReflected.Unreflect(); - - [Params("", "abccdahehkgbe387jwgr", "wfjwkhwfhwjgfkwjggwhjvfkwhwkgwjgbwjbwjbvbwvjwbvwjbvw")] - public string StringValue; - - [Benchmark(Description = "Direct call", Baseline = true)] - public int WithoutReflection() => StringValue.IndexOf('7', 0); - - [Benchmark(Description = "Reflection with DotNext using delegate type Func")] - public int WithTypedFastReflection() => IndexOf(StringValue, '7', 0); - - [Benchmark(Description = "Reflection with DotNext using delegate type Function")] - public int WithTypedFastReflectionSpecial() => IndexOfSpecial(StringValue, ('7', 0)); - - [Benchmark(Description = "Reflection with DotNext using delegate type Function")] - public object WithUntypedFastReflectionSpecial() => IndexOfSpecialUntyped(StringValue, ('7', 0)); - - [Benchmark(Description = "Reflection with DotNext using DynamicInvoker")] - public object WithDynamicInvoker() => IndexOfDynamicInvoker.Invoke(StringValue, '7', 0); - - [Benchmark(Description = ".NET reflection")] - public object WithReflection() => IndexOfReflected.Invoke(StringValue, new object[] { '7', 0 }); -} \ No newline at end of file diff --git a/src/DotNext.Benchmarks/Reflection/TryParseReflectionBenchmark.cs b/src/DotNext.Benchmarks/Reflection/TryParseReflectionBenchmark.cs deleted file mode 100644 index 9f36b0dd8..000000000 --- a/src/DotNext.Benchmarks/Reflection/TryParseReflectionBenchmark.cs +++ /dev/null @@ -1,51 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Order; -using System.Reflection; - -namespace DotNext.Reflection; - -[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] -[Orderer(SummaryOrderPolicy.FastestToSlowest)] -public class TryParseReflectionBenchmark -{ - private delegate bool TryParseDelegate(string text, out decimal result); - - private static readonly MethodInfo ReflectedMethod = typeof(decimal).GetMethod(nameof(decimal.TryParse), new[] { typeof(string), typeof(decimal).MakeByRefType() }); - private static readonly TryParseDelegate StronglyTyped = Type.Method.Get(nameof(decimal.TryParse), MethodLookup.Static); - - private static readonly Function<(string text, Ref result), bool> StronglyTypedSpecial = Type.GetStaticMethod<(string, Ref), bool>(nameof(decimal.TryParse)); - - private static readonly Function<(string text, decimal result), bool> StronglyTypedSpecialUnreflected = ReflectedMethod.Unreflect>(); - - private static readonly Function<(object text, object result), object> UntypedSpecialUnreflected = ReflectedMethod.Unreflect>(); - - private static readonly DynamicInvoker Invoker = ReflectedMethod.Unreflect(); - - private const string StringValue = "748383565500"; - - [Benchmark(Description = "Direct call", Baseline = true)] - public bool NoReflection() => decimal.TryParse(StringValue, out var _); - - [Benchmark(Description = "Reflection with DotNext using DynamicInvoker")] - public object UseDynamicInvoker() - { - (object, object) args = (StringValue, decimal.Zero); - return Invoker(null, args.AsSpan()); - } - - [Benchmark(Description = ".NET reflection")] - public object UseReflection() => ReflectedMethod.Invoke(null, new object[] { StringValue, decimal.Zero }); - - [Benchmark(Description = "Reflection with DotNext using delegate type TryParseDelegate")] - public bool UseStronglyTypedReflection() => StronglyTyped(StringValue, out var result); - - [Benchmark] - public bool UseStronglyTypedSpecialReflection() => StronglyTypedSpecial((StringValue, decimal.Zero)); - - [Benchmark(Description = "Reflection with DotNext using delegate type Function<(string text, decimal result), bool>")] - public bool UseStronglyTypedSpecialUnreflected() => StronglyTypedSpecialUnreflected((StringValue, decimal.Zero)); - - [Benchmark(Description = "Reflection with DotNext using delegate type `Function<(object text, object result), object>`")] - public object UseUntypedSpecialReflection() => UntypedSpecialUnreflected((StringValue, decimal.Zero)); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/ArrayExtensions.cs b/src/DotNext.Reflection/ArrayExtensions.cs deleted file mode 100644 index 87cc573fb..000000000 --- a/src/DotNext.Reflection/ArrayExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace DotNext; - -internal static class ArrayExtensions -{ - internal static bool Take(this T[] array, [MaybeNullWhen(false)] out T first, [MaybeNullWhen(false)] out T second, int startIndex = 0) - where T : notnull - { - if (startIndex + 1 < array.LongLength) - { - first = array[startIndex++]; - second = array[startIndex]; - return true; - } - - first = second = default; - return false; - } - - internal static bool Take(this T[] array, [MaybeNullWhen(false)] out T first, [MaybeNullWhen(false)] out T second, [MaybeNullWhen(false)] out T third, int startIndex = 0) - where T : notnull - { - if (startIndex + 2 < array.LongLength) - { - first = array[startIndex++]; - second = array[startIndex++]; - third = array[startIndex]; - return true; - } - - first = second = third = default; - return false; - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Assembly.cs b/src/DotNext.Reflection/Assembly.cs deleted file mode 100644 index df5e65922..000000000 --- a/src/DotNext.Reflection/Assembly.cs +++ /dev/null @@ -1,4 +0,0 @@ -using System.Runtime.InteropServices; - -[assembly: CLSCompliant(true)] -[assembly: ComVisible(false)] \ No newline at end of file diff --git a/src/DotNext.Reflection/Disposable.cs b/src/DotNext.Reflection/Disposable.cs deleted file mode 100644 index 90014aa45..000000000 --- a/src/DotNext.Reflection/Disposable.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using static System.Linq.Expressions.Expression; - -namespace DotNext; - -using Reflection; -using ConceptAttribute = Runtime.CompilerServices.ConceptAttribute; - -/// -/// Represents dispose pattern as a concept. -/// -/// -/// This concept provides access to Dispose method of type even -/// if it doesn't implement interface directly. -/// -/// A type which implements dispose pattern. -/// Implementing Dispose method -[Concept] -public static class Disposable<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T> -{ - private delegate void DisposeMethod(in T instance); - - private static readonly DisposeMethod DisposeMethodImpl; - - static Disposable() - { - var disposeMethod = typeof(T).GetDisposeMethod(); - if (disposeMethod?.DeclaringType is not null && disposeMethod.ReturnType == typeof(void)) - { - if (disposeMethod.DeclaringType.IsValueType) - { - Disposable.DisposeMethodImpl = disposeMethod.CreateDelegate(); - } - else - { - var instance = Parameter(typeof(T).MakeByRefType(), "this"); - Disposable.DisposeMethodImpl = Lambda(Call(instance, disposeMethod), instance).Compile(); - } - } - else - { - throw new MissingMethodException(typeof(T), nameof(IDisposable.Dispose), typeof(void), Type.EmptyTypes); - } - } - - /// - /// Disposes specified object. - /// - /// An object to dispose passed by reference. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Dispose(in T obj) => DisposeMethodImpl(in obj); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/DotNext.Reflection.csproj b/src/DotNext.Reflection/DotNext.Reflection.csproj deleted file mode 100644 index 50e5c3981..000000000 --- a/src/DotNext.Reflection/DotNext.Reflection.csproj +++ /dev/null @@ -1,73 +0,0 @@ - - - - net6.0 - DotNext - preview - enable - true - false - nullablePublicOnly - true - 4.9.0 - - .NET Foundation and Contributors - .NEXT Family of Libraries - Strongly typed reflection, concept types (type classes), fast reflection - Copyright © .NET Foundation and Contributors - MIT - https://dotnet.github.io/dotNext/ - https://github.com/dotnet/dotNext.git - git - Concept;Reflection;Typeclass - logo.png - https://github.com/dotnet/dotNext/blob/master/CHANGELOG.md - - - - true - - - - bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml - - - - true - none - - - - bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml - true - false - ../dotnext.snk - true - embedded - true - - - - - - - - - - - - - DotNext.ExceptionMessages.resources - - - - True - - - - - - - - - diff --git a/src/DotNext.Reflection/ExceptionMessages.cs b/src/DotNext.Reflection/ExceptionMessages.cs deleted file mode 100644 index 2aae47964..000000000 --- a/src/DotNext.Reflection/ExceptionMessages.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Resources; - -namespace DotNext; - -using static Collections.Generic.Sequence; -using static Resources.ResourceManagerExtensions; - -[ExcludeFromCodeCoverage] -internal static class ExceptionMessages -{ - private static readonly ResourceManager Resources = new("DotNext.ExceptionMessages", Assembly.GetExecutingAssembly()); - - internal static string AbstractDelegate => (string)Resources.Get(); - - internal static string ObjectOfTypeExpected(object? obj, Type t) - => Resources.Get().Format(obj, t.FullName); - - internal static string ReadOnlyField(string fieldName) - => Resources.Get().Format(fieldName); - - internal static string NullFieldValue => (string)Resources.Get(); - - internal static string PropertyWithoutGetter(string propertyName) - => Resources.Get().Format(propertyName); - - internal static string PropertyWithoutSetter(string propertyName) - => Resources.Get().Format(propertyName); - - internal static string ThisParamExpected => (string)Resources.Get(); - - internal static string MissingOperator(TEnum op) - where TEnum : struct, Enum - => Resources.Get().Format(op); - - internal static string MissingAttribute(Type attribute, Type target) - => Resources.Get().Format(attribute.FullName, target.FullName); - - internal static string MissingCtor(Type target, IEnumerable parameters) - => Resources.Get().Format(target.FullName, parameters.ToString(",")); - - internal static string MissingEvent(string eventName, Type handlerType, Type declaringType) - => Resources.Get().Format(eventName, handlerType.FullName, declaringType.FullName); - - internal static string MissingField(string fieldName, Type fieldType, Type declaringType) - => Resources.Get().Format(fieldName, fieldType.FullName, declaringType.FullName); - - internal static string MissingMethod(string methodName, IEnumerable parameters, Type returnType, Type declaringType) - => Resources.Get().Format(methodName, parameters.ToString(","), returnType, declaringType); - - internal static string MissingProperty(string propertyName, Type propertyType, Type declaringType) - => Resources.Get().Format(propertyName, propertyType.FullName, declaringType.FullName); - - internal static string ExtensionMethodExpected(MethodBase method) - => Resources.Get().Format(method.Name); - - internal static string ConceptTypeInvalidAttribution(Type conceptType) - where TAttribute : Attribute - => Resources.Get().Format(conceptType.FullName, typeof(TAttribute).FullName); - - internal static string StaticCtorDetected => (string)Resources.Get(); - - internal static string ModuleMemberDetected(MemberInfo member) - => Resources.Get().Format(member.Name); - - internal static string StaticFieldExpected => (string)Resources.Get(); - - internal static string InstanceFieldExpected => (string)Resources.Get(); - - internal static string AwaiterMustNotBeNull => (string)Resources.Get(); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/ExceptionMessages.restext b/src/DotNext.Reflection/ExceptionMessages.restext deleted file mode 100644 index 81cc09ae1..000000000 --- a/src/DotNext.Reflection/ExceptionMessages.restext +++ /dev/null @@ -1,21 +0,0 @@ -AbstractDelegate=Delegate type should not be abstract -ObjectOfTypeExpected=Object {0} must be of type {1} -ReadOnlyField=Field {0} is read-only -NullFieldValue=Field value cannot be null -PropertyWithoutGetter=Property {0} has no getter -PropertyWithoutSetter=Property {0} has no setter -ThisParamExpected=Delegate type should have THIS parameter -MissingOperator=Operator {0} doesn't exist -MissingAttribute=Attribute {0} is not defined for type {1} -MissingCtor=Type {0} doesn't have constructor with parameters ({1}) -MissingEvent=Event {0} of type {1} doesn't exist in type {2} -MissingField=Field {0} of type {1} doesn't exist in type {2} -MissingMethod=Method {0} with parameters [{1}] and return type {2} doesn't exist in type {3} -MissingProperty=Property {0} of type {1} doesn't exist in type {2} -ExtensionMethodExpected=Method {0} should be an extension method -ConceptTypeInvalidAttribution=Concept type {0} should be marked with {1} -StaticCtorDetected=Unable to reflect static constructor of type or module -ModuleMemberDetected=Unable to reflect module member {0} -StaticFieldExpected=Static field expected -InstanceFieldExpected=Instance field expected -AwaiterMustNotBeNull=Awaiter object must not be null \ No newline at end of file diff --git a/src/DotNext.Reflection/FodyWeavers.xml b/src/DotNext.Reflection/FodyWeavers.xml deleted file mode 100644 index 7a7b2cf0e..000000000 --- a/src/DotNext.Reflection/FodyWeavers.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/src/DotNext.Reflection/Function.cs b/src/DotNext.Reflection/Function.cs deleted file mode 100644 index 0150bc1af..000000000 --- a/src/DotNext.Reflection/Function.cs +++ /dev/null @@ -1,603 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace DotNext; - -/// -/// Represents a static function with arbitrary number of arguments -/// allocated on the stack. -/// -/// Function arguments in the form of public structure fields. -/// Type of structure with function arguments allocated on the stack. -/// Type of function return value. -/// Function return value. -public delegate TResult? Function(in TArgs arguments) - where TArgs : struct; - -/// -/// Represents an instance function with arbitrary number of arguments -/// allocated on the stack. -/// -/// Hidden this parameter. -/// Function arguments in the form of public structure fields. -/// Type of instance to be passed into underlying method. -/// Type of structure with function arguments allocated on the stack. -/// Type of function return value. -/// Function return value. -public delegate TResult? Function([DisallowNull] in T @this, in TArgs arguments) - where TArgs : struct; - -/// -/// Provides extension methods for delegates and . -/// -public static class Function -{ - private sealed class Closure - where TArgs : struct - { - private readonly Function function; - [NotNull] - private readonly T target; - - internal Closure(Function function, [DisallowNull] T target) - { - this.function = function; - this.target = target; - } - - internal TResult? Invoke(in TArgs arguments) => function(target, arguments); - } - - /// - /// Converts into through - /// capturing of the first argument of delegate. - /// - /// Type of instance to be passed into underlying method. - /// Type of structure with function arguments allocated on the stack. - /// Type of function return value. - /// The function to be converted. - /// The first argument to be captured. - /// The function instance. - public static Function Bind(this Function function, [DisallowNull] T @this) - where TArgs : struct - => new Closure(function, @this).Invoke; - - /// - /// Invokes function without throwing exception in case of its failure. - /// - /// The function to invoke. - /// Function arguments in the form of public structure fields. - /// Type of structure with function arguments allocated on the stack. - /// Type of function return value. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Result TryInvoke(this Function function, in TArgs arguments) - where TArgs : struct - { - Result result; - try - { - result = function(in arguments); - } - catch (Exception e) - { - result = new(e); - } - - return result; - } - - /// - /// Invokes function without throwing exception in case of its failure. - /// - /// The function to invoke. - /// Hidden this parameter. - /// Function arguments in the form of public structure fields. - /// Type of instance to be passed into underlying method. - /// Type of structure with function arguments allocated on the stack. - /// Type of function return value. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Result TryInvoke(this Function function, [DisallowNull] in T @this, in TArgs arguments) - where TArgs : struct - { - Result result; - try - { - result = function(in @this, in arguments); - } - catch (Exception e) - { - result = new(e); - } - - return result; - } - - /// - /// Allocates list of arguments on the stack. - /// - /// The type representing list of arguments. - /// The return type of the function. - /// The function instance. - /// Allocated list of arguments. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TArgs ArgList(this Function function) - where TArgs : struct - => new(); - - /// - /// Allocates list of arguments on the stack. - /// - /// Type of explicit this argument. - /// The type representing list of arguments. - /// The return type of the function. - /// The function instance. - /// Allocated list of arguments. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TArgs ArgList(this Function function) - where TArgs : struct - => new(); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance) - => function(in instance, default); - - /// - /// Invokes function. - /// - /// The type of function return value. - /// The function to be invoked. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function) - => function(default); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function, TResult> function, TParam arg) - => function(new ValueTuple(arg)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function, TResult> function, [DisallowNull] in T instance, TParam arg) - => function(in instance, new ValueTuple(arg)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2), TResult> function, T1 arg1, T2 arg2) - => function((arg1, arg2)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// The second function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2) - => function(in instance, (arg1, arg2)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3) - => function(in instance, (arg1, arg2, arg3)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(TParam1, TParam2, TParam3), TResult> function, TParam1 arg1, TParam2 arg2, TParam3 arg3) - => function((arg1, arg2, arg3)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - => function(in instance, (arg1, arg2, arg3, arg4)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2, T3, T4), TResult> function, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - => function((arg1, arg2, arg3, arg4)); - - /// - /// Invokes function. - /// - /// The type of the explicit this. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - => function(in instance, (arg1, arg2, arg3, arg4, arg5)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2, T3, T4, T5), TResult> function, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - => function((arg1, arg2, arg3, arg4, arg5)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - => function(in instance, (arg1, arg2, arg3, arg4, arg5, arg6)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2, T3, T4, T5, T6), TResult> function, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - => function((arg1, arg2, arg3, arg4, arg5, arg6)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - => function(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2, T3, T4, T5, T6, T7), TResult> function, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - => function((arg1, arg2, arg3, arg4, arg5, arg6, arg7)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - => function(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2, T3, T4, T5, T6, T7, T8), TResult> function, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - => function((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of the ninth function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// The ninth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - => function(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of the ninth function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// The ninth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2, T3, T4, T5, T6, T7, T8, T9), TResult> function, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - => function((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)); - - /// - /// Invokes function. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of the ninth function argument. - /// The type of the tenth function argument. - /// The type of function return value. - /// The function to be invoked. - /// Explicit this argument. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// The ninth function argument. - /// The tenth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function function, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - => function(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)); - - /// - /// Invokes function. - /// - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of the ninth function argument. - /// The type of the tenth function argument. - /// The type of function return value. - /// The function to be invoked. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// The ninth function argument. - /// The tenth function argument. - /// Function return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this Function<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), TResult> function, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - => function((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Number.cs b/src/DotNext.Reflection/Number.cs deleted file mode 100644 index 9f83394c8..000000000 --- a/src/DotNext.Reflection/Number.cs +++ /dev/null @@ -1,252 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Runtime.InteropServices; - -namespace DotNext; - -using Reflection; -using Runtime.CompilerServices; - -/// -/// Represents any primitive numeric type. -/// -/// -/// This type demonstrates how to build concept type -/// using method from type . -/// -/// Primitive numeric type. -[CLSCompliant(false)] -[Concept] -[StructLayout(LayoutKind.Auto)] -public readonly struct Number : IEquatable, IEquatable>, IComparable - where T : struct, IConvertible, IComparable, IEquatable, IFormattable -{ - #region Concept Definition - private static readonly Operator UnaryPlus, UnaryMinus; - - private static readonly Operator BinaryPlus, BinaryMinus, Multiply, Divide; - - private static readonly Function<(string text, Ref result), bool> TryParseMethod; - private static readonly Function<(string text, NumberStyles styles, IFormatProvider provider, Ref result), bool> AdvancedTryParseMethod; - - private static readonly Func ParseMethod; - - private static readonly Operator ToStringMethod; - - private static readonly Operator GetHashCodeMethod; - #endregion - - // explicit static ctor sets beforefieldinit flag to false - static Number() - { - UnaryPlus = Type.Operator.Require(UnaryOperator.Plus, OperatorLookup.Predefined); - UnaryMinus = Type.Operator.Require(UnaryOperator.Negate, OperatorLookup.Predefined); - BinaryPlus = Type.Operator.Require(BinaryOperator.Add, OperatorLookup.Predefined); - BinaryMinus = Type.Operator.Require(BinaryOperator.Subtract, OperatorLookup.Predefined); - Multiply = Type.Operator.Require(BinaryOperator.Multiply, OperatorLookup.Predefined); - Divide = Type.Operator.Require(BinaryOperator.Divide, OperatorLookup.Predefined); - TryParseMethod = Type.RequireStaticMethod<(string, Ref), bool>(nameof(int.TryParse)); - AdvancedTryParseMethod = Type.RequireStaticMethod<(string, NumberStyles, IFormatProvider, Ref), bool>(nameof(int.TryParse)); - ParseMethod = Type.Method.RequireStatic(nameof(int.Parse)); - ToStringMethod = Type.Method.Require>(nameof(int.ToString), MethodLookup.Instance); - GetHashCodeMethod = Type.Method.Require>(nameof(int.GetHashCode), MethodLookup.Instance); - } - - private readonly T value; - - /// - /// Initializes a new generic numeric value. - /// - /// Underlying numeric value. - public Number(T value) - => this.value = value; - - /// - /// Determines whether this number is equal to another. - /// - /// Other number to be compared. - /// if this number is equal to the given number; otherwise, . - public bool Equals(T other) => value.Equals(other); - - /// - bool IEquatable>.Equals(Number other) => Equals(other); - - /// - int IComparable.CompareTo(T other) => value.CompareTo(other); - - /// - /// Converts the number into string. - /// - /// The textual representation of the number. - public override string ToString() => ToStringMethod(in value) ?? string.Empty; - - /// - /// Computes hash code of the number. - /// - /// Number hash code. - public override int GetHashCode() => GetHashCodeMethod(in value); - - /// - /// Converts container instance into the underlying numeric type. - /// - /// The container instance to be converted. - public static implicit operator T(Number value) - => value.value; - - /// - /// Arithmetic unary plus operation. - /// - /// Unary plus operand. - /// The result of unary plus operation. - public static Number operator +(Number operand) - => new(UnaryPlus(operand)); - - /// - /// Arithmetic unary minus operation. - /// - /// Unary minus operand. - /// The result of unary minus operation. - public static Number operator -(Number operand) - => new(UnaryMinus(operand)); - - /// - /// Arithmetic addition operation. - /// - /// The left operand. - /// The right operand. - /// The result of addition. - public static Number operator +(Number left, T right) - => new(BinaryPlus(in left.value, in right)); - - /// - /// Arithmetic subtraction operation. - /// - /// The left operand. - /// The right operand. - /// The result of subtraction. - public static Number operator -(Number left, T right) - => new(BinaryMinus(in left.value, in right)); - - /// - /// Arithmetic multiplication operation. - /// - /// The left operand. - /// The right operand. - /// The result of multiplication operation. - public static Number operator *(Number left, T right) - => new(Multiply(in left.value, in right)); - - /// - /// Arithmetic division operation. - /// - /// The left operand. - /// The right operand. - /// The result of division operation. - public static Number operator /(Number left, T right) - => new(Divide(in left.value, in right)); - - /// - /// Performs equality check. - /// - /// The left operand. - /// The right operand. - /// , if two numbers are equal; otherwise, . - public static bool operator ==(Number left, T right) - => left.value.Equals(right); - - /// - /// Performs inequality check. - /// - /// The left operand. - /// The right operand. - /// , if two numbers are not equal; otherwise, . - public static bool operator !=(Number left, T right) - => !left.value.Equals(right); - - /// - /// Determines whether the first number is greater than the second number. - /// - /// The left operand. - /// The right operand. - /// if the first number is greater than the second number; otherwise, . - public static bool operator >(Number left, T right) => left.value.CompareTo(right) > 0; - - /// - /// Determines whether the first number is less than the second number. - /// - /// The left operand. - /// The right operand. - /// if the first number is less than the second number; otherwise, . - public static bool operator <(Number left, T right) => left.value.CompareTo(right) < 0; - - /// - /// Determines whether the first number is greater than or equal to the second number. - /// - /// The left operand. - /// The right operand. - /// if the first number is greater than or equal to the second number; otherwise, . - public static bool operator >=(Number left, T right) => left.value.CompareTo(right) >= 0; - - /// - /// Determines whether the first number is less than or equal to the second number. - /// - /// The left operand. - /// The right operand. - /// if the first number is less than or equal to the second number; otherwise, . - public static bool operator <=(Number left, T right) => left.value.CompareTo(right) < 0; - - /// - /// Determines whether this number is equal to the specified number. - /// - /// The number to compare. - /// , if two numbers are equal; otherwise, . - public override bool Equals([NotNullWhen(true)] object? other) => other switch - { - T number => Equals(number), - Number number => Equals(number), - _ => false, - }; - - /// - /// Converts the string representation of a number to its typed equivalent. - /// - /// A string containing a number to convert. - /// Converted number value. - /// Parsed number. - public static bool TryParse(string text, out Number value) - { - var args = TryParseMethod.ArgList(); - args.text = text; - var success = TryParseMethod(args); - value = new(args.result); - return success; - } - - /// - /// Converts the string representation of a number to its typed equivalent. - /// - /// A string containing a number to convert. - /// Style of the number supplied as a string. - /// An object that supplies culture-specific formatting information. - /// Converted number value. - /// Parsed number. - public static bool TryParse(string text, NumberStyles styles, IFormatProvider provider, out Number value) - { - var args = AdvancedTryParseMethod.ArgList(); - args.provider = provider; - args.styles = styles; - args.text = text; - var success = AdvancedTryParseMethod(args); - value = new(args.result); - return success; - } - - /// - /// Converts the string representation of a number to its typed equivalent. - /// - /// A string containing a number to convert. - /// Parsed number. - /// is . - /// is not in the correct format. - public static Number Parse(string text) => new(ParseMethod(text)); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Procedure.cs b/src/DotNext.Reflection/Procedure.cs deleted file mode 100644 index eb60dbde5..000000000 --- a/src/DotNext.Reflection/Procedure.cs +++ /dev/null @@ -1,475 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace DotNext; - -/// -/// Represents a static procedure with arbitrary number of arguments -/// allocated on the stack. -/// -/// Procedure arguments in the form of public structure fields. -/// Type of structure with procedure arguments allocated on the stack. -public delegate void Procedure(in TArgs arguments) - where TArgs : struct; - -/// -/// Represents an instance procedure with arbitrary number of arguments -/// allocated on the stack. -/// -/// Hidden this parameter. -/// Procedure arguments in the form of public structure fields. -/// Type of instance to be passed into underlying method. -/// Type of structure with procedure arguments allocated on the stack. -public delegate void Procedure([DisallowNull] in T @this, in TArgs arguments) - where TArgs : struct; - -/// -/// Provides extension methods for delegates and . -/// -public static class Procedure -{ - private sealed class Closure - where TArgs : struct - { - private readonly Procedure procedure; - [NotNull] - private readonly T target; - - internal Closure(Procedure procedure, [DisallowNull] T target) - { - this.procedure = procedure; - this.target = target; - } - - internal void Invoke(in TArgs arguments) => procedure(target, arguments); - } - - /// - /// Converts into through - /// capturing of the first argument of delegate. - /// - /// Type of instance to be passed into underlying method. - /// Type of structure with procedure arguments allocated on the stack. - /// The procedure to be converted. - /// The first argument to be captured. - /// The procedure instance. - public static Procedure Bind(this Procedure procedure, [DisallowNull] T @this) - where TArgs : struct - => new Closure(procedure, @this).Invoke; - - /// - /// Allocates list of arguments on the stack. - /// - /// The type representing list of arguments. - /// The procedure instance. - /// Allocated list of arguments. - public static TArgs ArgList(this Procedure procedure) - where TArgs : struct - => new(); - - /// - /// Allocates list of arguments on the stack. - /// - /// Type of explicit this argument. - /// The type representing list of arguments. - /// The procedure instance. - /// Allocated list of arguments. - public static TArgs ArgList(this Procedure procedure) - where TArgs : struct - => new(); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The procedure to be invoked. - /// Explicit this argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance) - => procedure(in instance, default); - - /// - /// Invokes procedure. - /// - /// The function to be invoked. - public static void Invoke(this Procedure procedure) - => procedure(default); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first function argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - public static void Invoke(this Procedure> procedure, [DisallowNull] in T instance, TParam arg) - => procedure(in instance, new ValueTuple(arg)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - public static void Invoke(this Procedure> procedure, T arg) - => procedure(new ValueTuple(arg)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2) - => procedure(in instance, (arg1, arg2)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - public static void Invoke(this Procedure<(T1, T2)> procedure, T1 arg1, T2 arg2) - => procedure((arg1, arg2)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3) - => procedure(in instance, (arg1, arg2, arg3)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3)> procedure, T1 arg1, T2 arg2, T3 arg3) - => procedure((arg1, arg2, arg3)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - => procedure(in instance, (arg1, arg2, arg3, arg4)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3, T4)> procedure, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - => procedure((arg1, arg2, arg3, arg4)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - => procedure(in instance, (arg1, arg2, arg3, arg4, arg5)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3, T4, T5)> procedure, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - => procedure((arg1, arg2, arg3, arg4, arg5)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - => procedure(in instance, (arg1, arg2, arg3, arg4, arg5, arg6)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3, T4, T5, T6)> procedure, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - => procedure((arg1, arg2, arg3, arg4, arg5, arg6)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - => procedure(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3, T4, T5, T6, T7)> procedure, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - => procedure((arg1, arg2, arg3, arg4, arg5, arg6, arg7)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - => procedure(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3, T4, T5, T6, T7, T8)> procedure, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - => procedure((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The type of the ninth procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - /// The ninth procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - => procedure(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The type of the ninth procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - /// The ninth procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3, T4, T5, T6, T7, T8, T9)> procedure, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - => procedure((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)); - - /// - /// Invokes procedure. - /// - /// The type of the explicit this argument. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The type of the ninth procedure argument. - /// The type of the tenth procedure argument. - /// The procedure to be invoked. - /// Explicit this argument. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - /// The ninth procedure argument. - /// The tenth procedure argument. - public static void Invoke(this Procedure procedure, [DisallowNull] in T instance, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - => procedure(in instance, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)); - - /// - /// Invokes procedure. - /// - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The type of the ninth procedure argument. - /// The type of the tenth procedure argument. - /// The procedure to be invoked. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - /// The ninth procedure argument. - /// The tenth procedure argument. - public static void Invoke(this Procedure<(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10)> procedure, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - => procedure((arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Record.cs b/src/DotNext.Reflection/Record.cs deleted file mode 100644 index 222771a86..000000000 --- a/src/DotNext.Reflection/Record.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace DotNext; - -using Reflection; -using Runtime.CompilerServices; - -/// -/// Represents a concept that describes a record class. -/// -/// The candidate type. -/// Records (C# Reference) -[CLSCompliant(false)] -[Concept] -public static class Record - where T : class -{ - private const string CloneMethodName = "$"; - - private static readonly Func CloneMethod; - - static Record() - { - CloneMethod = Type.Method.Require(CloneMethodName); - } - - /// - /// Creates a delegate that can be used to create a fresh copy of the original record. - /// - /// A record of reference type. - /// A factory that can produce fresh copies. - public static Func Bind(T record) - => CloneMethod.Method.CreateDelegate>(record); - - /// - /// Creates a copy of the record. - /// - /// A record of reference type. - /// A copy of the record. - public static T Clone(T record) => CloneMethod(record); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/AbstractDelegateException.cs b/src/DotNext.Reflection/Reflection/AbstractDelegateException.cs deleted file mode 100644 index cd5cc3534..000000000 --- a/src/DotNext.Reflection/Reflection/AbstractDelegateException.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace DotNext.Reflection; - -internal sealed class AbstractDelegateException : GenericArgumentException - where TDelegate : Delegate -{ - internal AbstractDelegateException() - : base(ExceptionMessages.AbstractDelegate, typeof(TDelegate).FullName) - { - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/BinaryOperator.cs b/src/DotNext.Reflection/Reflection/BinaryOperator.cs deleted file mode 100644 index 739cfb550..000000000 --- a/src/DotNext.Reflection/Reflection/BinaryOperator.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System.Diagnostics; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace DotNext.Reflection; - -/// -/// Represents binary operator. -/// -public enum BinaryOperator -{ - /// - /// An addition operation, such as a + b, without overflow checking. - /// - Add = ExpressionType.Add, - - /// - /// An addition operation, such as (a + b), with overflow checking. - /// - AddChecked = ExpressionType.AddChecked, - - /// - /// An subtraction operation, such as (a - b), without overflow checking. - /// - Subtract = ExpressionType.Subtract, - - /// - /// A subtraction operation, such as (a - b), with overflow checking. - /// - SubtractChecked = ExpressionType.SubtractChecked, - - /// - /// a & b - /// - And = ExpressionType.And, - - /// - /// a | b - /// - Or = ExpressionType.Or, - - /// - /// a / b - /// - Divide = ExpressionType.Divide, - - /// - /// A multiply operation, such as (a * b), without overflow checking. - /// - Multiply = ExpressionType.Multiply, - - /// - /// a << b - /// - LeftShift = ExpressionType.LeftShift, - - /// - /// a >> b - /// - RightShift = ExpressionType.RightShift, - - /// - /// A multiply operation, such as (a * b), with overflow checking. - /// - MultiplyChecked = ExpressionType.MultiplyChecked, - - /// - /// a % b - /// - Modulo = ExpressionType.Modulo, - - /// - /// a == b - /// - Equal = ExpressionType.Equal, - - /// - /// a != b - /// - NotEqual = ExpressionType.NotEqual, - - /// - /// a ^ b - /// - Xor = ExpressionType.ExclusiveOr, - - /// - /// a > b - /// - GreaterThan = ExpressionType.GreaterThan, - - /// - /// a >= b - /// - GreaterThanOrEqual = ExpressionType.GreaterThanOrEqual, - - /// - /// a < b - /// - LessThan = ExpressionType.LessThan, - - /// - /// a <= b - /// - LessThanOrEqual = ExpressionType.LessThanOrEqual, - - /// - /// POW(a, b) - /// - Power = ExpressionType.Power, -} - -/// -/// Represents reflected binary operator. -/// -/// The type of the first operand. -/// The type of the second operand. -/// The type of the operator result. -public sealed class BinaryOperator : Operator> -{ - private sealed class Cache : Cache> - { - private protected override BinaryOperator? Create(Operator.Kind kind) => Reflect(kind); - } - - private BinaryOperator(Expression> invoker, BinaryOperator type, MethodInfo? overloaded) - : base(invoker.Compile(), type.ToExpressionType(), overloaded) - { - } - - private protected override Type DeclaringType => typeof(TOperand1); - - /// - /// Invokes binary operator. - /// - /// First operand. - /// Second operand. - /// Result of binary operator. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TResult? Invoke(in TOperand1 first, in TOperand2 second) => Invoker(in first, in second); - - /// - /// Type of operator. - /// - public new BinaryOperator Type => (BinaryOperator)base.Type; - - private static Expression>? MakeBinary(Operator.Kind @operator, Operator.Operand first, Operator.Operand second, out MethodInfo? overloaded) - { - var resultType = typeof(TResult); - - // perform automatic cast from byte/short/ushort/sbyte so binary operators become available for these types - var usePrimitiveCast = resultType.IsPrimitive && first.NormalizePrimitive() && second.NormalizePrimitive(); - tail_call: // C# doesn't support tail calls so replace it with label/goto - overloaded = null; - try - { - var body = @operator.MakeBinary(first, second); - overloaded = body.Method; - return overloaded is null && usePrimitiveCast ? - Expression.Lambda>(Expression.Convert(body, resultType), first.Source, second.Source) : - Expression.Lambda>(body, first.Source, second.Source); - } - catch (ArgumentException e) - { - Debug.WriteLine(e); - return null; - } - catch (InvalidOperationException) - { - if (second.Upcast()) - { - goto tail_call; - } - else if (first.Upcast()) - { - second = second.Source; - goto tail_call; - } - else - { - return null; - } - } - } - - private static BinaryOperator? Reflect(Operator.Kind op) - { - var first = Expression.Parameter(typeof(TOperand1).MakeByRefType(), "first"); - var second = Expression.Parameter(typeof(TOperand2).MakeByRefType(), "second"); - return MakeBinary(op, first, second, out var overloaded) is { } expr - ? new BinaryOperator(expr, op, overloaded) - : null; - } - - private static BinaryOperator? GetOrCreate(Operator.Kind op) => Cache.Of(typeof(TOperand1)).GetOrCreate(op); - - internal static BinaryOperator? GetOrCreate(BinaryOperator @operator, OperatorLookup lookup) => lookup switch - { - OperatorLookup.Predefined => GetOrCreate(new Operator.Kind(@operator, false)), - OperatorLookup.Overloaded => GetOrCreate(new Operator.Kind(@operator, true)), - OperatorLookup.Any => GetOrCreate(new Operator.Kind(@operator, true)) ?? GetOrCreate(new Operator.Kind(@operator, false)), - _ => null, - }; -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/BuiltinOperatorInfo.cs b/src/DotNext.Reflection/Reflection/BuiltinOperatorInfo.cs deleted file mode 100644 index 8ee766058..000000000 --- a/src/DotNext.Reflection/Reflection/BuiltinOperatorInfo.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - -namespace DotNext.Reflection; - -using Enumerable = System.Linq.Enumerable; -using ExpressionType = System.Linq.Expressions.ExpressionType; - -internal sealed class BuiltinOperatorInfo : MemberInfo, IEquatable -{ - private readonly ExpressionType expression; - - internal BuiltinOperatorInfo(Type declaringType, ExpressionType op) - { - DeclaringType = declaringType; - expression = op; - } - - public override Type DeclaringType { get; } - - public override MemberTypes MemberType => MemberTypes.Custom; - - public override string Name => expression.ToString(); - - public override Type? ReflectedType => DeclaringType.ReflectedType; - - public override object[] GetCustomAttributes(bool inherit) => Array.Empty(); - - public override object[] GetCustomAttributes(Type attributeType, bool inherit) => Array.Empty(); - - public override bool IsDefined(Type attributeType, bool inherit) => false; - - public override bool HasSameMetadataDefinitionAs(MemberInfo other) => other is BuiltinOperatorInfo operatorInfo && expression == operatorInfo.expression; - - public override IEnumerable CustomAttributes => Enumerable.Empty(); - - public override IList GetCustomAttributesData() => Array.Empty(); - - public override Module Module => typeof(string).Module; // built-in operators always in core module - - public override int MetadataToken => 0; - - public override string ToString() => expression.ToString(); - - public bool Equals(BuiltinOperatorInfo? other) - => other is not null && expression == other.expression && DeclaringType == other.DeclaringType; - - public override bool Equals([NotNullWhen(true)] object? other) => Equals(other as BuiltinOperatorInfo); - - public override int GetHashCode() => HashCode.Combine(expression, DeclaringType); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/ConstraintViolationException.cs b/src/DotNext.Reflection/Reflection/ConstraintViolationException.cs deleted file mode 100644 index 1d3154ba1..000000000 --- a/src/DotNext.Reflection/Reflection/ConstraintViolationException.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Root type for all exceptions related to generic constraints. -/// -public abstract class ConstraintViolationException : GenericArgumentException -{ - private protected ConstraintViolationException(Type target, string message) - : base(target, message) - { - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Constructor.cs b/src/DotNext.Reflection/Reflection/Constructor.cs deleted file mode 100644 index 2035b9077..000000000 --- a/src/DotNext.Reflection/Reflection/Constructor.cs +++ /dev/null @@ -1,400 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Reflection; - -using Seq = Collections.Generic.Sequence; - -/// -/// Provides constructor definition based on delegate signature. -/// -/// Type of delegate representing constructor of type . -public sealed class Constructor : ConstructorInfo, IConstructor, IEquatable - where TSignature : MulticastDelegate -{ - private const BindingFlags PublicFlags = BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public; - private const BindingFlags NonPublicFlags = BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.NonPublic; - - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot?> CacheSlot = new(); - - private readonly TSignature invoker; - private readonly object ctorInfo; - - private Constructor(ConstructorInfo ctor, Expression invoker) - { - if (ctor is { IsStatic: true } or { DeclaringType: null }) - throw new ArgumentException(ExceptionMessages.StaticCtorDetected, nameof(ctor)); - ctorInfo = ctor; - this.invoker = invoker.Compile(); - } - - private Constructor(ConstructorInfo ctor, IEnumerable args, IEnumerable parameters) - : this(ctor, Expression.Lambda(Expression.New(ctor, args), parameters)) - { - } - - private Constructor(ConstructorInfo ctor, IEnumerable parameters) - : this(ctor, parameters, parameters) - { - } - - private Constructor(Type valueType, IEnumerable? parameters = null) - { - ctorInfo = valueType; - invoker = Expression.Lambda(Expression.Default(valueType), parameters ?? Enumerable.Empty()).Compile(); - } - - /// - /// Extracts delegate which can be used to invoke this constructor. - /// - /// The reflected constructor. - [return: NotNullIfNotNull(nameof(ctor))] - public static implicit operator TSignature?(Constructor? ctor) => ctor?.invoker; - - /// - /// Gets name of the constructor. - /// - public override string Name => ConstructorName; - - /// - ConstructorInfo IMember.Metadata => ctorInfo as ConstructorInfo ?? this; - - /// - TSignature IMember.Invoker => invoker; - - /// - /// Gets the attributes associated with this constructor. - /// - public override MethodAttributes Attributes => (ctorInfo as ConstructorInfo)?.Attributes ?? MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName; - - /// - /// Gets a handle to the internal metadata representation of a constructor. - /// - public override RuntimeMethodHandle MethodHandle => (ctorInfo as MethodBase ?? invoker.Method).MethodHandle; - - /// - /// Gets the class that declares this constructor. - /// - public override Type DeclaringType - { - get - { - switch (ctorInfo) - { - case ConstructorInfo ctor: - Debug.Assert(ctor.DeclaringType is not null); - return ctor.DeclaringType; - case Type vt: - Debug.Assert(vt.IsValueType); - return vt; - default: - return GetType(); - } - } - } - - /// - /// Gets the class object that was used to obtain this instance. - /// - public override Type? ReflectedType => (ctorInfo as MethodBase ?? invoker.Method).ReflectedType; - - /// - /// Gets a value indicating the calling conventions for this constructor. - /// - public override CallingConventions CallingConvention => (ctorInfo as MethodBase ?? invoker.Method).CallingConvention; - - /// - /// Gets a value indicating whether the generic method contains unassigned generic type parameters. - /// - public override bool ContainsGenericParameters => false; - - /// - /// Gets a collection that contains this member's custom attributes. - /// - public override IEnumerable CustomAttributes => GetCustomAttributesData(); - - /// - /// Provides access to the MSIL stream, local variables, and exceptions for the current constructor. - /// - /// An object that provides access to the MSIL stream, local variables, and exceptions for the current constructor. - public override MethodBody? GetMethodBody() => (ctorInfo as MethodBase ?? invoker.Method).GetMethodBody(); - - /// - /// Returns a list of custom attributes that have been applied to the target constructor. - /// - /// The data about the attributes that have been applied to the target constructor. - public override IList GetCustomAttributesData() => (ctorInfo as MethodBase ?? invoker.Method).GetCustomAttributesData(); - - /// - /// Returns the type arguments of a generic method or the type parameters of a generic method definition. - /// - /// The list of generic arguments. - public override Type[] GetGenericArguments() => Type.EmptyTypes; - - /// - /// Gets a value indicating whether the constructor is generic. - /// - public override bool IsGenericMethod => false; - - /// - /// Gets a value indicating whether the constructor is a generic method definition. - /// - public override bool IsGenericMethodDefinition => false; - - /// - /// Gets a value that indicates whether the constructor is security-critical or security-safe-critical at the current trust level, - /// and therefore can perform critical operations. - /// - public override bool IsSecurityCritical => ctorInfo switch - { - MethodBase ctor => ctor.IsSecurityCritical, - Type vt => vt.IsSecurityCritical, - _ => invoker.Method.IsSecurityCritical - }; - - /// - /// Gets a value that indicates whether the constructor is security-safe-critical at the current trust level; that is, - /// whether it can perform critical operations and can be accessed by transparent code. - /// - public override bool IsSecuritySafeCritical => ctorInfo switch - { - MethodBase ctor => ctor.IsSecuritySafeCritical, - Type vt => vt.IsSecuritySafeCritical, - _ => invoker.Method.IsSecuritySafeCritical - }; - - /// - /// Gets a value that indicates whether the current constructor is transparent at the current trust level, - /// and therefore cannot perform critical operations. - /// - public override bool IsSecurityTransparent => ctorInfo switch - { - MethodBase ctor => ctor.IsSecurityTransparent, - Type vt => vt.IsSecurityTransparent, - _ => invoker.Method.IsSecurityTransparent - }; - - /// - /// Always returns . - /// - public override MemberTypes MemberType => MemberTypes.Constructor; - - /// - /// Gets a value that identifies a metadata element. - /// - public override int MetadataToken => (ctorInfo as MethodBase ?? invoker.Method).MetadataToken; - - /// - /// Gets constructor implementation attributes. - /// - public override MethodImplAttributes MethodImplementationFlags => (ctorInfo as MethodBase ?? invoker.Method).MethodImplementationFlags; - - /// - /// Gets the module in which the type that declares the constructor represented by the current instance is defined. - /// - public override Module Module => DeclaringType.Module; - - /// - /// Invokes this constructor. - /// - /// Specifies the type of binding. - /// Defines a set of properties and enables the binding, coercion of argument types, and invocation of members using reflection. - /// A list of constructor arguments. - /// Used to govern the coercion of types. - /// Instantiated object. - public override object Invoke(BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) - => Invoke(null, invokeAttr, binder, parameters, culture); - - /// - /// Gets constructor implementation attributes. - /// - /// Implementation attributes. - public override MethodImplAttributes GetMethodImplementationFlags() => MethodImplementationFlags; - - /// - /// Gets constructor parameters. - /// - /// The array of constructor parameters. - public override ParameterInfo[] GetParameters() => (ctorInfo as MethodBase ?? invoker.Method).GetParameters(); - - /// - /// Invokes this constructor. - /// - /// The object on which to invoke the constructor. - /// Specifies the type of binding. - /// Defines a set of properties and enables the binding, coercion of argument types, and invocation of members using reflection. - /// A list of constructor arguments. - /// Used to govern the coercion of types. - /// Instantiated object. - public override object Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) - => (ctorInfo as MethodBase ?? invoker.Method).Invoke(obj, invokeAttr, binder, parameters, culture)!; - - /// - /// Returns an array of all custom attributes applied to this constructor. - /// - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this constructor. - public override object[] GetCustomAttributes(bool inherit) - => (ctorInfo as MethodBase ?? invoker.Method).GetCustomAttributes(inherit); - - /// - /// Returns an array of all custom attributes applied to this constructor. - /// - /// The type of attribute to search for. Only attributes that are assignable to this type are returned. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this constructor. - public override object[] GetCustomAttributes(Type attributeType, bool inherit) - => (ctorInfo as MethodBase ?? invoker.Method).GetCustomAttributes(attributeType, inherit); - - /// - /// Determines whether one or more attributes of the specified type or of its derived types is applied to this constructor. - /// - /// The type of custom attribute to search for. The search includes derived types. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// if one or more instances of or any of its derived types is applied to this constructor; otherwise, . - public override bool IsDefined(Type attributeType, bool inherit) - => (ctorInfo as MethodBase ?? invoker.Method).IsDefined(attributeType, inherit); - - /// - /// Determines whether this constructor is equal to the given constructor. - /// - /// Other constructor to compare. - /// if this object reflects the same constructor as the specified object; otherwise, . - public bool Equals(ConstructorInfo? other) => other is Constructor ctor ? Equals(ctorInfo, ctor.ctorInfo) : Equals(ctorInfo, other); - - /// - /// Determines whether this constructor is equal to the given constructor. - /// - /// Other constructor to compare. - /// if this object reflects the same constructor as the specified object; otherwise, . - public override bool Equals([NotNullWhen(true)] object? other) => other switch - { - Constructor ctor => Equals(ctorInfo, ctor.ctorInfo), - ConstructorInfo ctor => Equals(ctorInfo, ctor), - TSignature invoker => Equals(this.invoker, invoker), - _ => false, - }; - - /// - /// Returns textual representation of this constructor. - /// - /// The textual representation of this constructor. - public override string? ToString() => (ctorInfo as MethodBase ?? invoker.Method).ToString(); - - /// - /// Computes hash code uniquely identifies the reflected constructor. - /// - /// The hash code of the constructor. - public override int GetHashCode() => ctorInfo.GetHashCode(); - - private static Constructor? Reflect(Type declaringType, Type[] parameters, bool nonPublic) - { - ConstructorInfo? ctor = declaringType.GetConstructor(nonPublic ? NonPublicFlags : PublicFlags, Type.DefaultBinder, parameters, Array.Empty()); - if (ctor is null) - return declaringType.IsValueType && parameters.Length is 0 ? new(declaringType) : null; - return new(ctor, Array.ConvertAll(parameters, Expression.Parameter)); - } - - private static Constructor? Reflect(Type declaringType, Type argumentsType, bool nonPublic) - { - var (parameters, arglist, input) = Signature.Reflect(argumentsType); - ConstructorInfo? ctor = declaringType.GetConstructor(nonPublic ? NonPublicFlags : PublicFlags, Type.DefaultBinder, parameters, Array.Empty()); - - if (ctor is null) - return declaringType.IsValueType && parameters.Length is 0 ? new(declaringType, Seq.Singleton(input)) : null; - return new(ctor, arglist, Seq.Singleton(input)); - } - - private static Constructor? Reflect(bool nonPublic) - { - var delegateType = typeof(TSignature); - if (delegateType.IsGenericInstanceOf(typeof(Function<,>)) && typeof(TSignature).GetGenericArguments().Take(out var argumentsType, out var declaringType)) - { - return Reflect(declaringType, argumentsType, nonPublic); - } - else if (delegateType.IsAbstract) - { - throw new AbstractDelegateException(); - } - else - { - var invokeMethod = DelegateType.GetInvokeMethod(); - return Reflect(invokeMethod.ReturnType, invokeMethod.GetParameterTypes(), nonPublic); - } - } - - private static Constructor? Unreflect(ConstructorInfo ctor, Type argumentsType, Type returnType) - { - Debug.Assert(ctor.DeclaringType is not null); - var (_, arglist, input) = Signature.Reflect(argumentsType); - var prologue = new LinkedList(); - var epilogue = new LinkedList(); - var locals = new LinkedList(); - - // adjust arguments - if (!Signature.NormalizeArguments(ctor.GetParameters(), arglist, locals, prologue, epilogue)) - return null; - Expression body; - - // adjust return type - if (returnType == typeof(void) || returnType.IsAssignableFromWithoutBoxing(ctor.DeclaringType)) - body = Expression.New(ctor, arglist); - else if (returnType == typeof(object)) - body = Expression.Convert(Expression.New(ctor, arglist), returnType); - else - return null; - if (epilogue.Count == 0) - { - epilogue.AddFirst(body); - } - else - { - var returnArg = Expression.Parameter(returnType); - locals.AddFirst(returnArg); - body = Expression.Assign(returnArg, body); - epilogue.AddFirst(body); - epilogue.AddLast(returnArg); - } - - body = prologue.Count is 0 && epilogue.Count is 1 ? epilogue.First!.Value : Expression.Block(locals, prologue.Concat(epilogue)); - return new(ctor, Expression.Lambda(body, input)); - } - - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1013", Justification = "False positive")] - private static Constructor? Unreflect(ConstructorInfo ctor) - { - var delegateType = typeof(TSignature); - if (delegateType.IsAbstract) - throw new AbstractDelegateException(); - - switch (ctor) - { - case Constructor existing: - return existing; - case { IsGenericMethodDefinition: true } or { IsAbstract: true }: - return null; - } - - if (delegateType.IsGenericInstanceOf(typeof(Function<,>)) && delegateType.GetGenericArguments().Take(out var argumentsType, out var returnType)) - return Unreflect(ctor, argumentsType, returnType); - - var invokeMethod = DelegateType.GetInvokeMethod(); - return ctor.SignatureEquals(invokeMethod) && invokeMethod.ReturnType.IsAssignableFrom(ctor.DeclaringType) ? - new(ctor, Array.ConvertAll(ctor.GetParameterTypes(), Expression.Parameter)) : - null; - } - - internal static unsafe Constructor? GetOrCreate(ConstructorInfo ctor) - => ctor.GetUserData().GetOrSet(CacheSlot, ctor, &Unreflect); - - internal static unsafe Constructor? GetOrCreate(bool nonPublic) - { - var type = typeof(T); - var ctor = type.GetUserData().GetOrSet(CacheSlot, nonPublic, &Reflect); - return ctor?.DeclaringType == type ? ctor : null; - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/DynamicInvoker.cs b/src/DotNext.Reflection/Reflection/DynamicInvoker.cs deleted file mode 100644 index 03c12030b..000000000 --- a/src/DotNext.Reflection/Reflection/DynamicInvoker.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Reflection; - -/// -/// Represents invoker of a member. -/// -/// -/// Arguments dependending on the member: -/// -/// -/// Field -/// Read operation doesn't require arguments; Write operation requires single argument with field value. -/// -/// -/// Method -/// Arguments will be passed to the method as-is. -/// -/// -/// -/// Target object; for static members should be . -/// The arguments. -/// The result of member invocation. -public delegate object? DynamicInvoker(object? target, Span args); - -/// -/// Represents various extensions of delegate. -/// -public static class DynamicInvokerExtensions -{ - /// - /// Invokes the delegate. - /// - /// The delegate to invoke. - /// Target object; for static members should be . - /// The result of member invocation. - public static object? Invoke(this DynamicInvoker invoker, object? target) - => invoker(target, Span.Empty); - - /// - /// Invokes the delegate. - /// - /// The delegate to invoke. - /// Target object; for static members should be . - /// The first argument. - /// The result of member invocation. - public static object? Invoke(this DynamicInvoker invoker, object? target, object? arg) - => invoker(target, MemoryMarshal.CreateSpan(ref arg, 1)); - - /// - /// Invokes the delegate. - /// - /// The delegate to invoke. - /// Target object; for static members should be . - /// The first argument. - /// The second argument. - /// The result of member invocation. - public static object? Invoke(this DynamicInvoker invoker, object? target, object? arg1, object? arg2) - { - var args = (arg1, arg2); - return invoker(target, args.AsSpan()); - } - - /// - /// Invokes the delegate. - /// - /// The delegate to invoke. - /// Target object; for static members should be . - /// The first argument. - /// The second argument. - /// The third argument. - /// The result of member invocation. - public static object? Invoke(this DynamicInvoker invoker, object? target, object? arg1, object? arg2, object? arg3) - { - var args = (arg1, arg2, arg3); - return invoker(target, args.AsSpan()); - } - - /// - /// Invokes the delegate. - /// - /// The delegate to invoke. - /// Target object; for static members should be . - /// The first argument. - /// The second argument. - /// The third argument. - /// The fourth argument. - /// The result of member invocation. - public static object? Invoke(this DynamicInvoker invoker, object? target, object? arg1, object? arg2, object? arg3, object? arg4) - { - var args = (arg1, arg2, arg3, arg4); - return invoker(target, args.AsSpan()); - } - - /// - /// Invokes the delegate. - /// - /// The delegate to invoke. - /// Target object; for static members should be . - /// The first argument. - /// The second argument. - /// The third argument. - /// The fourth argument. - /// The fifth argument. - /// The result of member invocation. - public static object? Invoke(this DynamicInvoker invoker, object? target, object? arg1, object? arg2, object? arg3, object? arg4, object? arg5) - { - var args = (arg1, arg2, arg3, arg4, arg5); - return invoker(target, args.AsSpan()); - } - - /// - /// Invokes the delegate. - /// - /// The delegate to invoke. - /// Target object; for static members should be . - /// The first argument. - /// The second argument. - /// The third argument. - /// The fourth argument. - /// The fifth argument. - /// The sixth argument. - /// The result of member invocation. - public static object? Invoke(this DynamicInvoker invoker, object? target, object? arg1, object? arg2, object? arg3, object? arg4, object? arg5, object? arg6) - { - var args = (arg1, arg2, arg3, arg4, arg5, arg6); - return invoker(target, args.AsSpan()); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Event.cs b/src/DotNext.Reflection/Reflection/Event.cs deleted file mode 100644 index 742e6c16f..000000000 --- a/src/DotNext.Reflection/Reflection/Event.cs +++ /dev/null @@ -1,503 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace DotNext.Reflection; - -/// -/// Represents reflected event. -/// -/// A delegate representing event handler. -public class EventBase : EventInfo, IEvent, IEquatable - where THandler : MulticastDelegate -{ - private readonly EventInfo @event; - - private protected EventBase(EventInfo @event) => this.@event = @event; - - private static bool AddOrRemoveHandler(EventInfo @event, object? target, THandler handler, Action modifier) - { - if (@event.AddMethod is { IsStatic: true }) - { - if (target is null) - { - modifier(null, handler); - return true; - } - } - else if (@event.DeclaringType?.IsInstanceOfType(target) ?? false) - { - modifier(target, handler); - return true; - } - - return false; - } - - /// - /// Adds an event handler to an event source. - /// - /// The event source. - /// Encapsulates a method or methods to be invoked when the event is raised by the target. - /// , if arguments are correct; otherwise, . - public virtual bool AddEventHandler(object? target, THandler handler) - => AddOrRemoveHandler(@event, target, handler, @event.AddEventHandler); - - /// - /// Removes an event handler from an event source. - /// - /// The event source. - /// The delegate to be disassociated from the events raised by target. - /// , if arguments are correct; otherwise, . - public virtual bool RemoveEventHandler(object? target, THandler handler) - => AddOrRemoveHandler(@event, target, handler, @event.RemoveEventHandler); - - /// - EventInfo IMember.Metadata => @event; - - /// - /// Gets the class that declares this constructor. - /// - public sealed override Type? DeclaringType => @event.DeclaringType; - - /// - /// Always returns . - /// - public sealed override MemberTypes MemberType => @event.MemberType; - - /// - /// Gets name of the event. - /// - public sealed override string Name => @event.Name; - - /// - /// Gets the class object that was used to obtain this instance. - /// - public sealed override Type? ReflectedType => @event.ReflectedType; - - /// - /// Returns an array of all custom attributes applied to this event. - /// - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this event. - public sealed override object[] GetCustomAttributes(bool inherit) => @event.GetCustomAttributes(inherit); - - /// - /// Returns an array of all custom attributes applied to this event. - /// - /// The type of attribute to search for. Only attributes that are assignable to this type are returned. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this event. - public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => @event.GetCustomAttributes(attributeType, inherit); - - /// - /// Determines whether one or more attributes of the specified type or of its derived types is applied to this event. - /// - /// The type of custom attribute to search for. The search includes derived types. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// if one or more instances of or any of its derived types is applied to this event; otherwise, . - public sealed override bool IsDefined(Type attributeType, bool inherit) => @event.IsDefined(attributeType, inherit); - - /// - /// Gets a value that identifies a metadata element. - /// - public sealed override int MetadataToken => @event.MetadataToken; - - /// - /// Gets the module in which the type that declares the event represented by the current instance is defined. - /// - public sealed override Module Module => @event.Module; - - /// - /// Returns a list of custom attributes that have been applied to the target event. - /// - /// The data about the attributes that have been applied to the target event. - public sealed override IList GetCustomAttributesData() => @event.GetCustomAttributesData(); - - /// - /// Gets a collection that contains this member's custom attributes. - /// - public sealed override IEnumerable CustomAttributes => @event.CustomAttributes; - - /// - /// Gets the attributes associated with this event. - /// - public sealed override EventAttributes Attributes => @event.Attributes; - - /// - /// Gets a value indicating whether the event is multicast. - /// - public sealed override bool IsMulticast => @event.IsMulticast; - - /// - /// Gets the the underlying event-handler delegate associated with this event. - /// - public sealed override Type? EventHandlerType => @event.EventHandlerType; - - /// - /// Gets event subscription method. - /// - public sealed override MethodInfo? AddMethod => @event.AddMethod; - - /// - /// Gets the method that is called when the event is raised, including non-public methods. - /// - public sealed override MethodInfo? RaiseMethod => @event.RaiseMethod; - - /// - /// Gets event unsubscription method. - /// - public sealed override MethodInfo? RemoveMethod => @event.RemoveMethod; - - /// - /// Gets event subscription method. - /// - /// if non-public methods can be returned; otherwise, . - /// Event subscription method. - /// - /// is , the method used to add an event handler delegate is non-public, - /// and the caller does not have permission to reflect on non-public methods. - /// - public sealed override MethodInfo? GetAddMethod(bool nonPublic) => @event.GetAddMethod(nonPublic); - - /// - /// Gets event unsubscription method. - /// - /// if non-public methods can be returned; otherwise, . - /// Event unsubscription method. - /// - /// is , the method used to remove an event handler delegate is non-public, - /// and the caller does not have permission to reflect on non-public methods. - /// - public sealed override MethodInfo? GetRemoveMethod(bool nonPublic) => @event.GetRemoveMethod(nonPublic); - - /// - /// Gets the method that is called when the event is raised. - /// - /// if non-public methods can be returned; otherwise, . - /// Raise method. - /// - /// is , the method is non-public, and the caller does not have permission to reflect on non-public methods. - /// - public sealed override MethodInfo? GetRaiseMethod(bool nonPublic) => @event.GetRaiseMethod(nonPublic); - - /// - /// Returns the methods that have been associated with the event in metadata using the .other directive, specifying whether to include non-public methods. - /// - /// if non-public methods can be returned; otherwise, . - /// An array of event methods. - public sealed override MethodInfo[] GetOtherMethods(bool nonPublic) => @event.GetOtherMethods(); - - /// - /// Determines whether this event is equal to the given event. - /// - /// Other event to compare. - /// if this object reflects the same event as the specified object; otherwise, . - public bool Equals(EventInfo? other) => other is Event @event ? this.@event == @event.@event : this.@event == other; - - /// - /// Determines whether this event is equal to the given event. - /// - /// Other event to compare. - /// if this object reflects the same event as the specified object; otherwise, . - public sealed override bool Equals([NotNullWhen(true)] object? other) => other switch - { - EventBase @event => @event.@event == this.@event, - EventInfo @event => this.@event == @event, - _ => false, - }; - - /// - /// Computes hash code uniquely identifies the reflected event. - /// - /// The hash code of the event. - public sealed override int GetHashCode() => @event.GetHashCode(); - - /// - /// Returns textual representation of this event. - /// - /// The textual representation of this event. - public sealed override string? ToString() => @event.ToString(); -} - -/// -/// Provides typed access to static event declared in type . -/// -/// Type of event handler. -public sealed class Event : EventBase, IEvent - where THandler : MulticastDelegate -{ - private sealed class Cache : MemberCache> - { - private protected override Event? Create(string eventName, bool nonPublic) => Reflect(typeof(T), eventName, nonPublic); - } - - private const BindingFlags PublicFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; - private const BindingFlags NonPublicFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - private readonly Action? addMethod, removeMethod; - - private Event(EventInfo @event) - : base(@event) - { - addMethod = @event.AddMethod?.CreateDelegate>(); - removeMethod = @event.RemoveMethod?.CreateDelegate>(); - } - - /// - /// Adds static event handler. - /// - /// Should be . - /// Encapsulates a method or methods to be invoked when the event is raised by the target. - /// , if is , . - public override bool AddEventHandler(object? target, THandler handler) - { - if (target is null && addMethod is not null) - { - addMethod(handler); - return true; - } - - return false; - } - - /// - /// Removes static event handler. - /// - /// Should be . - /// The delegate to be disassociated from the events raised by target. - /// , if is , . - public override bool RemoveEventHandler(object? target, THandler handler) - { - if (target is null && removeMethod is not null) - { - removeMethod(handler); - return true; - } - - return false; - } - - /// - /// Add event handler. - /// - /// An event handler to add. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEventHandler(THandler handler) => addMethod?.Invoke(handler); - - /// - /// Adds static event handler. - /// - /// Should be . - /// Encapsulates a method or methods to be invoked when the event is raised by the target. - public override void AddEventHandler(object? target, Delegate? handler) - { - if (handler is THandler typedHandler) - AddEventHandler(typedHandler); - else - base.AddEventHandler(target, handler); - } - - /// - /// Remove event handler. - /// - /// An event handler to remove. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveEventHandler(THandler handler) => removeMethod?.Invoke(handler); - - /// - /// Removes static event handler. - /// - /// Should be . - /// The delegate to be disassociated from the events raised by target. - public override void RemoveEventHandler(object? target, Delegate? handler) - { - if (handler is THandler typedHandler) - RemoveEventHandler(typedHandler); - else - base.RemoveEventHandler(target, handler); - } - - /// - /// Returns a delegate which can be used to attach new handlers to the event. - /// - /// Reflected event. - /// The delegate which can be used to attach new handlers to the event. - [return: NotNullIfNotNull(nameof(@event))] - public static Action? operator +(Event? @event) => @event?.addMethod; - - /// - /// Returns a delegate which can be used to detach from the event. - /// - /// Reflected event. - /// The delegate which can be used to detach from the event. - [return: NotNullIfNotNull(nameof(@event))] - public static Action? operator -(Event? @event) => @event?.removeMethod; - - private static Event? Reflect(Type declaringType, string eventName, bool nonPublic) - { - EventInfo? @event = declaringType.GetEvent(eventName, nonPublic ? NonPublicFlags : PublicFlags); - return @event is null ? null : new(@event); - } - - internal static Event? GetOrCreate(string eventName, bool nonPublic) - => Cache.Of>(typeof(T)).GetOrCreate(eventName, nonPublic); -} - -/// -/// Provides typed access to instance event declared in type . -/// -/// Declaring type. -/// Type of event handler. -public sealed class Event : EventBase, IEvent - where THandler : MulticastDelegate -{ - private sealed class Cache : MemberCache> - { - private protected override Event? Create(string eventName, bool nonPublic) => Reflect(eventName, nonPublic); - } - - private const BindingFlags PublicFlags = BindingFlags.Instance | BindingFlags.Public; - private const BindingFlags NonPublicFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - /// - /// Represents event accessor. - /// - /// The event target. - /// The event handler. - public delegate void Accessor([DisallowNull] in T instance, THandler handler); - - private readonly Accessor? addMethod, removeMethod; - - private Event(EventInfo @event) - : base(@event) - { - if (@event.DeclaringType is null) - throw new ArgumentException(ExceptionMessages.ModuleMemberDetected(@event), nameof(@event)); - var instanceParam = Expression.Parameter(@event.DeclaringType.MakeByRefType()); - if (@event.EventHandlerType is null) - { - addMethod = removeMethod = null; - } - else - { - var handlerParam = Expression.Parameter(@event.EventHandlerType); - addMethod = @event.AddMethod is null ? null : CompileAccessor(@event.AddMethod, instanceParam, handlerParam); - removeMethod = @event.RemoveMethod is null ? null : CompileAccessor(@event.RemoveMethod, instanceParam, handlerParam); - } - } - - private static Accessor CompileAccessor(MethodInfo accessor, ParameterExpression instanceParam, ParameterExpression handlerParam) - { - if (accessor.DeclaringType is null) - throw new ArgumentException(ExceptionMessages.ModuleMemberDetected(accessor), nameof(accessor)); - if (accessor.DeclaringType.IsValueType) - return accessor.CreateDelegate(); - return Expression.Lambda( - Expression.Call(instanceParam, accessor, handlerParam), instanceParam, handlerParam).Compile(); - } - - /// - /// Adds an event handler to an event source. - /// - /// The event source. - /// Encapsulates a method or methods to be invoked when the event is raised by the target. - /// , if arguments are correct; otherwise, . - public override bool AddEventHandler(object? target, THandler handler) - { - if (target is T instance && addMethod is not null) - { - addMethod(instance, handler); - return true; - } - - return false; - } - - /// - /// Removes an event handler from an event source. - /// - /// The event source. - /// The delegate to be disassociated from the events raised by target. - /// , if arguments are correct; otherwise, . - public override bool RemoveEventHandler(object? target, THandler handler) - { - if (target is T instance && removeMethod is not null) - { - removeMethod(instance, handler); - return true; - } - - return false; - } - - /// - /// Adds an event handler to an event source. - /// - /// The event source. - /// Encapsulates a method or methods to be invoked when the event is raised by the target. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddEventHandler([DisallowNull] in T target, THandler handler) - => addMethod?.Invoke(in target, handler); - - /// - /// Adds an event handler to an event source. - /// - /// The event source. - /// Encapsulates a method or methods to be invoked when the event is raised by the target. - public override void AddEventHandler(object? target, Delegate? handler) - { - if (target is T typedTarget && handler is THandler typedHandler) - AddEventHandler(typedTarget, typedHandler); - else - base.AddEventHandler(target, handler); - } - - /// - /// Removes an event handler from an event source. - /// - /// The event source. - /// The delegate to be disassociated from the events raised by target. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveEventHandler([DisallowNull] in T target, THandler handler) - => removeMethod?.Invoke(in target, handler); - - /// - /// Removes an event handler from an event source. - /// - /// The event source. - /// The delegate to be disassociated from the events raised by target. - public override void RemoveEventHandler(object? target, Delegate? handler) - { - if (target is T typedTarget && handler is THandler typedHandler) - RemoveEventHandler(typedTarget, typedHandler); - else - base.RemoveEventHandler(target, handler); - } - - /// - /// Returns a delegate which can be used to attach new handlers to the event. - /// - /// Reflected event. - /// The delegate which can be used to attach new handlers to the event. - [return: NotNullIfNotNull(nameof(@event))] - public static Accessor? operator +(Event? @event) => @event?.addMethod; - - /// - /// Returns a delegate which can be used to detach from the event. - /// - /// Reflected event. - /// The delegate which can be used to detach from the event. - [return: NotNullIfNotNull(nameof(@event))] - public static Accessor? operator -(Event? @event) => @event?.removeMethod; - - private static Event? Reflect(string eventName, bool nonPublic) - { - EventInfo? @event = typeof(T).GetEvent(eventName, nonPublic ? NonPublicFlags : PublicFlags); - return @event is null ? null : new(@event); - } - - internal static Event? GetOrCreate(string eventName, bool nonPublic) - => Cache.Of(typeof(T)).GetOrCreate(eventName, nonPublic); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/ExtensionRegistry.cs b/src/DotNext.Reflection/Reflection/ExtensionRegistry.cs deleted file mode 100644 index 289603fff..000000000 --- a/src/DotNext.Reflection/Reflection/ExtensionRegistry.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - -namespace DotNext.Reflection; - -using Seq = Collections.Generic.Sequence; - -/// -/// Represents registry of extension methods that can be registered -/// for the specified type and be available using strongly typed reflection via . -/// -public sealed class ExtensionRegistry : ConcurrentBag -{ - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot InstanceMethods = new(); - - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot StaticMethods = new(); - - private ExtensionRegistry() - { - } - - private static ExtensionRegistry Create() => new(); - - private static IEnumerable GetMethods(IEnumerable types, UserDataSlot registrySlot) - { - foreach (var type in types) - { - foreach (var method in type.GetUserData().Get(registrySlot) ?? Enumerable.Empty()) - yield return method; - } - } - - private static IEnumerable GetStaticMethods(Type target) - => GetMethods(Seq.Singleton(target.NonRefType()), StaticMethods); - - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1013", Justification = "False positive")] - private static IEnumerable GetInstanceMethods(Type target) - { - IEnumerable types; - - switch (target) - { - case { IsValueType: true }: - types = Seq.Singleton(target); - break; - case { IsByRef: true }: - var underlyingType = target.GetElementType(); - Debug.Assert(underlyingType is not null); - types = Seq.Singleton(underlyingType); - break; - default: - types = target.GetBaseTypes(includeTopLevel: true, includeInterfaces: true); - break; - } - - return GetMethods(types, InstanceMethods); - } - - internal static IEnumerable GetMethods(Type target, MethodLookup lookup) => lookup switch - { - MethodLookup.Static => GetStaticMethods(target), - MethodLookup.Instance => GetInstanceMethods(target), - _ => Enumerable.Empty(), - }; - - private static unsafe ExtensionRegistry GetOrCreateRegistry(Type target, MethodLookup lookup) - { - var registrySlot = lookup switch - { - MethodLookup.Instance => InstanceMethods, - MethodLookup.Static => StaticMethods, - _ => throw new ArgumentOutOfRangeException(nameof(lookup)), - }; - - return target.GetUserData().GetOrSet(registrySlot, &Create); - } - - /// - /// Registers static method for the specified type in ad-hoc manner so - /// it will be available using and related methods. - /// - /// The type to be extended with static method. - /// The static method implementation. - public static void RegisterStatic(MethodInfo method) => GetOrCreateRegistry(typeof(T), MethodLookup.Static).Add(method); - - /// - /// Registers static method for the specified type in ad-hoc manner so - /// it will be available using and related methods. - /// - /// The type to be extended with static method. - /// The type of the delegate. - /// The delegate instance representing extension method. - public static void RegisterStatic(TExtension @delegate) - where TExtension : Delegate - => RegisterStatic(@delegate.Method); - - /// - /// Registers extension method as instance method which will be included into strongly typed - /// reflection lookup performed by and related methods. - /// - /// Static method to register. Cannot be . - public static void RegisterInstance(MethodInfo method) - { - var thisParam = method.GetParameterTypes().FirstOrDefault(); - if (!method.IsStatic || thisParam is null) - throw new ArgumentException(ExceptionMessages.ExtensionMethodExpected(method), nameof(method)); - GetOrCreateRegistry(thisParam.NonRefType(), MethodLookup.Instance).Add(method); - } - - /// - /// Registers extension method which will be included into strongly typed - /// reflection lookup performed by - /// or methods. - /// - /// The type of the delegate. - /// The delegate instance representing extension method. - public static void RegisterInstance(TExtension @delegate) - where TExtension : Delegate - => RegisterInstance(@delegate.Method); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Field.cs b/src/DotNext.Reflection/Reflection/Field.cs deleted file mode 100644 index 774e3136b..000000000 --- a/src/DotNext.Reflection/Reflection/Field.cs +++ /dev/null @@ -1,582 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Runtime.CompilerServices; -using static System.Linq.Expressions.Expression; -using static InlineIL.IL; -using static InlineIL.IL.Emit; -using static InlineIL.MethodRef; -using static InlineIL.TypeRef; - -namespace DotNext.Reflection; - -using ReflectionUtils = Runtime.CompilerServices.ReflectionUtils; - -/// -/// Represents reflected field. -/// -/// Type of field value. -public abstract class FieldBase : FieldInfo, IField, IEquatable -{ - private readonly FieldInfo field; - - private protected FieldBase(FieldInfo field) => this.field = field; - - private protected static bool IsVolatile(FieldInfo field) - { - var volatileModifier = typeof(IsVolatile); - foreach (var modified in field.GetRequiredCustomModifiers()) - { - if (modified == volatileModifier) - return true; - } - - return false; - } - - /// - /// Returns the value of a field supported by a given object. - /// - /// The object whose field value will be returned. - /// An object containing the value of the field reflected by this instance. - /// , if field value is obtained successfully; otherwise, . - public abstract bool GetValue(object? obj, out TValue? value); - - /// - /// Sets the value of the field supported by the given object. - /// - /// The object whose field value will be set. - /// The value to assign to the field. - /// , if field value is modified successfully; otherwise, . - public abstract bool SetValue(object? obj, TValue value); - - /// - /// Gets the class that declares this field. - /// - public sealed override Type? DeclaringType => field.DeclaringType; - - /// - /// Always returns . - /// - public sealed override MemberTypes MemberType => field.MemberType; - - /// - /// Gets name of the field. - /// - public sealed override string Name => field.Name; - - /// - /// Gets the class object that was used to obtain this instance. - /// - public sealed override Type? ReflectedType => field.ReflectedType; - - /// - /// Returns an array of all custom attributes applied to this field. - /// - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this field. - public sealed override object[] GetCustomAttributes(bool inherit) => field.GetCustomAttributes(inherit); - - /// - /// Returns an array of all custom attributes applied to this field. - /// - /// The type of attribute to search for. Only attributes that are assignable to this type are returned. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this field. - public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => field.GetCustomAttributes(attributeType, inherit); - - /// - /// Determines whether one or more attributes of the specified type or of its derived types is applied to this field. - /// - /// The type of custom attribute to search for. The search includes derived types. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// if one or more instances of or any of its derived types is applied to this field; otherwise, . - public sealed override bool IsDefined(Type attributeType, bool inherit) => field.IsDefined(attributeType, inherit); - - /// - /// Gets a value that identifies a metadata element. - /// - public sealed override int MetadataToken => field.MetadataToken; - - /// - /// Gets the module in which the type that declares the field represented by the current instance is defined. - /// - public sealed override Module Module => field.Module; - - /// - public sealed override IList GetCustomAttributesData() => field.GetCustomAttributesData(); - - /// - /// Gets a collection that contains this member's custom attributes. - /// - public sealed override IEnumerable CustomAttributes => field.CustomAttributes; - - /// - /// Gets the attributes associated with this field. - /// - public sealed override FieldAttributes Attributes => field.Attributes; - - /// - /// Gets a handle to the internal metadata representation of this field. - /// - public sealed override RuntimeFieldHandle FieldHandle => field.FieldHandle; - - /// - /// Gets type of this field. - /// - public sealed override Type FieldType => field.FieldType; - - /// - /// Gets an array of types that identify the optional custom modifiers of the field. - /// - /// An array of objects that identify the optional custom modifiers of the current field. - public sealed override Type[] GetOptionalCustomModifiers() => field.GetOptionalCustomModifiers(); - - /// - /// Returns a literal value associated with the field by a compiler. - /// - /// The literal value associated with the field. If the literal value is a class type with an element value of zero, - /// the return value is . - public sealed override object? GetRawConstantValue() => field.GetRawConstantValue(); - - /// - /// Gets an array of types that identify the required custom modifiers of the field. - /// - /// An array of objects that identify the required custom modifiers of the current field. - public sealed override Type[] GetRequiredCustomModifiers() => field.GetRequiredCustomModifiers(); - - /// - /// Returns the value of a field supported by a given object. - /// - /// The object whose field value will be returned. - /// An object containing the value of the field reflected by this instance. - public override object? GetValue(object? obj) => field.GetValue(obj); - - /// - /// Returns the value of a field supported by a given object. - /// - /// A managed pointer to a location and a runtime representation of the type that might be stored at that location. - /// An object containing the value of the field reflected by this instance. - [CLSCompliant(false)] - public sealed override object? GetValueDirect(TypedReference obj) => field.GetValueDirect(obj); - - /// - /// Gets a value that indicates whether the field is security-critical or security-safe-critical at the current trust level, - /// and therefore can perform critical operations. - /// - public sealed override bool IsSecurityCritical => field.IsSecurityCritical; - - /// - /// Gets a value that indicates whether the field is security-safe-critical at the current trust level; that is, - /// whether it can perform critical operations and can be accessed by transparent code. - /// - public sealed override bool IsSecuritySafeCritical => field.IsSecuritySafeCritical; - - /// - /// Gets a value that indicates whether the current field is transparent at the current trust level, - /// and therefore cannot perform critical operations. - /// - public sealed override bool IsSecurityTransparent => field.IsSecurityTransparent; - - /// - /// Sets the value of the field supported by the given object. - /// - /// The object whose field value will be returned. - /// The value to assign to the field. - /// The type of binding that is desired. - /// A set of properties that enables the binding, coercion of argument types, and invocation of members through reflection. - /// The software preferences of a particular culture. - public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) - => field.SetValue(obj, value, invokeAttr, binder, culture); - - /// - /// Sets the value of the field supported by the given object. - /// - /// A managed pointer to a location and a runtime representation of the type that might be stored at that location. - /// The value to assign to the field. - [CLSCompliant(false)] - public sealed override void SetValueDirect(TypedReference obj, object value) - => field.SetValueDirect(obj, value); - - /// - FieldInfo IMember.Metadata => field; - - /// - /// Determines whether this field is equal to the given field. - /// - /// Other field to compare. - /// if this object reflects the same field as the specified object; otherwise, . - public bool Equals(FieldInfo? other) => other is FieldBase field ? field.field == this.field : this.field == other; - - /// - /// Computes hash code uniquely identifies the reflected field. - /// - /// The hash code of the field. - public sealed override int GetHashCode() => field.GetHashCode(); - - /// - /// Determines whether this field is equal to the given field. - /// - /// Other field to compare. - /// if this object reflects the same field as the specified object; otherwise, . - public sealed override bool Equals([NotNullWhen(true)] object? other) => other switch - { - FieldBase field => this.field == field.field, - FieldInfo field => this.field == field, - _ => false, - }; - - /// - /// Returns textual representation of this field. - /// - /// The textual representation of this field. - public sealed override string? ToString() => field.ToString(); -} - -/// -/// Provides typed access to instance field declared in type . -/// -/// Declaring type. -/// Type of field value. -public sealed class Field : FieldBase, IField -{ - private delegate ref TValue? Provider(in T instance); - - private sealed class Cache : MemberCache> - { - private protected override Field? Create(string fieldName, bool nonPublic) => Reflect(fieldName, nonPublic); - } - - private const BindingFlags PubicFlags = BindingFlags.Instance | BindingFlags.Public; - private const BindingFlags NonPublicFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot> CacheSlot = new(); - - private readonly MemberGetter getter; - private readonly MemberSetter? setter; - private readonly Provider provider; - - private Field(FieldInfo field) - : base(field) - { - if (field.DeclaringType is null) - throw new ArgumentException(ExceptionMessages.ModuleMemberDetected(field), nameof(field)); - var instanceParam = Parameter(typeof(T).MakeByRefType()); - var isVolatile = IsVolatile(field); - - // TODO: Should be optimized when LINQ Expression will have a support for ref return - provider = Lambda(Call(typeof(Unsafe), nameof(Unsafe.AsRef), new[] { field.FieldType }, Field(instanceParam, field)), instanceParam).Compile(); - - Push(provider); - if (isVolatile) - Ldftn(Method(Type>(), nameof(GetValueVolatile), Type(), Type().MakeByRefType())); - else - Ldftn(Method(Type>(), nameof(GetValue), Type(), Type().MakeByRefType())); - Newobj(Constructor(Type>(), Type(), Type())); - Pop(out MemberGetter getter); - this.getter = getter; - - if (field.IsInitOnly) - { - setter = null; - } - else - { - Push(provider); - if (isVolatile) - Ldftn(Method(Type>(), nameof(SetValueVolatile), Type(), Type().MakeByRefType(), Type())); - else - Ldftn(Method(Type>(), nameof(SetValue), Type(), Type().MakeByRefType(), Type())); - Newobj(Constructor(Type>(), Type(), Type())); - Pop(out MemberSetter setter); - this.setter = setter; - } - } - - private static TValue? GetValue(Provider provider, [DisallowNull] in T instance) => provider(instance); - - private static TValue? GetValueVolatile(Provider provider, [DisallowNull] in T instance) - => ReflectionUtils.VolatileRead(ref provider(instance)); - - private static void SetValue(Provider provider, [DisallowNull] in T instance, TValue value) => provider(instance) = value; - - private static void SetValueVolatile(Provider provider, [DisallowNull] in T instance, TValue value) - => ReflectionUtils.VolatileWrite(ref provider(instance), value); - - /// - /// Obtains field getter in the form of the delegate instance. - /// - /// The reflected field. - [return: NotNullIfNotNull(nameof(field))] - public static implicit operator MemberGetter?(Field? field) => field?.getter; - - /// - /// Obtains field setter in the form of the delegate instance. - /// - /// The reflected field. - public static implicit operator MemberSetter?(Field? field) => field?.setter; - - /// - /// Returns the value of a field supported by a given object. - /// - /// The object whose field value will be returned. - /// An object containing the value of the field reflected by this instance. - /// , if field value is obtained successfully; otherwise, . - public override bool GetValue(object? obj, out TValue? value) - { - if (obj is T instance) - { - value = getter(in instance); - return true; - } - - value = default; - return false; - } - - /// - /// Sets the value of the field supported by the given object. - /// - /// The object whose field value will be set. - /// The value to assign to the field. - /// , if field value is modified successfully; otherwise, . - public override bool SetValue(object? obj, TValue value) - { - if (setter is null) - return false; - if (obj is T instance) - { - setter(in instance, value); - return true; - } - - return false; - } - - /// - /// Returns the value of a field supported by a given object. - /// - /// The object whose field value will be returned. - /// An object containing the value of the field reflected by this instance. - public override object? GetValue(object? obj) - => obj is T instance ? getter(in instance) : throw new ArgumentException(ExceptionMessages.ObjectOfTypeExpected(obj, typeof(T))); - - /// - /// Sets the value of the field supported by the given object. - /// - /// The object whose field value will be returned. - /// The value to assign to the field. - /// The type of binding that is desired. - /// A set of properties that enables the binding, coercion of argument types, and invocation of members through reflection. - /// The software preferences of a particular culture. - public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) - { - if (setter is null) - throw new InvalidOperationException(ExceptionMessages.ReadOnlyField(Name)); - if (obj is not T instance) - throw new ArgumentException(ExceptionMessages.ObjectOfTypeExpected(obj, typeof(T))); - if (value is null) - setter(in instance, FieldType.IsValueType ? throw new ArgumentException(ExceptionMessages.NullFieldValue) : default(TValue)!); - if (value is not TValue typedValue) - throw new ArgumentException(ExceptionMessages.ObjectOfTypeExpected(value, typeof(TValue))); - setter(in instance, typedValue); - } - - /// - /// Gets or sets instance field value. - /// - /// - /// If the underlying field is volatile then you need to use static methods from explicitly to - /// implement volatile semantics. - /// - /// this argument. - public ref TValue? this[[DisallowNull] in T @this] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref provider(@this); - } - - private static Field? Reflect(string fieldName, bool nonPublic) - { - FieldInfo? field = typeof(T).GetField(fieldName, nonPublic ? NonPublicFlags : PubicFlags); - return field is null ? null : new(field); - } - - internal static Field? GetOrCreate(string fieldName, bool nonPublic) - => Cache.Of(typeof(T)).GetOrCreate(fieldName, nonPublic); - - private static Field Unreflect(FieldInfo field) - => field.IsStatic ? throw new ArgumentException(ExceptionMessages.InstanceFieldExpected, nameof(field)) : new(field); - - internal static unsafe Field GetOrCreate(FieldInfo field) => field.GetUserData().GetOrSet(CacheSlot, field, &Unreflect); -} - -/// -/// Provides typed access to static field declared in type . -/// -/// Type of field value. -public sealed class Field : FieldBase, IField -{ - private delegate ref TValue? Provider(); - - private sealed class Cache : MemberCache> - { - private protected override Field? Create(string fieldName, bool nonPublic) => Reflect(typeof(T), fieldName, nonPublic); - } - - private const BindingFlags PubicFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; - private const BindingFlags NonPublicFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot> CacheSlot = new(); - - private readonly MemberGetter getter; - private readonly MemberSetter? setter; - private readonly Provider provider; - - private Field(FieldInfo field) - : base(field) - { - // TODO: Should be optimized when LINQ Expression will have a support for ref return - provider = Lambda(Call(typeof(Unsafe), nameof(Unsafe.AsRef), new[] { field.FieldType }, Field(null, field))).Compile(); - var isVolatile = IsVolatile(field); - - Push(provider); - if (isVolatile) - Ldftn(Method(Type>(), nameof(GetValueVolatile), Type())); - else - Ldftn(Method(Type>(), nameof(GetValue), Type())); - Newobj(Constructor(Type>(), Type(), Type())); - Pop(out MemberGetter getter); - this.getter = getter; - - if (field.IsInitOnly) - { - setter = null; - } - else - { - Push(provider); - if (isVolatile) - Ldftn(Method(Type>(), nameof(SetValueVolatile), Type(), Type())); - else - Ldftn(Method(Type>(), nameof(SetValue), Type(), Type())); - Newobj(Constructor(Type>(), Type(), Type())); - Pop(out MemberSetter setter); - this.setter = setter; - } - } - - private static TValue? GetValue(Provider provider) => provider(); - - private static TValue? GetValueVolatile(Provider provider) => ReflectionUtils.VolatileRead(ref provider()); - - private static void SetValue(Provider provider, TValue value) => provider() = value; - - private static void SetValueVolatile(Provider provider, TValue value) => ReflectionUtils.VolatileWrite(ref provider(), value); - - /// - /// Obtains field getter in the form of the delegate instance. - /// - /// The reflected field. - [return: NotNullIfNotNull(nameof(field))] - public static implicit operator MemberGetter?(Field? field) => field?.getter; - - /// - /// Obtains field setter in the form of the delegate instance. - /// - /// The reflected field. - public static implicit operator MemberSetter?(Field? field) => field?.setter; - - /// - /// Returns the value of a field supported by a given object. - /// - /// Must be . - /// An object containing the value of the field reflected by this instance. - /// , if field value is obtained successfully; otherwise, . - public override bool GetValue(object? obj, out TValue? value) - { - if (obj is null) - { - value = getter(); - return true; - } - - value = default; - return false; - } - - /// - /// Sets the value of the field supported by the given object. - /// - /// Must be . - /// The value to assign to the field. - /// , if field value is modified successfully; otherwise, . - public override bool SetValue(object? obj, TValue value) - { - if (setter is null) - return false; - if (obj is null) - { - setter(value); - return true; - } - - return false; - } - - /// - /// Returns the value of a field supported by a given object. - /// - /// Must be . - /// An object containing the value of the field reflected by this instance. - public override object? GetValue(object? obj) => getter(); - - /// - /// Sets the value of the field supported by the given object. - /// - /// Must be . - /// The value to assign to the field. - /// The type of binding that is desired. - /// A set of properties that enables the binding, coercion of argument types, and invocation of members through reflection. - /// The software preferences of a particular culture. - public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, CultureInfo? culture) - { - if (setter is null) - throw new InvalidOperationException(ExceptionMessages.ReadOnlyField(Name)); - if (value is null) - setter(FieldType.IsValueType ? throw new ArgumentException(ExceptionMessages.NullFieldValue) : default(TValue)!); - if (value is not TValue typedValue) - throw new ArgumentException(ExceptionMessages.ObjectOfTypeExpected(value, typeof(TValue))); - setter(typedValue); - } - - /// - /// Obtains managed pointer to the static field. - /// - /// - /// If the underlying field is volatile then you need to use static methods from explicitly to - /// implement volatile semantics. - /// - /// The managed pointer to the static field. - public ref TValue? Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref provider()!; - } - - private static Field? Reflect(Type declaringType, string fieldName, bool nonPublic) - { - FieldInfo? field = declaringType.GetField(fieldName, nonPublic ? NonPublicFlags : PubicFlags); - return field is null ? null : new(field); - } - - private static Field Unreflect(FieldInfo field) - => field.IsStatic ? new(field) : throw new ArgumentException(ExceptionMessages.StaticFieldExpected, nameof(field)); - - internal static unsafe Field GetOrCreate(FieldInfo field) => field.GetUserData().GetOrSet(CacheSlot, field, &Unreflect); - - internal static Field? GetOrCreate(string fieldName, bool nonPublic) - => Cache.Of>(typeof(T)).GetOrCreate(fieldName, nonPublic); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/IConstructor.cs b/src/DotNext.Reflection/Reflection/IConstructor.cs deleted file mode 100644 index a5956b36d..000000000 --- a/src/DotNext.Reflection/Reflection/IConstructor.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Represents typed access to type constructor. -/// -/// Type of delegate representing constructor signature. -public interface IConstructor : IMethod - where TSignature : Delegate -{ -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/IEvent.cs b/src/DotNext.Reflection/Reflection/IEvent.cs deleted file mode 100644 index cc01030b2..000000000 --- a/src/DotNext.Reflection/Reflection/IEvent.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Represents reflected event. -/// -public interface IEvent : IMember -{ -} - -/// -/// Represents static event. -/// -/// Type of event handler. -public interface IEvent : IEvent - where THandler : MulticastDelegate -{ - /// - /// Add event handler. - /// - /// An event handler to add. - void AddEventHandler(THandler handler); - - /// - /// Remove event handler. - /// - /// An event handler to remove. - void RemoveEventHandler(THandler handler); -} - -/// -/// Represents instance event. -/// -/// Type of event declaring type. -/// Type of event handler. -public interface IEvent : IEvent - where THandler : MulticastDelegate -{ - /// - /// Add event handler. - /// - /// Object with declared event. - /// An event handler to add. - void AddEventHandler([DisallowNull] in T instance, THandler handler); - - /// - /// Remove event handler. - /// - /// Object with declared event. - /// An event handler to remove. - void RemoveEventHandler([DisallowNull] in T instance, THandler handler); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/IField.cs b/src/DotNext.Reflection/Reflection/IField.cs deleted file mode 100644 index a1b17f8a1..000000000 --- a/src/DotNext.Reflection/Reflection/IField.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Represents reflected field. -/// -public interface IField : IMember -{ - /// - /// Indicates that field is read-only. - /// - bool IsReadOnly => Metadata is { IsInitOnly: true } or { IsLiteral: true }; -} - -/// -/// Represents static field. -/// -/// Type of field . -public interface IField : IField -{ - /// - /// Obtains managed pointer to the static field. - /// - /// The managed pointer to the static field. - ref TValue? Value { get; } -} - -/// -/// Represents instance field. -/// -/// Field declaring type. -/// Type of field. -public interface IField : IField -{ - /// - /// Obtains managed pointer to the field. - /// - /// A reference to this parameter. - /// The managed pointer to the instance field. - ref TValue? this[[DisallowNull] in T @this] { get; } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/IMember.cs b/src/DotNext.Reflection/Reflection/IMember.cs deleted file mode 100644 index 0c7bd8a23..000000000 --- a/src/DotNext.Reflection/Reflection/IMember.cs +++ /dev/null @@ -1,684 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace DotNext.Reflection; - -/// -/// Basic interface for all reflected members. -/// -/// Type of reflected member. -public interface IMember : ICustomAttributeProvider - where TMember : MemberInfo -{ - /// - /// Name of member. - /// - string Name { get; } - - /// - /// Member metadata. - /// - TMember Metadata { get; } -} - -/// -/// Represents callable program element. -/// -/// Type of reflected member. -/// Type of delegate. -public interface IMember : IMember, ISupplier - where TMember : MemberInfo - where TInvoker : Delegate -{ - /// - /// Gets delegate that can be used to invoke member. - /// - TInvoker Invoker { get; } - - /// - TInvoker ISupplier.Invoke() => Invoker; -} - -/// -/// Provides extension methods for interface or . -/// -public static class Member -{ - /// - /// Invokes member. - /// - /// The type of the member. - /// The type representing invocation arguments. - /// The return type. - /// Callable member. - /// Invocation arguments placed onto stack. - /// Invocation result. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this IMember> member, [DisallowNull] in TArgs arguments) - where TMember : MemberInfo - where TArgs : struct - => member.Invoker(in arguments); - - /// - /// Invokes instance member. - /// - /// The type of the member. - /// The type whose member will be invoked. - /// The type representing invocation arguments. - /// The return type. - /// Callable member. - /// The object whose member will be invoked. - /// Invocation arguments placed onto stack. - /// Invocation result. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this IMember> member, [DisallowNull] in T @this, in TArgs arguments) - where TMember : MemberInfo - where TArgs : struct - => member.Invoker(in @this, in arguments); - - /// - /// Invokes instance member without arguments. - /// - /// The type of the member. - /// The type of the instance. - /// The return type. - /// Callable member. - /// this argument. - /// Invocation result. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this IMember> member, [DisallowNull] in T @this) - where TMember : MemberInfo - => member.Invoke(in @this, default); - - /// - /// Invokes member without arguments. - /// - /// The type of the member. - /// The return type. - /// Callable member. - /// Invocation result. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? Invoke(this IMember> member) - where TMember : MemberInfo - => member.Invoke(default); - - /// - /// Allocates uninitialized structure for placing arguments. - /// - /// The type of the member. - /// The type of the instance. - /// The type representing invocation arguments. - /// The return type. - /// Callable member. - /// Allocated structure for placing arguments. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TArgs ArgList(this IMember> member) - where TMember : MemberInfo - where TArgs : struct - => member.Invoker.ArgList(); - - /// - /// Allocates uninitialized structure for placing arguments. - /// - /// Callable member. - /// Type of callable member. - /// Type of arguments list. - /// Type of function result. - /// Allocated list of arguments. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TArgs ArgList(this IMember> member) - where TMember : MemberInfo - where TArgs : struct - => member.Invoker.ArgList(); - - /// - /// Allocates uninitialized structure for placing arguments. - /// - /// The type of the member. - /// The type of the instance. - /// Type of arguments list. - /// Callable member. - /// Allocated list of arguments. - public static TArgs ArgList(this IMember> member) - where TMember : MemberInfo - where TArgs : struct - => member.Invoker.ArgList(); - - /// - /// Allocates uninitialized structure for placing arguments. - /// - /// The type of the member. - /// Type of arguments list. - /// Callable member. - /// Allocated list of arguments. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TArgs ArgList(this IMember> member) - where TMember : MemberInfo - where TArgs : struct - => member.Invoker.ArgList(); - - #region Functions - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// Type of function result. - /// Callable member. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member) - where TMember : MemberInfo - => member.Invoker(); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, TParam arg) - where TMember : MemberInfo - => member.Invoker(arg); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2) - where TMember : MemberInfo - => member.Invoker(arg1, arg2); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of the ninth function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// The ninth function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - - /// - /// Invokes member as function. - /// - /// The type of the member. - /// The type of the first function argument. - /// The type of the second function argument. - /// The type of the third function argument. - /// The type of the fourth function argument. - /// The type of the fifth function argument. - /// The type of the sixth function argument. - /// The type of the seventh function argument. - /// The type of the eighth function argument. - /// The type of the ninth function argument. - /// The type of the tenth function argument. - /// Type of function result. - /// Callable member. - /// The first function argument. - /// The second function argument. - /// The third function argument. - /// The fourth function argument. - /// The fifth function argument. - /// The sixth function argument. - /// The seventh function argument. - /// The eighth function argument. - /// The ninth function argument. - /// The tenth function argument. - /// Return value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - #endregion - - #region Actions - - /// - /// Invokes member as action. - /// - /// The type of the member. - /// Callable member. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember member) - where TMember : MemberInfo - => member.Invoker(); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// Callable member. - /// The first procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, TParam arg) - where TMember : MemberInfo - => member.Invoker(arg); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2) - where TMember : MemberInfo - => member.Invoker(arg1, arg2); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The type of the ninth procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - /// The ninth procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - - /// - /// Invokes member as procedure. - /// - /// The type of the member. - /// The type of the first procedure argument. - /// The type of the second procedure argument. - /// The type of the third procedure argument. - /// The type of the fourth procedure argument. - /// The type of the fifth procedure argument. - /// The type of the sixth procedure argument. - /// The type of the seventh procedure argument. - /// The type of the eighth procedure argument. - /// The type of the ninth procedure argument. - /// The type of the tenth procedure argument. - /// Callable member. - /// The first procedure argument. - /// The second procedure argument. - /// The third procedure argument. - /// The fourth procedure argument. - /// The fifth procedure argument. - /// The sixth procedure argument. - /// The seventh procedure argument. - /// The eighth procedure argument. - /// The ninth procedure argument. - /// The tenth procedure argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) - where TMember : MemberInfo - => member.Invoker(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - #endregion - - #region Members - - /// - /// Gets property or field value. - /// - /// The type of the member. - /// The type of the member value. - /// The property or field. - /// The member value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TValue? Invoke(this IMember> member) - where TMember : MemberInfo - => member.Invoker(); - - /// - /// Gets property or field value. - /// - /// The type of the member. - /// The type whose property value will be returned. - /// The type of the member value. - /// The property or field. - /// The object whose property or field value will be returned. - /// The member value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TValue? Invoke(this IMember> member, [DisallowNull] in T @this) - where TMember : MemberInfo - => member.Invoker(@this); - - /// - /// Sets property or field. - /// - /// The type of the member. - /// The type of the member value. - /// The property or field. - /// The new value of the field or property. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, TValue value) - where TMember : MemberInfo - => member.Invoker(value); - - /// - /// Sets property or field. - /// - /// The type of the member. - /// The type whose property or field value will be set. - /// The type of the member value. - /// The property or field. - /// The object whose property or field value will be set. - /// The new value of the field or property. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Invoke(this IMember> member, [DisallowNull] in T @this, TValue value) - where TMember : MemberInfo - => member.Invoker(@this, value); - - #endregion -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/IMethod.cs b/src/DotNext.Reflection/Reflection/IMethod.cs deleted file mode 100644 index b5b0cac35..000000000 --- a/src/DotNext.Reflection/Reflection/IMethod.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Represents reflected statically typed method. -/// -/// The type of the method. -/// Type of delegate describing method signature. -public interface IMethod : IMember - where TMethod : MethodBase - where TSignature : Delegate -{ -} - -/// -/// Represents regular method. -/// -/// Type of delegate describing method signature. -public interface IMethod : IMethod - where TSignature : Delegate -{ -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/IOperator.cs b/src/DotNext.Reflection/Reflection/IOperator.cs deleted file mode 100644 index 8b85337ad..000000000 --- a/src/DotNext.Reflection/Reflection/IOperator.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Represents operator. -/// -/// Type of delegate describing signature of operator. -public interface IOperator : IMember - where TSignature : Delegate -{ - /// - /// Gets type of operator. - /// - ExpressionType Type { get; } - - /// - object[] ICustomAttributeProvider.GetCustomAttributes(bool inherit) => Metadata.GetCustomAttributes(inherit); - - /// - object[] ICustomAttributeProvider.GetCustomAttributes(Type attributeType, bool inherit) => Metadata.GetCustomAttributes(attributeType, inherit); - - /// - bool ICustomAttributeProvider.IsDefined(Type attributeType, bool inherit) => Metadata.IsDefined(attributeType, inherit); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/IProperty.cs b/src/DotNext.Reflection/Reflection/IProperty.cs deleted file mode 100644 index 62d1aba3e..000000000 --- a/src/DotNext.Reflection/Reflection/IProperty.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Represents reflected property. -/// -public interface IProperty : IMember -{ - /// - /// Gets a value indicating whether the property can be read. - /// - bool CanRead { get; } - - /// - /// Gets a value indicating whether the property can be written to. - /// - bool CanWrite { get; } -} - -/// -/// Represents static property. -/// -/// Type of property value. -public interface IProperty : IProperty -{ - /// - /// Gets or sets property value. - /// - [MaybeNull] - TValue Value { get; set; } -} - -/// -/// Represents instance property. -/// -/// Property declaring type. -/// Type of property value. -public interface IProperty : IProperty -{ - /// - /// Gets or sets property value. - /// - /// The object whose property value will be set or returned. - /// Property value. - [MaybeNull] - TValue this[[DisallowNull] in T @this] { get; set; } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Indexer.cs b/src/DotNext.Reflection/Reflection/Indexer.cs deleted file mode 100644 index 374690870..000000000 --- a/src/DotNext.Reflection/Reflection/Indexer.cs +++ /dev/null @@ -1,514 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace DotNext.Reflection; - -/// -/// Represents reflected indexer property. -/// -/// The type representing indexer arguments. -/// The type of the property. -public abstract class IndexerBase : PropertyInfo, IProperty, IEquatable - where TIndicies : struct -{ - private readonly PropertyInfo property; - - private protected IndexerBase(PropertyInfo property) - => this.property = property; - - /// - /// Returns the property value of a specified object with index values for indexed properties. - /// - /// The object whose property value will be returned. - /// Index values for indexed properties. - /// The property value of the specified object. - public override object? GetValue(object? obj, object?[]? index) => property.GetValue(obj, index); - - /// - /// Sets the property value of a specified object with optional index values for index properties. - /// - /// The object whose property value will be set. - /// The new property value. - /// The property value of the specified object. - public override void SetValue(object? obj, object? value, object?[]? index) => property.SetValue(obj, value, index); - - /// - /// Gets name of the property. - /// - public sealed override string Name => property.Name; - - /// - /// Gets a value indicating whether the property can be read. - /// - public sealed override bool CanRead => property.CanRead; - - /// - /// Gets a value indicating whether the property can be written to. - /// - public sealed override bool CanWrite => property.CanWrite; - - /// - /// Gets the get accessor for this property. - /// - public sealed override MethodInfo? GetMethod => property.GetMethod; - - /// - /// Gets the attributes for this property. - /// - public sealed override PropertyAttributes Attributes => property.Attributes; - - /// - /// Gets the type of this property. - /// - public sealed override Type PropertyType => property.PropertyType; - - /// - /// Gets the set accessor for this property. - /// - public sealed override MethodInfo? SetMethod => property.SetMethod; - - /// - /// Returns an array whose elements reflect the public and, if specified, non-public get and set accessors of the - /// property reflected by the current instance. - /// - /// Indicates whether non-public methods should be returned in the returned array. - /// An array whose elements reflect the get and set accessors of the property reflected by the current instance. - public sealed override MethodInfo[] GetAccessors(bool nonPublic) => property.GetAccessors(nonPublic); - - /// - /// Returns a literal value associated with the property by a compiler. - /// - /// The literal value associated with the property. - public sealed override object? GetConstantValue() => property.GetConstantValue(); - - /// - /// Returns the public or non-public get accessor for this property. - /// - /// Indicates whether a non-public get accessor should be returned. - /// The object representing the get accessor for this property. - public sealed override MethodInfo? GetGetMethod(bool nonPublic) => property.GetGetMethod(nonPublic); - - /// - /// Returns an array of all the index parameters for the property. - /// - /// An array containing the parameters for the indexes. - public sealed override ParameterInfo[] GetIndexParameters() => property.GetIndexParameters(); - - /// - /// Returns an array of types representing the optional custom modifiers of the property. - /// - /// An array of objects that identify the optional custom modifiers of the current property. - public sealed override Type[] GetOptionalCustomModifiers() => property.GetOptionalCustomModifiers(); - - /// - /// Returns a literal value associated with the property by a compiler. - /// - /// An object that contains the literal value associated with the property. - public sealed override object? GetRawConstantValue() => property.GetRawConstantValue(); - - /// - /// Returns an array of types representing the required custom modifiers of the property. - /// - /// An array of objects that identify the required custom modifiers of the current property. - public sealed override Type[] GetRequiredCustomModifiers() => property.GetRequiredCustomModifiers(); - - /// - /// Gets the set accessor for this property. - /// - /// Indicates whether a non-public set accessor should be returned. - /// The object representing the set accessor for this property. - public sealed override MethodInfo? GetSetMethod(bool nonPublic) => property.GetSetMethod(nonPublic); - - /// - /// Returns the property value of a specified object with index values for indexed properties. - /// - /// The object whose property value will be returned. - /// Specifies the type of binding. - /// Defines a set of properties and enables the binding, coercion of argument types, and invocation of members using reflection. - /// Index values for indexed properties. - /// Used to govern the coercion of types. - /// The property value of the specified object. - public override object? GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) - => property.GetValue(obj, invokeAttr, binder, index, culture); - - /// - /// Sets the property value for a specified object that has the specified binding, index, and culture-specific information. - /// - /// The object whose property value will be set. - /// The new value of the property. - /// Specifies the type of binding. - /// Defines a set of properties and enables the binding, coercion of argument types, and invocation of members using reflection. - /// Index values for indexed properties. - /// Used to govern the coercion of types. - public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) - => property.SetValue(obj, value, invokeAttr, binder, index, culture); - - /// - /// Gets the class that declares this property. - /// - public sealed override Type? DeclaringType => property.DeclaringType; - - /// - /// Always returns . - /// - public sealed override MemberTypes MemberType => property.MemberType; - - /// - /// Gets the class object that was used to obtain this instance. - /// - public sealed override Type? ReflectedType => property.ReflectedType; - - /// - /// Returns an array of all custom attributes applied to this property. - /// - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this property. - public sealed override object[] GetCustomAttributes(bool inherit) => property.GetCustomAttributes(inherit); - - /// - /// Returns an array of all custom attributes applied to this property. - /// - /// The type of attribute to search for. Only attributes that are assignable to this type are returned. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this property. - public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => property.GetCustomAttributes(attributeType, inherit); - - /// - /// Determines whether one or more attributes of the specified type or of its derived types is applied to this property. - /// - /// The type of custom attribute to search for. The search includes derived types. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// if one or more instances of or any of its derived types is applied to this property; otherwise, . - public sealed override bool IsDefined(Type attributeType, bool inherit) => property.IsDefined(attributeType, inherit); - - /// - /// Gets a value that identifies a metadata element. - /// - public sealed override int MetadataToken => property.MetadataToken; - - /// - /// Gets the module in which the type that declares the property represented by the current instance is defined. - /// - public sealed override Module Module => property.Module; - - /// - /// Returns a list of custom attributes that have been applied to the target property. - /// - /// The data about the attributes that have been applied to the target property. - public sealed override IList GetCustomAttributesData() => property.GetCustomAttributesData(); - - /// - /// Gets a collection that contains this member's custom attributes. - /// - public sealed override IEnumerable CustomAttributes => property.CustomAttributes; - - /// - PropertyInfo IMember.Metadata => property; - - /// - /// Determines whether this property is equal to the given property. - /// - /// Other property to compare. - /// if this object reflects the same property as the specified object; otherwise, . - public bool Equals(PropertyInfo? other) => other is IndexerBase property ? property.property == this.property : this.property == other; - - /// - /// Determines whether this property is equal to the given property. - /// - /// Other property to compare. - /// if this object reflects the same property as the specified object; otherwise, . - public override bool Equals([NotNullWhen(true)] object? other) => other switch - { - IndexerBase property => this.property == property.property, - PropertyInfo property => this.property == property, - _ => false, - }; - - /// - /// Computes hash code uniquely identifies the reflected property. - /// - /// The hash code of the property. - public override int GetHashCode() => property.GetHashCode(); - - /// - /// Returns textual representation of this property. - /// - /// The textual representation of this property. - public override string? ToString() => property.ToString(); -} - -/// -/// Represents static indexer property. -/// -/// A structure representing parameters of indexer. -/// Property value. -public sealed class Indexer : IndexerBase - where TIndicies : struct -{ - private sealed class Cache : MemberCache> - { - private protected override Indexer? Create(string propertyName, bool nonPublic) => Reflect(typeof(T), propertyName, nonPublic); - } - - private const BindingFlags PublicFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy; - private const BindingFlags NonPublicFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - /// - /// Represents property getter. - /// - /// Index values for indexed properties. - /// The property value. - public delegate TValue? Getter(in TIndicies index); - - /// - /// Represents property setter. - /// - /// Index values for indexed properties. - /// The new value of the property. - public delegate void Setter(in TIndicies index, TValue value); - - private Indexer(PropertyInfo property, Method? getter, Method? setter) - : base(property) - { - GetMethod = getter; - SetMethod = setter; - } - - /// - /// Gets indexer property getter. - /// - public new Method? GetMethod { get; } - - /// - /// Gets indexer property setter. - /// - public new Method? SetMethod { get; } - - /// - /// Gets or sets instance indexer property value. - /// - /// Index values for indexed properties. - /// The value of the property. - [MaybeNull] - public TValue this[in TIndicies index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => GetMethod is null ? throw new InvalidOperationException(ExceptionMessages.PropertyWithoutGetter(Name)) : GetMethod.Invoker(index); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - if (SetMethod is null) - throw new InvalidOperationException(ExceptionMessages.PropertyWithoutSetter(Name)); - else - SetMethod.Invoker(index, value); - } - } - - /// - /// Obtains property getter. - /// - /// The reflected property instance. - public static implicit operator Getter?(Indexer? indexer) => indexer?.GetMethod; - - /// - /// Obtains property setter. - /// - /// The reflected property instance. - public static implicit operator Setter?(Indexer? indexer) => indexer?.SetMethod; - - private static Indexer? Reflect(Type declaringType, string propertyName, bool nonPublic) - { - PropertyInfo? property = declaringType.GetProperty(propertyName, nonPublic ? NonPublicFlags : PublicFlags); - if (property is null || property.PropertyType != typeof(TValue)) - return null; - var (actualParams, arglist, input) = Signature.Reflect(); - - // reflect getter - Method? getter; - if (property.CanRead) - { - Debug.Assert(property.GetMethod is not null); - if (property.GetMethod.SignatureEquals(actualParams)) - getter = new(property.GetMethod, arglist, new[] { input }); - else - return null; - } - else - { - getter = null; - } - - // reflect setter - Method? setter; - actualParams = actualParams.Insert(property.PropertyType, actualParams.LongLength); - if (property.CanWrite) - { - Debug.Assert(property.SetMethod is not null); - if (property.SetMethod.SignatureEquals(actualParams)) - { - var valueParam = Expression.Parameter(property.PropertyType, "value"); - arglist = arglist.Insert(valueParam, arglist.LongLength); - setter = new(property.SetMethod, arglist, new[] { input, valueParam }); - } - else - { - return null; - } - } - else - { - setter = null; - } - - return new(property, getter, setter); - } - - internal static Indexer? GetOrCreate(string propertyName, bool nonPublic) - => Cache.Of>(typeof(T)).GetOrCreate(propertyName, nonPublic); -} - -/// -/// Represents static indexer property. -/// -/// Type of instance with indexer property. -/// A structure representing parameters of indexer. -/// Property value. -public sealed class Indexer : IndexerBase - where TIndicies : struct -{ - private sealed class Cache : MemberCache> - { - private protected override Indexer? Create(string propertyName, bool nonPublic) => Reflect(propertyName, nonPublic); - } - - private const BindingFlags PublicFlags = BindingFlags.Instance | BindingFlags.Public; - private const BindingFlags NonPublicFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - /// - /// Represents property getter. - /// - /// The object whose property value will be returned. - /// Index values for indexed properties. - /// The property value. - public delegate TValue? Getter([DisallowNull] in T @this, in TIndicies index); - - /// - /// Represents property setter. - /// - /// The object whose property value will be set. - /// The property value of the specified object. - /// The new property value. - public delegate void Setter([DisallowNull] in T @this, in TIndicies index, TValue value); - - private Indexer(PropertyInfo property, Method? getter, Method? setter) - : base(property) - { - GetMethod = getter; - SetMethod = setter; - } - - /// - /// Obtains property getter. - /// - /// The reflected property instance. - public static implicit operator Getter?(Indexer? indexer) => indexer?.GetMethod; - - /// - /// Obtains property setter. - /// - /// The reflected property instance. - public static implicit operator Setter?(Indexer? indexer) => indexer?.SetMethod; - - /// - /// Gets indexer property getter. - /// - public new Method? GetMethod { get; } - - /// - /// Gets indexer property setter. - /// - public new Method? SetMethod { get; } - - /// - /// Gets or sets instance property. - /// - /// The object whose property value will be set or returned. - /// Index values for indexed properties. - /// The value of the property. - [MaybeNull] - public TValue this[[DisallowNull] in T @this, in TIndicies index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (GetMethod is null) - throw new InvalidOperationException(ExceptionMessages.PropertyWithoutGetter(Name)); - Debug.Assert(@this is not null); - return GetMethod.Invoker(@this, index); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - if (SetMethod is null) - throw new InvalidOperationException(ExceptionMessages.PropertyWithoutSetter(Name)); - Debug.Assert(@this is not null); - SetMethod.Invoker(@this, index, value); - } - } - - private static Indexer? Reflect(string propertyName, bool nonPublic) - { - PropertyInfo? property = typeof(T).GetProperty(propertyName, nonPublic ? NonPublicFlags : PublicFlags); - if (property?.DeclaringType is null || property.PropertyType != typeof(TValue)) - return null; - var (actualParams, arglist, input) = Signature.Reflect(); - var thisParam = Expression.Parameter(property.DeclaringType.MakeByRefType(), "this"); - - // reflect getter - Method? getter; - if (property.CanRead) - { - Debug.Assert(property.GetMethod is not null); - if (property.GetMethod.SignatureEquals(actualParams)) - getter = new(property.GetMethod, thisParam, arglist, new[] { input }); - else - return null; - } - else - { - getter = null; - } - - // reflect setter - Method? setter; - actualParams = actualParams.Insert(property.PropertyType, actualParams.LongLength); - if (property.CanWrite) - { - Debug.Assert(property.SetMethod is not null); - if (property.SetMethod.SignatureEquals(actualParams)) - { - var valueParam = Expression.Parameter(property.PropertyType, "value"); - arglist = arglist.Insert(valueParam, arglist.LongLength); - setter = new(property.SetMethod, thisParam, arglist, new[] { input, valueParam }); - } - else - { - return null; - } - } - else - { - setter = null; - } - - return new(property, getter, setter); - } - - internal static Indexer? GetOrCreate(string propertyName, bool nonPublic) - => Cache.Of(typeof(T)).GetOrCreate(propertyName, nonPublic); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MemberAccessor.cs b/src/DotNext.Reflection/Reflection/MemberAccessor.cs deleted file mode 100644 index 03fb5d3bb..000000000 --- a/src/DotNext.Reflection/Reflection/MemberAccessor.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace DotNext.Reflection; - -/// -/// Represents static property or field value getter. -/// -/// Type of the property of field. -/// The value of the property of field. -public delegate TValue? MemberGetter(); - -/// -/// Represents static property or field setter. -/// -/// Type of the property of field. -/// The new value of the property or field. -public delegate void MemberSetter(TValue value); - -/// -/// Represents instance field/property getter. -/// -/// This parameter. -/// Declaring type. -/// Member type. -/// Field value. -public delegate TValue? MemberGetter([DisallowNull] in T @this); - -/// -/// Represents field setter. -/// -/// This parameter. -/// A value to set. -/// Declaring type. -/// Member type. -public delegate void MemberSetter([DisallowNull] in T @this, TValue value); \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MemberCache.cs b/src/DotNext.Reflection/Reflection/MemberCache.cs deleted file mode 100644 index d49cde483..000000000 --- a/src/DotNext.Reflection/Reflection/MemberCache.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace DotNext.Reflection; - -using ReaderWriterSpinLock = Threading.ReaderWriterSpinLock; - -internal abstract class Cache - where TKey : notnull - where TValue : class -{ - /* - * Can't use ConcurrentDictionary here because GetOrAdd method can call factory multiple times for the same key - */ - private readonly IDictionary elements; - private ReaderWriterSpinLock syncObject; - - private protected Cache(IEqualityComparer comparer) => elements = new Dictionary(comparer); - - private protected Cache() - : this(EqualityComparer.Default) - { - } - - private protected abstract TValue? Create(TKey cacheKey); - - internal TValue? GetOrCreate(TKey cacheKey) - { - syncObject.EnterReadLock(); - var exists = elements.TryGetValue(cacheKey, out TValue? item); - syncObject.ExitReadLock(); - if (exists) - goto exit; - - // non-fast path, discover item - syncObject.EnterWriteLock(); - if (elements.TryGetValue(cacheKey, out item)) - { - syncObject.ExitWriteLock(); - } - else - { - try - { - item = Create(cacheKey); - if (item is not null) - elements.Add(cacheKey, item); - } - finally - { - syncObject.ExitWriteLock(); - } - } - - exit: - return item; - } -} - -[StructLayout(LayoutKind.Auto)] -internal readonly struct MemberKey : IEquatable -{ - private const StringComparison NameComparison = StringComparison.Ordinal; - internal readonly bool NonPublic; - internal readonly string Name; - - internal MemberKey(string name, bool nonPublic) - { - NonPublic = nonPublic; - Name = name; - } - - public bool Equals(MemberKey other) => NonPublic == other.NonPublic && string.Equals(Name, other.Name, NameComparison); - - public override bool Equals([NotNullWhen(true)] object? other) => other is MemberKey key && Equals(key); - - public override int GetHashCode() - { - var result = new HashCode(); - result.Add(NonPublic); - result.Add(Name, StringComparer.FromComparison(NameComparison)); - return result.ToHashCode(); - } -} - -internal abstract class MemberCache : Cache - where TMember : MemberInfo - where TDescriptor : class, IMember -{ - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot> Slot = new(); - - internal TDescriptor? GetOrCreate(string memberName, bool nonPublic) => GetOrCreate(new MemberKey(memberName, nonPublic)); - - private protected abstract TDescriptor? Create(string memberName, bool nonPublic); - - private protected sealed override TDescriptor? Create(MemberKey key) => Create(key.Name, key.NonPublic); - - internal static MemberCache Of(MemberInfo member) - where TCache : MemberCache, new() - => member.GetUserData().GetOrSet, TCache>(Slot); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Method.cs b/src/DotNext.Reflection/Reflection/Method.cs deleted file mode 100644 index 521585128..000000000 --- a/src/DotNext.Reflection/Reflection/Method.cs +++ /dev/null @@ -1,598 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Represents reflected method. -/// -/// Type of delegate describing signature of the reflected method. -public sealed class Method : MethodInfo, IMethod, IEquatable - where TSignature : MulticastDelegate -{ - private sealed class Cache : MemberCache> - { - private protected override Method? Create(string methodName, bool nonPublic) => Reflect(methodName, nonPublic); - } - - private sealed class Cache : MemberCache> - { - private protected override Method? Create(string methodName, bool nonPublic) => Reflect(typeof(T), methodName, nonPublic); - } - - private const BindingFlags StaticPublicFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; - private const BindingFlags StaticNonPublicFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - private const BindingFlags InstancePublicFlags = BindingFlags.Instance | BindingFlags.Public; - private const BindingFlags InstanceNonPublicFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot?> CacheSlot = new(); - - private readonly MethodInfo method; - internal readonly TSignature Invoker; - - private Method(MethodInfo method, Expression lambda) - { - this.method = method; - Invoker = lambda.Compile(); - } - - internal Method(MethodInfo method, IEnumerable args, IEnumerable parameters) - : this(method, Expression.Lambda(Expression.Call(method, args), true, parameters)) - { - } - - internal Method(MethodInfo method, ParameterExpression instance, IEnumerable args, IEnumerable parameters) - : this(method, Expression.Lambda(Expression.Call(instance, method, args), true, parameters.Prepend(instance))) - { - } - - private Method(MethodInfo method) - { - this.method = method; - Invoker = method.CreateDelegate(); - } - - /// - /// Gets the attributes associated with this method. - /// - public override MethodAttributes Attributes => method.Attributes; - - /// - /// Gets a value indicating the calling conventions for this constructor. - /// - public override CallingConventions CallingConvention => method.CallingConvention; - - /// - /// Gets a value indicating whether the generic method contains unassigned generic type parameters. - /// - public override bool ContainsGenericParameters => method.ContainsGenericParameters; - - /// - /// Creates a delegate of the specified type from this method. - /// - /// The type of the delegate to create. - /// The delegate for this method. - public override Delegate CreateDelegate(Type delegateType) => method.CreateDelegate(delegateType); - - /// - /// Creates a delegate of the specified type from this method. - /// - /// The type of the delegate to create. - /// The object targeted by the delegate. - /// The delegate for this method. - public override Delegate CreateDelegate(Type delegateType, object? target) => method.CreateDelegate(delegateType, target); - - /// - /// Gets a collection that contains this member's custom attributes. - /// - public override IEnumerable CustomAttributes => method.CustomAttributes; - - /// - /// Gets the class that declares this method. - /// - public override Type? DeclaringType => method.DeclaringType; - - /// - /// Returns the method on the direct or indirect base class in which the method represented by this instance was first declared. - /// - /// The first implementation of this method. - public override MethodInfo GetBaseDefinition() => method.GetBaseDefinition(); - - /// - /// Returns an array of all custom attributes applied to this method. - /// - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this method. - public override object[] GetCustomAttributes(bool inherit) => method.GetCustomAttributes(inherit); - - /// - /// Returns an array of all custom attributes applied to this method. - /// - /// The type of attribute to search for. Only attributes that are assignable to this type are returned. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this method. - public override object[] GetCustomAttributes(Type attributeType, bool inherit) => method.GetCustomAttributes(attributeType, inherit); - - /// - /// Returns a list of custom attributes that have been applied to the target method. - /// - /// The data about the attributes that have been applied to the target method. - public override IList GetCustomAttributesData() => method.GetCustomAttributesData(); - - /// - /// Returns the type arguments of a generic method or the type parameters of a generic method definition. - /// - /// The list of generic arguments. - public override Type[] GetGenericArguments() => method.GetGenericArguments(); - - /// - /// Returns generic method definition from which the current method can be constructed. - /// - /// Generic method definition from which the current method can be constructed. - public override MethodInfo GetGenericMethodDefinition() => method.GetGenericMethodDefinition(); - - /// - /// Provides access to the MSIL stream, local variables, and exceptions for the current method. - /// - /// An object that provides access to the MSIL stream, local variables, and exceptions for the current method. - public override MethodBody? GetMethodBody() => method.GetMethodBody(); - - /// - /// Gets method implementation attributes. - /// - /// Implementation attributes. - public override MethodImplAttributes GetMethodImplementationFlags() => method.GetMethodImplementationFlags(); - - /// - /// Gets method parameters. - /// - /// The array of method parameters. - public override ParameterInfo[] GetParameters() => method.GetParameters(); - - /// - /// Invokes this method. - /// - /// The object on which to invoke the method. - /// Specifies the type of binding. - /// Defines a set of properties and enables the binding, coercion of argument types, and invocation of members using reflection. - /// A list of method arguments. - /// Used to govern the coercion of types. - /// The return value of the invoked method. - public override object? Invoke(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? parameters, CultureInfo? culture) - => method.Invoke(obj, invokeAttr, binder, parameters, culture); - - /// - /// Determines whether one or more attributes of the specified type or of its derived types is applied to this method. - /// - /// The type of custom attribute to search for. The search includes derived types. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// if one or more instances of or any of its derived types is applied to this method; otherwise, . - public override bool IsDefined(Type attributeType, bool inherit) - => method.IsDefined(attributeType, inherit); - - /// - /// Gets a value indicating whether the method is generic. - /// - public override bool IsGenericMethod => method.IsGenericMethod; - - /// - /// Gets a value indicating whether the method is a generic method definition. - /// - public override bool IsGenericMethodDefinition => method.IsGenericMethodDefinition; - - /// - /// Gets a value that indicates whether the method is security-critical or security-safe-critical at the current trust level, - /// and therefore can perform critical operations. - /// - public override bool IsSecurityCritical => method.IsSecurityCritical; - - /// - /// Gets a value that indicates whether the method is security-safe-critical at the current trust level; that is, - /// whether it can perform critical operations and can be accessed by transparent code. - /// - public override bool IsSecuritySafeCritical => method.IsSecuritySafeCritical; - - /// - /// Gets a value that indicates whether the current method is transparent at the current trust level, - /// and therefore cannot perform critical operations. - /// - public override bool IsSecurityTransparent => method.IsSecurityTransparent; - - /// - /// Substitutes the elements of an array of types for the type parameters of the current generic method definition, - /// and returns the resulting constructed method. - /// - /// An array of types to be substituted for the type parameters of the current generic method definition. - /// The constructed method formed by substituting the elements of for the type parameters of the current generic method definition. - public override MethodInfo MakeGenericMethod(params Type[] typeArguments) => method.MakeGenericMethod(typeArguments); - - /// - /// Always returns . - /// - public override MemberTypes MemberType => MemberTypes.Method; - - /// - /// Gets a value that identifies a metadata element. - /// - public override int MetadataToken => method.MetadataToken; - - /// - /// Gets a handle to the internal metadata representation of a method. - /// - public override RuntimeMethodHandle MethodHandle => method.MethodHandle; - - /// - /// Gets method implementation attributes. - /// - public override MethodImplAttributes MethodImplementationFlags => method.MethodImplementationFlags; - - /// - /// Gets the module in which the type that declares the method represented by the current instance is defined. - /// - public override Module Module => method.Module; - - /// - /// Gets name of this method. - /// - public override string Name => method.Name; - - /// - /// Gets the class object that was used to obtain this instance. - /// - public override Type? ReflectedType => method.ReflectedType; - - /// - /// Gets information about the return type of the method, such as whether the return type has custom modifiers. - /// - public override ParameterInfo ReturnParameter => method.ReturnParameter; - - /// - /// Gets the return type of this method. - /// - public override Type ReturnType => method.ReturnType; - - /// - /// Gets the custom attributes for the return type. - /// - public override ICustomAttributeProvider ReturnTypeCustomAttributes => method.ReturnTypeCustomAttributes; - - /// - /// Determines whether this method is equal to the given method. - /// - /// Other constructor to compare. - /// if this object reflects the same method as the specified object; otherwise, . - public bool Equals(MethodInfo? other) => other is Method method ? Equals(this.method, method.method) : Equals(this.method, other); - - /// - /// Determines whether this method is equal to the given method. - /// - /// Other constructor to compare. - /// if this object reflects the same method as the specified object; otherwise, . - public override bool Equals([NotNullWhen(true)] object? other) => other switch - { - Method method => this.method == method.method, - MethodInfo method => this.method == method, - TSignature invoker => Equals(Invoker, invoker), - _ => false, - }; - - /// - /// Computes hash code uniquely identifies the reflected method. - /// - /// The hash code of the method. - public override int GetHashCode() => method.GetHashCode(); - - /// - /// Gets a delegate representing this method. - /// - /// The reflected method. - [return: NotNullIfNotNull(nameof(method))] - public static implicit operator TSignature?(Method? method) => method?.Invoker; - - /// - MethodInfo IMember.Metadata => method; - - /// - TSignature IMember.Invoker => Invoker; - - /// - /// Returns textual representation of this method. - /// - /// The textual representation of this method. - public override string? ToString() => method.ToString(); - - private static Method? ReflectStatic(Type declaringType, Type[] parameters, Type returnType, string methodName, bool nonPublic) - { - // lookup in declaring type - MethodInfo? targetMethod = declaringType.GetMethod( - methodName, - nonPublic ? StaticNonPublicFlags : StaticPublicFlags, - Type.DefaultBinder, - parameters, - Array.Empty()); - - // lookup in extension methods - if (targetMethod is null || returnType != targetMethod.ReturnType) - { - targetMethod = ExtensionRegistry.GetMethods(declaringType, MethodLookup.Static) - .FirstOrDefault(candidate => - candidate.Name == methodName && candidate.SignatureEquals(parameters) && - candidate.ReturnType == returnType); - } - - return targetMethod is null ? null : new(targetMethod); - } - - private static Method? ReflectStatic(Type declaringType, Type argumentsType, Type returnType, string methodName, bool nonPublic) - { - var (parameters, arglist, input) = Signature.Reflect(argumentsType); - - // lookup in declaring type - MethodInfo? targetMethod = declaringType.GetMethod( - methodName, - nonPublic ? StaticNonPublicFlags : StaticPublicFlags, - Type.DefaultBinder, - parameters, - Array.Empty()); - - // lookup in extension methods - if (targetMethod is null || returnType != targetMethod.ReturnType) - { - targetMethod = ExtensionRegistry.GetMethods(declaringType, MethodLookup.Static) - .FirstOrDefault(candidate => - candidate.Name == methodName && candidate.SignatureEquals(parameters) && - candidate.ReturnType == returnType); - } - - return targetMethod is null ? null : new(targetMethod, arglist, new[] { input }); - } - - private static Method? ReflectInstance(Type thisParam, Type[] parameters, Type returnType, string methodName, bool nonPublic) - { - // lookup in declaring type - MethodInfo? targetMethod = thisParam.NonRefType().GetMethod( - methodName, - nonPublic ? InstanceNonPublicFlags : InstancePublicFlags, - Type.DefaultBinder, - parameters, - Array.Empty()); - - // lookup in extension methods - if (targetMethod is null || returnType != targetMethod.ReturnType) - targetMethod = ExtensionRegistry.GetMethods(thisParam, MethodLookup.Instance).FirstOrDefault(candidate => candidate.Name == methodName && Enumerable.SequenceEqual(candidate.GetParameterTypes().RemoveFirst(1), parameters) && candidate.ReturnType == returnType); - - // this parameter can be passed as REF so handle this situation - // first parameter should be passed by REF for structure types - if (targetMethod is null) - return null; - - if (thisParam.IsByRef ^ thisParam.NonRefType().IsValueType) - { - ParameterExpression[] parametersDeclaration; - if (targetMethod.IsStatic) - { - parametersDeclaration = Array.ConvertAll(targetMethod.GetParameterTypes(), Expression.Parameter); - return new(targetMethod, parametersDeclaration, parametersDeclaration); - } - else - { - var thisParamDeclaration = Expression.Parameter(thisParam); - parametersDeclaration = Array.ConvertAll(parameters, Expression.Parameter); - return new(targetMethod, thisParamDeclaration, parametersDeclaration, parametersDeclaration); - } - } - - return new(targetMethod); - } - - private static Method? ReflectInstance(Type thisParam, Type argumentsType, Type returnType, string methodName, bool nonPublic) - { - var (parameters, arglist, input) = Signature.Reflect(argumentsType); - var thisParamDeclaration = Expression.Parameter(thisParam.MakeByRefType()); - - // lookup in declaring type - MethodInfo? targetMethod = thisParam.GetMethod( - methodName, - nonPublic ? InstanceNonPublicFlags : InstancePublicFlags, - Type.DefaultBinder, - parameters, - Array.Empty()); - - // lookup in extension methods - if (targetMethod is null || returnType != targetMethod.ReturnType) - { - targetMethod = null; - foreach (var candidate in ExtensionRegistry.GetMethods(thisParam, MethodLookup.Instance)) - { - if (candidate.Name == methodName && Enumerable.SequenceEqual(candidate.GetParameterTypes().RemoveFirst(1), parameters) && candidate.ReturnType == returnType) - { - targetMethod = candidate; - break; - } - } - } - - return targetMethod is null ? null : new(targetMethod, thisParamDeclaration, arglist, new[] { input }); - } - - private static Method? Reflect(string methodName, bool nonPublic) - { - var delegateType = typeof(TSignature); - if (delegateType.IsAbstract) - { - throw new AbstractDelegateException(); - } - else if (delegateType.IsGenericInstanceOf(typeof(Function<,,>)) && delegateType.GetGenericArguments().Take(out var thisParam, out var argumentsType, out var returnType)) - { - return ReflectInstance(thisParam, argumentsType, returnType, methodName, nonPublic); - } - else if (delegateType.IsGenericInstanceOf(typeof(Procedure<,>)) && delegateType.GetGenericArguments().Take(out thisParam, out argumentsType)) - { - return ReflectInstance(thisParam, argumentsType, typeof(void), methodName, nonPublic); - } - else - { - var invokeMethod = DelegateType.GetInvokeMethod(); - var parameters = invokeMethod.GetParameterTypes(); - thisParam = parameters?.FirstOrDefault() ?? throw new ArgumentException(ExceptionMessages.ThisParamExpected); - return ReflectInstance(thisParam, parameters.RemoveFirst(1), invokeMethod.ReturnType, methodName, nonPublic); - } - } - - /// - /// Reflects static method. - /// - /// Declaring type. - /// Name of method. - /// True to reflect non-public static method. - /// Reflected static method. - private static Method? Reflect(Type declaringType, string methodName, bool nonPublic) - { - var delegateType = typeof(TSignature); - if (delegateType.IsAbstract) - { - throw new AbstractDelegateException(); - } - else if (delegateType.IsGenericInstanceOf(typeof(Function<,>)) && delegateType.GetGenericArguments().Take(out var argumentsType, out var returnType)) - { - return ReflectStatic(declaringType, argumentsType, returnType, methodName, nonPublic); - } - else if (delegateType.IsGenericInstanceOf(typeof(Procedure<>))) - { - return ReflectStatic(declaringType, delegateType.GetGenericArguments()[0], typeof(void), methodName, nonPublic); - } - else - { - var invokeMethod = DelegateType.GetInvokeMethod(); - return ReflectStatic(declaringType, invokeMethod.GetParameterTypes(), invokeMethod.ReturnType, methodName, nonPublic); - } - } - - private static Method? Unreflect(MethodInfo method, ParameterExpression? thisParam, Type argumentsType, Type returnType) - { - var (_, arglist, input) = Signature.Reflect(argumentsType); - var prologue = new LinkedList(); - var epilogue = new LinkedList(); - var locals = new LinkedList(); - - // adjust THIS - Expression? thisArg; - if (thisParam is null || method.DeclaringType is null) - { - thisArg = null; - } - else if (method.DeclaringType.IsAssignableFromWithoutBoxing(thisParam.Type)) - { - thisArg = thisParam; - } - else if (thisParam.Type == typeof(object)) - { - thisArg = method.DeclaringType.IsValueType ? - Expression.Unbox(thisParam, method.DeclaringType) : - Expression.Convert(thisParam, method.DeclaringType); - } - else - { - return null; - } - - // adjust arguments - if (!Signature.NormalizeArguments(method.GetParameters(), arglist, locals, prologue, epilogue)) - return null; - Expression body; - - // adjust return type - if (returnType == typeof(void) || returnType.IsAssignableFromWithoutBoxing(method.ReturnType)) - body = Expression.Call(thisArg, method, arglist); - else if (returnType == typeof(object)) - body = Expression.Convert(Expression.Call(thisArg, method, arglist), returnType); - else - return null; - if (epilogue.Count == 0) - { - epilogue.AddFirst(body); - } - else if (method.ReturnType != typeof(void)) - { - var returnArg = Expression.Parameter(returnType); - locals.AddFirst(returnArg); - body = Expression.Assign(returnArg, body); - epilogue.AddFirst(body); - epilogue.AddLast(returnArg); - } - - body = prologue.Count == 0 && epilogue.Count == 1 ? epilogue.First!.Value : Expression.Block(locals, prologue.Concat(epilogue)); - return new(method, thisParam is null ? Expression.Lambda(body, input) : Expression.Lambda(body, thisParam, input)); - } - - private static Method? UnreflectStatic(MethodInfo method) - { - var delegateType = typeof(TSignature); - if (delegateType.IsGenericInstanceOf(typeof(Function<,>)) && delegateType.GetGenericArguments().Take(out var argumentsType, out var returnType)) - return Unreflect(method, null, argumentsType, returnType); - - if (delegateType.IsGenericInstanceOf(typeof(Procedure<>))) - return Unreflect(method, null, delegateType.GetGenericArguments()[0], typeof(void)); - - return DelegateType.GetInvokeMethod().SignatureEquals(method) ? new(method) : null; - } - - private static Method? UnreflectInstance(MethodInfo method) - { - var delegateType = typeof(TSignature); - if (delegateType.IsGenericInstanceOf(typeof(Function<,,>)) && delegateType.GetGenericArguments().Take(out var thisParam, out var argumentsType, out var returnType)) - return Unreflect(method, Expression.Parameter(thisParam.MakeByRefType()), argumentsType, returnType); - - if (delegateType.IsGenericInstanceOf(typeof(Procedure<,>)) && delegateType.GetGenericArguments().Take(out thisParam, out argumentsType)) - return Unreflect(method, Expression.Parameter(thisParam.MakeByRefType()), argumentsType, typeof(void)); - - var invokeMethod = DelegateType.GetInvokeMethod(); - var parameters = invokeMethod.GetParameterTypes(); - thisParam = parameters?.FirstOrDefault() ?? throw new ArgumentException(ExceptionMessages.ThisParamExpected); - parameters = parameters.RemoveFirst(1); - if (method.SignatureEquals(parameters) && method.ReturnType == invokeMethod.ReturnType) - { - if (thisParam.IsByRef ^ method.DeclaringType is { IsValueType: true }) - { - var arguments = Array.ConvertAll(parameters, Expression.Parameter); - return new(method, Expression.Parameter(thisParam), arguments, arguments); - } - - return new(method); - } - - return null; - } - - private static Method? Unreflect(MethodInfo method) - { - var delegateType = typeof(TSignature); - if (delegateType.IsAbstract) - throw new AbstractDelegateException(); - - return method switch - { - Method existing => existing, - { IsGenericMethodDefinition: true } or { IsConstructor: true } => null, - { IsStatic: true } => UnreflectStatic(method), - _ => UnreflectInstance(method) - }; - } - - internal static unsafe Method? GetOrCreate(MethodInfo method) - => method.GetUserData().GetOrSet(CacheSlot, method, &Unreflect); - - internal static Method? GetOrCreate(string methodName, bool nonPublic, MethodLookup lookup) - { - MemberCache>? cache = lookup switch - { - MethodLookup.Instance => Cache.Of(typeof(T)), - MethodLookup.Static => Cache.Of>(typeof(T)), - _ => null, - }; - return cache?.GetOrCreate(methodName, nonPublic); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MethodLookup.cs b/src/DotNext.Reflection/Reflection/MethodLookup.cs deleted file mode 100644 index 51b4a1861..000000000 --- a/src/DotNext.Reflection/Reflection/MethodLookup.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Represents method declaration type. -/// -public enum MethodLookup : byte -{ - /// - /// Represents static method. - /// - Static = 0, - - /// - /// Represents instance method. - /// - Instance = 1, -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MissingAttributeException.cs b/src/DotNext.Reflection/Reflection/MissingAttributeException.cs deleted file mode 100644 index 43b5026cc..000000000 --- a/src/DotNext.Reflection/Reflection/MissingAttributeException.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Indicates that requested attribute doesn't exist. -/// -public sealed class MissingAttributeException : ConstraintViolationException -{ - /// - /// Initializes a new exception indicating that requested attribute doesn't exist. - /// - /// The inspected type. - /// The type of missing attribute. - public MissingAttributeException(Type target, Type attributeType) - : base(target, ExceptionMessages.MissingAttribute(attributeType, target)) - { - AttributeType = attributeType; - } - - /// - /// Gets type of missing attribute. - /// - public Type AttributeType { get; } - - internal static MissingAttributeException Create() - where TArgs : Attribute - => new(typeof(T), typeof(TArgs)); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MissingConstructorException.cs b/src/DotNext.Reflection/Reflection/MissingConstructorException.cs deleted file mode 100644 index 09083bf34..000000000 --- a/src/DotNext.Reflection/Reflection/MissingConstructorException.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Indicates that requested constructor doesn't exist. -/// -public sealed class MissingConstructorException : ConstraintViolationException -{ - /// - /// Initializes a new exception indicating that requested constructor doesn't exist. - /// - /// The inspected type. - /// An array of types representing constructor parameters. - public MissingConstructorException(Type target, params Type[] parameters) - : base(target, ExceptionMessages.MissingCtor(target, parameters)) - { - Parameters = parameters; - } - - /// - /// An array of types representing constructor parameters. - /// - public IReadOnlyList Parameters { get; } - - internal static MissingConstructorException Create() - where TSignature : Delegate - { - var invokeMethod = DelegateType.GetInvokeMethod(); - return new MissingConstructorException(invokeMethod.ReturnType, invokeMethod.GetParameterTypes()); - } - - internal static MissingConstructorException Create() - where TArgs : struct - => new(typeof(T), Signature.Reflect(typeof(TArgs)).Parameters); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MissingEventException.cs b/src/DotNext.Reflection/Reflection/MissingEventException.cs deleted file mode 100644 index 59980410c..000000000 --- a/src/DotNext.Reflection/Reflection/MissingEventException.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Indicates that requested event doesn't exist. -/// -public sealed class MissingEventException : ConstraintViolationException -{ - /// - /// Initializes a new exception indicating that requested event doesn't exist. - /// - /// The inspected type. - /// The name of the event. - /// The type of the event handler. - public MissingEventException(Type declaringType, string eventName, Type handlerType) - : base(declaringType, ExceptionMessages.MissingEvent(eventName, handlerType, declaringType)) - { - HandlerType = handlerType; - EventName = eventName; - } - - internal static MissingEventException Create(string eventName) - where THandler : MulticastDelegate - => new(typeof(T), eventName, typeof(THandler)); - - /// - /// Gets event handler type. - /// - public Type HandlerType { get; } - - /// - /// Gets name of the missing event. - /// - public string EventName { get; } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MissingFieldException.cs b/src/DotNext.Reflection/Reflection/MissingFieldException.cs deleted file mode 100644 index 34e5d4cd6..000000000 --- a/src/DotNext.Reflection/Reflection/MissingFieldException.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Indicates that requested field doesn't exist. -/// -public sealed class MissingFieldException : ConstraintViolationException -{ - /// - /// Initializes a new exception indicating that requested field doesn't exist. - /// - /// The inspected type. - /// The name of the missing field. - /// The type of the missing field. - public MissingFieldException(Type declaringType, string fieldName, Type fieldType) - : base(declaringType, ExceptionMessages.MissingField(fieldName, fieldType, declaringType)) - { - FieldType = fieldType; - FieldName = fieldName; - } - - internal static MissingFieldException Create(string fieldName) - => new(typeof(T), fieldName, typeof(TValue)); - - /// - /// Gets type of the field. - /// - public Type FieldType { get; } - - /// - /// Gets name of the missing field. - /// - public string FieldName { get; } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MissingMethodException.cs b/src/DotNext.Reflection/Reflection/MissingMethodException.cs deleted file mode 100644 index aa74e2244..000000000 --- a/src/DotNext.Reflection/Reflection/MissingMethodException.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Indicates that requested method doesn't exist. -/// -public sealed class MissingMethodException : ConstraintViolationException -{ - /// - /// Initializes a new exception indicating that requested method doesn't exist. - /// - /// The inspected type. - /// The name of the method. - /// The return type of the missing method. - /// The parameters of the missing method. - public MissingMethodException(Type declaringType, string methodName, Type returnType, params Type[] parameters) - : base(declaringType, ExceptionMessages.MissingMethod(methodName, parameters, returnType, declaringType)) - { - MethodName = methodName; - ReturnType = returnType; - Parameters = parameters; - } - - /// - /// Gets name of missing method. - /// - public string MethodName { get; } - - /// - /// Gets return type of missing method. - /// - public Type ReturnType { get; } - - /// - /// Gets parameters of missing method. - /// - public IReadOnlyList Parameters { get; } - - internal static MissingMethodException Create(string methodName) - where TSignature : Delegate - { - var invokeMethod = DelegateType.GetInvokeMethod(); - return new MissingMethodException(typeof(T), methodName, invokeMethod.ReturnType, invokeMethod.GetParameterTypes()); - } - - internal static MissingMethodException Create(string methodName) - where TArgs : struct - { - var type = typeof(TResult); - if (type == typeof(Missing)) - type = typeof(void); - return new MissingMethodException(typeof(T), methodName, type, Signature.Reflect(typeof(TArgs)).Parameters); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MissingOperatorException.cs b/src/DotNext.Reflection/Reflection/MissingOperatorException.cs deleted file mode 100644 index 37ce3773a..000000000 --- a/src/DotNext.Reflection/Reflection/MissingOperatorException.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq.Expressions; - -namespace DotNext.Reflection; - -/// -/// Indicates that requested operator doesn't exist. -/// -public sealed class MissingOperatorException : ConstraintViolationException -{ - /// - /// Initializes a new exception indicating that requested operator doesn't exist. - /// - /// The inspected type. - /// Missing operator. - public MissingOperatorException(Type target, ExpressionType @operator) - : base(target, ExceptionMessages.MissingOperator(@operator)) - { - } - - internal static MissingOperatorException Create(UnaryOperator @operator) - => new(typeof(T), @operator.ToExpressionType()); - - internal static MissingOperatorException Create(BinaryOperator @operator) - => new(typeof(T), @operator.ToExpressionType()); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/MissingPropertyException.cs b/src/DotNext.Reflection/Reflection/MissingPropertyException.cs deleted file mode 100644 index 61fcf2b1f..000000000 --- a/src/DotNext.Reflection/Reflection/MissingPropertyException.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Indicates that requested property doesn't exist. -/// -public sealed class MissingPropertyException : ConstraintViolationException -{ - /// - /// Initializes a new exception indicating that requested property doesn't exist. - /// - /// The inspected type. - /// The name of the missing property. - /// The type of the missing property. - public MissingPropertyException(Type declaringType, string propertyName, Type propertyType) - : base(declaringType, ExceptionMessages.MissingProperty(propertyName, propertyType, declaringType)) - { - PropertyType = propertyType; - PropertyName = propertyName; - } - - internal static MissingPropertyException Create(string propertyName) - => new(typeof(T), propertyName, typeof(TValue)); - - /// - /// Gets type of the missing property. - /// - public Type PropertyType { get; } - - /// - /// Gets name of the missing property. - /// - public string PropertyName { get; } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Operator.cs b/src/DotNext.Reflection/Reflection/Operator.cs deleted file mode 100644 index 02a461e0b..000000000 --- a/src/DotNext.Reflection/Reflection/Operator.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reflection; -using static System.Diagnostics.Debug; - -namespace DotNext.Reflection; - -internal static class Operator -{ - internal static ExpressionType ToExpressionType(this UnaryOperator @operator) => (ExpressionType)@operator; - - internal static ExpressionType ToExpressionType(this BinaryOperator @operator) => (ExpressionType)@operator; - - internal readonly struct Kind : IEquatable - { - private readonly bool overloaded; - private readonly ExpressionType operatorType; - - internal Kind(UnaryOperator operatorType, bool overloaded) - { - this.operatorType = operatorType.ToExpressionType(); - this.overloaded = overloaded; - } - - public static implicit operator UnaryOperator(in Kind key) => (UnaryOperator)key.operatorType; - - public static implicit operator BinaryOperator(in Kind key) => (BinaryOperator)key.operatorType; - - public static implicit operator ExpressionType(in Kind key) => key.operatorType; - - internal Kind(BinaryOperator operatorType, bool overloaded) - { - this.operatorType = operatorType.ToExpressionType(); - this.overloaded = overloaded; - } - - private bool Equals(in Kind other) => operatorType == other.operatorType && overloaded == other.overloaded; - - bool IEquatable.Equals(Kind other) => Equals(in other); - - public override bool Equals([NotNullWhen(true)] object? other) - => other is Kind key && Equals(in key); - - public override int GetHashCode() => HashCode.Combine(overloaded, operatorType); - - private InvalidOperationException OperatorNotExists() - => new(ExceptionMessages.MissingOperator(operatorType)); - - internal UnaryExpression MakeUnary(in Operand operand) - { - var result = Expression.MakeUnary(operatorType, operand.Argument, typeof(TResult)); - return result.Method is null ^ overloaded ? result : throw OperatorNotExists(); - } - - internal BinaryExpression MakeBinary(in Operand first, in Operand second) - { - var result = Expression.MakeBinary(operatorType, first.Argument, second.Argument); - return result.Method is null ^ overloaded ? result : throw OperatorNotExists(); - } - } - - internal readonly ref struct Operand - { - internal readonly Expression Argument; - internal readonly ParameterExpression Source; - - private Operand(ParameterExpression operand) => Argument = Source = operand; - - internal Operand(ParameterExpression supplier, Type expectedType) => Argument = Expression.Convert(Source = supplier, expectedType); - - public static implicit operator Operand(ParameterExpression operand) => new(operand); - } - - internal static bool Upcast(this ref Operand operand) - { - var baseType = operand.Argument.Type.BaseType; - - // do not walk through inheritance hierarchy for value types - if (baseType is null || operand.Argument.Type.IsValueType) - { - return false; - } - else - { - operand = new Operand(operand.Source, baseType); - return true; - } - } - - internal static bool NormalizePrimitive(this ref Operand operand) - { - switch (Type.GetTypeCode(operand.Argument.Type)) - { - case TypeCode.Byte or TypeCode.UInt16 or TypeCode.SByte or TypeCode.Int16: - operand = new Operand(operand.Source, typeof(int)); - return true; - default: - return false; - } - } -} - -/// -/// Abstract class for all reflected operators. -/// -/// Type of delegate representing operator signature. -public abstract class Operator : IOperator - where TSignature : Delegate -{ - private protected abstract class Cache : Cache - where TOperand : class, IOperator - { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] - private static readonly UserDataSlot> Slot = new(); - - internal static Cache Of(Type cacheHolder) - where TCache : Cache, new() - => cacheHolder.GetUserData().GetOrSet, TCache>(Slot); - } - - private protected readonly TSignature Invoker; - - private protected Operator(TSignature invoker, ExpressionType type, MethodInfo? overloaded) - { - Type = type; - Invoker = invoker; - Method = overloaded; - } - - /// - /// Gets the implementing method for the operator. - /// - public MethodInfo? Method { get; } - - private protected abstract Type DeclaringType { get; } - - /// - MemberInfo IMember.Metadata => Method ?? (MemberInfo)new BuiltinOperatorInfo(DeclaringType, Type); - - /// - TSignature IMember.Invoker => Invoker; - - /// - string IMember.Name => Type.ToString(); - - /// - /// Returns the delegate instance that can be used to invoke operator. - /// - /// The reflected operator. - [return: NotNullIfNotNull(nameof(op))] - public static implicit operator TSignature?(Operator? op) => op?.Invoker; - - /// - /// Gets type of operator. - /// - public ExpressionType Type { get; } - - private static Expression? Convert(ParameterExpression parameter, Expression operand, Type conversionType, bool @checked) - { - try - { - return Expression.Lambda(@checked ? Expression.ConvertChecked(operand, conversionType) : Expression.Convert(operand, conversionType), parameter); - } - catch (ArgumentException e) - { - WriteLine(e); - return null; - } - catch (InvalidOperationException) - { - // do not walk through inheritance hierarchy for value types - if (parameter.Type.IsValueType) - return null; - var lookup = operand.Type.BaseType; - return lookup is null ? null : Convert(parameter, Expression.Convert(parameter, lookup), conversionType, @checked); - } - } - - private protected static Expression? MakeConvert(ParameterExpression parameter, bool @checked) => Convert(parameter, parameter, typeof(T), @checked); - - /// - /// Determines whether this object reflects the same operator as other object. - /// - /// Other reflected operator to be compared. - /// , if this object reflects the same operator as other object; otherwise, . - public override bool Equals([NotNullWhen(true)] object? other) => other switch - { - Operator op => Type == op.Type && Method == op.Method, - TSignature invoker => Equals(Invoker, invoker), - _ => false, - }; - - /// - /// Computes hash code of the reflected operator. - /// - /// The hash code of the reflected operator. - public override int GetHashCode() => HashCode.Combine(typeof(TSignature), Type, Method); - - /// - /// Returns textual representation of the reflected operator. - /// - /// The textual representation of the reflected operator. - public override string ToString() => Type.ToString(); -} - -/// -/// A delegate representing unary operator. -/// -/// Operand. -/// Type of operand. -/// Type of operator result. -/// Result of unary operation. -public delegate TResult? Operator(in TOperand operand); - -/// -/// Represents binary operator. -/// -/// First operand. -/// Second operand. -/// Type of first operand. -/// Type of second operand. -/// Type of operator result. -/// Result of binary operator. -public delegate TResult? Operator(in T1 first, in T2 second); \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/OperatorLookup.cs b/src/DotNext.Reflection/Reflection/OperatorLookup.cs deleted file mode 100644 index 5c4691d80..000000000 --- a/src/DotNext.Reflection/Reflection/OperatorLookup.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace DotNext.Reflection; - -/// -/// Represents operator resolution strategy. -/// -public enum OperatorLookup : byte -{ - /// - /// Check for predefined operator only. - /// - Predefined = 0, - - /// - /// Check for user-defined (overloaded) operator only. - /// - Overloaded = 1, - - /// - /// Check for any operator. - /// - Any = 2, -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Property.cs b/src/DotNext.Reflection/Reflection/Property.cs deleted file mode 100644 index fa51b0dce..000000000 --- a/src/DotNext.Reflection/Reflection/Property.cs +++ /dev/null @@ -1,483 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace DotNext.Reflection; - -/// -/// Represents non-indexer property. -/// -/// Type of property value. -public abstract class PropertyBase : PropertyInfo, IProperty, IEquatable -{ - private readonly PropertyInfo property; - - private protected PropertyBase(PropertyInfo property) => this.property = property; - - /// - /// Returns the value of the property supported by a given object. - /// - /// The object whose property value will be returned. - /// An object containing the value of the property reflected by this instance. - /// , if property value is obtained successfully; otherwise, . - public abstract bool GetValue(object? obj, out TValue? value); - - /// - /// Sets the value of the property supported by the given object. - /// - /// The object whose property value will be set. - /// The value to assign to the property. - /// , if property value is modified successfully; otherwise, . - public abstract bool SetValue(object? obj, TValue value); - - /// - /// Returns the property value of a specified object with index values for indexed properties. - /// - /// The object whose property value will be returned. - /// Index values for indexed properties. - /// The property value of the specified object. - public override object? GetValue(object? obj, object?[]? index) => property.GetValue(obj, index); - - /// - /// Sets the property value of a specified object with optional index values for index properties. - /// - /// The object whose property value will be set. - /// The new property value. - /// The property value of the specified object. - public override void SetValue(object? obj, object? value, object?[]? index) => property.SetValue(obj, value, index); - - /// - /// Gets name of the property. - /// - public sealed override string Name => property.Name; - - /// - /// Gets a value indicating whether the property can be read. - /// - public sealed override bool CanRead => property.CanRead; - - /// - /// Gets a value indicating whether the property can be written to. - /// - public sealed override bool CanWrite => property.CanWrite; - - /// - /// Gets the get accessor for this property. - /// - public sealed override MethodInfo? GetMethod => property.GetMethod; - - /// - /// Gets the attributes for this property. - /// - public sealed override PropertyAttributes Attributes => property.Attributes; - - /// - /// Gets the type of this property. - /// - public sealed override Type PropertyType => property.PropertyType; - - /// - /// Gets the set accessor for this property. - /// - public sealed override MethodInfo? SetMethod => property.SetMethod; - - /// - /// Returns an array whose elements reflect the public and, if specified, non-public get and set accessors of the - /// property reflected by the current instance. - /// - /// Indicates whether non-public methods should be returned in the returned array. - /// An array whose elements reflect the get and set accessors of the property reflected by the current instance. - public sealed override MethodInfo[] GetAccessors(bool nonPublic) => property.GetAccessors(nonPublic); - - /// - /// Returns a literal value associated with the property by a compiler. - /// - /// The literal value associated with the property. - public sealed override object? GetConstantValue() => property.GetConstantValue(); - - /// - /// Returns the public or non-public get accessor for this property. - /// - /// Indicates whether a non-public get accessor should be returned. - /// The object representing the get accessor for this property. - public sealed override MethodInfo? GetGetMethod(bool nonPublic) => property.GetGetMethod(nonPublic); - - /// - /// Returns an array of all the index parameters for the property. - /// - /// An array containing the parameters for the indexes. - public sealed override ParameterInfo[] GetIndexParameters() => property.GetIndexParameters(); - - /// - /// Returns an array of types representing the optional custom modifiers of the property. - /// - /// An array of objects that identify the optional custom modifiers of the current property. - public sealed override Type[] GetOptionalCustomModifiers() => property.GetOptionalCustomModifiers(); - - /// - /// Returns a literal value associated with the property by a compiler. - /// - /// An object that contains the literal value associated with the property. - public sealed override object? GetRawConstantValue() => property.GetRawConstantValue(); - - /// - /// Returns an array of types representing the required custom modifiers of the property. - /// - /// An array of objects that identify the required custom modifiers of the current property. - public sealed override Type[] GetRequiredCustomModifiers() => property.GetRequiredCustomModifiers(); - - /// - /// Gets the set accessor for this property. - /// - /// Indicates whether a non-public set accessor should be returned. - /// The object representing the set accessor for this property. - public sealed override MethodInfo? GetSetMethod(bool nonPublic) => property.GetSetMethod(nonPublic); - - /// - /// Returns the property value of a specified object with index values for indexed properties. - /// - /// The object whose property value will be returned. - /// Specifies the type of binding. - /// Defines a set of properties and enables the binding, coercion of argument types, and invocation of members using reflection. - /// Index values for indexed properties. - /// Used to govern the coercion of types. - /// The property value of the specified object. - public override object? GetValue(object? obj, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) - => property.GetValue(obj, invokeAttr, binder, index, culture); - - /// - /// Sets the property value for a specified object that has the specified binding, index, and culture-specific information. - /// - /// The object whose property value will be set. - /// The new value of the property. - /// Specifies the type of binding. - /// Defines a set of properties and enables the binding, coercion of argument types, and invocation of members using reflection. - /// Index values for indexed properties. - /// Used to govern the coercion of types. - public override void SetValue(object? obj, object? value, BindingFlags invokeAttr, Binder? binder, object?[]? index, CultureInfo? culture) - => property.SetValue(obj, value, invokeAttr, binder, index, culture); - - /// - /// Gets the class that declares this property. - /// - public sealed override Type? DeclaringType => property.DeclaringType; - - /// - /// Always returns . - /// - public sealed override MemberTypes MemberType => property.MemberType; - - /// - /// Gets the class object that was used to obtain this instance. - /// - public sealed override Type? ReflectedType => property.ReflectedType; - - /// - /// Returns an array of all custom attributes applied to this property. - /// - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this property. - public sealed override object[] GetCustomAttributes(bool inherit) => property.GetCustomAttributes(inherit); - - /// - /// Returns an array of all custom attributes applied to this property. - /// - /// The type of attribute to search for. Only attributes that are assignable to this type are returned. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// An array that contains all the custom attributes applied to this property. - public sealed override object[] GetCustomAttributes(Type attributeType, bool inherit) => property.GetCustomAttributes(attributeType, inherit); - - /// - /// Determines whether one or more attributes of the specified type or of its derived types is applied to this property. - /// - /// The type of custom attribute to search for. The search includes derived types. - /// to search this member's inheritance chain to find the attributes; otherwise, . - /// if one or more instances of or any of its derived types is applied to this property; otherwise, . - public sealed override bool IsDefined(Type attributeType, bool inherit) => property.IsDefined(attributeType, inherit); - - /// - /// Gets a value that identifies a metadata element. - /// - public sealed override int MetadataToken => property.MetadataToken; - - /// - /// Gets the module in which the type that declares the property represented by the current instance is defined. - /// - public sealed override Module Module => property.Module; - - /// - /// Returns a list of custom attributes that have been applied to the target property. - /// - /// The data about the attributes that have been applied to the target property. - public sealed override IList GetCustomAttributesData() => property.GetCustomAttributesData(); - - /// - /// Gets a collection that contains this member's custom attributes. - /// - public sealed override IEnumerable CustomAttributes => property.CustomAttributes; - - /// - PropertyInfo IMember.Metadata => property; - - /// - /// Determines whether this property is equal to the given property. - /// - /// Other property to compare. - /// if this object reflects the same property as the specified object; otherwise, . - public bool Equals(PropertyInfo? other) => other is PropertyBase property ? Equals(property.property, this.property) : Equals(this.property, other); - - /// - /// Determines whether this property is equal to the given property. - /// - /// Other property to compare. - /// if this object reflects the same property as the specified object; otherwise, . - public override bool Equals([NotNullWhen(true)] object? other) => other switch - { - PropertyBase property => this.property == property.property, - PropertyInfo property => this.property == property, - _ => false, - }; - - /// - /// Computes hash code uniquely identifies the reflected property. - /// - /// The hash code of the property. - public override int GetHashCode() => property.GetHashCode(); - - /// - /// Returns textual representation of this property. - /// - /// The textual representation of this property. - public override string? ToString() => property.ToString(); -} - -/// -/// Provides typed access to static property. -/// -/// Type of property. -public sealed class Property : PropertyBase, IProperty -{ - private sealed class Cache : MemberCache> - { - private protected override Property? Create(string propertyName, bool nonPublic) => Reflect(typeof(T), propertyName, nonPublic); - } - - private const BindingFlags PublicFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly; - private const BindingFlags NonPublicFlags = BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - private Property(PropertyInfo property) - : base(property) - { - GetMethod = property.GetMethod?.Unreflect>(); - SetMethod = property.SetMethod?.Unreflect>(); - } - - /// - /// Obtains property getter in the form of the delegate instance. - /// - /// The reflected property. - public static implicit operator MemberGetter?(Property? property) => property?.GetMethod; - - /// - /// Obtains property setter in the form of the delegate instance. - /// - /// The reflected property. - public static implicit operator MemberSetter?(Property? property) => property?.SetMethod; - - /// - /// Gets property getter. - /// - public new Method>? GetMethod { get; } - - /// - /// Gets property setter. - /// - public new Method>? SetMethod { get; } - - /// - /// Gets or sets property value. - /// - [MaybeNull] - public TValue Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => GetMethod is null ? throw new InvalidOperationException(ExceptionMessages.PropertyWithoutGetter(Name)) : GetMethod.Invoke(); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - if (SetMethod is null) - throw new InvalidOperationException(ExceptionMessages.PropertyWithoutSetter(Name)); - SetMethod.Invoke(value); - } - } - - /// - /// Returns the value of the property supported by a given object. - /// - /// Must be . - /// An object containing the value of the property reflected by this instance. - /// , if property value is obtained successfully; otherwise, . - public override bool GetValue(object? obj, out TValue? value) - { - if (GetMethod is not null && obj is not null) - { - value = GetMethod.Invoke(); - return true; - } - - value = default; - return false; - } - - /// - /// Sets the value of the property supported by the given object. - /// - /// Must be . - /// The value to assign to the property. - /// , if property value is modified successfully; otherwise, . - public override bool SetValue(object? obj, TValue value) - { - if (SetMethod is not null && obj is not null) - { - SetMethod.Invoke(value); - return true; - } - - return false; - } - - private static Property? Reflect(Type declaringType, string propertyName, bool nonPublic) - { - PropertyInfo? property = declaringType.GetProperty(propertyName, nonPublic ? NonPublicFlags : PublicFlags); - return property?.PropertyType == typeof(TValue) && property.GetIndexParameters().IsNullOrEmpty() ? - new Property(property) : - null; - } - - internal static Property? GetOrCreate(string propertyName, bool nonPublic) - => Cache.Of>(typeof(T)).GetOrCreate(propertyName, nonPublic); -} - -/// -/// Provides typed access to instance property declared in type . -/// -/// Declaring type. -/// Type of property. -public sealed class Property : PropertyBase, IProperty -{ - private sealed class Cache : MemberCache> - { - private protected override Property? Create(string propertyName, bool nonPublic) => Reflect(propertyName, nonPublic); - } - - private const BindingFlags PublicFlags = BindingFlags.Instance | BindingFlags.Public; - private const BindingFlags NonPublicFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly; - - private Property(PropertyInfo property) - : base(property) - { - GetMethod = property.GetMethod?.Unreflect>(); - SetMethod = property.SetMethod?.Unreflect>(); - } - - /// - /// Obtains property getter in the form of the delegate instance. - /// - /// The reflected property. - public static implicit operator MemberGetter?(Property? property) - => property?.GetMethod; - - /// - /// Obtains property setter in the form of the delegate instance. - /// - /// The reflected property. - public static implicit operator MemberSetter?(Property? property) - => property?.SetMethod; - - /// - /// Gets property getter. - /// - public new Method>? GetMethod { get; } - - /// - /// Gets property setter. - /// - public new Method>? SetMethod { get; } - - /// - /// Returns the value of the property supported by a given object. - /// - /// The object whose property value will be returned. - /// An object containing the value of the property reflected by this instance. - /// , if property value is obtained successfully; otherwise, . - public override bool GetValue(object? obj, out TValue? value) - { - if (GetMethod is not null && obj is T thisArg) - { - value = GetMethod.Invoke(in thisArg); - return true; - } - - value = default; - return false; - } - - /// - /// Sets the value of the property supported by the given object. - /// - /// The object whose property value will be set. - /// The value to assign to the property. - /// , if property value is modified successfully; otherwise, . - public override bool SetValue(object? obj, TValue value) - { - if (SetMethod is not null && obj is T thisArg) - { - SetMethod.Invoke(thisArg, value); - return true; - } - - return false; - } - - /// - /// Gets or sets property value. - /// - /// this argument. - /// Property value. - [MaybeNull] - public TValue this[[DisallowNull] in T @this] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (GetMethod is null) - throw new InvalidOperationException(ExceptionMessages.PropertyWithoutGetter(Name)); - - Debug.Assert(@this is not null); - return GetMethod.Invoke(@this); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - if (SetMethod is null) - throw new InvalidOperationException(ExceptionMessages.PropertyWithoutSetter(Name)); - - Debug.Assert(@this is not null); - SetMethod.Invoke(@this, value); - } - } - - private static Property? Reflect(string propertyName, bool nonPublic) - { - PropertyInfo? property = typeof(T).GetProperty(propertyName, nonPublic ? NonPublicFlags : PublicFlags); - return property?.PropertyType == typeof(TValue) && property.GetIndexParameters().IsNullOrEmpty() ? - new Property(property) : - null; - } - - internal static Property? GetOrCreate(string propertyName, bool nonPublic) - => Cache.Of(typeof(T)).GetOrCreate(propertyName, nonPublic); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Ref.cs b/src/DotNext.Reflection/Reflection/Ref.cs deleted file mode 100644 index 676db8b4e..000000000 --- a/src/DotNext.Reflection/Reflection/Ref.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; - -namespace DotNext.Reflection; - -using static Runtime.Intrinsics; - -internal static class Ref -{ - private static bool Is(Type type) => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Ref<>); - - internal static bool Reflect(Type byRefType, [MaybeNullWhen(false)] out Type underlyingType, [MaybeNullWhen(false)] out FieldInfo valueField) - { - if (Is(byRefType)) - { - underlyingType = byRefType.GetGenericArguments()[0]; - valueField = byRefType.GetField(nameof(Ref.Value), BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance); - Debug.Assert(valueField is not null); - return true; - } - - underlyingType = null; - valueField = null; - return false; - } -} - -/// -/// Wrapper for by-ref argument. -/// -/// -/// This type has special semantics when used as argument type -/// for delegates , , -/// , when -/// using strongly typed reflection. Argument of this type -/// means that it should be passed into reflected method or constructor -/// by reference. In all other scenarios, including -/// or , this type treated as regular value type -/// without special semantics. -/// -/// Referenced type. -[SecuritySafeCritical] -[StructLayout(LayoutKind.Auto)] -public struct Ref : IStrongBox, IEquatable> -{ - /// - /// Gets or sets value. - /// - [AllowNull] - public T Value; - - /// - object? IStrongBox.Value - { - get => Value; - set => Value = (T)value!; - } - - /// - /// Extracts actual value from the reference. - /// - /// Typed reference. - /// Dereferenced value. - public static implicit operator T(in Ref reference) => reference.Value; - - /// - /// Obtains a reference to the value. - /// - /// A value. - /// A reference to a value. - public static implicit operator Ref(T value) => new() { Value = value }; - - /// - /// Identifies that two references point to the same location. - /// - /// The first reference. - /// The second reference. - /// True, if both references are equal. - public static bool operator ==(in Ref first, in Ref second) - => AreSame(in first.Value, in second.Value); - - /// - /// Identifies that two references point to different locations. - /// - /// The first reference. - /// The second reference. - /// True, if both references are not equal. - public static bool operator !=(in Ref first, in Ref second) - => !AreSame(in first.Value, in second.Value); - - /// - /// Gets hash code of this reference based on its address. - /// - /// Hash code of the reference. - public override int GetHashCode() => AddressOf(in Value).GetHashCode(); - - /// - /// Always returns because two boxed references - /// have different address. - /// - /// - /// Use equality operator instead. - /// - /// Other object to compare. - /// Always . - public override bool Equals(object? other) => false; - - /// - bool IEquatable>.Equals(Ref other) => false; -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Reflector.DynamicFieldInvoker.cs b/src/DotNext.Reflection/Reflection/Reflector.DynamicFieldInvoker.cs deleted file mode 100644 index a7661745d..000000000 --- a/src/DotNext.Reflection/Reflection/Reflector.DynamicFieldInvoker.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Reflection; - -using static Runtime.CompilerServices.ReflectionUtils; - -public static partial class Reflector -{ - private static MemberExpression BuildFieldAccess(FieldInfo field, ParameterExpression target) - { - Expression? owner = field switch - { - { IsStatic: true } or { DeclaringType: null } => null, - { DeclaringType: { IsValueType: true } } => Expression.Unbox(target, field.DeclaringType), - _ => Expression.Convert(target, field.DeclaringType) - }; - - return Expression.Field(owner, field); - } - - private static Expression BuildFieldGetter(MemberExpression field, bool volatileAccess) - { - Expression fieldAccess = field; - - if (volatileAccess) - fieldAccess = VolatileRead(fieldAccess); - - if (fieldAccess.Type.IsPointer) - fieldAccess = Wrap(fieldAccess); - - if (fieldAccess.Type.IsValueType) - fieldAccess = Expression.Convert(fieldAccess, typeof(object)); - - return fieldAccess; - } - - private static DynamicInvoker BuildFieldGetter(FieldInfo field, bool volatileAccess) - { - var target = Expression.Parameter(typeof(object)); - var arguments = Expression.Parameter(typeof(Span)); - return Expression.Lambda(BuildFieldGetter(BuildFieldAccess(field, target), volatileAccess), target, arguments).Compile(); - } - - private static Expression BuildFieldSetter(MemberExpression field, ParameterExpression arguments, bool volatileAccess) - { - Expression getter = Get(arguments, Expression.Constant(0)); - if (field.Type.IsPointer) - getter = Unwrap(getter, field.Type); - if (getter.Type != field.Type) - getter = Expression.Convert(getter, field.Type); - - Expression body = volatileAccess ? - VolatileWrite(field, getter) : - Expression.Assign(field, getter); - return Expression.Block(typeof(object), body, Expression.Default(typeof(object))); - } - - private static DynamicInvoker BuildFieldSetter(FieldInfo field, bool volatileAccess) - { - var target = Expression.Parameter(typeof(object)); - var arguments = Expression.Parameter(typeof(Span)); - return Expression.Lambda(BuildFieldSetter(BuildFieldAccess(field, target), arguments, volatileAccess), target, arguments).Compile(); - } - - private static DynamicInvoker BuildFieldAccess(FieldInfo field, bool volatileAccess) - { - var target = Expression.Parameter(typeof(object)); - var arguments = Expression.Parameter(typeof(Span)); - var fieldAccess = BuildFieldAccess(field, target); - var body = Expression.Condition( - Expression.Equal(SpanLength(arguments), Expression.Constant(0)), - BuildFieldGetter(fieldAccess, volatileAccess), - BuildFieldSetter(fieldAccess, arguments, volatileAccess), - typeof(object)); - return Expression.Lambda(body, target, arguments).Compile(); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Reflector.DynamicMethodInvoker.cs b/src/DotNext.Reflection/Reflection/Reflector.DynamicMethodInvoker.cs deleted file mode 100644 index 0e6b8a889..000000000 --- a/src/DotNext.Reflection/Reflection/Reflector.DynamicMethodInvoker.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Reflection; - -using static Runtime.CompilerServices.ReflectionUtils; - -public static partial class Reflector -{ - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1013", Justification = "False positive")] - private static unsafe DynamicInvoker Unreflect(TMethod method, delegate*, Expression> resultBuilder) - where TMethod : MethodBase - { - var target = Expression.Parameter(typeof(object)); - var arguments = Expression.Parameter(typeof(Span)); - Expression? thisArg = method switch - { - { IsStatic: true } or { MemberType: MemberTypes.Constructor } or { DeclaringType: null } => null, - { DeclaringType.IsValueType: true } => Expression.Unbox(target, method.DeclaringType), - _ => Expression.Convert(target, method.DeclaringType) - }; - - ICollection arglist = new LinkedList(), prologue = new LinkedList(), epilogue = new LinkedList(); - ICollection tempVars = new LinkedList(); - - // handle parameters - foreach (var parameter in method.GetParameters()) - { - var position = Expression.Constant(parameter.Position); - var getter = Get(arguments, position); - MethodCallExpression SetArgument(Expression value) => Set(arguments, position, value); - Expression argument; - - switch (parameter.ParameterType) - { - case { IsByRefLike: true }: - throw new NotSupportedException(); - case { IsByRef: true }: - var parameterType = parameter.ParameterType.GetElementType(); - Debug.Assert(parameterType is not null); - - // value type parameter can be passed as unboxed reference - if (parameterType.IsValueType) - { - argument = Expression.Unbox(getter, parameterType); - } - else - { - var tempVar = Expression.Variable(parameterType); - tempVars.Add(tempVar); - prologue.Add(Expression.Assign(tempVar, parameterType.IsPointer ? Unwrap(getter, parameterType) : Expression.Convert(getter, parameterType))); - if (parameterType.IsPointer) - epilogue.Add(SetArgument(Wrap(tempVar))); - else - epilogue.Add(SetArgument(tempVar)); - argument = tempVar; - } - - break; - case { IsPointer: true }: - argument = Unwrap(getter, parameter.ParameterType); - break; - default: - argument = Expression.Convert(getter, parameter.ParameterType); - break; - } - - arglist.Add(argument); - } - - // construct body of the method - Expression result = resultBuilder(thisArg, method, arglist); - if (result.Type.IsByRefLike) - throw new NotSupportedException(); - else if (result.Type == typeof(void)) - epilogue.Add(Expression.Default(typeof(object))); - else if (result.Type.IsPointer) - result = Wrap(result); - else if (result.Type.IsValueType) - result = Expression.Convert(result, typeof(object)); - - // construct lambda expression - bool useTailCall; - if (epilogue.Count is 0) - { - useTailCall = true; - } - else if (result.Type == typeof(void)) - { - result = Expression.Block(typeof(object), tempVars, prologue.Append(result).Concat(epilogue)); - useTailCall = false; - } - else - { - var resultVar = Expression.Variable(typeof(object)); - tempVars.Add(resultVar); - result = Expression.Assign(resultVar, result); - epilogue.Add(resultVar); - result = Expression.Block(typeof(object), tempVars, prologue.Append(result).Concat(epilogue)); - useTailCall = false; - } - - // help GC - arglist.Clear(); - prologue.Clear(); - epilogue.Clear(); - tempVars.Clear(); - return Expression.Lambda(result, useTailCall, target, arguments).Compile(); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Reflector.cs b/src/DotNext.Reflection/Reflection/Reflector.cs deleted file mode 100644 index 60cb8c9f7..000000000 --- a/src/DotNext.Reflection/Reflection/Reflector.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Provides access to fast reflection routines. -/// -public static partial class Reflector -{ - private static MemberInfo? MemberOf(LambdaExpression exprTree) => exprTree.Body switch - { - MemberExpression expr => expr.Member, - MethodCallExpression expr => expr.Method, - NewExpression expr => expr.Constructor, - BinaryExpression expr => expr.Method, - UnaryExpression expr => expr.Method, - IndexExpression expr => expr.Indexer, - _ => null, - }; - - /// - /// Extracts member metadata from expression tree. - /// - /// Expression tree. - /// Type of member to reflect. - /// The type of lambda expression. - /// Reflected member; or , if lambda expression doesn't reference a member. - public static TMember? MemberOf(Expression exprTree) - where TMember : MemberInfo - where TDelegate : Delegate - => MemberOf(exprTree) as TMember; - - /// - /// Unreflects constructor to its typed and callable representation. - /// - /// A delegate representing signature of constructor. - /// Constructor to unreflect. - /// Unreflected constructor. - public static Constructor? Unreflect(this ConstructorInfo ctor) - where TDelegate : MulticastDelegate => Constructor.GetOrCreate(ctor); - - /// - /// Unreflects method to its typed and callable representation. - /// - /// A delegate representing signature of method. - /// A method to unreflect. - /// Unreflected method. - public static Method? Unreflect(this MethodInfo method) - where TDelegate : MulticastDelegate => Method.GetOrCreate(method); - - /// - /// Obtains managed pointer to the static field. - /// - /// The field type. - /// The field to unreflect. - /// Unreflected static field. - public static Field Unreflect(this FieldInfo field) => Field.GetOrCreate(field); - - /// - /// Obtains managed pointer to the instance field. - /// - /// The type of the object that declares instance field. - /// The field type. - /// The field to unreflect. - /// Unreflected instance field. - public static Field Unreflect(this FieldInfo field) - where T : notnull => Field.GetOrCreate(field); - - /// - /// Creates dynamic invoker for the field. - /// - /// - /// This method doesn't cache the result so the caller is responsible for storing delegate to the field or cache. - /// supports the following combination of values: , or - /// both. - /// - /// The field to unreflect. - /// Describes the access to the field using invoker. - /// to generate volatile access to the field. - /// The delegate that can be used to access field value. - /// is invalid. - /// The type of is ref-like value type. - public static DynamicInvoker Unreflect(this FieldInfo field, BindingFlags flags = BindingFlags.GetField | BindingFlags.SetField, bool volatileAccess = false) - { - if (field.FieldType.IsByRefLike) - throw new NotSupportedException(); - - return flags switch - { - BindingFlags.GetField => BuildFieldGetter(field, volatileAccess), - BindingFlags.SetField => BuildFieldSetter(field, volatileAccess), - BindingFlags.GetField | BindingFlags.SetField => BuildFieldAccess(field, volatileAccess), - _ => throw new ArgumentOutOfRangeException(nameof(flags)) - }; - } - - /// - /// Creates dynamic invoker for the method. - /// - /// The method to unreflect. - /// The delegate that can be used to invoke the method. - /// The type of parameter or return type is ref-like value type. - public static unsafe DynamicInvoker Unreflect(this MethodInfo method) - => Unreflect(method, &Expression.Call); - - /// - /// Creates dynamic invoker for the constructor. - /// - /// The constructor to unreflect. - /// The delegate that can be used to create an object instance. - /// The type of parameter is ref-like value type. - public static unsafe DynamicInvoker Unreflect(this ConstructorInfo ctor) - { - return Unreflect(ctor, &New); - - static Expression New(Expression? thisArg, ConstructorInfo ctor, IEnumerable args) - => Expression.New(ctor, args); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Signature.cs b/src/DotNext.Reflection/Reflection/Signature.cs deleted file mode 100644 index 458668854..000000000 --- a/src/DotNext.Reflection/Reflection/Signature.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System.Diagnostics; -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Reflection; - -/// -/// Describes signature of function or procedure. -/// -internal static class Signature -{ - private static void Reflect(ParameterExpression argListParameter, out Type[] parameters, out Expression[] arglist) - { - var publicFields = argListParameter.Type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly); - parameters = new Type[publicFields.LongLength]; - arglist = new Expression[publicFields.LongLength]; - for (var i = 0L; i < publicFields.LongLength; i++) - { - var field = publicFields[i]; - if (Ref.Reflect(field.FieldType, out var underlyingType, out var valueField)) - { - parameters[i] = underlyingType.MakeByRefType(); - arglist[i] = Expression.Field(Expression.Field(argListParameter, field), valueField); - } - else - { - parameters[i] = field.FieldType; - arglist[i] = Expression.Field(argListParameter, field); - } - } - } - - internal static (Type[] Parameters, Expression[] ArgList, ParameterExpression ArgListParameter) Reflect(Type argumentsType) - { - var argListParameter = argumentsType.IsByRef ? - Expression.Parameter(argumentsType, "arguments") : - Expression.Parameter(argumentsType.MakeByRefType(), "arguments"); - Reflect(argListParameter, out var parameters, out var arglist); - - return (Parameters: parameters, ArgList: arglist, ArgListParameter: argListParameter); - } - - internal static (Type[] Parameters, Expression[] ArgList, ParameterExpression ArgListParameter) Reflect() - where TArgs : struct - => Reflect(typeof(TArgs)); - - private static Expression NormalizeOutArgument(Type actualParameter, Expression expectedArgument, out ParameterExpression localVar, out Expression prologue, out Expression epilogue) - { - Debug.Assert(actualParameter.IsByRef); - var elementType = actualParameter.GetElementType(); - Debug.Assert(elementType is not null); - - // for value type - // object local = args.param; - // if (local is null) local = default(T) - // ...call(ref Unbox(local)) - // args.param = local; - if (elementType.IsValueType) - { - localVar = Expression.Variable(expectedArgument.Type); - var assignment = Expression.Assign(localVar, expectedArgument); - var condition = Expression.IfThen( - Expression.ReferenceEqual(localVar, Expression.Constant(null, expectedArgument.Type)), - Expression.Assign(localVar, Expression.Convert(Expression.Default(elementType), expectedArgument.Type))); - prologue = Expression.Block(assignment, condition); - epilogue = Expression.Assign(expectedArgument, localVar); - return Expression.Unbox(localVar, elementType); - } - - // for reference type - // T local = (T)args.param; - // ...call(ref local) - // args.param = local - localVar = Expression.Variable(elementType); - prologue = Expression.Assign(localVar, Expression.Convert(expectedArgument, elementType)); - epilogue = Expression.Assign(expectedArgument, localVar); - - return localVar; - } - - private static Expression NormalizeRefArgument(Type actualParameter, Expression expectedArgument, out ParameterExpression? localVar, out Expression? prologue, out Expression? epilogue) - { - Debug.Assert(actualParameter.IsByRef); - var elementType = actualParameter.GetElementType(); - Debug.Assert(elementType is not null); - - // for value type use unboxed reference to value type - if (elementType.IsValueType) - { - epilogue = prologue = localVar = null; - return Expression.Unbox(expectedArgument, elementType); - } - - // for reference type, roundrip via local variable is required - // the same as for out reference type parameter - return NormalizeOutArgument(actualParameter, expectedArgument, out localVar, out prologue, out epilogue); - } - - private static Expression NormalizeArgument(Type actualParameter, Expression expectedArgument, out ParameterExpression? localVar, out Expression? prologue, out Expression? epilogue) - { - epilogue = prologue = localVar = null; - - // convert object to the actual type of the parameter - return actualParameter == expectedArgument.Type ? - expectedArgument : - Expression.Convert(expectedArgument, actualParameter); - } - - private static Expression NormalizeArgument(ParameterInfo actualParameter, Expression expectedArgument, out ParameterExpression? localVar, out Expression? prologue, out Expression? epilogue) - { - if (actualParameter.ParameterType.IsAssignableFromWithoutBoxing(expectedArgument.Type)) - { - epilogue = prologue = localVar = null; - return expectedArgument; - } - - if (expectedArgument.Type == typeof(object)) - { - // out parameters can be passed as null - if (actualParameter.IsOut) - return NormalizeOutArgument(actualParameter.ParameterType, expectedArgument, out localVar, out prologue, out epilogue); - - // ref parameter of value type should never be null - // ref parameter of reference type can be null - if (actualParameter.ParameterType.IsByRef) - return NormalizeRefArgument(actualParameter.ParameterType, expectedArgument, out localVar, out prologue, out epilogue); - - // regular argument should be always converted to target type - return NormalizeArgument(actualParameter.ParameterType, expectedArgument, out localVar, out prologue, out epilogue); - } - - epilogue = prologue = localVar = null; - - if (actualParameter.ParameterType.IsByRef) - return expectedArgument; - - return Expression.Convert(expectedArgument, actualParameter.ParameterType); - } - - internal static bool NormalizeArguments(ParameterInfo[] actualParameters, Expression[] expectedArguments, ICollection locals, ICollection prologue, ICollection epilogue) - { - if (actualParameters.LongLength != expectedArguments.LongLength) - return false; - for (var i = 0L; i < actualParameters.LongLength; i++) - { - if ((expectedArguments[i] = NormalizeArgument(actualParameters[i], expectedArguments[i], out var localVar, out var pro, out var epi)) is null) - return false; - - if (localVar is not null) - locals.Add(localVar); - if (pro is not null) - prologue.Add(pro); - if (epi is not null) - epilogue.Add(epi); - } - - return true; - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Attributes.cs b/src/DotNext.Reflection/Reflection/Type.Attributes.cs deleted file mode 100644 index 3b9a0c00f..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Attributes.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Reflection; - -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Provides typed access to the type attribute. - /// - /// Type of attribute. - public static class Attribute - where TAttribute : Attribute - { - /// - /// Returns attribute associated with the type . - /// - /// True to find inherited attribute. - /// Optional predicate to check attribute properties. - /// Attribute associated with type ; or null, if attribute doesn't exist. - public static TAttribute? Get(bool inherit = false, Predicate? condition = null) - { - var attr = RuntimeType.GetCustomAttribute(inherit); - return attr is null || condition is null || condition(attr) ? attr : null; - } - - /// - /// Returns attribute associated with the type . - /// - /// True to find inherited attribute. - /// Optional predicate to check attribute properties. - /// Attribute associated with type . - /// Event doesn't exist. - public static TAttribute Require(bool inherit = false, Predicate? condition = null) - => Get(inherit, condition) ?? throw MissingAttributeException.Create(); - - /// - /// Get all custom attributes of type . - /// - /// True to find inherited attribute. - /// Optional predicate to check attribute properties. - /// All attributes associated with type . - public static IEnumerable GetAll(bool inherit = false, Predicate? condition = null) - => from attr in RuntimeType.GetCustomAttributes(inherit) - where condition is null || condition(attr) - select attr; - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Ctors.cs b/src/DotNext.Reflection/Reflection/Type.Ctors.cs deleted file mode 100644 index 5f465f81e..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Ctors.cs +++ /dev/null @@ -1,716 +0,0 @@ -using DefaultMemberAttribute = System.Reflection.DefaultMemberAttribute; - -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Reflects constructor as function. - /// - /// to reflect non-public constructor. - /// A structure describing constructor signature. - /// Constructor for type ; or null, if it doesn't exist. - public static Reflection.Constructor>? GetConstructor(bool nonPublic = false) - where TArgs : struct - => Constructor.Get>(nonPublic); - - /// - /// Reflects constructor as function. - /// - /// to reflect non-public constructor. - /// A structure describing constructor signature. - /// Constructor for type . - /// Constructor doesn't exist. - public static Reflection.Constructor> RequireConstructor(bool nonPublic = false) - where TArgs : struct - => GetConstructor(nonPublic) ?? throw MissingConstructorException.Create(); - - /// - /// Creates a new instance of type . - /// - /// The structure containing arguments to be passed into constructor. - /// True to reflect non-public constructor. - /// A structure describing constructor signature. - /// A new instance of type . - /// Constructor doesn't exist. - public static T NewInstance(in TArgs args, bool nonPublic = false) - where TArgs : struct - => RequireConstructor(nonPublic).Invoke(args)!; - - /// - /// Provides access to constructor of type without parameters. - /// - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Reflects constructor of type which signature - /// is specified by delegate type. - /// - /// to reflect non-public constructor. - /// Type of delegate describing constructor signature. - /// Reflected constructor; or , if constructor doesn't exist. - public static Reflection.Constructor? Get(bool nonPublic = false) - where TSignature : MulticastDelegate - => Reflection.Constructor.GetOrCreate(nonPublic); - - /// - /// Reflects constructor of type which signature - /// is specified by delegate type. - /// - /// to reflect non-public constructor. - /// Type of delegate describing constructor signature. - /// Reflected constructor. - /// Constructor doesn't exist. - public static Reflection.Constructor Require(bool nonPublic = false) - where TSignature : MulticastDelegate - => Get(nonPublic) ?? throw MissingConstructorException.Create(); - - /// - /// Returns public constructor of type without parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor without parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Get>(nonPublic); - - /// - /// Returns public constructor of type without parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor without parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(); - } - - /// - /// Invokes constructor. - /// - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(bool nonPublic = false) => Require(nonPublic).Invoke(); - } - - /// - /// Provides access to constructor of type with single parameter. - /// - /// Type of constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with single parameter of type . - /// - /// to reflect non-public constructor. - /// Reflected constructor with single parameter; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with single parameter of type . - /// - /// to reflect non-public constructor. - /// Reflected constructor with single parameter. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// Constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(TParam arg, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg); - } - - /// - /// Invokes constructor. - /// - /// Constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(TParam arg, bool nonPublic = false) => Require(nonPublic).Invoke(arg); - } - - /// - /// Provides access to constructor of type with two parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with two - /// parameters of type and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with two parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with two - /// parameters of type and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with two parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, bool nonPublic = false) => Require(nonPublic).Invoke(arg1, arg2); - } - - /// - /// Provides access to constructor of type with three parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with three - /// parameters of type , and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with three parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with three - /// parameters of type , and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with three parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, bool nonPublic = false) => Require(nonPublic).Invoke(arg1, arg2, arg3); - } - - /// - /// Provides access to constructor of type with four parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - /// Type of fourth constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with four - /// parameters of type , , and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with four parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with four - /// parameters of type , , and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with four parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3, arg4); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, bool nonPublic = false) => Require(nonPublic).Invoke(arg1, arg2, arg3, arg4); - } - - /// - /// Provides access to constructor of type with five parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - /// Type of fourth constructor parameter. - /// Type of fifth constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with five - /// parameters of type , , - /// , and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with five parameters; or null, if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with five - /// parameters of type , , - /// , and . - /// - /// to reflect non-public constructor. - /// Reflected constructor with five parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3, arg4, arg5); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, bool nonPublic = false) - => Require(nonPublic).Invoke(arg1, arg2, arg3, arg4, arg5); - } - - /// - /// Provides access to constructor of type with six parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - /// Type of fourth constructor parameter. - /// Type of fifth constructor parameter. - /// Type of sixth constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with six parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with six parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with six parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with six parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3, arg4, arg5, arg6); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, bool nonPublic = false) - => Require(nonPublic).Invoke(arg1, arg2, arg3, arg4, arg5, arg6); - } - - /// - /// Provides access to constructor of type with seven parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - /// Type of fourth constructor parameter. - /// Type of fifth constructor parameter. - /// Type of sixth constructor parameter. - /// Type of seventh constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with seven parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with seven parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with seven parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with seven parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3, arg4, arg5, arg6, arg7); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, bool nonPublic = false) - => Require(nonPublic).Invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7); - } - - /// - /// Provides access to constructor of type with eight parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - /// Type of fourth constructor parameter. - /// Type of fifth constructor parameter. - /// Type of sixth constructor parameter. - /// Type of seventh constructor parameter. - /// Type of eighth constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with eight parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with eight parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with eight parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with eight parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// Eighth constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// Eighth constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, bool nonPublic = false) - => Require(nonPublic).Invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - } - - /// - /// Provides access to constructor of type with nine parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - /// Type of fourth constructor parameter. - /// Type of fifth constructor parameter. - /// Type of sixth constructor parameter. - /// Type of seventh constructor parameter. - /// Type of eighth constructor parameter. - /// Type of ninth constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with nine parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with nine parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with nine parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with nine parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// Eighth constructor argument. - /// Ninth constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// Eighth constructor argument. - /// Ninth constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, bool nonPublic = false) - => Require(nonPublic).Invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); - } - - /// - /// Provides access to constructor of type with nine parameters. - /// - /// Type of first constructor parameter. - /// Type of second constructor parameter. - /// Type of third constructor parameter. - /// Type of fourth constructor parameter. - /// Type of fifth constructor parameter. - /// Type of sixth constructor parameter. - /// Type of seventh constructor parameter. - /// Type of eighth constructor parameter. - /// Type of ninth constructor parameter. - /// Type of tenth constructor parameter. - [DefaultMember(nameof(Invoke))] - public static class Constructor - { - /// - /// Returns constructor with ten parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with ten parameters; or , if it doesn't exist. - public static Reflection.Constructor>? Get(bool nonPublic = false) - => Constructor.Get>(nonPublic); - - /// - /// Returns constructor with ten parameters. - /// - /// to reflect non-public constructor. - /// Reflected constructor with ten parameters. - /// Constructor doesn't exist. - public static Reflection.Constructor> Require(bool nonPublic = false) - => Get(nonPublic) ?? throw MissingConstructorException.Create>(); - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// Eighth constructor argument. - /// Ninth constructor argument. - /// Tenth constructor argument. - /// to reflect non-public constructor. - /// Instance of if constructor exists. - public static Optional TryInvoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, bool nonPublic = false) - { - Func? ctor = Get(nonPublic); - return ctor is null ? Optional.None : ctor(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - } - - /// - /// Invokes constructor. - /// - /// First constructor argument. - /// Second constructor argument. - /// Third constructor argument. - /// Fourth constructor argument. - /// Fifth constructor argument. - /// Sixth constructor argument. - /// Seventh constructor argument. - /// Eighth constructor argument. - /// Ninth constructor argument. - /// Tenth constructor argument. - /// to reflect non-public constructor. - /// Instance of . - /// Constructor doesn't exist. - public static T Invoke(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, bool nonPublic = false) - => Require(nonPublic).Invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Events.cs b/src/DotNext.Reflection/Reflection/Type.Events.cs deleted file mode 100644 index 8eff9b552..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Events.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Provides typed access to instance event declared in type . - /// - /// Type of event handler. - public static class Event - where THandler : MulticastDelegate - { - /// - /// Gets instance event. - /// - /// Name of event. - /// to reflect non-public event. - /// Instance event; or , if event doesn't exist. - public static Event? Get(string eventName, bool nonPublic = false) - => Event.GetOrCreate(eventName, nonPublic); - - /// - /// Gets instance event. - /// - /// Name of event. - /// to reflect non-public event. - /// Instance event. - /// Event doesn't exist. - public static Event Require(string eventName, bool nonPublic = false) - => Get(eventName, nonPublic) ?? throw MissingEventException.Create(eventName); - - /// - /// Gets static event. - /// - /// Name of event. - /// to reflect non-public event. - /// Static event; or , if event doesn't exist. - public static Reflection.Event? GetStatic(string eventName, bool nonPublic = false) - => Reflection.Event.GetOrCreate(eventName, nonPublic); - - /// - /// Gets static event. - /// - /// Name of event. - /// to reflect non-public event. - /// Static event. - /// Event doesn't exist. - public static Reflection.Event RequireStatic(string eventName, bool nonPublic = false) - => GetStatic(eventName, nonPublic) ?? throw MissingEventException.Create(eventName); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Fields.cs b/src/DotNext.Reflection/Reflection/Type.Fields.cs deleted file mode 100644 index 1242d0eeb..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Fields.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Provides typed access to instance field declared in type . - /// - /// Type of field value. - public static class Field - { - /// - /// Gets instance field. - /// - /// Name of field. - /// to reflect non-public field. - /// Instance field; or , if field doesn't exist. - public static Field? Get(string fieldName, bool nonPublic = false) - => Field.GetOrCreate(fieldName, nonPublic); - - /// - /// Gets instance field. - /// - /// Name of field. - /// to reflect non-public field. - /// Instance field. - /// Field doesn't exist. - public static Field Require(string fieldName, bool nonPublic = false) - => Get(fieldName, nonPublic) ?? throw MissingFieldException.Create(fieldName); - - /// - /// Gets static field. - /// - /// Name of field. - /// to reflect non-public field. - /// Instance field; or , if field doesn't exist. - public static Reflection.Field? GetStatic(string fieldName, bool nonPublic = false) - => Reflection.Field.GetOrCreate(fieldName, nonPublic); - - /// - /// Gets static field. - /// - /// Name of field. - /// to reflect non-public field. - /// Instance field. - /// Field doesn't exist. - public static Reflection.Field RequireStatic(string fieldName, bool nonPublic = false) - => GetStatic(fieldName, nonPublic) ?? throw MissingFieldException.Create(fieldName); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Indexers.cs b/src/DotNext.Reflection/Reflection/Type.Indexers.cs deleted file mode 100644 index 62360a30e..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Indexers.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Provides access to indexer property declared in type . - /// - /// A structure representing parameters of indexer. - /// Property value. - public static class Indexer - where TIndicies : struct - { - private const string DefaultIndexerName = "Item"; - - /// - /// Reflects static indexer property. - /// - /// The name of the indexer property. - /// to reflect non-public property. - /// The reflected property; or , if property doesn't exist. - public static Reflection.Indexer? GetStatic(string propertyName, bool nonPublic = false) - => Reflection.Indexer.GetOrCreate(propertyName, nonPublic); - - /// - /// Reflects static indexer property. - /// - /// The name of the indexer property. - /// to reflect non-public property. - /// The reflected indexer property. - /// The property doesn't exist. - public static Reflection.Indexer RequireStatic(string propertyName, bool nonPublic = false) - => GetStatic(propertyName, nonPublic) ?? throw MissingPropertyException.Create(propertyName); - - /// - /// Reflects instance indexer property. - /// - /// The name of the indexer property. - /// to reflect non-public property. - /// The reflected property; or , if property doesn't exist. - public static Indexer? Get(string propertyName = DefaultIndexerName, bool nonPublic = false) - => Indexer.GetOrCreate(propertyName, nonPublic); - - /// - /// Reflects instance indexer property. - /// - /// The name of the indexer property. - /// to reflect non-public property. - /// The reflected indexer property. - /// The property doesn't exist. - public static Indexer Require(string propertyName = DefaultIndexerName, bool nonPublic = false) - => Get(propertyName, nonPublic) ?? throw MissingPropertyException.Create(propertyName); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Methods.cs b/src/DotNext.Reflection/Reflection/Type.Methods.cs deleted file mode 100644 index 427ac5ad3..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Methods.cs +++ /dev/null @@ -1,814 +0,0 @@ -using Missing = System.Reflection.Missing; - -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Reflects static method declared in type which - /// returns value of type and has arguments described - /// by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; or if method doesn't exist. - public static Reflection.Method>? GetStaticMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method declared in type which - /// returns value of type and has arguments described - /// by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The method doesn't exist. - public static Reflection.Method> RequireStaticMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => GetStaticMethod(methodName, nonPublic) ?? throw MissingMethodException.Create(methodName); - - /// - /// Reflects instance method declared in type which - /// returns value of type and has arguments described - /// by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; or if method doesn't exist. - public static Reflection.Method>? GetMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method declared in type which - /// returns value of type and has arguments described - /// by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The method doesn't exist. - public static Reflection.Method> RequireMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => GetMethod(methodName, nonPublic) ?? throw MissingMethodException.Create(methodName); - - /// - /// Reflects static method declared in type without return value - /// and has arguments described by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; or if method doesn't exist. - public static Reflection.Method>? GetStaticMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method declared in type without return value - /// and has arguments described by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; or . - /// The method doesn't exist. - public static Reflection.Method> RequireStaticMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => GetStaticMethod(methodName, nonPublic) ?? throw MissingMethodException.Create(methodName); - - /// - /// Reflects instance method declared in type without return value - /// and has arguments described by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; or if method doesn't exist. - public static Reflection.Method>? GetMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method declared in type without return value - /// and has arguments described by type . - /// - /// - /// The reflected method is represented by universal delegate type which - /// allocates arguments on the stack instead of registry-based allocated or any other optimizations - /// performed by .NET Runtime. - /// - /// The value type describing arguments of the method. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; or . - /// The method doesn't exist. - public static Reflection.Method> RequireMethod(string methodName, bool nonPublic = false) - where TArgs : struct - => GetMethod(methodName, nonPublic) ?? throw MissingMethodException.Create(methodName); - - /// - /// Provides access to methods declared in type . - /// - public static class Method - { - /// - /// Reflects class method. - /// - /// - /// This method supports special types of delegates: or for instance methods, - /// or for static methods. - /// The value returned by this method is cached by the given delegate type and method name. - /// Two calls of this method with the same arguments will return the same object. - /// - /// The delegate describing signature of the requested method. - /// The name of the method. - /// The type of the method to be resolved. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method? Get(string methodName, MethodLookup methodType, bool nonPublic = false) - where TSignature : MulticastDelegate - => Reflection.Method.GetOrCreate(methodName, nonPublic, methodType); - - /// - /// Reflects class method. - /// - /// - /// This method supports special types of delegates: or for instance methods, - /// or for static methods. - /// The value returned by this method is cached by the given delegate type and method name. - /// Two calls of this method with the same arguments will return the same object. - /// - /// The delegate describing signature of the requested method. - /// The name of the method. - /// The type of the method to be resolved. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method Require(string methodName, MethodLookup methodType, bool nonPublic = false) - where TSignature : MulticastDelegate - => Get(methodName, methodType, nonPublic) ?? throw MissingMethodException.Create(methodName); - - /// - /// Reflects instance parameterless method without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance parameterless method without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create(methodName); - - /// - /// Reflects static parameterless method without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method? GetStatic(string methodName, bool nonPublic = false) - => Get(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static parameterless method without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create(methodName); - - /// - /// Reflects instance parameterless method which as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance parameterless method which as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static parameterless method which as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static parameterless method which as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - } - - /// - /// Provides access to methods with single parameter declared in type . - /// - /// Type of method parameter. - public static class Method - { - /// - /// Reflects instance method with single parameter and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with single parameter and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with single parameter and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with single parameter and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects instance method with single parameter as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with single parameter as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with single parameter as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with single parameter as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - } - - /// - /// Provides access to methods with two parameters declared in type . - /// - /// Type of first method parameter. - /// Type of second method parameter. - public static class Method - { - /// - /// Reflects instance method with two parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with two parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with two parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with two parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects instance method with two parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with two parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with two parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with two parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - } - - /// - /// Provides access to methods with three parameters declared in type . - /// - /// Type of first method parameter. - /// Type of second method parameter. - /// Type of third method parameter. - public static class Method - { - /// - /// Reflects instance method with three parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with three parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with three parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with three parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects instance method with three parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with three parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with three parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with three parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - } - - /// - /// Provides access to methods with four parameters declared in type . - /// - /// Type of first method parameter. - /// Type of second method parameter. - /// Type of third method parameter. - /// Type of fourth method parameter. - public static class Method - { - /// - /// Reflects instance method with four parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with four parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with four parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with four parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects instance method with four parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with four parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with four parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with four parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - } - - /// - /// Provides access to methods with five parameters declared in type . - /// - /// Type of first method parameter. - /// Type of second method parameter. - /// Type of third method parameter. - /// Type of fourth method parameter. - /// Type of fifth method parameter. - public static class Method - { - /// - /// Reflects instance method with five parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with five parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with five parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with five parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects instance method with five parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with five parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with five parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with five parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - } - - /// - /// Provides access to methods with six parameters declared in type . - /// - /// Type of first method parameter. - /// Type of second method parameter. - /// Type of third method parameter. - /// Type of fourth method parameter. - /// Type of fifth method parameter. - /// Type of sixth method parameter. - public static class Method - { - /// - /// Reflects instance method with six parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with six parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with six parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with six parameters and without return type as delegate type . - /// - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects instance method with six parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? Get(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Instance, nonPublic); - - /// - /// Reflects instance method with six parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> Require(string methodName, bool nonPublic = false) - => Get(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - - /// - /// Reflects static method with six parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method; otherwise, if method doesn't exist. - public static Reflection.Method>? GetStatic(string methodName, bool nonPublic = false) - => Method.Get>(methodName, MethodLookup.Static, nonPublic); - - /// - /// Reflects static method with six parameters as delegate type . - /// - /// The method return type. - /// The name of the method. - /// to reflect non-public method. - /// The reflected method. - /// The requested method doesn't exist. - public static Reflection.Method> RequireStatic(string methodName, bool nonPublic = false) - => GetStatic(methodName, nonPublic) ?? throw MissingMethodException.Create>(methodName); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Operators.cs b/src/DotNext.Reflection/Reflection/Type.Operators.cs deleted file mode 100644 index 0acb007c0..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Operators.cs +++ /dev/null @@ -1,72 +0,0 @@ -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Represents unary operator applicable to type . - /// - public static class Operator - { - /// - /// Gets unary operator. - /// - /// Unary operator type. - /// Operator resolution strategy. - /// Result of unary operator. - /// Unary operator; or , if it doesn't exist. - public static UnaryOperator? Get(UnaryOperator op, OperatorLookup lookup = OperatorLookup.Any) => UnaryOperator.GetOrCreate(op, lookup); - - /// - /// Gets unary operator of the same result type as its operand. - /// - /// Unary operator type. - /// Operator resolution strategy. - /// Unary operator; or , if it doesn't exist. - public static UnaryOperator? Get(UnaryOperator op, OperatorLookup lookup = OperatorLookup.Any) => Get(op, lookup); - - /// - /// Gets unary operator. - /// - /// Unary operator type. - /// Operator resolution strategy. - /// Result of unary operator. - /// Unary operator. - /// Operator doesn't exist. - public static UnaryOperator Require(UnaryOperator op, OperatorLookup lookup = OperatorLookup.Any) => Get(op, lookup) ?? throw MissingOperatorException.Create(op); - - /// - /// Gets unary operator of the same result type as its operand. - /// - /// Unary operator type. - /// Operator resolution strategy. - /// Unary operator. - /// Operator doesn't exist. - public static UnaryOperator Require(UnaryOperator op, OperatorLookup lookup = OperatorLookup.Any) => Require(op, lookup); - } - - /// - /// Represents binary operator applicable to type . - /// - /// Type of second operand. - public static class Operator - { - /// - /// Gets binary operator. - /// - /// Binary operator type. - /// Operator resolution strategy. - /// Result of binary operator. - /// Binary operator; or , if it doesn't exist. - public static BinaryOperator? Get(BinaryOperator op, OperatorLookup lookup = OperatorLookup.Any) => BinaryOperator.GetOrCreate(op, lookup); - - /// - /// Gets binary operator. - /// - /// Binary operator type. - /// Operator resolution strategy. - /// Result of binary operator. - /// Binary operator. - /// Operator doesn't exist. - public static BinaryOperator Require(BinaryOperator op, OperatorLookup lookup = OperatorLookup.Any) => Get(op, lookup) ?? throw MissingOperatorException.Create(op); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.Properties.cs b/src/DotNext.Reflection/Reflection/Type.Properties.cs deleted file mode 100644 index 74d6370e5..000000000 --- a/src/DotNext.Reflection/Reflection/Type.Properties.cs +++ /dev/null @@ -1,125 +0,0 @@ -namespace DotNext.Reflection; - -public static partial class Type -{ - /// - /// Provides access to property declared in type . - /// - /// Type of property. - public static class Property - { - /// - /// Reflects instance property. - /// - /// Name of property. - /// to reflect non-public property. - /// Property field; or , if property doesn't exist. - public static Property? Get(string propertyName, bool nonPublic = false) - => Property.GetOrCreate(propertyName, nonPublic); - - /// - /// Reflects instance property. - /// - /// Name of property. - /// to reflect non-public property. - /// Instance property. - /// Property doesn't exist. - public static Property Require(string propertyName, bool nonPublic = false) - => Get(propertyName, nonPublic) ?? throw MissingPropertyException.Create(propertyName); - - /// - /// Reflects instance property getter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected getter method; or , if getter method doesn't exist. - public static Reflection.Method>? GetGetter(string propertyName, bool nonPublic = false) - => Get(propertyName, nonPublic)?.GetMethod; - - /// - /// Reflects instance property getter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected getter method. - /// The getter doesn't exist. - public static Reflection.Method> RequireGetter(string propertyName, bool nonPublic = false) - => GetGetter(propertyName, nonPublic) ?? throw MissingMethodException.Create>(propertyName.ToGetterName()); - - /// - /// Reflects instance property setter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected setter method; or , if setter method doesn't exist. - public static Reflection.Method>? GetSetter(string propertyName, bool nonPublic = false) - => Get(propertyName, nonPublic)?.SetMethod; - - /// - /// Reflects instance property setter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected setter method. - /// The setter doesn't exist. - public static Reflection.Method> RequireSetter(string propertyName, bool nonPublic = false) - => GetSetter(propertyName, nonPublic) ?? throw MissingMethodException.Create>(propertyName.ToSetterName()); - - /// - /// Reflects static property. - /// - /// Name of property. - /// True to reflect non-public property. - /// Instance property; or , if property doesn't exist. - public static Reflection.Property? GetStatic(string propertyName, bool nonPublic = false) - => Reflection.Property.GetOrCreate(propertyName, nonPublic); - - /// - /// Reflects static property. - /// - /// Name of property. - /// True to reflect non-public property. - /// Instance property. - /// Property doesn't exist. - public static Reflection.Property RequireStatic(string propertyName, bool nonPublic = false) - => GetStatic(propertyName, nonPublic) ?? throw MissingFieldException.Create(propertyName); - - /// - /// Reflects static property getter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected getter method; or , if getter method doesn't exist. - public static Reflection.Method>? GetStaticGetter(string propertyName, bool nonPublic = false) - => GetStatic(propertyName, nonPublic)?.GetMethod; - - /// - /// Reflects static property setter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected setter method. - /// The setter doesn't exist. - public static Reflection.Method> RequireStaticGetter(string propertyName, bool nonPublic = false) - => GetStaticGetter(propertyName, nonPublic) ?? throw MissingMethodException.Create>(propertyName.ToGetterName()); - - /// - /// Reflects static property setter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected setter method; or , if setter method doesn't exist. - public static Reflection.Method>? GetStaticSetter(string propertyName, bool nonPublic = false) - => GetStatic(propertyName, nonPublic)?.SetMethod; - - /// - /// Reflects static property setter method. - /// - /// The name of the property. - /// to reflect non-public property. - /// The reflected setter method. - /// The setter doesn't exist. - public static Reflection.Method> RequireStaticSetter(string propertyName, bool nonPublic = false) - => GetStaticSetter(propertyName, nonPublic) ?? throw MissingMethodException.Create>(propertyName.ToSetterName()); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/Type.cs b/src/DotNext.Reflection/Reflection/Type.cs deleted file mode 100644 index 55bc10f08..000000000 --- a/src/DotNext.Reflection/Reflection/Type.cs +++ /dev/null @@ -1,168 +0,0 @@ -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using static System.Linq.Expressions.Expression; -using static System.Runtime.CompilerServices.RuntimeHelpers; - -namespace DotNext.Reflection; - -using Intrinsics = Runtime.Intrinsics; - -/// -/// Provides typed access to class or value type metadata. -/// -/// Reflected type. -public static partial class Type<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> -{ - /// - /// Gets reflected type. - /// - public static Type RuntimeType => typeof(T); - - /// - /// Returns default value for this type. - /// - public static T? Default => default; - - /// - /// Provides smart hash code computation. - /// - /// - /// For reference types, this delegate always calls virtual method. - /// For value type, it calls if it is overridden by the value type; otherwise, - /// it calls . - /// - public static new readonly Operator GetHashCode; - - /// - /// Provides smart equality check. - /// - /// - /// If type has equality operator then use it. - /// Otherwise, for reference types, this delegate always calls method. - /// For value type, it calls equality operator or if it is implemented by the value type; else, - /// it calls . - /// - public static new readonly Operator Equals; - - static Type() - { - var inputParam = Parameter(RuntimeType.MakeByRefType(), "obj"); - var secondParam = Parameter(RuntimeType.MakeByRefType(), "other"); - - // 1. try to resolve equality operator - Operator? equalsOp = Operator.Get(BinaryOperator.Equal, OperatorLookup.Overloaded); - if (RuntimeType.IsValueType) - { - // hash code calculator - var method = RuntimeType.GetHashCodeMethod(); - if (method is null) - { - method = typeof(BitwiseComparer<>) - .MakeGenericType(RuntimeType) - .GetMethod(nameof(BitwiseComparer.GetHashCode), 0, new[] { RuntimeType.MakeByRefType(), typeof(bool) }); - Debug.Assert(method is not null); - GetHashCode = Lambda>(Call(null, method, inputParam, Constant(true)), inputParam).Compile(); - } - else - { - GetHashCode = method.CreateDelegate>(); - } - - // equality checker - if (equalsOp is null) - { - // 2. try to find IEquatable.Equals implementation - if (typeof(IEquatable).IsAssignableFrom(RuntimeType)) - { - method = typeof(IEquatable).GetMethod(nameof(IEquatable.Equals), BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); - Debug.Assert(method is not null); - equalsOp = Lambda>(Call(inputParam, method, secondParam), inputParam, secondParam).Compile(); - } - - // 3. Use bitwise equality - else - { - method = typeof(BitwiseComparer<>) - .MakeGenericType(RuntimeType) - .GetMethod(nameof(BitwiseComparer.Equals), BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public) - ?.MakeGenericMethod(RuntimeType); - Debug.Assert(method is not null); - equalsOp = Lambda>(Call(null, method, inputParam, secondParam), inputParam, secondParam).Compile(); - } - } - } - else - { - // hash code calculator - GetHashCode = Lambda>(Call(inputParam, typeof(object).GetHashCodeMethod()!), inputParam).Compile(); - - // equality checker - if (equalsOp is null) - { - var equalsMethod = typeof(object).GetMethod(nameof(Equals), BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly); - Debug.Assert(equalsMethod is not null); - equalsOp = Lambda>(Call(null, equalsMethod, inputParam, secondParam), inputParam, secondParam).Compile(); - } - } - - Equals = equalsOp; - } - - /// - /// Calls static constructor of type . - /// - /// - /// This method doesn't call static constructor if type is already initialized. - /// - public static void Initialize() => RunClassConstructor(Intrinsics.TypeOf()); - - /// - /// Determines whether an instance of a specified type can be assigned to an instance of the current type. - /// - /// The type to compare with the current type. - /// , if instance of type can be assigned to type . - public static bool IsAssignableFrom() => RuntimeType.IsAssignableFrom(typeof(TOther)); - - /// - /// Determines whether an instance of the current type can be assigned to an instance of the specified type. - /// - /// The type to compare with the current type. - /// , if instance of type can be assigned to type . - public static bool IsAssignableTo() => Type.IsAssignableFrom(); - - /// - /// Applies type cast to the given object respecting overloaded cast operator. - /// - /// The type of object to be converted. - /// The value to be converted. - /// Optional conversion result if it is supported for the given type. - public static Optional TryConvert(TSource value) - { - Operator? converter = Type.Operator.Get(UnaryOperator.Convert); - return converter is null ? Optional.None : converter(value)!; - } - - /// - /// Applies type cast to the given object respecting overloaded cast operator. - /// - /// The type of object to be converted. - /// The value to be converted. - /// The conversion result. - /// , if conversion is supported by the given type; otherwise, . - public static bool TryConvert(TSource value, [MaybeNullWhen(false)] out T result) => TryConvert(value).TryGet(out result); - - /// - /// Converts object into type . - /// - /// - /// Semantics of this method includes typecast as well as conversion between numeric types - /// and implicit/explicit cast operators. - /// - /// The value to convert. - /// Type of value to convert. - /// Converted value. - /// Cannot convert values. - [return: NotNull] - public static T Convert(TSource value) => TryConvert(value).OrThrow(); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/TypeExtensions.cs b/src/DotNext.Reflection/Reflection/TypeExtensions.cs deleted file mode 100644 index c6ac59eb5..000000000 --- a/src/DotNext.Reflection/Reflection/TypeExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Diagnostics; -using System.Reflection; - -namespace DotNext.Reflection; - -internal static class TypeExtensions -{ - private const BindingFlags PublicInstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly; - - internal static MethodInfo? GetHashCodeMethod(this Type type) - => type.GetMethod(nameof(GetHashCode), PublicInstanceFlags, null, Type.EmptyTypes, null); - - internal static string ToGetterName(this string propertyName) => string.Concat("get_", propertyName); - - internal static string ToSetterName(this string propertyName) => string.Concat("set_", propertyName); - - internal static Type NonRefType(this Type type) - { - if (type.IsByRef) - { - var result = type.GetElementType(); - Debug.Assert(result is not null); - return result; - } - - return type; - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Reflection/UnaryOperator.cs b/src/DotNext.Reflection/Reflection/UnaryOperator.cs deleted file mode 100644 index 3aaa77d48..000000000 --- a/src/DotNext.Reflection/Reflection/UnaryOperator.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System.Diagnostics; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; - -namespace DotNext.Reflection; - -/// -/// Represents unary operator. -/// -public enum UnaryOperator : int -{ - /// - /// A unary plus operation, such as (+a). - /// - Plus = ExpressionType.UnaryPlus, - - /// - /// An arithmetic negation operation, such as (-a) - /// - Negate = ExpressionType.Negate, - - /// - /// A cast or unchecked conversion operation. - /// - Convert = ExpressionType.Convert, - - /// - /// A cast or checked conversion operation. - /// - ConvertChecked = ExpressionType.ConvertChecked, - - /// - /// A bitwise complement or logical negation operation. - /// - Not = ExpressionType.Not, - - /// - /// A ones complement operation. - /// - OnesComplement = ExpressionType.OnesComplement, - - /// - /// A unary increment operation, such as (a + 1). - /// - Increment = ExpressionType.Increment, - - /// - /// A unary decrement operation, such as (a - 1). - /// - Decrement = ExpressionType.Decrement, - - /// - /// A type test, such as obj is T - /// - IsInstanceOf = ExpressionType.TypeIs, - - /// - /// An exact type test. - /// - TypeTest = ExpressionType.TypeEqual, - - /// - /// Safe typecast operation, such as obj as T - /// - TryConvert = ExpressionType.TypeAs, - - /// - /// if(value) - /// - IsTrue = ExpressionType.IsTrue, - - /// - /// if(!value) - /// - IsFalse = ExpressionType.IsFalse, -} - -/// -/// Represents unary operator applicable to type . -/// -/// Target type. -/// Type of unary operator result. -[DefaultMember(nameof(Invoke))] -public sealed class UnaryOperator : Operator> -{ - private sealed class Cache : Cache> - { - private protected override UnaryOperator? Create(Operator.Kind kind) => Reflect(kind); - } - - private UnaryOperator(Expression> invoker, UnaryOperator type, MethodInfo? overloaded) - : base(invoker.Compile(), type.ToExpressionType(), overloaded) - { - } - - private protected override Type DeclaringType => typeof(TOperand); - - /// - /// Type of operator. - /// - public new UnaryOperator Type => (UnaryOperator)base.Type; - - /// - /// Invokes unary operator. - /// - /// An operand. - /// Result of unary operator. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TResult? Invoke(in TOperand operand) => Invoker(in operand); - - private static Expression>? MakeUnary(Operator.Kind @operator, Operator.Operand operand, out MethodInfo? overloaded) - { - var resultType = typeof(TResult); - bool usePrimitiveCast; - - // perform automatic cast from byte/short/ushort/sbyte so unary operators become available for these types - usePrimitiveCast = (ExpressionType)@operator switch - { - ExpressionType.Convert or ExpressionType.ConvertChecked => true, - _ => resultType.IsPrimitive && operand.NormalizePrimitive() - }; - - tail_call: // C# doesn't support tail calls so replace it with label/goto - overloaded = null; - try - { - var body = @operator.MakeUnary(operand); - overloaded = body.Method; - if (overloaded is null && usePrimitiveCast) - body = Expression.Convert(body, resultType); - return Expression.Lambda>(body, operand.Source); - } - catch (ArgumentException e) - { - Debug.WriteLine(e); - return null; - } - catch (InvalidOperationException) - { - // ignore exception - } - - if (operand.Upcast()) - goto tail_call; - else - return null; - } - - private static UnaryOperator? Reflect(Operator.Kind op) - { - var parameter = Expression.Parameter(typeof(TOperand).MakeByRefType(), "operand"); - var result = MakeUnary(op, parameter, out var overloaded); - if (result is null) - return null; - - // handle situation when trying to cast two incompatible reference types - return overloaded is null && (op == ExpressionType.Convert || op == ExpressionType.ConvertChecked) && !parameter.Type.IsValueType && !typeof(TResult).IsAssignableFrom(parameter.Type) - ? null - : new UnaryOperator(result, op, overloaded); - } - - private static UnaryOperator? GetOrCreate(Operator.Kind op) => Cache.Of(typeof(TOperand)).GetOrCreate(op); - - internal static UnaryOperator? GetOrCreate(UnaryOperator @operator, OperatorLookup lookup) - => lookup switch - { - OperatorLookup.Predefined => GetOrCreate(new Operator.Kind(@operator, false)), - OperatorLookup.Overloaded => GetOrCreate(new Operator.Kind(@operator, true)), - OperatorLookup.Any => GetOrCreate(new Operator.Kind(@operator, true)) ?? GetOrCreate(new Operator.Kind(@operator, false)), - _ => null, - }; -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Runtime/CompilerServices/Awaitable.cs b/src/DotNext.Reflection/Runtime/CompilerServices/Awaitable.cs deleted file mode 100644 index 825266102..000000000 --- a/src/DotNext.Reflection/Runtime/CompilerServices/Awaitable.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DotNext.Runtime.CompilerServices; - -using Reflection; - -/// -/// Represents awaitable concept type. -/// -/// The constrained type. -/// The type constrained with concept . -/// TAP -[Concept] -[StructLayout(LayoutKind.Auto)] -public readonly struct Awaitable))] TAwaiter> - where TAwaiter : ICriticalNotifyCompletion -{ - private static readonly Operator GetAwaiterMethod = Type.Method.Require>(nameof(Task.GetAwaiter), MethodLookup.Instance); - - static Awaitable() => Concept.Assert>(); - - private readonly T awaitable; - - /// - /// Wraps value of type into awaitable value compatible with expression. - /// - /// Underlying awaitable object. - public Awaitable(T awaitable) => this.awaitable = awaitable; - - /// - /// Gets awaiter used to await asynchronous result represented by type . - /// - /// An awaiter instance. - /// returned by is . - public Awaiter GetAwaiter() => new(GetAwaiter(in awaitable)); - - /// - /// Gets underlying awaitable object. - /// - /// Awaitable object container. - public static implicit operator T(in Awaitable awaitable) => awaitable.awaitable; - - /// - /// Gets awaiter used to await asynchronous result represented by type . - /// - /// The object representing asynchronous computing. - /// An awaiter instance. - /// returned by is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - public static TAwaiter GetAwaiter(in T obj) => GetAwaiterMethod(in obj) ?? throw new InvalidOperationException(ExceptionMessages.AwaiterMustNotBeNull); -} - -/// -/// Represents awaitable concept type for the task type with non-void result. -/// -/// The constrained type. -/// The type constrained with concept . -/// The type of asynchronous result. -/// TAP -[Concept] -[StructLayout(LayoutKind.Auto)] -public readonly struct Awaitable))] TAwaiter, TResult> - where TAwaiter : ICriticalNotifyCompletion -{ - private static readonly Operator GetAwaiterMethod = Type.Method.Require>(nameof(Task.GetAwaiter), MethodLookup.Instance); - - static Awaitable() => Concept.Assert>(); - - private readonly T awaitable; - - /// - /// Wraps value of type into awaitable value compatible with expression. - /// - /// Underlying awaitable object. - public Awaitable(T awaitable) => this.awaitable = awaitable; - - /// - /// Gets awaiter used to await asynchronous result represented by type . - /// - /// An awaiter instance. - /// returned by is . - public Awaiter GetAwaiter() => new(GetAwaiter(in awaitable)); - - /// - /// Gets underlying awaitable object. - /// - /// Awaitable object container. - public static implicit operator T(in Awaitable awaitable) => awaitable.awaitable; - - /// - /// Gets awaiter used to await asynchronous result represented by type . - /// - /// The object representing asynchronous computing. - /// An awaiter instance. - /// returned by is . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - public static TAwaiter GetAwaiter(in T obj) => GetAwaiterMethod(in obj) ?? throw new InvalidOperationException(ExceptionMessages.AwaiterMustNotBeNull); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Runtime/CompilerServices/Awaiter.cs b/src/DotNext.Reflection/Runtime/CompilerServices/Awaiter.cs deleted file mode 100644 index 1a849ffc0..000000000 --- a/src/DotNext.Reflection/Runtime/CompilerServices/Awaiter.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DotNext.Runtime.CompilerServices; - -using Reflection; - -/// -/// Represents awaiter pattern for type -/// with non- result. -/// -/// Any type implementing awaiter pattern. -/// Type of asynchronous result. -/// -/// -[Concept] -[StructLayout(LayoutKind.Auto)] -public readonly struct Awaiter<[Constraint(typeof(NotifyCompletion<>))]TAwaiter, TResult> : INotifyCompletion - where TAwaiter : INotifyCompletion -{ - private static readonly MemberGetter GetResultMethod = Type.Method.Require>(nameof(TaskAwaiter.GetResult), MethodLookup.Instance)!; - - static Awaiter() => Concept.Assert(typeof(NotifyCompletion)); - - private readonly TAwaiter awaiter; - - /// - /// Initializes a new generic awaiter object. - /// - /// Underlying awaiter object. - public Awaiter([DisallowNull] TAwaiter awaiter) => this.awaiter = awaiter; - - /// - /// Ends the wait for the completion of the asynchronous task. - /// - /// The result of the completed task. - /// The task was cancelled. - /// Task is in faulted state. - public TResult? GetResult() => GetResult(in awaiter); - - /// - /// Gets a value that indicates whether the asynchronous task has completed. - /// - public bool IsCompleted => NotifyCompletion.IsCompleted(in awaiter); - - /// - void INotifyCompletion.OnCompleted(Action continuation) => awaiter.OnCompleted(continuation); - - /// - /// Extracts underlying awaiter object from this wrapper. - /// - /// Generic awaiter object. - public static implicit operator TAwaiter(in Awaiter awaiter) => awaiter.awaiter; - - /// - /// Ends the wait for the completion of the asynchronous task. - /// - /// An object that waits for the completion of an asynchronous task. - /// The result of the completed task. - /// The task was cancelled. - /// Task is in faulted state. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TResult? GetResult(in TAwaiter awaiter) => GetResultMethod(in awaiter); -} - -/// -/// Represents awaiter pattern for type . -/// with result. -/// -/// Any type implementing awaiter pattern. -/// -/// -[Concept] -[StructLayout(LayoutKind.Auto)] -public readonly struct Awaiter<[Constraint(typeof(NotifyCompletion<>))]TAwaiter> : INotifyCompletion - where TAwaiter : INotifyCompletion -{ - private delegate void GetResultMethod(in TAwaiter awaiter); - - static Awaiter() => Concept.Assert(typeof(NotifyCompletion)); - - private static readonly GetResultMethod GetResultImpl = Type.Method.Require(nameof(TaskAwaiter.GetResult), MethodLookup.Instance)!; - - private readonly TAwaiter awaiter; - - /// - /// Initializes a new generic awaiter object. - /// - /// Underlying awaiter object. - public Awaiter([DisallowNull] TAwaiter awaiter) => this.awaiter = awaiter; - - /// - /// Ends the wait for the completion of the asynchronous task. - /// - /// The task was cancelled. - /// Task is in faulted state. - public void GetResult() => GetResult(in awaiter); - - /// - /// Gets a value that indicates whether the asynchronous task has completed. - /// - public bool IsCompleted => NotifyCompletion.IsCompleted(in awaiter); - - /// - void INotifyCompletion.OnCompleted(Action continuation) => awaiter.OnCompleted(continuation); - - /// - /// Extracts underlying awaiter object from this wrapper. - /// - /// Generic awaiter object. - public static implicit operator TAwaiter(in Awaiter awaiter) => awaiter.awaiter; - - /// - /// Ends the wait for the completion of the asynchronous task. - /// - /// An object that waits for the completion of an asynchronous task. - /// The task was cancelled. - /// Task is in faulted state. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void GetResult(in TAwaiter awaiter) => GetResultImpl(in awaiter); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Runtime/CompilerServices/Concept.cs b/src/DotNext.Reflection/Runtime/CompilerServices/Concept.cs deleted file mode 100644 index 1c46d086d..000000000 --- a/src/DotNext.Reflection/Runtime/CompilerServices/Concept.cs +++ /dev/null @@ -1,55 +0,0 @@ -using static System.Runtime.CompilerServices.RuntimeHelpers; -using static System.Runtime.ExceptionServices.ExceptionDispatchInfo; - -namespace DotNext.Runtime.CompilerServices; - -using Reflection; - -/// -/// Provides a check of constraints defined by concept types. -/// -public static class Concept -{ - /// - /// Applies constraints described by concept type. - /// - /// A static type describing concept. - /// One or more constraints defined by concept type are violated. - /// is not marked with . - public static void Assert(Type conceptType) - { - if (!conceptType.IsDefined()) - throw new ArgumentException(ExceptionMessages.ConceptTypeInvalidAttribution(conceptType), nameof(conceptType)); - try - { - // run class constructor for concept type and its parents - for (Type? lookup = conceptType; lookup is not null; lookup = lookup.BaseType) - RunClassConstructor(conceptType.TypeHandle); - } - catch (TypeInitializationException e) when (e.InnerException is ConstraintViolationException violation) - { - Capture(violation).Throw(); - } - } - - /// - /// Applies constraints described by concept type. - /// - /// A type describing concept. - /// One or more constraints defined by concept type are violated. - /// is not marked with . - public static void Assert() => Assert(typeof(TConcept)); - - /// - /// Applies a chain of constraints described by multiple concept types. - /// - /// A static type describing concept. - /// A set of static types describing concept. - /// Constraints defined by concept types are violated. - /// One or more concept types are not marked with . - public static void Assert(Type conceptType, params Type[] other) - { - Assert(conceptType); - Array.ForEach(other, Assert); - } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Runtime/CompilerServices/ConceptAttribute.cs b/src/DotNext.Reflection/Runtime/CompilerServices/ConceptAttribute.cs deleted file mode 100644 index e841319ad..000000000 --- a/src/DotNext.Reflection/Runtime/CompilerServices/ConceptAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace DotNext.Runtime.CompilerServices; - -/// -/// Indicates that attributed class is a concept type. -/// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] -public sealed class ConceptAttribute : Attribute -{ -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Runtime/CompilerServices/ConstraintAttribute.cs b/src/DotNext.Reflection/Runtime/CompilerServices/ConstraintAttribute.cs deleted file mode 100644 index 0e005ce86..000000000 --- a/src/DotNext.Reflection/Runtime/CompilerServices/ConstraintAttribute.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace DotNext.Runtime.CompilerServices; - -/// -/// Indicates that generic parameter is constrained with a concept. -/// -[AttributeUsage(AttributeTargets.GenericParameter, AllowMultiple = true, Inherited = true)] -public sealed class ConstraintAttribute : Attribute -{ - /// - /// Initializes a new attribute and specify concept type. - /// - /// Concept type. - public ConstraintAttribute(Type conceptType) => Concept = conceptType; - - /// - /// Gets type of concept. - /// - public Type Concept { get; } -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Runtime/CompilerServices/NotifyCompletion.cs b/src/DotNext.Reflection/Runtime/CompilerServices/NotifyCompletion.cs deleted file mode 100644 index b00c651cf..000000000 --- a/src/DotNext.Reflection/Runtime/CompilerServices/NotifyCompletion.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Runtime.CompilerServices; - -using Reflection; - -/// -/// Represents base concept of awaiter pattern. -/// -/// -/// This concept doesn't provide methods to obtain task result. -/// -/// Any type implementing awaiter pattern. -[Concept] -public static class NotifyCompletion - where TAwaiter : INotifyCompletion -{ - private static readonly MemberGetter IsCompletedImpl = Type.Property.RequireGetter(nameof(TaskAwaiter.IsCompleted))!; - - /// - /// Gets a value that indicates whether the asynchronous task has completed. - /// - /// An object that waits for the completion of an asynchronous task. - /// if the task has completed; otherwise, . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsCompleted(in TAwaiter awaiter) => IsCompletedImpl(awaiter); - - /// - /// Schedules the continuation action that's invoked when the instance completes. - /// - /// An object that waits for the completion of an asynchronous task. - /// The action to invoke when the operation completes. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void OnCompleted(in TAwaiter awaiter, Action continuation) - => Unsafe.AsRef(in awaiter).OnCompleted(continuation); -} \ No newline at end of file diff --git a/src/DotNext.Reflection/Runtime/CompilerServices/ReflectionUtils.cs b/src/DotNext.Reflection/Runtime/CompilerServices/ReflectionUtils.cs deleted file mode 100644 index 37290f557..000000000 --- a/src/DotNext.Reflection/Runtime/CompilerServices/ReflectionUtils.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System.Diagnostics; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; -using static InlineIL.IL; -using static InlineIL.IL.Emit; - -namespace DotNext.Runtime.CompilerServices; - -internal static class ReflectionUtils -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe object Wrap(T* ptr) - where T : unmanaged => Pointer.Box(ptr, typeof(T*)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe T* Unwrap(object ptr) - where T : unmanaged => (T*)Pointer.Unbox(ptr); - - internal static Expression Wrap(Expression expression) - { - Debug.Assert(expression.Type.IsPointer); - if (expression.Type == typeof(void*)) - return Expression.Call(typeof(Pointer), nameof(Pointer.Box), Type.EmptyTypes, expression, Expression.Constant(typeof(void*))); - - var pointerType = expression.Type.GetElementType(); - Debug.Assert(pointerType is not null); - return Expression.Call(typeof(ReflectionUtils), nameof(Wrap), new[] { pointerType }, expression); - } - - internal static Expression Unwrap(Expression expression, Type expectedType) - { - Debug.Assert(expectedType.IsPointer); - if (expectedType == typeof(void*)) - return Expression.Call(typeof(Pointer), nameof(Pointer.Unbox), Type.EmptyTypes, expression); - - var pointerType = expectedType.GetElementType(); - Debug.Assert(pointerType is not null); - return Expression.Call(typeof(ReflectionUtils), nameof(Unwrap), new[] { pointerType }, expression); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T VolatileRead(ref T? fieldRef) - { - Push(ref fieldRef); - Volatile(); - Ldobj(); - return Return(); - } - - internal static Expression VolatileRead(Expression expression) - { - if (!expression.Type.IsPointer) - return Expression.Call(typeof(ReflectionUtils), nameof(VolatileRead), new[] { expression.Type }, expression); - - if (expression.Type == typeof(void*)) - return Expression.Call(typeof(ReflectionUtils), nameof(VolatileReadPointer), Type.EmptyTypes, expression); - - var pointerType = expression.Type.GetElementType(); - Debug.Assert(pointerType is not null); - return Expression.Call(typeof(ReflectionUtils), nameof(VolatileReadPointer), new[] { pointerType }, expression); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe T* VolatileReadPointer(ref T* fieldRef) - where T : unmanaged - { - Ldarg(nameof(fieldRef)); - Volatile(); - Ldind_I(); - return ReturnPointer(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void* VolatileReadPointer(ref void* fieldRef) - { - Ldarg(nameof(fieldRef)); - Volatile(); - Ldind_I(); - return ReturnPointer(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref T? fieldRef, T value) - { - Push(ref fieldRef); - Push(value); - Volatile(); - Stobj(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void VolatileWritePointer(ref T* fieldRef, T* value) - where T : unmanaged - { - Ldarg(nameof(fieldRef)); - Push(value); - Volatile(); - Stind_I(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void VolatileWritePointer(ref void* fieldRef, void* value) - { - Ldarg(nameof(fieldRef)); - Push(value); - Volatile(); - Stind_I(); - } - - internal static Expression VolatileWrite(Expression expression, Expression value) - { - if (!expression.Type.IsPointer) - return Expression.Call(typeof(ReflectionUtils), nameof(VolatileWrite), new[] { expression.Type }, expression, value); - - if (expression.Type == typeof(void*)) - return Expression.Call(typeof(ReflectionUtils), nameof(VolatileWritePointer), Type.EmptyTypes, expression, value); - - var pointerType = expression.Type.GetElementType(); - Debug.Assert(pointerType is not null); - return Expression.Call(typeof(ReflectionUtils), nameof(VolatileWritePointer), new[] { pointerType }, expression, value); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object? Get(in Span span, int index) => span[index]; - - internal static MethodCallExpression Get(ParameterExpression span, ConstantExpression index) - => Expression.Call(typeof(ReflectionUtils), nameof(Get), Type.EmptyTypes, span, index); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Set(in Span span, int index, object? value) => span[index] = value; - - internal static MethodCallExpression Set(ParameterExpression span, ConstantExpression index, Expression value) - => Expression.Call(typeof(ReflectionUtils), nameof(Set), Type.EmptyTypes, span, index, value); - - internal static MemberExpression SpanLength(ParameterExpression span) - => Expression.Property(span, nameof(Span.Length)); -} \ No newline at end of file diff --git a/src/DotNext.Tests/DelegateHelpersTests.cs b/src/DotNext.Tests/DelegateHelpersTests.cs index 780d6c9a1..f80ad8603 100644 --- a/src/DotNext.Tests/DelegateHelpersTests.cs +++ b/src/DotNext.Tests/DelegateHelpersTests.cs @@ -234,28 +234,6 @@ public static void OpenDelegateForPropertySetter() Equal(42, obj.Prop); } - [Fact] - public static void BindFunction() - { - static int Sum(in int x, in int y) => x + y; - - Function fn = Sum; - - Equal(42, fn.Bind(40).Invoke(2)); - } - - [Fact] - public static void BindProcedure() - { - static void Append(in StringBuilder x, in int y) => x.Append(y); - - Procedure proc = Append; - - var builder = new StringBuilder(); - proc.Bind(builder).Invoke(42); - Equal("42", builder.ToString(), StringComparer.Ordinal); - } - [Fact] public static void Constant() { diff --git a/src/DotNext.Tests/DisposableTests.cs b/src/DotNext.Tests/DisposableTests.cs index c54af7a0c..1d0aa0488 100644 --- a/src/DotNext.Tests/DisposableTests.cs +++ b/src/DotNext.Tests/DisposableTests.cs @@ -2,48 +2,6 @@ namespace DotNext; public sealed class DisposableTests : Test { - public struct DisposableStruct : IDisposable - { - public int Disposed; - - void IDisposable.Dispose() => Disposed += 1; - } - - public struct DisposableStruct2 - { - public int Disposed; - - public void Dispose() => Disposed += 1; - } - - [Fact] - public static void IDisposableStruct() - { - var s = new DisposableStruct(); - Disposable.Dispose(s); - Equal(1, s.Disposed); - Disposable.Dispose(s); - Equal(2, s.Disposed); - } - - [Fact] - public static void DisposePattern() - { - var s = new DisposableStruct2(); - Disposable.Dispose(s); - Equal(1, s.Disposed); - Disposable.Dispose(s); - Equal(2, s.Disposed); - } - - [Fact] - public static void MemoryStreamTest() - { - var ms = new MemoryStream(new byte[] { 1, 2, 3 }); - Disposable.Dispose(ms); - Throws(() => ms.ReadByte()); - } - private sealed class DisposableObject : Disposable, IAsyncDisposable { public new bool IsDisposed => base.IsDisposed; diff --git a/src/DotNext.Tests/DotNext.Tests.csproj b/src/DotNext.Tests/DotNext.Tests.csproj index cb36d0bd9..3872b3ade 100644 --- a/src/DotNext.Tests/DotNext.Tests.csproj +++ b/src/DotNext.Tests/DotNext.Tests.csproj @@ -43,7 +43,6 @@ - diff --git a/src/DotNext.Tests/NumberConceptTests.cs b/src/DotNext.Tests/NumberConceptTests.cs deleted file mode 100644 index 454860d02..000000000 --- a/src/DotNext.Tests/NumberConceptTests.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace DotNext; - -using Concept = Runtime.CompilerServices.Concept; -using ConstraintViolationException = Reflection.ConstraintViolationException; - -public sealed class NumberConceptTests : Test -{ - [Fact] - public static void ApplyConceptToLong() - { - var value = new Number(42); - value += 1; - Equal(43L, value); - Equal(43L.GetHashCode(), value.GetHashCode()); - Equal(43L.ToString(), value.ToString()); - True(value.Equals(43L)); - value -= 1L; - Equal(42L, value); - value = Number.Parse("100500"); - Equal(100500L, value); - Number.TryParse("42", out value); - Equal(42L, value); - value *= 2L; - Equal(84L, value); - value /= 10; - Equal(8L, value); - Equal(8, (byte)value); - } - - [Fact] - public static void ApplyConceptToByte() - { - var value = new Number(42); - value += 1; - Equal(43, value); - value -= 1; - Equal(42, value); - value *= 2; - Equal(84, value); - } - - [Fact] - public static void InvalidActualType() - { - ThrowsAny(() => Concept.Assert(typeof(Number))); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Reflection/DynamicInvocationTests.cs b/src/DotNext.Tests/Reflection/DynamicInvocationTests.cs deleted file mode 100644 index b3762f9c3..000000000 --- a/src/DotNext.Tests/Reflection/DynamicInvocationTests.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System.Drawing; -using System.Reflection; -using static System.Globalization.CultureInfo; -using static System.Runtime.CompilerServices.Unsafe; - -namespace DotNext.Reflection; - -public sealed class DynamicInvocationTests : Test -{ - private sealed class MyClass - { - public int ValueTypeField; - public string ObjectField; - public unsafe byte* TypedPointerField; - public unsafe void* UntypedPointerField; - } - - private struct MyType - { - public int X; - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void StaticFieldGet(bool volatileAccess) - { - var staticField = typeof(string).GetField(nameof(string.Empty)); - var reader = staticField.Unreflect(BindingFlags.GetField, volatileAccess); - Same(string.Empty, reader.Invoke(null)); - } - - private static void ObjectFieldTest(DynamicInvoker reader, DynamicInvoker writer) - { - var obj = new MyClass(); - Null(writer.Invoke(obj, "Hello, world")); - - Equal("Hello, world", reader.Invoke(obj)); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void ObjectFieldGetSet(bool volatileAccess) - { - var field = typeof(MyClass).GetField(nameof(MyClass.ObjectField)); - var reader = field.Unreflect(BindingFlags.GetField, volatileAccess); - var writer = field.Unreflect(BindingFlags.SetField, volatileAccess); - ObjectFieldTest(reader, writer); - reader = writer = field.Unreflect(volatileAccess: volatileAccess); - ObjectFieldTest(reader, writer); - } - - [Fact] - public static void InvalidFlags() - { - var field = typeof(MyClass).GetField(nameof(MyClass.ValueTypeField)); - Throws(() => field.Unreflect(BindingFlags.GetProperty)); - } - - private static void ValueTypeFieldTest(DynamicInvoker reader, DynamicInvoker writer) - { - var obj = new MyClass(); - Null(writer.Invoke(obj, 42)); - - Equal(42, reader.Invoke(obj)); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void ValueTypeFieldGetSet(bool volatileAccess) - { - var field = typeof(MyClass).GetField(nameof(MyClass.ValueTypeField)); - var reader = field.Unreflect(BindingFlags.GetField, volatileAccess); - var writer = field.Unreflect(BindingFlags.SetField, volatileAccess); - ValueTypeFieldTest(reader, writer); - reader = writer = field.Unreflect(volatileAccess: volatileAccess); - ValueTypeFieldTest(reader, writer); - } - - private static unsafe void TypedPointerFieldTest(DynamicInvoker reader, DynamicInvoker writer) - { - var obj = new MyClass() { TypedPointerField = (byte*)new IntPtr(42) }; - Equal(new IntPtr(42), new IntPtr(Pointer.Unbox(reader.Invoke(obj)))); - - Null(writer.Invoke(obj, Pointer.Box(new IntPtr(56).ToPointer(), typeof(byte*)))); - Equal(new IntPtr(56), new IntPtr(Pointer.Unbox(reader.Invoke(obj)))); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static void TypedPointerFieldGetSet(bool volatileAccess) - { - var field = typeof(MyClass).GetField(nameof(MyClass.TypedPointerField)); - NotNull(field); - var reader = field.Unreflect(BindingFlags.GetField, volatileAccess); - var writer = field.Unreflect(BindingFlags.SetField, volatileAccess); - TypedPointerFieldTest(reader, writer); - reader = writer = field.Unreflect(volatileAccess: volatileAccess); - TypedPointerFieldTest(reader, writer); - } - - private static unsafe void PointerFieldTest(DynamicInvoker reader, DynamicInvoker writer) - { - var obj = new MyClass() { UntypedPointerField = (void*)new IntPtr(42) }; - Equal(new IntPtr(42), new IntPtr(Pointer.Unbox(reader.Invoke(obj)))); - - Null(writer.Invoke(obj, Pointer.Box(new IntPtr(56).ToPointer(), typeof(void*)))); - Equal(new IntPtr(56), new IntPtr(Pointer.Unbox(reader.Invoke(obj)))); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public static unsafe void PointerFieldGetSet(bool volatileAccess) - { - var field = typeof(MyClass).GetField(nameof(MyClass.UntypedPointerField)); - NotNull(field); - var reader = field.Unreflect(BindingFlags.GetField, volatileAccess); - var writer = field.Unreflect(BindingFlags.SetField, volatileAccess); - PointerFieldTest(reader, writer); - reader = writer = field.Unreflect(volatileAccess: volatileAccess); - PointerFieldTest(reader, writer); - } - - [Fact] - public static void MethodDynamicInvoke() - { - var method = typeof(int).GetMethod(nameof(int.ToString), new[] { typeof(string) }).Unreflect(); - Equal("C", method.Invoke(12, "X")); - method = typeof(int).GetMethod(nameof(int.TryParse), new[] { typeof(string), typeof(int).MakeByRefType() }).Unreflect(); - object[] args = { "123", 0 }; - Equal(true, method(null, args)); - Equal(123, args[1]); - } - - [Fact] - public static unsafe void OperatorDynamicInvoke() - { - var method = typeof(IntPtr).GetMethod(nameof(IntPtr.ToPointer), Type.EmptyTypes).Unreflect(); - Equal(new IntPtr(42), new IntPtr(Pointer.Unbox(method.Invoke(new IntPtr(42))))); - - method = typeof(IntPtr).GetMethod("op_Explicit", new[] { typeof(void*) }).Unreflect(); - Equal(new IntPtr(42), (IntPtr)method.Invoke(null, Pointer.Box(new IntPtr(42).ToPointer(), typeof(void*)))); - } - - [Fact] - public static void InstantiateDynamically() - { - var ctor = typeof(string).GetConstructor(new[] { typeof(char), typeof(int) }).Unreflect(); - Equal("aaa", ctor.Invoke(null, 'a', 3)); - } - - [Fact] - public static void ModifyPropertyByRef() - { - object point = new Point(); - var setter = point.GetType().GetProperty(nameof(Point.X)).SetMethod.Unreflect(); - setter.Invoke(point, 42); - Equal(42, Unbox(point).X); - } - - [Fact] - public static void ModifyFieldByRef() - { - object value = new MyType(); - var setter = value.GetType().GetField(nameof(MyType.X)).Unreflect(BindingFlags.SetField); - setter.Invoke(value, 42); - Equal(42, Unbox(value).X); - } - - public static unsafe int* GetTypedPointer() => (int*)42; - - [Fact] - public unsafe void ReturnTypedPointer() - { - var getTypedPointer = GetType().GetMethod(nameof(GetTypedPointer)).Unreflect(); - True(((int*)42) == Pointer.Unbox(getTypedPointer.Invoke(null))); - } - - [Fact] - public static void UnreflectInterfaceMethod() - { - var toStringMethod = typeof(IFormattable).GetMethod("ToString").Unreflect(); - object i = 42; - Equal("42", toStringMethod.Invoke(i, null, InvariantCulture)); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Reflection/ExtensionRegistryTests.cs b/src/DotNext.Tests/Reflection/ExtensionRegistryTests.cs deleted file mode 100644 index 0e7baffe2..000000000 --- a/src/DotNext.Tests/Reflection/ExtensionRegistryTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace DotNext.Reflection; - -public sealed class ExtensionRegistryTest : Test -{ - private delegate void ZeroMethod(ref int value); - private delegate bool ContainsMethod(ref string value, char ch); - - private static string ToHex(int value) => value.ToString("X"); - - private static void Zero(ref int value) => value = 0; - - private static int GetLength(string value) => value.Length; - - private static bool Contains(ref string value, char ch) => value.IndexOf(ch) >= 0; - - public ExtensionRegistryTest() - { - ExtensionRegistry.RegisterInstance(new Func(ToHex)); - ExtensionRegistry.RegisterInstance(new ZeroMethod(Zero)); - ExtensionRegistry.RegisterInstance(new Func(StringExtensions.Reverse)); - ExtensionRegistry.RegisterStatic>(GetLength); - ExtensionRegistry.RegisterStatic(new ContainsMethod(Contains)); - } - - [Fact] - public void StaticExtensionTest() - { - Func length = Type.Method.RequireStatic(nameof(GetLength)); - var str = "123"; - Equal(3, length(str)); - ContainsMethod contains = Type.Method.Require(nameof(Contains), MethodLookup.Static); - True(contains(ref str, '3')); - } - - [Fact] - public void InstanceExtensionTest() - { - Func toHex = Type.Method.Require(nameof(ToHex)); - int value = 0xBB; - Equal("BB", toHex(value)); - ZeroMethod zero = Type.Method.Require(nameof(Zero), MethodLookup.Instance); - zero(ref value); - Equal(0, value); - Func reverse = Type.Method.Require(nameof(StringExtensions.Reverse)); - var str = "abc"; - str = reverse(str); - Equal("cba", str); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Reflection/OperatorTests.cs b/src/DotNext.Tests/Reflection/OperatorTests.cs deleted file mode 100644 index 7430fe4a8..000000000 --- a/src/DotNext.Tests/Reflection/OperatorTests.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace DotNext.Reflection; - -public sealed class OperatorTests : Test -{ - public class BaseClass - { - public static bool operator ==(int first, BaseClass second) => true; - public static bool operator !=(int first, BaseClass second) => false; - - public static bool operator ==(BaseClass first, BaseClass second) => true; - public static bool operator !=(BaseClass first, BaseClass second) => false; - - public static string operator +(BaseClass bc) => bc?.ToString(); - - public override bool Equals(object obj) => obj is BaseClass; - - public override int GetHashCode() => GetType().GetHashCode(); - - public static implicit operator string(BaseClass obj) => obj?.ToString(); - } - - public sealed class DerivedClass : BaseClass - { - public override bool Equals(object obj) => obj is DerivedClass; - - public override int GetHashCode() => GetType().GetHashCode(); - } - - [Fact] - public void BinaryOperatorTest() - { - var op = Type.Operator.Require(BinaryOperator.Equal); - True(op.Invoke(10, new BaseClass())); - True(20 == new DerivedClass()); - var op2 = Type.Operator.Require(BinaryOperator.Equal); - True(op2.Invoke(20, new DerivedClass())); - } - - [Fact] - public void UnaryOperatorTest() - { - var unaryPlus = Type.Operator.Require(UnaryOperator.Plus); - var obj = new DerivedClass(); - Equal(obj.ToString(), unaryPlus.Invoke(obj)); - Operator negate = Type.Operator.Require(UnaryOperator.Negate, OperatorLookup.Predefined); - Equal(-42, negate(42)); - //typecast - var toLong = Type.Operator.Require(UnaryOperator.Convert); - Equal(42UL, toLong.Invoke(42)); - var toString = Type.Operator.Require(UnaryOperator.Convert, OperatorLookup.Any); - NotEmpty(toString.Invoke(new DerivedClass())); - NotEmpty(Type.Convert(new DerivedClass())); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Reflection/RecordConceptTests.cs b/src/DotNext.Tests/Reflection/RecordConceptTests.cs deleted file mode 100644 index 4782e8626..000000000 --- a/src/DotNext.Tests/Reflection/RecordConceptTests.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace DotNext.Reflection; - -public sealed class RecordConceptTests : Test -{ - public record class RecordClass(int A); - - [Fact] - public static void CreateCopy() - { - var obj = new RecordClass(42); - var copy = Record.Clone(obj); - NotSame(obj, copy); - } - - [Fact] - public static void Bind() - { - var obj = new RecordClass(42); - NotSame(obj, Record.Bind(obj).Invoke()); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Reflection/RefTests.cs b/src/DotNext.Tests/Reflection/RefTests.cs deleted file mode 100644 index 6cddd6b9e..000000000 --- a/src/DotNext.Tests/Reflection/RefTests.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace DotNext.Reflection; - -public sealed class RefTests : Test -{ - [Fact] - public static void ReferenceEquality() - { - Ref ref1 = 10; - Ref ref2 = 20; - False(ref1 == ref2); - True(ref1 != ref2); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Reflection/ReflectorTests.cs b/src/DotNext.Tests/Reflection/ReflectorTests.cs deleted file mode 100644 index ed0a6227c..000000000 --- a/src/DotNext.Tests/Reflection/ReflectorTests.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System.Drawing; -using static System.Globalization.CultureInfo; -using static System.Runtime.CompilerServices.Unsafe; - -namespace DotNext.Reflection; - -public sealed class ReflectorTests : Test -{ - [Fact] - public static void ConstructorBinding() - { - var ctor = typeof(string).GetConstructor(new[] { typeof(char), typeof(int) }); - Func reflected = ctor.Unreflect>(); - NotNull(reflected); - Equal("ccc", reflected('c', 3)); - Function<(char, int), string> reflected2 = ctor.Unreflect>(); - NotNull(reflected2); - Equal("ccc", reflected2(('c', 3))); - } - - [Fact] - public static void MutableInstanceMethodCall() - { - Procedure> setter = typeof(Point).GetProperty("X").SetMethod.Unreflect>>(); - NotNull(setter); - object point = new Point(); - setter.Invoke(point, 42); - Equal(42, Unbox(point).X); - } - - [Fact] - public static void InstanceMethodBinding() - { - var indexOf = typeof(string).GetMethod(nameof(string.IndexOf), new[] { typeof(char), typeof(int) }); - Func reflected = indexOf.Unreflect>(); - NotNull(reflected); - Equal(1, reflected("abc", 'b', 0)); - Function reflected2 = indexOf.Unreflect>(); - NotNull(reflected2); - Equal(1, reflected2("abc", ('b', 0))); - } - - [Fact] - public static void StaticMethodBinding() - { - var compare = typeof(string).GetMethod(nameof(string.Compare), new[] { typeof(string), typeof(string) }); - var reflected = compare.Unreflect>(); - NotNull(reflected); - Equal(1, reflected.Invoke("bb", "aa")); - var reflected2 = compare.Unreflect>(); - NotNull(reflected2); - Equal(1, reflected2.Invoke(("bb", "aa"))); - } - - [Fact] - public static void TryParseFastInvoke() - { - //static - var method = typeof(long).GetMethod("TryParse", new[] { typeof(string), typeof(long).MakeByRefType() }); - Function<(string text, long result), bool> invoker = method.Unreflect>(); - var args = invoker.ArgList(); - args.text = "100500"; - True(invoker(args)); - Equal(100500L, args.result); - //untyped - Function<(object text, object result), object> weakInvoker = method.Unreflect>(); - var weakArgs = weakInvoker.ArgList(); - weakArgs.text = "100500"; - True((bool)weakInvoker(weakArgs)); - Equal(100500L, weakArgs.result); - //partially typed - Function<(object text, object result), bool> weakInvoker2 = method.Unreflect>(); - weakArgs = weakInvoker.ArgList(); - weakArgs.text = "100500"; - weakArgs.result = null; - True(weakInvoker2(weakArgs)); - Equal(100500L, weakArgs.result); - } - - [Fact] - public static void ToInt32FastInvoke() - { - var method = typeof(IntPtr).GetMethod(nameof(IntPtr.ToInt32)); - Function weakInvoker = method.Unreflect>(); - Equal(42, weakInvoker(new IntPtr(42), new ValueTuple())); - } - - [Fact] - public static void NewIntPtr() - { - var ctor = typeof(IntPtr).GetConstructor(new[] { typeof(int) }); - Function, IntPtr> factory = ctor.Unreflect, IntPtr>>(); - var value = factory(new ValueTuple(10)); - Equal(new IntPtr(10), value); - - Function, IntPtr> ctor2 = ctor.Unreflect, IntPtr>>(); - value = ctor2(new ValueTuple(42)); - Equal(new IntPtr(42), value); - - Function, object> ctor3 = ctor.Unreflect, object>>(); - value = (IntPtr)ctor3(new ValueTuple(50)); - Equal(new IntPtr(50), value); - } - - private static int staticField; - private int instanceField; - - [Fact] - public void UnreflectStaticField() - { - ref var field = ref GetType().GetField(nameof(staticField), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.DeclaredOnly).Unreflect().Value; - field = 42; - Equal(staticField, field); - True(AreSame(ref field, ref staticField)); - } - - [Fact] - public void UnreflectInstanceField() - { - ref var field = ref GetType().GetField(nameof(instanceField), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.NonPublic).Unreflect()[this]; - field = 56; - Equal(instanceField, field); - True(AreSame(ref field, ref instanceField)); - } - - [Fact] - public static void UnreflectInterfaceMethod() - { - Function typedToString = Type.RequireMethod<(string, IFormatProvider), string>(nameof(IFormattable.ToString)); - NotNull(typedToString); - IFormattable i = 42; - Equal("42", typedToString.Invoke(i, null, InvariantCulture)); - - Function untypedToString = typeof(IFormattable).GetMethod(nameof(IFormattable.ToString)).Unreflect>(); - NotNull(untypedToString); - Equal("42", untypedToString.Invoke(i, null, InvariantCulture)); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Reflection/TypeExtensionsTests.cs b/src/DotNext.Tests/Reflection/TypeExtensionsTests.cs index fd5a399d0..e0c0eb801 100644 --- a/src/DotNext.Tests/Reflection/TypeExtensionsTests.cs +++ b/src/DotNext.Tests/Reflection/TypeExtensionsTests.cs @@ -114,16 +114,6 @@ public static void EqualsMethodResolution() Equal(getTypeMethod, overriddenMethod); } - [Fact] - public static void ReflectMember() - { - NotNull(Reflector.MemberOf>(() => string.Empty)); - NotNull(Reflector.MemberOf>>(() => Optional.None)); - NotNull(Reflector.MemberOf>(() => new string('a', 3))); - NotNull(Reflector.MemberOf>(static x => -x)); - Null(Reflector.MemberOf>(static i => -i)); - } - [Fact] public static unsafe void DefaultValues() { diff --git a/src/DotNext.Tests/Reflection/TypeTests.cs b/src/DotNext.Tests/Reflection/TypeTests.cs deleted file mode 100644 index 32d2d25e3..000000000 --- a/src/DotNext.Tests/Reflection/TypeTests.cs +++ /dev/null @@ -1,369 +0,0 @@ -namespace DotNext.Reflection; - -public sealed class TypeTests : Test -{ - internal static event EventHandler StaticEvent; - private event EventHandler InstanceEvent; - - private struct Point : IEquatable - { - public int X, Y; - - public Point(int x, int y) - { - X = x; - Y = y; - } - - public void Zero() => X = Y = 0; - - bool IEquatable.Equals(Point other) - => X == other.X && Y == other.Y; - } - - private delegate void ByRefAction(in T value); - - private delegate R ByRefFunc(in T1 value, T2 arg); - - [Fact] - public void NonExistentMethodTest() - { - Throws(() => Type.Method.Require(nameof(string.IndexOf))); - } - - [Fact] - public void InstanceMethodTest() - { - Func indexOf = Type.Method.Require(nameof(string.IndexOf)); - var result = indexOf("aba", 'b'); - Equal(1, result); - - ByRefFunc indexOf2 = Type.Method.Get>(nameof(string.IndexOf), MethodLookup.Instance); - NotNull(indexOf2); - Equal(2, indexOf("abca", 'c')); - - Func indexOf3 = Type.Method.Require(nameof(string.IndexOf)); - Equal(1, indexOf3("aba", 'b', 1)); - - ByRefAction zero = Type.Method.Get>(nameof(Point.Zero), MethodLookup.Instance); - NotNull(zero); - var point = new Point() { X = 10, Y = 20 }; - zero(point); - Equal(0, point.X); - Equal(0, point.Y); - - var indexOf4 = Type.RequireMethod<(char, int), int>(nameof(string.IndexOf)); - Equal(1, indexOf4.Invoke("aba", ('b', 1))); - } - - [Fact] - public void StaticMethodTest() - { - Func compare = Type.Method.RequireStatic(nameof(string.Compare)); - True(compare("a", "b") < 0); - - var compare2 = Type.RequireStaticMethod<(string first, string second), int>(nameof(string.Compare)); - True(compare2.Invoke((first: "a", second: "b")) < 0); - } - - [Fact] - public static void ConstructorTests() - { - Func stringCtor = Type.Constructor.Require(); - var str = stringCtor('a', 3); - Equal("aaa", str); - Func objCtor = Type.Constructor.Get(); - NotNull(objCtor()); - Throws(() => Type.Constructor.Require()); - Func classCtor = Type.Constructor.Get(true); - var obj = classCtor(10); - Equal(10, obj.ReadOnlyProp); - } - - [Fact] - public static void SpecialConstructorTests() - { - var stringCtor = Type.RequireConstructor<(char, int)>(); - var str = stringCtor.Invoke(('a', 3)); - Equal("aaa", str); - - Null(Type.GetConstructor<(bool, bool)>()); - - var ctorWithRef = Type.RequireConstructor<(int first, Ref second)>(); - var args = ctorWithRef.ArgList(); - args.first = 20; - args.second = false; - NotNull(ctorWithRef.Invoke(args)); - True(args.second); - } - - [Fact] - public static void ValueTypeTests() - { - Func longCtor = Type.Constructor.Get(); - Equal(0L, longCtor()); - Func pointCtor = Type.Constructor.Get(); - NotNull(pointCtor); - var point = pointCtor(10, 20); - Equal(10, point.X); - Equal(20, point.Y); - Function<(int, int), Point> pointCtor2 = Type.RequireConstructor<(int, int)>(); - point = pointCtor2((30, 40)); - Equal(30, point.X); - Equal(40, point.Y); - } - - private sealed class ClassWithProperties - { - - internal static long StaticProp { get; set; } - - internal int value; - internal volatile int volatileField; - - public ClassWithProperties() { } - - public ClassWithProperties(int val, out bool result) - { - value = val; - result = true; - } - - internal ClassWithProperties(int val) => value = val; - - public string ReadWriteProperty { get; set; } - - public int ReadOnlyProp => value; - - public int WriteOnlyProp - { - set => this.value = value; - } - } - - private struct StructWithProperties - { - private int value; - - public string ReadWriteProperty { get; set; } - - public int ReadOnlyProp => value; - - public int WriteOnlyProp - { - set => this.value = value; - } - } - - [Fact] - public static void InstanceProperty() - { - var instance = new StructWithProperties(); - var rwProperty = Type.Property.Require(nameof(StructWithProperties.ReadWriteProperty)); - True(rwProperty.CanRead); - True(rwProperty.CanWrite); - NotNull(rwProperty.GetMethod); - NotNull(rwProperty.SetMethod); - rwProperty[instance] = "Hello, world"; - Equal("Hello, world", instance.ReadWriteProperty); - Equal("Hello, world", rwProperty[instance]); - True(rwProperty.GetValue(instance, out var str)); - Equal("Hello, world", str); - var wProperty = Type.Property.Require(nameof(StructWithProperties.WriteOnlyProp)); - True(wProperty.CanWrite); - False(wProperty.CanRead); - NotNull(wProperty.SetMethod); - Null(wProperty.GetMethod); - wProperty[instance] = 42; - var rProperty = Type.Property.Require(nameof(StructWithProperties.ReadOnlyProp)); - Equal(42, rProperty[instance]); - } - - [Fact] - public static void StructProperty() - { - var instance = new StructWithProperties(); - var rwProperty = Type.Property.Require(nameof(StructWithProperties.ReadWriteProperty)); - rwProperty[instance] = "Hello, world"; - Equal("Hello, world", instance.ReadWriteProperty); - Equal("Hello, world", rwProperty.GetValue(instance)); - var wProperty = Type.Property.Require(nameof(StructWithProperties.WriteOnlyProp)); - True(wProperty.CanWrite); - False(wProperty.CanRead); - NotNull(wProperty.SetMethod); - Null(wProperty.GetMethod); - wProperty[instance] = 42; - var rProperty = Type.Property.Require(nameof(StructWithProperties.ReadOnlyProp)); - False(rProperty.CanWrite); - True(rProperty.CanRead); - Equal(42, rProperty[instance]); - //Equal(42, ((MemberAccess.Getter)rProperty.GetMethod).Invoke(instance)); - } - - [Fact] - public static void StaticProperty() - { - var property = Type.Property.RequireStatic(nameof(ClassWithProperties.StaticProp), true); - True(property.CanRead); - True(property.CanWrite); - property.Value = 42; - Equal(42L, property.Value); - } - - [Fact] - public void InstanceEventTest() - { - var ev = Type.Event.Require(nameof(AppDomain.TypeResolve)); - ResolveEventHandler handler = (sender, args) => null; - ev.AddEventHandler(AppDomain.CurrentDomain, handler); - ev.RemoveEventHandler(AppDomain.CurrentDomain, handler); - var ev2 = Type.Event.Require(nameof(InstanceEvent), true); - Null(InstanceEvent); - EventHandler handler2 = (sender, args) => { }; - ev2.AddEventHandler(this, handler2); - Equal(InstanceEvent, handler2); - ev2.RemoveEventHandler(this, handler2); - Null(InstanceEvent); - } - - [Fact] - public static void StaticEventTest() - { - var ev = Type.Event.RequireStatic(nameof(StaticEvent), true); - EventHandler handler = (sender, args) => { }; - ev.AddEventHandler(handler); - Equal(StaticEvent, handler); - ev.RemoveEventHandler(handler); - Null(StaticEvent); - } - - private static long Field = 0; - private static volatile int VolatileField = 0; - - [Fact] - public static void StaticFieldTest() - { - var statField = Type.Field.RequireStatic(nameof(Field), true); - statField.Value = 42L; - Equal(Field, statField.Value); - MemberGetter getter = statField; - Equal(42L, getter()); - } - - [Fact] - public static void StaticVolatileField() - { - var statField = Type.Field.RequireStatic(nameof(VolatileField), true); - True(statField.GetValue(null, out int value)); - Equal(VolatileField, value); - True(statField.SetValue(null, 42)); - True(statField.GetValue(null, out value)); - Equal(42, value); - } - - [Fact] - public void InstanceFieldTest() - { - var s = new StructWithProperties(); - var structField = Type.Field.Require("value", true); - structField[s] = 42; - Equal(42, s.ReadOnlyProp); - Equal(42, structField[s]); - - var obj = new ClassWithProperties(); - var classField = Type.Field.Require(nameof(ClassWithProperties.value), true); - classField[obj] = 42; - Equal(42, obj.ReadOnlyProp); - Equal(42, classField[obj]); - } - - [Fact] - public void InstanceVolatileFieldTest() - { - var obj = new ClassWithProperties(); - var classField = Type.Field.Require(nameof(ClassWithProperties.volatileField), true); - True(classField.SetValue(obj, 42)); - True(classField.GetValue(obj, out int value)); - Equal(42, value); - } - - [Fact] - public static void InvalidConversionTest() - { - False(Type.TryConvert(23, out _)); - False(Type.TryConvert(new object(), out _)); - False(Type.TryConvert(new object(), out _)); - True(Type.TryConvert(42, out _)); - } - - [Fact] - public static void GetHashCodeTest() - { - Equal("Hello".GetHashCode(), Type.GetHashCode("Hello")); - NotEqual(new object().GetHashCode(), Type.GetHashCode(new object())); - var guid = Guid.NewGuid(); - Equal(guid.GetHashCode(), Type.GetHashCode(guid)); - NotEqual(BitwiseComparer.GetHashCode(guid), Type.GetHashCode(guid)); - var point = new Point { X = 10, Y = 20 }; - NotEqual(point.GetHashCode(), Type.GetHashCode(point)); - Equal(BitwiseComparer.GetHashCode(point), Type.GetHashCode(point)); - } - - [Fact] - public static void BitwiseEqualityTest() - { - var guid = Guid.NewGuid(); - True(Type.Equals(in guid, in guid)); - var point = new Point { X = 10, Y = 20 }; - True(Type.Equals(point, point)); - True(Type.Equals(new string("Hello"), "Hello")); - False(Type.Equals(new object(), new object())); - True(Type.Equals(new StructWithProperties() { WriteOnlyProp = 20 }, new StructWithProperties { WriteOnlyProp = 20 })); - False(Type.Equals(new StructWithProperties() { WriteOnlyProp = 10 }, new StructWithProperties { WriteOnlyProp = 20 })); - } - - public class ClassA - { - public int PropertyName { get; set; } - } - - public class ClassB : ClassA - { - public new int PropertyName { get; set; } - } - - [Fact] - public void PropertyOverloadingTest() - { - MemberGetter property = Type.Property.Require(nameof(ClassB.PropertyName)); - var obj = new ClassB() { PropertyName = 42 }; - Equal(42, property(obj)); - Equal(0, ((ClassA)obj).PropertyName); - } - - [Fact] - public void StaticIndexerTest() - { - var property = Type.Indexer, string>.RequireStatic("MyIndexer"); - True(property.CanRead); - True(property.CanWrite); - TypeWithStaticIndexer.BackedArray[1] = "Hello, world"; - Equal("Hello, world", property[1]); - property[1] = "Barry Burton"; - Equal("Barry Burton", property[1]); - } - - [Fact] - public void InstanceIndexerTest() - { - var list = new List() { 10, 40, 100 }; - var property = Type>.Indexer, long>.Require(); - True(property.CanRead); - True(property.CanWrite); - Equal(40, property[list, 1]); - Equal(100, property[list, 2]); - property[list, 1] = 120; - Equal(120, list[1]); - Equal(120, property[list, 1]); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Runtime/CompilerServices/AwaitableTests.cs b/src/DotNext.Tests/Runtime/CompilerServices/AwaitableTests.cs deleted file mode 100644 index 63e9434c9..000000000 --- a/src/DotNext.Tests/Runtime/CompilerServices/AwaitableTests.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Runtime.CompilerServices; - -public sealed class AwaitableTests : Test -{ - [Fact] - public static void TaskWithResultTest() - { - var task = Task.Factory.StartNew(() => 42); - task.Wait(DefaultTimeout); - var awaiter = Awaitable, TaskAwaiter, long>.GetAwaiter(task); - True(NotifyCompletion>.IsCompleted(awaiter)); - Equal(42, Awaiter, long>.GetResult(awaiter)); - } - - public sealed class ValueHolder - { - public volatile int Value; - - public void ChangeValue() => Value = 42; - } - - [Fact] - public static void TaskWithoutResultTest() - { - var holder = new ValueHolder(); - var task = Task.Factory.StartNew(holder.ChangeValue); - task.Wait(DefaultTimeout); - var awaiter = Awaitable.GetAwaiter(task); - True(NotifyCompletion.IsCompleted(awaiter)); - Awaiter.GetResult(awaiter); - Equal(42, holder.Value); - } - - [Fact] - public static async Task AwaitUsingConcept() - { - var task = new Task(() => 42); - task.Start(); - var result = await new Awaitable, TaskAwaiter, int>(task); - Equal(42, result); - } -} \ No newline at end of file diff --git a/src/DotNext.sln b/src/DotNext.sln index 08503e124..f3e71293a 100644 --- a/src/DotNext.sln +++ b/src/DotNext.sln @@ -9,8 +9,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Tests", "DotNext.Te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Benchmarks", "DotNext.Benchmarks\DotNext.Benchmarks.csproj", "{D8438838-088B-4559-8605-7DEAC889902C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Reflection", "DotNext.Reflection\DotNext.Reflection.csproj", "{328F0106-4014-49E1-8DA0-35A37E8A0E96}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Unsafe", "DotNext.Unsafe\DotNext.Unsafe.csproj", "{BEFBC460-FFF8-4685-8ACF-EC37530845FB}" EndProject Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "VisualBasicElements", "VisualBasicElements\VisualBasicElements.vbproj", "{9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}" From b75ac8b6e8cfe8aca24dff1d8c22b514cc8f8ca5 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:10:00 +0200 Subject: [PATCH 003/155] Switched to .net 8.0 --- src/DotNext/DotNext.csproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/DotNext/DotNext.csproj b/src/DotNext/DotNext.csproj index b6f60f3b9..f47ba677c 100644 --- a/src/DotNext/DotNext.csproj +++ b/src/DotNext/DotNext.csproj @@ -1,8 +1,8 @@  - net6.0 - preview + net8.0 + latest enable true true @@ -50,7 +50,6 @@ - From 6c3af1b281843797cbaddde5e84d15be87146cc4 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:10:09 +0200 Subject: [PATCH 004/155] Removed unused dependency --- src/Directory.Packages.props | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 12af125f0..532de5fc9 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -16,7 +16,6 @@ - From cf9f3116cb8db44e57111dadc1978b4dc5edfd4b Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:10:53 +0200 Subject: [PATCH 005/155] Removed RequiresPreviewFeatures from interfaces with static abstract --- src/DotNext/IOptionMonad.cs | 7 +- src/DotNext/IResultMonad.cs | 3 - src/DotNext/Optional.cs | 130 ++++++++++++--------------- src/DotNext/Result.cs | 99 ++++++++++---------- src/DotNext/Runtime/SoftReference.cs | 2 +- 5 files changed, 104 insertions(+), 137 deletions(-) diff --git a/src/DotNext/IOptionMonad.cs b/src/DotNext/IOptionMonad.cs index d88c87f8a..d18c009f4 100644 --- a/src/DotNext/IOptionMonad.cs +++ b/src/DotNext/IOptionMonad.cs @@ -1,5 +1,3 @@ -using System.Runtime.Versioning; - namespace DotNext; /// @@ -22,8 +20,8 @@ public interface IOptionMonad : ISupplier /// /// If a value is present, returns the value, otherwise return default value. /// - /// The value, if present, otherwise default. - T? OrDefault(); + /// The value, if present, otherwise default. + T? ValueOrDefault { get; } /// /// Returns the value if present; otherwise return default value. @@ -52,7 +50,6 @@ public interface IOptionMonad : ISupplier /// /// The type of the value in the container. /// The implementing type. -[RequiresPreviewFeatures] public interface IOptionMonad : IOptionMonad where TSelf : struct, IOptionMonad { diff --git a/src/DotNext/IResultMonad.cs b/src/DotNext/IResultMonad.cs index 4b99620b9..f6a445a75 100644 --- a/src/DotNext/IResultMonad.cs +++ b/src/DotNext/IResultMonad.cs @@ -1,5 +1,3 @@ -using System.Runtime.Versioning; - namespace DotNext; /// @@ -29,7 +27,6 @@ public interface IResultMonad : IOptionMonad /// The type of the result. /// The type that represents an error. /// The implementing type. -[RequiresPreviewFeatures] public interface IResultMonad : IResultMonad, IOptionMonad where TError : notnull where TSelf : struct, IResultMonad diff --git a/src/DotNext/Optional.cs b/src/DotNext/Optional.cs index 16299c8e8..35bd7c620 100644 --- a/src/DotNext/Optional.cs +++ b/src/DotNext/Optional.cs @@ -166,63 +166,6 @@ public static Optional Null() where T : class? => new(null); - private static ref readonly T GetReference(in Optional optional, TException exceptionFactory) - where T : struct - where TException : struct, ISupplier - { - optional.Validate(exceptionFactory); - return ref Optional.GetReference(in optional); - } - - /// - /// Obtains immutable reference to the value in the container. - /// - /// The type of the value. - /// The type of the exception to throw if the optional container has no value. - /// The optional container. - /// The immutable reference to the value in the container. - public static ref readonly T GetReference(in Optional optional) - where T : struct - where TException : Exception, new() - => ref GetReference(in optional, new Activator()); - - /// - /// Obtains immutable reference to the value in the container. - /// - /// The type of the value. - /// The optional container. - /// The factory used to produce exception if the container has no value. - /// The immutable reference to the value in the container. - public static ref readonly T GetReference(in Optional optional, Func exceptionFactory) - where T : struct - => ref GetReference>(in optional, exceptionFactory); - - /// - /// Obtains immutable reference to the value in the container. - /// - /// The type of the value. - /// The optional container. - /// The factory used to produce exception if the container has no value. - /// The immutable reference to the value in the container. - [CLSCompliant(false)] - public static unsafe ref readonly T GetReference(in Optional optional, delegate* exceptionFactory) - where T : struct - => ref GetReference>(in optional, exceptionFactory); - - /// - /// Obtains immutable reference to the value in the container. - /// - /// The type of the value. - /// The optional container. - /// The immutable reference to the value in the container. - /// No value is present. - public static ref readonly T GetReference(in Optional optional) - where T : struct - { - optional.Validate(); - return ref Optional.GetReference(in optional); - } - /// /// Converts the monad to . /// @@ -248,7 +191,6 @@ public static Optional Flatten(this in Optional> optional) /// A container object which may or may not contain a value. /// /// Type of value. -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Auto)] public readonly struct Optional : IEquatable>, IEquatable, IStructuralEquatable, IOptionMonad> { @@ -313,10 +255,6 @@ private static byte GetKindUnsafe([DisallowNull] ref T optionalValue) public static bool IsValueDefined([NotNullWhen(true)] T? value) => value is not null && (!IsOptional || GetKindUnsafe(ref value) is NotEmptyValue); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ref readonly T? GetReference(in Optional optional) - => ref optional.value; - /// /// Represents optional container without value. /// @@ -475,22 +413,12 @@ private T OrInvoke(TSupplier defaultFunc) [CLSCompliant(false)] public unsafe T OrInvoke(delegate* defaultFunc) => OrInvoke>(defaultFunc); - /// - /// If a value is present, returns the value, otherwise return default value. - /// - /// The value, if present, otherwise default. - [Obsolete("Use ValueOrDefault property instead.")] - public T? OrDefault() => ValueOrDefault; - /// /// If a value is present, returns the value, otherwise default value. /// /// The value, if present, otherwise default. public T? ValueOrDefault => value; - /// - T? IOptionMonad.OrDefault() => ValueOrDefault; - /// /// If a value is present, returns the value, otherwise throw exception. /// @@ -505,6 +433,61 @@ public T Value } } + /// + /// Obtains immutable reference to the value in the container. + /// + /// No value is present. + [UnscopedRef] + [DisallowNull] + public ref readonly T ValueRef + { + get + { + Validate(); + return ref value!; + } + } + + [UnscopedRef] + private ref readonly T GetReference(TException exceptionFactory) + where TException : struct, ISupplier + { + Validate(exceptionFactory); + return ref value!; + } + + /// + /// Obtains immutable reference to the value in the container. + /// + /// The type of the exception to throw if the optional container has no value. + /// The immutable reference to the value in the container. + [UnscopedRef] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public ref readonly T GetReference() + where TException : Exception, new() + => ref GetReference(new Activator()); + + /// + /// Obtains immutable reference to the value in the container. + /// + /// The factory used to produce exception if the container has no value. + /// The immutable reference to the value in the container. + [UnscopedRef] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public ref readonly T GetReference(Func exceptionFactory) + => ref GetReference>(exceptionFactory); + + /// + /// Obtains immutable reference to the value in the container. + /// + /// The factory used to produce exception if the container has no value. + /// The immutable reference to the value in the container. + [CLSCompliant(false)] + [UnscopedRef] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public unsafe ref readonly T GetReference(delegate* exceptionFactory) + => ref GetReference>(exceptionFactory); + [MemberNotNull(nameof(value))] internal void Validate() { @@ -756,5 +739,4 @@ public bool Equals(object? other, IEqualityComparer comparer) /// [MemberNotNullWhen(false, nameof(ValueOrDefault))] public static bool operator false(in Optional optional) => optional.kind < NotEmptyValue; -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/DotNext/Result.cs b/src/DotNext/Result.cs index 366fd463c..fb438a761 100644 --- a/src/DotNext/Result.cs +++ b/src/DotNext/Result.cs @@ -73,35 +73,12 @@ public static class Result /// The exception to be placed to the container. /// The exception encapsulated by . public static Result FromException(Exception e) => new(e); - - /// - /// Gets a reference to the underlying value. - /// - /// The type of the result. - /// The result container. - /// The reference to the result. - /// The result is unavailable. - public static ref readonly T GetReference(in Result result) - => ref Result.GetReference(in result); - - /// - /// Gets a reference to the underlying value. - /// - /// The type of the result. - /// The type of the error code. - /// The result container. - /// The reference to the result. - /// The result is undefined. - public static ref readonly T GetReference(in Result result) - where TError : struct, Enum - => ref Result.GetReference(in result); } /// /// Represents a result of operation which can be actual result or exception. /// /// The type of the value stored in the Result monad. -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Auto)] public readonly struct Result : IResultMonad> { @@ -125,7 +102,7 @@ public Result(T value) public Result(Exception error) { exception = ExceptionDispatchInfo.Capture(error); - value = default!; + Unsafe.SkipInit(out value); } private Result(ExceptionDispatchInfo dispatchInfo) @@ -134,13 +111,6 @@ private Result(ExceptionDispatchInfo dispatchInfo) exception = dispatchInfo; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ref readonly T GetReference(in Result result) - { - result.Validate(); - return ref result.value; - } - /// /// Initializes a new unsuccessful result. /// @@ -183,6 +153,27 @@ public T Value } } + /// + /// Gets a reference to the underlying value. + /// + /// The reference to the result. + /// The result is unavailable. + [UnscopedRef] + public ref readonly T ValueRef + { + get + { + Validate(); + return ref value; + } + } + + /// + /// Gets the value if present; otherwise return default value. + /// + /// The value, if present, otherwise default. + public T? ValueOrDefault => value; + private void Validate() => exception?.Throw(); /// @@ -251,12 +242,6 @@ public bool TryGet(out T value) /// The value, if present, otherwise . public T? Or(T? defaultValue) => exception is null ? value : defaultValue; - /// - /// Returns the value if present; otherwise return default value. - /// - /// The value, if present, otherwise default. - public T? OrDefault() => value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] private T OrInvoke(TSupplier defaultFunc) where TSupplier : struct, ISupplier @@ -395,7 +380,6 @@ public unsafe T OrInvoke(delegate* defaultFunc) public Result(T value) { this.value = value; - errorCode = default(TError); } /// @@ -405,7 +389,7 @@ public Result(T value) /// represents a successful code. public Result(TError error) { - value = default!; + Unsafe.SkipInit(out value); errorCode = Intrinsics.IsDefault(in error) ? throw new ArgumentOutOfRangeException(nameof(error)) : error; } @@ -417,13 +401,6 @@ public Result(TError error) /// represents a successful code. static Result IResultMonad>.FromError(TError error) => new(error); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ref readonly T GetReference(in Result result) - { - result.Validate(); - return ref result.value; - } - /// /// Gets boxed representation of the result. /// @@ -443,6 +420,27 @@ public T Value } } + /// + /// Gets a reference to the underlying value. + /// + /// The reference to the result. + /// The value is unavailable. + [UnscopedRef] + public ref readonly T ValueRef + { + get + { + Validate(); + return ref value; + } + } + + /// + /// Returns the value if present; otherwise return default value. + /// + /// The value, if present, otherwise default. + public T? ValueOrDefault => value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Validate() { @@ -488,12 +486,6 @@ public bool TryGet(out T value) /// Option monad representing value in this monad. public Optional TryGet() => IsSuccessful ? new(value) : Optional.None; - /// - /// Returns the value if present; otherwise return default value. - /// - /// The value, if present, otherwise default. - public T? OrDefault() => value; - /// /// Returns the value if present; otherwise return default value. /// @@ -660,5 +652,4 @@ public unsafe T OrThrow(delegate* exceptionFactory) /// The result to check. /// if this result is successful; if this result represents exception. public static bool operator false(in Result result) => !result.IsSuccessful; -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/DotNext/Runtime/SoftReference.cs b/src/DotNext/Runtime/SoftReference.cs index df7f77721..323ee998e 100644 --- a/src/DotNext/Runtime/SoftReference.cs +++ b/src/DotNext/Runtime/SoftReference.cs @@ -177,7 +177,7 @@ public bool TryGetTarget([NotNullWhen(true)] out T? target) T? IOptionMonad.Or(T? defaultValue) => Target ?? defaultValue; /// - T? IOptionMonad.OrDefault() => Target; + T? IOptionMonad.ValueOrDefault => Target; /// T IOptionMonad.OrInvoke(Func defaultFunc) => Target ?? defaultFunc(); From 43e16771299f3298a184061af200a61f4b3825fc Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:15:04 +0200 Subject: [PATCH 006/155] Fixed compiler warning --- src/DotNext/Text/Json/OptionalConverterFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNext/Text/Json/OptionalConverterFactory.cs b/src/DotNext/Text/Json/OptionalConverterFactory.cs index 6e28397b5..50ecebeaa 100644 --- a/src/DotNext/Text/Json/OptionalConverterFactory.cs +++ b/src/DotNext/Text/Json/OptionalConverterFactory.cs @@ -11,13 +11,13 @@ namespace DotNext.Text.Json; /// For AOT and self-contained app deployment models, use /// converter explicitly as an argument for . /// -public sealed class OptionalConverterFactory : JsonConverterFactory // TODO: Add [RequiresDynamicCode] in .NET 7 +[RequiresUnreferencedCode("This type instantiates OptionalConverter dynamically.")] +public sealed class OptionalConverterFactory : JsonConverterFactory { /// public override bool CanConvert(Type typeToConvert) => typeToConvert.IsOptional(); /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "No way to annotate the entire class")] public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var underlyingType = Optional.GetUnderlyingType(typeToConvert); From 17f20b8e124fb7ce5637573181adc477bde4dab6 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:21:12 +0200 Subject: [PATCH 007/155] Convert to file-local types --- src/DotNext/Buffers/PooledArrayBufferWriter.cs | 3 +-- src/DotNext/Buffers/PooledBufferWriter.cs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/DotNext/Buffers/PooledArrayBufferWriter.cs b/src/DotNext/Buffers/PooledArrayBufferWriter.cs index 5ad48ea36..ae1b7f054 100644 --- a/src/DotNext/Buffers/PooledArrayBufferWriter.cs +++ b/src/DotNext/Buffers/PooledArrayBufferWriter.cs @@ -564,8 +564,7 @@ protected override void Dispose(bool disposing) } } -// TODO: Convert to file-local class in C# 11 -internal static class PooledArrayBufferWriter +file static class PooledArrayBufferWriter { internal static readonly Histogram AllocationMeter; diff --git a/src/DotNext/Buffers/PooledBufferWriter.cs b/src/DotNext/Buffers/PooledBufferWriter.cs index a2bd6dd83..7c85e11ee 100644 --- a/src/DotNext/Buffers/PooledBufferWriter.cs +++ b/src/DotNext/Buffers/PooledBufferWriter.cs @@ -174,8 +174,7 @@ protected override void Dispose(bool disposing) } } -// TODO: Convert to file-local class in C# 11 -internal static class PooledBufferWriter +file static class PooledBufferWriter { internal static readonly Histogram AllocationMeter; From 9f5c993753edd5a048de37a8f8999c70e5d7aa43 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:25:02 +0200 Subject: [PATCH 008/155] Migration to unscopedref --- .../Collections/Specialized/InvocationList.cs | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/DotNext/Collections/Specialized/InvocationList.cs b/src/DotNext/Collections/Specialized/InvocationList.cs index a8c8ad264..caf0d4a95 100644 --- a/src/DotNext/Collections/Specialized/InvocationList.cs +++ b/src/DotNext/Collections/Specialized/InvocationList.cs @@ -1,4 +1,5 @@ using System.Collections; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Unsafe = System.Runtime.CompilerServices.Unsafe; @@ -207,14 +208,16 @@ public InvocationList Remove(TDelegate? d) /// IEnumerator IEnumerable.GetEnumerator() => GetEnumeratorCore(); - private static ReadOnlySpan GetSpan(in object? list) => list switch + /// + /// Gets a span over list of delegates. + /// + [UnscopedRef] + public ReadOnlySpan Span => list switch { - null => ReadOnlySpan.Empty, + null => [], TDelegate => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref Unsafe.AsRef(in list)), 1), _ => Unsafe.As(list), }; - - internal static ReadOnlySpan GetList(in InvocationList list) => GetSpan(in list.list); } /// @@ -222,16 +225,6 @@ public InvocationList Remove(TDelegate? d) /// public static class InvocationList { - /// - /// Gets a span over the delegates in the list. - /// - /// The type of the delegates. - /// The list of delegates. - /// A span over the delegates in the list. - public static ReadOnlySpan AsSpan(this ref InvocationList delegates) - where TDelegate : MulticastDelegate - => InvocationList.GetList(in delegates); - /// /// Invokes all actions in the list. /// @@ -240,7 +233,7 @@ public static ReadOnlySpan AsSpan(this ref InvocationList< /// The argument of the action. public static void Invoke(this InvocationList> actions, T arg) { - foreach (var action in actions.AsSpan()) + foreach (var action in actions.Span) action(arg); } @@ -254,7 +247,7 @@ public static void Invoke(this InvocationList> actions, T arg) /// The second argument of the action. public static void Invoke(this InvocationList> actions, T1 arg1, T2 arg2) { - foreach (var action in actions.AsSpan()) + foreach (var action in actions.Span) action(arg1, arg2); } } \ No newline at end of file From b98e0cee60f6278bb30fbb2801ba1f02d06b1427 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:31:11 +0200 Subject: [PATCH 009/155] Removed RequiresPreviewFeatures --- .../Buffers/Binary/BinaryTransformations.Bitwise.cs | 13 ------------- .../Binary/BinaryTransformations.Endianness.cs | 10 ---------- src/DotNext/Buffers/Binary/BinaryTransformations.cs | 3 --- .../Buffers/BufferHelpers.BufferWriterSlim.cs | 3 --- src/DotNext/Buffers/BufferHelpers.cs | 3 --- src/DotNext/Buffers/IBinaryFormattable.cs | 2 -- src/DotNext/IBuildable.cs | 3 --- 7 files changed, 37 deletions(-) diff --git a/src/DotNext/Buffers/Binary/BinaryTransformations.Bitwise.cs b/src/DotNext/Buffers/Binary/BinaryTransformations.Bitwise.cs index 8ed3072b1..b2da16b04 100644 --- a/src/DotNext/Buffers/Binary/BinaryTransformations.Bitwise.cs +++ b/src/DotNext/Buffers/Binary/BinaryTransformations.Bitwise.cs @@ -1,14 +1,11 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.Buffers.Binary; public static partial class BinaryTransformations { -#pragma warning disable CA2252 // TODO: Remove in .NET 7 - /// /// Performs bitwise AND operation between two vectors in-place. /// @@ -81,9 +78,7 @@ public static void BitwiseXor(this ReadOnlySpan x, Span y) public static void OnesComplement(this Span values) where T : unmanaged => Transform(values); -#pragma warning restore CA2252 - [RequiresPreviewFeatures] private static void Transform(ref byte x, int length) where TTransformation : struct, IUnaryTransformation, IUnaryTransformation> { @@ -115,7 +110,6 @@ private static void Transform(ref byte x, int length) } } - [RequiresPreviewFeatures] private static unsafe void Transform(Span values) where T : unmanaged where TTransformation : struct, IUnaryTransformation, IUnaryTransformation> @@ -130,7 +124,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(values)), } } - [RequiresPreviewFeatures] private static void Transform([In] ref byte x, ref byte y, int length) where TTransformation : struct, IBinaryTransformation, IBinaryTransformation> { @@ -168,7 +161,6 @@ private static void Transform([In] ref byte x, ref byte y, int } } - [RequiresPreviewFeatures] private static unsafe void Transform(ReadOnlySpan x, Span y) where T : unmanaged where TTransformation : struct, IBinaryTransformation, IBinaryTransformation> @@ -184,7 +176,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(y)), } } - [RequiresPreviewFeatures] private readonly struct BitwiseAndTransformation : IBinaryTransformation, IBinaryTransformation> { static Vector IBinaryTransformation>.Transform(Vector x, Vector y) => Vector.BitwiseAnd(x, y); @@ -192,7 +183,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(y)), static nuint IBinaryTransformation.Transform(nuint x, nuint y) => x & y; } - [RequiresPreviewFeatures] private readonly struct BitwiseOrTransformation : IBinaryTransformation, IBinaryTransformation> { static Vector IBinaryTransformation>.Transform(Vector x, Vector y) => Vector.BitwiseOr(x, y); @@ -200,7 +190,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(y)), static nuint IBinaryTransformation.Transform(nuint x, nuint y) => x | y; } - [RequiresPreviewFeatures] private readonly struct BitwiseXorTransformation : IBinaryTransformation, IBinaryTransformation> { static Vector IBinaryTransformation>.Transform(Vector x, Vector y) => Vector.Xor(x, y); @@ -208,7 +197,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(y)), static nuint IBinaryTransformation.Transform(nuint x, nuint y) => x ^ y; } - [RequiresPreviewFeatures] private readonly struct BitwiseAndNotTransformation : IBinaryTransformation, IBinaryTransformation> { static Vector IBinaryTransformation>.Transform(Vector x, Vector y) => Vector.AndNot(x, y); @@ -216,7 +204,6 @@ ref Unsafe.As(ref MemoryMarshal.GetReference(y)), static nuint IBinaryTransformation.Transform(nuint x, nuint y) => x & ~y; } - [RequiresPreviewFeatures] private readonly struct OnesComplementTransformation : IUnaryTransformation, IUnaryTransformation> { static Vector IUnaryTransformation>.Transform(Vector value) => Vector.OnesComplement(value); diff --git a/src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs b/src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs index ebcf36e2d..c2b608670 100644 --- a/src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs +++ b/src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs @@ -3,7 +3,6 @@ using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; -using System.Runtime.Versioning; namespace DotNext.Buffers.Binary; @@ -11,14 +10,12 @@ namespace DotNext.Buffers.Binary; public static partial class BinaryTransformations { - [RequiresPreviewFeatures] private interface IEndiannessTransformation : IUnaryTransformation where T : unmanaged { Vector128 ReorderMask { get; } } - [RequiresPreviewFeatures] private static void ReverseEndianness(Span buffer, TTransformation transformation) where T : unmanaged where TTransformation : struct, IEndiannessTransformation @@ -48,7 +45,6 @@ private static void ReverseEndianness(Span buffer, TTrans item = TTransformation.Transform(item); } - [RequiresPreviewFeatures] [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ReverseEndianness(Span buffer) where T : unmanaged @@ -68,8 +64,6 @@ private static void ReverseEndianness(Span buffer) } } -#pragma warning disable CA2252 // TODO: Remove in .NET 7 - /// /// Reverse endianness of 16-bit signed integers in-place. /// @@ -114,9 +108,7 @@ public static void ReverseEndianness(this Span buffer) [CLSCompliant(false)] public static void ReverseEndianness(this Span buffer) => ReverseEndianness(buffer); -#pragma warning restore CA2252 - [RequiresPreviewFeatures] [StructLayout(LayoutKind.Auto)] private readonly struct UInt16Transformation : IEndiannessTransformation { @@ -143,7 +135,6 @@ public UInt16Transformation() => ReorderMask = Vector128.Create( static ushort IUnaryTransformation.Transform(ushort value) => BinaryPrimitives.ReverseEndianness(value); } - [RequiresPreviewFeatures] [StructLayout(LayoutKind.Auto)] private readonly struct UInt32Transformation : IEndiannessTransformation { @@ -170,7 +161,6 @@ public UInt32Transformation() => ReorderMask = Vector128.Create( static uint IUnaryTransformation.Transform(uint value) => BinaryPrimitives.ReverseEndianness(value); } - [RequiresPreviewFeatures] [StructLayout(LayoutKind.Auto)] private readonly struct UInt64Transformation : IEndiannessTransformation { diff --git a/src/DotNext/Buffers/Binary/BinaryTransformations.cs b/src/DotNext/Buffers/Binary/BinaryTransformations.cs index 59233f9da..7f91347f0 100644 --- a/src/DotNext/Buffers/Binary/BinaryTransformations.cs +++ b/src/DotNext/Buffers/Binary/BinaryTransformations.cs @@ -1,7 +1,6 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using System.Runtime.Versioning; namespace DotNext.Buffers.Binary; @@ -10,14 +9,12 @@ namespace DotNext.Buffers.Binary; /// public static partial class BinaryTransformations { - [RequiresPreviewFeatures] private interface IUnaryTransformation where T : unmanaged { public static abstract T Transform(T value); } - [RequiresPreviewFeatures] private interface IBinaryTransformation where T : unmanaged { diff --git a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs index 491ec72a0..91778fed2 100644 --- a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs +++ b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Runtime.Versioning; using System.Text; using static System.Buffers.Binary.BinaryPrimitives; @@ -253,7 +252,6 @@ public static void Concat(this ref BufferWriterSlim writer, scoped ReadOnl /// The type of formattable value. /// The buffer writer. /// The value to convert. - [RequiresPreviewFeatures] public static void WriteFormattable(this ref BufferWriterSlim writer, T value) where T : notnull, IBinaryFormattable { @@ -268,7 +266,6 @@ public static void WriteFormattable(this ref BufferWriterSlim writer, T /// The type of formattable value. /// The buffer writer. /// A sequence of values to convert. - [RequiresPreviewFeatures] public static void WriteFormattable(this ref BufferWriterSlim writer, scoped ReadOnlySpan values) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext/Buffers/BufferHelpers.cs b/src/DotNext/Buffers/BufferHelpers.cs index 114c3a264..7f2dbc38c 100644 --- a/src/DotNext/Buffers/BufferHelpers.cs +++ b/src/DotNext/Buffers/BufferHelpers.cs @@ -1,7 +1,6 @@ using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Text; namespace DotNext.Buffers; @@ -417,7 +416,6 @@ public static int WriteFormattable(this IBufferWriter writer, T value, /// The type of formattable value. /// The buffer writer. /// The value to convert. - [RequiresPreviewFeatures] public static void WriteFormattable(this IBufferWriter writer, T value) where T : notnull, IBinaryFormattable { @@ -432,7 +430,6 @@ public static void WriteFormattable(this IBufferWriter writer, T value) /// The type of formattable value. /// The buffer writer. /// A sequence of values to convert. - [RequiresPreviewFeatures] public static void WriteFormattable(this IBufferWriter writer, scoped ReadOnlySpan values) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext/Buffers/IBinaryFormattable.cs b/src/DotNext/Buffers/IBinaryFormattable.cs index 55b7d2bb9..27cc350e2 100644 --- a/src/DotNext/Buffers/IBinaryFormattable.cs +++ b/src/DotNext/Buffers/IBinaryFormattable.cs @@ -1,7 +1,6 @@ using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Runtime.Versioning; namespace DotNext.Buffers; @@ -9,7 +8,6 @@ namespace DotNext.Buffers; /// Represents an object that can be converted to and restored from the binary representation. /// /// The implementing type. -[RequiresPreviewFeatures] public interface IBinaryFormattable where TSelf : notnull, IBinaryFormattable { diff --git a/src/DotNext/IBuildable.cs b/src/DotNext/IBuildable.cs index 168e8246f..98f6cdb05 100644 --- a/src/DotNext/IBuildable.cs +++ b/src/DotNext/IBuildable.cs @@ -1,5 +1,3 @@ -using System.Runtime.Versioning; - namespace DotNext; /// @@ -15,6 +13,5 @@ public interface IBuildable /// Creates a new builder for type . /// /// A new builder for type . - [RequiresPreviewFeatures] public static abstract TBuilder CreateBuilder(); } \ No newline at end of file From c31a5f40071d460fe4aa02bfa7b801d3f5724139 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:47:46 +0200 Subject: [PATCH 010/155] Added IReadOnlyList implementation --- src/DotNext/Collections/Specialized/InvocationList.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/DotNext/Collections/Specialized/InvocationList.cs b/src/DotNext/Collections/Specialized/InvocationList.cs index caf0d4a95..b23f65a4a 100644 --- a/src/DotNext/Collections/Specialized/InvocationList.cs +++ b/src/DotNext/Collections/Specialized/InvocationList.cs @@ -17,7 +17,7 @@ namespace DotNext.Collections.Specialized; /// /// The type of delegates in the list. [StructLayout(LayoutKind.Auto)] -public readonly struct InvocationList : IReadOnlyCollection // TODO: Workaround for https://github.com/dotnet/runtime/issues/4556 +public readonly struct InvocationList : IReadOnlyList // TODO: Workaround for https://github.com/dotnet/runtime/issues/4556 where TDelegate : MulticastDelegate { /// @@ -160,6 +160,9 @@ public InvocationList Remove(TDelegate? d) _ => Unsafe.As(list).Length, }; + /// + public TDelegate this[int index] => Span[index]; + /// /// Combines the delegates in the list to a single delegate. /// From b1697fdec9f5f1c9dead9d2bda30eeba7474e475 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:47:59 +0200 Subject: [PATCH 011/155] Switched to net8.0 + dependencies --- src/Directory.Packages.props | 24 +++++++++--------- .../DotNext.Benchmarks.csproj | 4 +-- src/DotNext.IO/DotNext.IO.csproj | 6 ++--- .../DotNext.MaintenanceServices.csproj | 6 ++--- .../DotNext.Metaprogramming.csproj | 6 ++--- .../Specialized/InvocationListTests.cs | 6 ++--- src/DotNext.Tests/DotNext.Tests.csproj | 6 ++--- src/DotNext.Tests/OptionalTests.cs | 10 ++++---- src/DotNext.Tests/ResultTests.cs | 4 +-- .../Runtime/SoftReferenceTests.cs | 4 +-- .../DotNext.Threading.csproj | 6 ++--- src/DotNext.Unsafe/DotNext.Unsafe.csproj | 4 +-- src/DotNext.sln | 9 ------- src/DotNext/DotNext.csproj | 2 +- .../Reflection/TypeWithStaticIndexer.vb | 25 ------------------- .../VisualBasicElements.vbproj | 9 ------- .../DotNext.AspNetCore.Cluster.csproj | 6 ++--- .../DotNext.Net.Cluster.csproj | 6 ++--- .../CommandLineAMI/CommandLineAMI.csproj | 4 +-- .../HyParViewPeer/HyParViewPeer.csproj | 4 +-- src/examples/RaftNode/RaftNode.csproj | 4 +-- 21 files changed, 56 insertions(+), 99 deletions(-) delete mode 100644 src/VisualBasicElements/Reflection/TypeWithStaticIndexer.vb delete mode 100644 src/VisualBasicElements/VisualBasicElements.vbproj diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 532de5fc9..36c68cb07 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -11,22 +11,22 @@ - + - - - - + + + + - - - - - - - + + + + + + + diff --git a/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj b/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj index e8d586b82..e628c863c 100644 --- a/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj +++ b/src/DotNext.Benchmarks/DotNext.Benchmarks.csproj @@ -2,13 +2,13 @@ Exe - net6.0 + net8.0 latest false DotNext DotNext.Program false - 4.14.0 + 5.0.0 .NET Foundation and Contributors .NEXT Family of Libraries Various benchmarks demonstrating performance aspects of .NEXT extensions diff --git a/src/DotNext.IO/DotNext.IO.csproj b/src/DotNext.IO/DotNext.IO.csproj index 243d43aad..2b67c3836 100644 --- a/src/DotNext.IO/DotNext.IO.csproj +++ b/src/DotNext.IO/DotNext.IO.csproj @@ -1,8 +1,8 @@  - net6.0 - preview + net8.0 + latest enable true true @@ -11,7 +11,7 @@ .NET Foundation and Contributors .NEXT Family of Libraries - 4.15.0 + 5.0.0 DotNext.IO MIT diff --git a/src/DotNext.MaintenanceServices/DotNext.MaintenanceServices.csproj b/src/DotNext.MaintenanceServices/DotNext.MaintenanceServices.csproj index 58aad87ed..475aa1420 100644 --- a/src/DotNext.MaintenanceServices/DotNext.MaintenanceServices.csproj +++ b/src/DotNext.MaintenanceServices/DotNext.MaintenanceServices.csproj @@ -1,8 +1,8 @@ - net6.0 - preview + net8.0 + latest enable true true @@ -11,7 +11,7 @@ .NET Foundation and Contributors .NEXT Family of Libraries - 0.2.0 + 0.3.0 DotNext.MaintenanceServices MIT diff --git a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj index 13543077b..443bfa67e 100644 --- a/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj +++ b/src/DotNext.Metaprogramming/DotNext.Metaprogramming.csproj @@ -1,14 +1,14 @@  - net6.0 + net8.0 DotNext - preview + latest enable true false nullablePublicOnly - 4.15.0 + 5.0.0 .NET Foundation .NEXT Family of Libraries diff --git a/src/DotNext.Tests/Collections/Specialized/InvocationListTests.cs b/src/DotNext.Tests/Collections/Specialized/InvocationListTests.cs index d45425b0d..e64e17284 100644 --- a/src/DotNext.Tests/Collections/Specialized/InvocationListTests.cs +++ b/src/DotNext.Tests/Collections/Specialized/InvocationListTests.cs @@ -26,13 +26,13 @@ public static void Operators() public static void GetInvocationList() { InvocationList> list = default; - True(list.AsSpan().IsEmpty); + True(list.Span.IsEmpty); list += Predicate.Constant(true); - Same(Predicate.Constant(true), list.AsSpan()[0]); + Same(Predicate.Constant(true), list.Span[0]); list += Predicate.Constant(false); - Equal(2, list.AsSpan().Length); + Equal(2, list.Span.Length); } [Fact] diff --git a/src/DotNext.Tests/DotNext.Tests.csproj b/src/DotNext.Tests/DotNext.Tests.csproj index 3872b3ade..35878a145 100644 --- a/src/DotNext.Tests/DotNext.Tests.csproj +++ b/src/DotNext.Tests/DotNext.Tests.csproj @@ -1,12 +1,12 @@  - net6.0 + net8.0 DotNext - preview + latest true false - 4.14.0 + 5.0.0 false .NET Foundation and Contributors .NEXT Family of Libraries diff --git a/src/DotNext.Tests/OptionalTests.cs b/src/DotNext.Tests/OptionalTests.cs index c5c864a9a..a01e491a8 100644 --- a/src/DotNext.Tests/OptionalTests.cs +++ b/src/DotNext.Tests/OptionalTests.cs @@ -219,12 +219,12 @@ public static void NoneSomeNull() public static void GettingReference() { var optional = Optional.None; - Throws(() => Optional.GetReference(optional)); - Throws(() => Optional.GetReference(in optional)); + Throws(() => optional.GetReference()); + Throws(() => optional.ValueRef); optional = 23; - Equal(23, Optional.GetReference(optional)); - Equal(23, Optional.GetReference(optional, static () => new InvalidOperationException())); - Equal(23, Optional.GetReference(in optional)); + Equal(23, optional.GetReference()); + Equal(23, optional.GetReference(static () => new InvalidOperationException())); + Equal(23, optional.ValueRef); } [Fact] diff --git a/src/DotNext.Tests/ResultTests.cs b/src/DotNext.Tests/ResultTests.cs index fe6b8d612..e72b6565d 100644 --- a/src/DotNext.Tests/ResultTests.cs +++ b/src/DotNext.Tests/ResultTests.cs @@ -49,7 +49,7 @@ public static void RaiseError() Throws(() => r.Value); NotNull(r.Error); Equal(20M, r.Or(20M)); - Equal(0M, r.OrDefault()); + Equal(0M, r.ValueOrDefault); Null(r.OrNull()); } @@ -60,7 +60,7 @@ public static void RaiseError2() Equal(EnvironmentVariableTarget.Machine, Throws>(() => r.Value).ErrorCode); Equal(EnvironmentVariableTarget.Machine, r.Error); Equal(20M, r.Or(20M)); - Equal(0M, r.OrDefault()); + Equal(0M, r.ValueOrDefault); Null(r.OrNull()); } diff --git a/src/DotNext.Tests/Runtime/SoftReferenceTests.cs b/src/DotNext.Tests/Runtime/SoftReferenceTests.cs index 27323b208..2e244d10a 100644 --- a/src/DotNext.Tests/Runtime/SoftReferenceTests.cs +++ b/src/DotNext.Tests/Runtime/SoftReferenceTests.cs @@ -100,13 +100,13 @@ public static void OptionMonadInterfaceInterop() False(monad.HasValue); False(monad.TryGet(out _)); Equal(string.Empty, monad.OrInvoke(Func.Constant(string.Empty))); - Null(monad.OrDefault()); + Null(monad.ValueOrDefault); Equal(string.Empty, monad.Or(string.Empty)); monad = new SoftReference(new()); True(monad.HasValue); True(monad.TryGet(out var target)); - Same(monad.OrDefault(), target); + Same(monad.ValueOrDefault, target); Same(target, monad.Or(string.Empty)); Same(target, monad.OrInvoke(Func.Constant(string.Empty))); } diff --git a/src/DotNext.Threading/DotNext.Threading.csproj b/src/DotNext.Threading/DotNext.Threading.csproj index 55d1499e2..cdaf7d9bc 100644 --- a/src/DotNext.Threading/DotNext.Threading.csproj +++ b/src/DotNext.Threading/DotNext.Threading.csproj @@ -1,13 +1,13 @@  - net6.0 + net8.0 DotNext - preview + latest enable true true nullablePublicOnly - 4.15.2 + 5.0.0 .NET Foundation and Contributors .NEXT Family of Libraries diff --git a/src/DotNext.Unsafe/DotNext.Unsafe.csproj b/src/DotNext.Unsafe/DotNext.Unsafe.csproj index b9166bc58..1b0686f66 100644 --- a/src/DotNext.Unsafe/DotNext.Unsafe.csproj +++ b/src/DotNext.Unsafe/DotNext.Unsafe.csproj @@ -1,13 +1,13 @@  - net6.0 + net8.0 DotNext latest enable true true - 4.14.0 + 5.0.0 nullablePublicOnly .NET Foundation and Contributors diff --git a/src/DotNext.sln b/src/DotNext.sln index f3e71293a..9f4e169c6 100644 --- a/src/DotNext.sln +++ b/src/DotNext.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Benchmarks", "DotNe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Unsafe", "DotNext.Unsafe\DotNext.Unsafe.csproj", "{BEFBC460-FFF8-4685-8ACF-EC37530845FB}" EndProject -Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "VisualBasicElements", "VisualBasicElements\VisualBasicElements.vbproj", "{9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Metaprogramming", "DotNext.Metaprogramming\DotNext.Metaprogramming.csproj", "{C8415985-3BC5-44EB-BDB4-551777B73AB4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Threading", "DotNext.Threading\DotNext.Threading.csproj", "{956A2A3E-F151-4FA4-9932-862BC5CB2F87}" @@ -79,12 +77,6 @@ Global {BEFBC460-FFF8-4685-8ACF-EC37530845FB}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEFBC460-FFF8-4685-8ACF-EC37530845FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEFBC460-FFF8-4685-8ACF-EC37530845FB}.Release|Any CPU.Build.0 = Release|Any CPU - {9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}.Bench|Any CPU.ActiveCfg = Release|Any CPU - {9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}.Bench|Any CPU.Build.0 = Release|Any CPU - {9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}.Release|Any CPU.Build.0 = Release|Any CPU {C8415985-3BC5-44EB-BDB4-551777B73AB4}.Bench|Any CPU.ActiveCfg = Release|Any CPU {C8415985-3BC5-44EB-BDB4-551777B73AB4}.Bench|Any CPU.Build.0 = Release|Any CPU {C8415985-3BC5-44EB-BDB4-551777B73AB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -146,7 +138,6 @@ Global GlobalSection(NestedProjects) = preSolution {43A2FE6F-B997-4295-BBD2-B0BBC1861194} = {4956E982-79AA-462C-B592-E904D24EFFAE} {D8438838-088B-4559-8605-7DEAC889902C} = {4956E982-79AA-462C-B592-E904D24EFFAE} - {9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17} = {4956E982-79AA-462C-B592-E904D24EFFAE} {89E2D94F-E278-4365-B05F-2C8BEC2F94DC} = {511B9F61-9A94-44CB-B506-74E49E3A0B41} {D9A4D7A3-A948-465A-B679-546411DC885D} = {511B9F61-9A94-44CB-B506-74E49E3A0B41} {EF588447-3DB4-4719-A9FB-210C21035C65} = {F8DCA620-40E7-411E-8970-85DC808B6BAF} diff --git a/src/DotNext/DotNext.csproj b/src/DotNext/DotNext.csproj index f47ba677c..bed98ce7c 100644 --- a/src/DotNext/DotNext.csproj +++ b/src/DotNext/DotNext.csproj @@ -11,7 +11,7 @@ .NET Foundation and Contributors .NEXT Family of Libraries - 4.15.2 + 5.0.0 DotNext MIT diff --git a/src/VisualBasicElements/Reflection/TypeWithStaticIndexer.vb b/src/VisualBasicElements/Reflection/TypeWithStaticIndexer.vb deleted file mode 100644 index a5ca7adc2..000000000 --- a/src/VisualBasicElements/Reflection/TypeWithStaticIndexer.vb +++ /dev/null @@ -1,25 +0,0 @@ -Imports System.Diagnostics.CodeAnalysis - -Namespace Reflection - - - Public NotInheritable Class TypeWithStaticIndexer - - Public Shared ReadOnly BackedArray As String() = New String(10) {} - - Public Shared Property MyIndexer(ByVal index As Integer) As String - Get - Return BackedArray(index) - End Get - Set(value As String) - BackedArray(index) = value - End Set - End Property - - Public Shared Operator ^(ByVal left As TypeWithStaticIndexer, ByVal right As Integer) As Integer - Return right - End Operator - - End Class -End Namespace - diff --git a/src/VisualBasicElements/VisualBasicElements.vbproj b/src/VisualBasicElements/VisualBasicElements.vbproj deleted file mode 100644 index a870d7192..000000000 --- a/src/VisualBasicElements/VisualBasicElements.vbproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - DotNext - net6.0 - latest - - - diff --git a/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj b/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj index 4504b62b1..1f775e8ad 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj +++ b/src/cluster/DotNext.AspNetCore.Cluster/DotNext.AspNetCore.Cluster.csproj @@ -1,14 +1,14 @@  - net6.0 + net8.0 DotNext enable - preview + latest true true nullablePublicOnly - 4.15.3 + 5.0.0 .NET Foundation and Contributors .NEXT Family of Libraries diff --git a/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj b/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj index f58177373..7adfd6756 100644 --- a/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj +++ b/src/cluster/DotNext.Net.Cluster/DotNext.Net.Cluster.csproj @@ -1,14 +1,14 @@  - net6.0 + net8.0 DotNext - preview + latest true enable true nullablePublicOnly - 4.15.3 + 5.0.0 .NET Foundation and Contributors .NEXT Family of Libraries diff --git a/src/examples/CommandLineAMI/CommandLineAMI.csproj b/src/examples/CommandLineAMI/CommandLineAMI.csproj index e2174d309..f0450afb3 100644 --- a/src/examples/CommandLineAMI/CommandLineAMI.csproj +++ b/src/examples/CommandLineAMI/CommandLineAMI.csproj @@ -2,11 +2,11 @@ Exe - net6.0 + net8.0 latest enable true - 4.8.0 + 5.0.0 diff --git a/src/examples/HyParViewPeer/HyParViewPeer.csproj b/src/examples/HyParViewPeer/HyParViewPeer.csproj index ecac37dac..afb482a96 100644 --- a/src/examples/HyParViewPeer/HyParViewPeer.csproj +++ b/src/examples/HyParViewPeer/HyParViewPeer.csproj @@ -2,11 +2,11 @@ Exe - net6.0 + net8.0 latest enable true - 4.11.0 + 5.0.0 diff --git a/src/examples/RaftNode/RaftNode.csproj b/src/examples/RaftNode/RaftNode.csproj index b12d11344..d0f2f8ea8 100644 --- a/src/examples/RaftNode/RaftNode.csproj +++ b/src/examples/RaftNode/RaftNode.csproj @@ -2,11 +2,11 @@ Exe - net6.0 + net8.0 latest enable true - 4.15.0 + 5.0.0 From 887be27871792f50e78007a11a84ed3e0ec808bf Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 09:53:34 +0200 Subject: [PATCH 012/155] Migration to Random.Shuffle --- src/DotNext/Collections/Generic/List.cs | 10 ++++++---- src/DotNext/Span.cs | 16 ---------------- .../HyParView/PeerController.Shuffle.cs | 6 +++--- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/DotNext/Collections/Generic/List.cs b/src/DotNext/Collections/Generic/List.cs index 69f1f8b3d..6a6088bc1 100644 --- a/src/DotNext/Collections/Generic/List.cs +++ b/src/DotNext/Collections/Generic/List.cs @@ -345,20 +345,22 @@ public static ListSegment Slice(this IList list, Range range) /// The source of random values. public static void Shuffle(this IList list, Random random) { - // TODO: Reuse https://github.com/dotnet/runtime/issues/73864 + Span span; switch (list) { case List typedList: - CollectionsMarshal.AsSpan(typedList).Shuffle(random); + span = CollectionsMarshal.AsSpan(typedList); break; case T[] array: - Span.Shuffle(array, random); + span = array; break; default: ShuffleSlow(list, random); - break; + return; } + random.Shuffle(span); + [MethodImpl(MethodImplOptions.NoInlining)] static void ShuffleSlow(IList list, Random random) { diff --git a/src/DotNext/Span.cs b/src/DotNext/Span.cs index 511711609..bf2156fdc 100644 --- a/src/DotNext/Span.cs +++ b/src/DotNext/Span.cs @@ -579,22 +579,6 @@ public static void CopyTo(this ReadOnlySpan source, Span destination, o public static void CopyTo(this Span source, Span destination, out int writtenCount) => CopyTo((ReadOnlySpan)source, destination, out writtenCount); - /// - /// Shuffles elements in the span. - /// - /// The type of the elements in the span. - /// The span of elements to shuffle. - /// The source of random values. - public static void Shuffle(this Span span, Random random) - { - // TODO: Remove in .NET 8: https://github.com/dotnet/runtime/issues/73864 - for (var i = span.Length - 1; i > 0; i--) - { - var randomIndex = random.Next(i + 1); - Intrinsics.Swap(ref span[randomIndex], ref span[i]); - } - } - /// /// Gets first element in the span. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs index eb5d4ba75..81b815eb3 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs @@ -31,10 +31,10 @@ private async Task ProcessShuffleAsync() using (var activeViewCopy = activeView.Remove(activePeer).Copy()) { - activeViewCopy.Span.Shuffle(random); + random.Shuffle(activeViewCopy.Span); using var passiveViewCopy = passiveView.Copy(); - passiveViewCopy.Span.Shuffle(random); + random.Shuffle(passiveViewCopy.Span); // add randomly selected peers from active and passive views peersToSend = new PooledArrayBufferWriter { Capacity = shuffleActiveViewCount + shufflePassiveViewCount }; @@ -121,7 +121,7 @@ private async Task ProcessShuffleAsync(EndPoint sender, EndPoint origin, IReadOn // send random part of passive view back to origin randomizedPassiveView.AddAll(passiveView); - randomizedPassiveView.WrittenArray.AsSpan().Shuffle(random); + random.Shuffle(randomizedPassiveView.WrittenArray); if (randomizedPassiveView.WrittenCount > announcement.Count) randomizedPassiveView.RemoveLast(randomizedPassiveView.WrittenCount - announcement.Count); await ShuffleReplyAsync(origin, randomizedPassiveView, LifecycleToken).ConfigureAwait(false); From 672c586d3985aafeb3d71c4eae03e35469fe46be Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 10:46:29 +0200 Subject: [PATCH 013/155] Fixed compiler warning --- .../Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs index 81b815eb3..c8bd4dd72 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs @@ -121,7 +121,7 @@ private async Task ProcessShuffleAsync(EndPoint sender, EndPoint origin, IReadOn // send random part of passive view back to origin randomizedPassiveView.AddAll(passiveView); - random.Shuffle(randomizedPassiveView.WrittenArray); + random.Shuffle(randomizedPassiveView.WrittenArray); if (randomizedPassiveView.WrittenCount > announcement.Count) randomizedPassiveView.RemoveLast(randomizedPassiveView.WrittenCount - announcement.Count); await ShuffleReplyAsync(origin, randomizedPassiveView, LifecycleToken).ConfigureAwait(false); From 4f1045ff6e2be4722cc7be57c621202b10361908 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 10:46:36 +0200 Subject: [PATCH 014/155] Removed dead reference --- src/DotNext.Tests/DotNext.Tests.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/DotNext.Tests/DotNext.Tests.csproj b/src/DotNext.Tests/DotNext.Tests.csproj index 35878a145..cbe3e0325 100644 --- a/src/DotNext.Tests/DotNext.Tests.csproj +++ b/src/DotNext.Tests/DotNext.Tests.csproj @@ -39,7 +39,6 @@ - From 2594a63156e3adbae04d5bdff0c93bdd8c021e76 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 10:47:32 +0200 Subject: [PATCH 015/155] Removed redundant checks --- src/DotNext/Buffers/SpanReader.cs | 73 ++++++++++++------------------- src/DotNext/Buffers/SpanWriter.cs | 72 ++++++++++++------------------ 2 files changed, 55 insertions(+), 90 deletions(-) diff --git a/src/DotNext/Buffers/SpanReader.cs b/src/DotNext/Buffers/SpanReader.cs index d12c59019..263b71f8b 100644 --- a/src/DotNext/Buffers/SpanReader.cs +++ b/src/DotNext/Buffers/SpanReader.cs @@ -12,7 +12,8 @@ namespace DotNext.Buffers; [StructLayout(LayoutKind.Auto)] public ref struct SpanReader { - private readonly ReadOnlySpan span; + private readonly ref T reference; + private readonly int length; private int position; /// @@ -21,8 +22,8 @@ public ref struct SpanReader /// The span to read from. public SpanReader(ReadOnlySpan span) { - this.span = span; - position = 0; + reference = ref MemoryMarshal.GetReference(span); + length = span.Length; } /// @@ -30,13 +31,16 @@ public SpanReader(ReadOnlySpan span) /// /// Managed pointer to the memory block. /// The length of the elements referenced by the pointer. + /// is negative. public SpanReader(ref T reference, int length) { - if (Unsafe.IsNullRef(ref reference)) + ArgumentOutOfRangeException.ThrowIfNegative(length); + + if (Unsafe.IsNullRef(ref reference) && length > 0) throw new ArgumentNullException(nameof(reference)); - span = MemoryMarshal.CreateReadOnlySpan(ref reference, length); - position = 0; + this.reference = ref reference; + this.length = length; } /// @@ -48,10 +52,10 @@ public readonly ref readonly T Current { get { - if ((uint)position >= (uint)span.Length) + if ((uint)position >= (uint)length) ThrowInvalidOperationException(); - return ref Unsafe.Add(ref MemoryMarshal.GetReference(span), position); + return ref Unsafe.Add(ref reference, position); [DoesNotReturn] [StackTraceHidden] @@ -67,37 +71,32 @@ public int ConsumedCount readonly get => position; set { - if ((uint)value > (uint)span.Length) - ThrowArgumentOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)value, (uint)length); position = value; - - [StackTraceHidden] - [DoesNotReturn] - static void ThrowArgumentOutOfRangeException() - => throw new ArgumentOutOfRangeException(nameof(value)); } } /// /// Gets the number of unread elements. /// - public readonly int RemainingCount => span.Length - position; + public readonly int RemainingCount => length - position; /// /// Gets underlying span. /// - public readonly ReadOnlySpan Span => span; + public readonly ReadOnlySpan Span => MemoryMarshal.CreateReadOnlySpan(ref reference, length); /// /// Gets the span over consumed elements. /// - public readonly ReadOnlySpan ConsumedSpan => span.Slice(0, position); + public readonly ReadOnlySpan ConsumedSpan => MemoryMarshal.CreateReadOnlySpan(ref reference, position); /// /// Gets the remaining part of the span. /// - public readonly ReadOnlySpan RemainingSpan => span.Slice(position); + public readonly ReadOnlySpan RemainingSpan + => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref reference, position), RemainingCount); /// /// Advances the position of this reader. @@ -106,8 +105,7 @@ static void ThrowArgumentOutOfRangeException() /// is greater than the available space in the rest of the memory block. public void Advance(int count) { - if ((uint)count > (uint)RemainingCount) - ThrowCountOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)RemainingCount); position += count; } @@ -119,8 +117,7 @@ public void Advance(int count) /// is less than zero or greater than . public void Rewind(int count) { - if ((uint)count > (uint)position) - ThrowCountOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)position); position -= count; } @@ -147,16 +144,13 @@ public bool TryRead(scoped Span output) /// is negative. public bool TryRead(int count, out ReadOnlySpan result) { - if (count < 0) - ThrowCountOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfNegative(count); var newLength = position + count; - if ((uint)newLength <= (uint)span.Length) + if ((uint)newLength <= (uint)length) { - result = MemoryMarshal.CreateReadOnlySpan( - ref Unsafe.Add(ref MemoryMarshal.GetReference(span), position), - count); + result = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref reference, position), count); position = newLength; return true; } @@ -172,9 +166,9 @@ ref Unsafe.Add(ref MemoryMarshal.GetReference(span), position), /// if element is obtained successfully; otherwise, . public bool TryRead([MaybeNullWhen(false)] out T result) { - if ((uint)position < (uint)span.Length) + if ((uint)position < (uint)length) { - result = Unsafe.Add(ref MemoryMarshal.GetReference(span), position++); + result = Unsafe.Add(ref reference, position++); return true; } @@ -225,15 +219,6 @@ public ReadOnlySpan Read(int count) [StackTraceHidden] private static void ThrowInternalBufferOverflowException() => throw new InternalBufferOverflowException(); - [DoesNotReturn] - [StackTraceHidden] - private static void ThrowCountOutOfRangeException() => throw new ArgumentOutOfRangeException("count"); - - // TODO: Replace with ArgumentNullException.ThrowIfNull in .NET 8 - [DoesNotReturn] - [StackTraceHidden] - private static void ThrowArgumentNullException() => throw new ArgumentNullException("reader"); - /// /// Decodes the value from the block of memory. /// @@ -246,8 +231,7 @@ public ReadOnlySpan Read(int count) [CLSCompliant(false)] public unsafe TResult Read(delegate*, TResult> reader, int count) { - if (reader is null) - ThrowArgumentNullException(); + ArgumentNullException.ThrowIfNull(reader); if (!TryRead(count, out var buffer)) ThrowInternalBufferOverflowException(); @@ -267,8 +251,7 @@ public unsafe TResult Read(delegate*, TResult> reader, [CLSCompliant(false)] public unsafe bool TryRead(delegate*, TResult> reader, int count, [MaybeNullWhen(false)] out TResult result) { - if (reader is null) - ThrowArgumentNullException(); + ArgumentNullException.ThrowIfNull(reader); if (TryRead(count, out var buffer)) { @@ -287,7 +270,7 @@ public unsafe bool TryRead(delegate*, TResult> reader, public ReadOnlySpan ReadToEnd() { var result = RemainingSpan; - position = span.Length; + position = length; return result; } diff --git a/src/DotNext/Buffers/SpanWriter.cs b/src/DotNext/Buffers/SpanWriter.cs index 2fe131296..2362eb151 100644 --- a/src/DotNext/Buffers/SpanWriter.cs +++ b/src/DotNext/Buffers/SpanWriter.cs @@ -12,7 +12,8 @@ namespace DotNext.Buffers; [StructLayout(LayoutKind.Auto)] public ref struct SpanWriter { - private readonly Span span; + private readonly ref T reference; + private readonly int length; private int position; /// @@ -21,8 +22,8 @@ public ref struct SpanWriter /// The span used to write elements. public SpanWriter(Span span) { - this.span = span; - position = 0; + reference = ref MemoryMarshal.GetReference(span); + length = span.Length; } /// @@ -30,13 +31,16 @@ public SpanWriter(Span span) /// /// Managed pointer to the memory block. /// The length of the elements referenced by the pointer. + /// is negative. public SpanWriter(ref T reference, int length) { - if (Unsafe.IsNullRef(ref reference)) + ArgumentOutOfRangeException.ThrowIfNegative(length); + + if (Unsafe.IsNullRef(ref reference) && length > 0) throw new ArgumentNullException(nameof(reference)); - span = MemoryMarshal.CreateSpan(ref reference, length); - position = 0; + this.reference = ref reference; + this.length = length; } /// @@ -48,10 +52,10 @@ public readonly ref T Current { get { - if ((uint)position >= (uint)span.Length) + if ((uint)position >= (uint)length) ThrowInvalidOperationException(); - return ref Unsafe.Add(ref MemoryMarshal.GetReference(span), position); + return ref Unsafe.Add(ref reference, position); [DoesNotReturn] [StackTraceHidden] @@ -62,7 +66,7 @@ public readonly ref T Current /// /// Gets the available space in the underlying span. /// - public readonly int FreeCapacity => span.Length - position; + public readonly int FreeCapacity => length - position; /// /// Gets the number of occupied elements in the underlying span. @@ -72,22 +76,16 @@ public int WrittenCount readonly get => position; set { - if ((uint)value > span.Length) - ThrowArgumentOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)value, (uint)length); position = value; - - [DoesNotReturn] - [StackTraceHidden] - static void ThrowArgumentOutOfRangeException() - => throw new ArgumentOutOfRangeException(nameof(value)); } } /// /// Gets the remaining part of the span. /// - public readonly Span RemainingSpan => span.Slice(position); + public readonly Span RemainingSpan => MemoryMarshal.CreateSpan(ref Unsafe.Add(ref reference, position), FreeCapacity); /// /// Advances the position of this writer. @@ -96,8 +94,7 @@ static void ThrowArgumentOutOfRangeException() /// is greater than the available space in the rest of the memory block. public void Advance(int count) { - if ((uint)count > (uint)FreeCapacity) - ThrowCountOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)FreeCapacity); position += count; } @@ -109,8 +106,7 @@ public void Advance(int count) /// is less than zero or greater than . public void Rewind(int count) { - if ((uint)count > (uint)position) - ThrowCountOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)FreeCapacity); position -= count; } @@ -124,12 +120,12 @@ public void Rewind(int count) /// Gets the span over written elements. /// /// The segment of underlying span containing written elements. - public readonly Span WrittenSpan => span.Slice(0, position); + public readonly Span WrittenSpan => MemoryMarshal.CreateSpan(ref reference, position); /// /// Gets underlying span. /// - public readonly Span Span => span; + public readonly Span Span => MemoryMarshal.CreateSpan(ref reference, length); /// /// Copies the elements to the underlying span. @@ -141,7 +137,7 @@ public void Rewind(int count) /// public bool TryWrite(scoped ReadOnlySpan input) { - if (!input.TryCopyTo(span.Slice(position))) + if (!input.TryCopyTo(RemainingSpan)) return false; position += input.Length; @@ -170,9 +166,9 @@ public int Write(scoped ReadOnlySpan input) /// public bool TryAdd(T item) { - if ((uint)position < (uint)span.Length) + if ((uint)position < (uint)length) { - Unsafe.Add(ref MemoryMarshal.GetReference(span), position++) = item; + Unsafe.Add(ref reference, position++) = item; return true; } @@ -202,15 +198,12 @@ public void Add(T item) /// is negative. public bool TrySlide(int count, out Span segment) { - if (count < 0) - ThrowCountOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfNegative(count); var newLength = position + count; - if ((uint)newLength <= (uint)span.Length) + if ((uint)newLength <= (uint)length) { - segment = MemoryMarshal.CreateSpan( - ref Unsafe.Add(ref MemoryMarshal.GetReference(span), position), - count); + segment = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref reference, position), count); position = newLength; return true; } @@ -237,15 +230,6 @@ public Span Slide(int count) [StackTraceHidden] private static void ThrowInternalBufferOverflowException() => throw new InternalBufferOverflowException(ExceptionMessages.NotEnoughMemory); - [DoesNotReturn] - [StackTraceHidden] - private static void ThrowCountOutOfRangeException() => throw new ArgumentOutOfRangeException("count"); - - // TODO: Replace with ArgumentNullException.ThrowIfNull in .NET 8 - [DoesNotReturn] - [StackTraceHidden] - private static void ThrowArgumentNullException() => throw new ArgumentNullException("action"); - /// /// Writes a portion of data. /// @@ -258,8 +242,7 @@ public Span Slide(int count) [CLSCompliant(false)] public unsafe void Write(delegate*, void> action, TArg arg, int count) { - if (action is null) - ThrowArgumentNullException(); + ArgumentNullException.ThrowIfNull(action); if (!TrySlide(count, out var buffer)) ThrowInternalBufferOverflowException(); @@ -278,8 +261,7 @@ public unsafe void Write(delegate*, void> action, TArg arg, [CLSCompliant(false)] public unsafe bool TryWrite(delegate*, out int, bool> action, TArg arg) { - if (action is null) - ThrowArgumentNullException(); + ArgumentNullException.ThrowIfNull(action); if (!action(arg, RemainingSpan, out var writtenCount)) return false; From 8557d5758d3e99b398544a6df3fe0f4540e477c2 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 11:00:02 +0200 Subject: [PATCH 016/155] Fixed reference checks --- src/DotNext/Buffers/SpanReader.cs | 11 +++++++---- src/DotNext/Buffers/SpanWriter.cs | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/DotNext/Buffers/SpanReader.cs b/src/DotNext/Buffers/SpanReader.cs index 263b71f8b..8980b6e7e 100644 --- a/src/DotNext/Buffers/SpanReader.cs +++ b/src/DotNext/Buffers/SpanReader.cs @@ -34,10 +34,13 @@ public SpanReader(ReadOnlySpan span) /// is negative. public SpanReader(ref T reference, int length) { - ArgumentOutOfRangeException.ThrowIfNegative(length); - - if (Unsafe.IsNullRef(ref reference) && length > 0) - throw new ArgumentNullException(nameof(reference)); + switch (length) + { + case < 0: + throw new ArgumentOutOfRangeException(nameof(length)); + case > 0 when Unsafe.IsNullRef(ref reference): + throw new ArgumentNullException(nameof(reference)); + } this.reference = ref reference; this.length = length; diff --git a/src/DotNext/Buffers/SpanWriter.cs b/src/DotNext/Buffers/SpanWriter.cs index 2362eb151..17afa18bb 100644 --- a/src/DotNext/Buffers/SpanWriter.cs +++ b/src/DotNext/Buffers/SpanWriter.cs @@ -34,10 +34,13 @@ public SpanWriter(Span span) /// is negative. public SpanWriter(ref T reference, int length) { - ArgumentOutOfRangeException.ThrowIfNegative(length); - - if (Unsafe.IsNullRef(ref reference) && length > 0) - throw new ArgumentNullException(nameof(reference)); + switch (length) + { + case < 0: + throw new ArgumentOutOfRangeException(nameof(length)); + case > 0 when Unsafe.IsNullRef(ref reference): + throw new ArgumentNullException(nameof(reference)); + } this.reference = ref reference; this.length = length; From fa32e831b40fad88428b6ae98cb8817d7566eb95 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 13:18:59 +0200 Subject: [PATCH 017/155] Removed support of ReverseEndianness presented in .NET --- .../Binary/BinaryTransformationsTests.cs | 126 ------------ .../BinaryTransformations.Endianness.cs | 189 ------------------ .../Buffers/Binary/BinaryTransformations.cs | 44 ---- 3 files changed, 359 deletions(-) delete mode 100644 src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs diff --git a/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs b/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs index 33fb0f998..a7b17ad39 100644 --- a/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs +++ b/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs @@ -6,132 +6,6 @@ namespace DotNext.Buffers.Binary; public sealed class BinaryTransformationsTests : Test { - [Theory] - [InlineData(4)] - [InlineData(10)] - [InlineData(16 + 16 + 10)] // two 256 bit vectors, one 128 bit vector, and 2 elements - public static void ReverseEndiannessInt16(int size) - { - var expected = new short[size]; - Random.Shared.NextBytes(MemoryMarshal.AsBytes(expected)); - - var actual = expected.ToArray(); - ReverseEndianessSlow(actual); - BinaryTransformations.ReverseEndianness(actual); - Equal(expected, actual); - - static void ReverseEndianessSlow(Span values) - { - foreach (ref var item in values) - item = BinaryPrimitives.ReverseEndianness(item); - } - } - - [Theory] - [InlineData(4)] - [InlineData(10)] - [InlineData(16 + 16 + 10)] // two 256 bit vectors, one 128 bit vector, and 2 elements - public static void ReverseEndiannessUInt16(int size) - { - var expected = new ushort[size]; - Random.Shared.NextBytes(MemoryMarshal.AsBytes(expected)); - - var actual = expected.ToArray(); - ReverseEndianessSlow(actual); - BinaryTransformations.ReverseEndianness(actual); - Equal(expected, actual); - - static void ReverseEndianessSlow(Span values) - { - foreach (ref var item in values) - item = BinaryPrimitives.ReverseEndianness(item); - } - } - - [Theory] - [InlineData(2)] - [InlineData(5)] - [InlineData(8 + 8 + 6)] // two 256 bit vectors, one 128 bit vector, and 2 elements - public static void ReverseEndiannessInt32(int size) - { - var expected = new int[size]; - Random.Shared.NextBytes(MemoryMarshal.AsBytes(expected)); - - var actual = expected.ToArray(); - ReverseEndianessSlow(actual); - BinaryTransformations.ReverseEndianness(actual); - Equal(expected, actual); - - static void ReverseEndianessSlow(Span values) - { - foreach (ref var item in values) - item = BinaryPrimitives.ReverseEndianness(item); - } - } - - [Theory] - [InlineData(2)] - [InlineData(5)] - [InlineData(8 + 8 + 6)] // two 256 bit vectors, one 128 bit vector, and 2 elements - public static void ReverseEndiannessUInt32(int size) - { - var expected = new uint[size]; - Random.Shared.NextBytes(MemoryMarshal.AsBytes(expected)); - - var actual = expected.ToArray(); - ReverseEndianessSlow(actual); - BinaryTransformations.ReverseEndianness(actual); - Equal(expected, actual); - - static void ReverseEndianessSlow(Span values) - { - foreach (ref var item in values) - item = BinaryPrimitives.ReverseEndianness(item); - } - } - - [Theory] - [InlineData(1)] - [InlineData(3)] - [InlineData(4 + 4 + 3)] - public static void ReverseEndiannessInt64(int size) - { - var expected = new long[size]; - Random.Shared.NextBytes(MemoryMarshal.AsBytes(expected)); - - var actual = expected.ToArray(); - ReverseEndianessSlow(actual); - BinaryTransformations.ReverseEndianness(actual); - Equal(expected, actual); - - static void ReverseEndianessSlow(Span values) - { - foreach (ref var item in values) - item = BinaryPrimitives.ReverseEndianness(item); - } - } - - [Theory] - [InlineData(1)] - [InlineData(3)] - [InlineData(4 + 4 + 3)] - public static void ReverseEndiannessUInt64(int size) - { - var expected = new ulong[size]; - Random.Shared.NextBytes(MemoryMarshal.AsBytes(expected)); - - var actual = expected.ToArray(); - ReverseEndianessSlow(actual); - BinaryTransformations.ReverseEndianness(actual); - Equal(expected, actual); - - static void ReverseEndianessSlow(Span values) - { - foreach (ref var item in values) - item = BinaryPrimitives.ReverseEndianness(item); - } - } - [Theory] [InlineData(32 + 16 + 3)] [InlineData(32 + 3)] diff --git a/src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs b/src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs deleted file mode 100644 index c2b608670..000000000 --- a/src/DotNext/Buffers/Binary/BinaryTransformations.Endianness.cs +++ /dev/null @@ -1,189 +0,0 @@ -using System.Buffers.Binary; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace DotNext.Buffers.Binary; - -using Intrinsics = Runtime.Intrinsics; - -public static partial class BinaryTransformations -{ - private interface IEndiannessTransformation : IUnaryTransformation - where T : unmanaged - { - Vector128 ReorderMask { get; } - } - - private static void ReverseEndianness(Span buffer, TTransformation transformation) - where T : unmanaged - where TTransformation : struct, IEndiannessTransformation - { - if (Ssse3.IsSupported) - { - if (Avx2.IsSupported) - { - for (var reorderMask256 = Vector256.Create(transformation.ReorderMask, transformation.ReorderMask); buffer.Length >= Vector256.Count; buffer = buffer.Slice(Vector256.Count)) - { - var vector = LoadVector256(buffer); - vector = Avx2.Shuffle(vector.AsByte(), reorderMask256).As(); - StoreVector256(vector, buffer); - } - } - - for (Vector128 vector; buffer.Length >= Vector128.Count; buffer = buffer.Slice(Vector128.Count)) - { - vector = LoadVector128(buffer); - vector = Ssse3.Shuffle(vector.AsByte(), transformation.ReorderMask).As(); - StoreVector128(vector, buffer); - } - } - - // software fallback - foreach (ref var item in buffer) - item = TTransformation.Transform(item); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void ReverseEndianness(Span buffer) - where T : unmanaged - where TTransformation : struct, IEndiannessTransformation - { - switch (buffer.Length) - { - case 0: - break; - case 1: - ref var item = ref buffer[0]; - item = TTransformation.Transform(item); - break; - default: - ReverseEndianness(buffer, new TTransformation()); - break; - } - } - - /// - /// Reverse endianness of 16-bit signed integers in-place. - /// - /// The buffer to modify. - public static void ReverseEndianness(this Span buffer) - => ReverseEndianness(Intrinsics.ReinterpretCast(buffer)); - - /// - /// Reverse endianness of 16-bit unsigned integers in-place. - /// - /// The buffer to modify. - [CLSCompliant(false)] - public static void ReverseEndianness(this Span buffer) - => ReverseEndianness(buffer); - - /// - /// Reverse endianness of 32-bit signed integers in-place. - /// - /// The buffer to modify. - public static void ReverseEndianness(this Span buffer) - => ReverseEndianness(Intrinsics.ReinterpretCast(buffer)); - - /// - /// Reverse endianness of 32-bit unsigned integers in-place. - /// - /// The buffer to modify. - [CLSCompliant(false)] - public static void ReverseEndianness(this Span buffer) - => ReverseEndianness(buffer); - - /// - /// Reverse endianness of 64-bit signed integers in-place. - /// - /// The buffer to modify. - public static void ReverseEndianness(this Span buffer) - => ReverseEndianness(Intrinsics.ReinterpretCast(buffer)); - - /// - /// Reverse endianness of 64-bit unsigned integers in-place. - /// - /// The buffer to modify. - [CLSCompliant(false)] - public static void ReverseEndianness(this Span buffer) - => ReverseEndianness(buffer); - - [StructLayout(LayoutKind.Auto)] - private readonly struct UInt16Transformation : IEndiannessTransformation - { - public UInt16Transformation() => ReorderMask = Vector128.Create( - (byte)1, - 0, - 3, - 2, - 5, - 4, - 7, - 6, - 9, - 8, - 11, - 10, - 13, - 12, - 15, - 14); - - public Vector128 ReorderMask { get; } - - static ushort IUnaryTransformation.Transform(ushort value) => BinaryPrimitives.ReverseEndianness(value); - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct UInt32Transformation : IEndiannessTransformation - { - public UInt32Transformation() => ReorderMask = Vector128.Create( - (byte)3, - 2, - 1, - 0, - 7, - 6, - 5, - 4, - 11, - 10, - 9, - 8, - 15, - 14, - 13, - 12); - - public Vector128 ReorderMask { get; } - - static uint IUnaryTransformation.Transform(uint value) => BinaryPrimitives.ReverseEndianness(value); - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct UInt64Transformation : IEndiannessTransformation - { - public UInt64Transformation() => ReorderMask = Vector128.Create( - (byte)7, - 6, - 5, - 4, - 3, - 2, - 1, - 0, - 15, - 14, - 13, - 12, - 11, - 10, - 9, - 8); - - public Vector128 ReorderMask { get; } - - static ulong IUnaryTransformation.Transform(ulong value) => BinaryPrimitives.ReverseEndianness(value); - } -} \ No newline at end of file diff --git a/src/DotNext/Buffers/Binary/BinaryTransformations.cs b/src/DotNext/Buffers/Binary/BinaryTransformations.cs index 7f91347f0..65f1910da 100644 --- a/src/DotNext/Buffers/Binary/BinaryTransformations.cs +++ b/src/DotNext/Buffers/Binary/BinaryTransformations.cs @@ -1,7 +1,3 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Runtime.Intrinsics; - namespace DotNext.Buffers.Binary; /// @@ -20,44 +16,4 @@ private interface IBinaryTransformation { public static abstract T Transform(T x, T y); } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 LoadVector128(ref T input) - where T : unmanaged - => Unsafe.ReadUnaligned>(ref Unsafe.As(ref input)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 LoadVector128(ReadOnlySpan input) - where T : unmanaged - => LoadVector128(ref MemoryMarshal.GetReference(input)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 LoadVector256(ref T input) - where T : unmanaged - => Unsafe.ReadUnaligned>(ref Unsafe.As(ref input)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector256 LoadVector256(ReadOnlySpan input) - where T : unmanaged - => LoadVector256(ref MemoryMarshal.GetReference(input)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void StoreVector128(Vector128 input, ref T output) - where T : unmanaged - => Unsafe.WriteUnaligned(ref Unsafe.As(ref output), input); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void StoreVector128(Vector128 input, Span output) - where T : unmanaged - => StoreVector128(input, ref MemoryMarshal.GetReference(output)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void StoreVector256(Vector256 input, ref T output) - where T : unmanaged - => Unsafe.WriteUnaligned(ref Unsafe.As(ref output), input); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void StoreVector256(Vector256 input, Span output) - where T : unmanaged - => StoreVector256(input, ref MemoryMarshal.GetReference(output)); } \ No newline at end of file From db25b5e8c88e2d0b670ec15858646cd99053b61b Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 14:29:49 +0200 Subject: [PATCH 018/155] Added AOT compatibility --- src/DotNext/Text/Json/OptionalConverter.cs | 27 +++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/DotNext/Text/Json/OptionalConverter.cs b/src/DotNext/Text/Json/OptionalConverter.cs index 69d5b0ab0..216220c75 100644 --- a/src/DotNext/Text/Json/OptionalConverter.cs +++ b/src/DotNext/Text/Json/OptionalConverter.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; namespace DotNext.Text.Json; @@ -8,10 +9,9 @@ namespace DotNext.Text.Json; /// Represents JSON converter for data type. /// /// The type of the value in container. -public sealed class OptionalConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.PublicConstructors)]T> : JsonConverter> +public sealed class OptionalConverter : JsonConverter> { /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "Public properties/fields are preserved")] [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1013", Justification = "False positive")] public override void Write(Utf8JsonWriter writer, Optional value, JsonSerializerOptions options) { @@ -23,14 +23,29 @@ public override void Write(Utf8JsonWriter writer, Optional value, JsonSeriali writer.WriteNullValue(); break; default: - // TODO: Attempt to extract IJsonTypeInfo resolver for type T in .NET 7 to avoid Reflection - JsonSerializer.Serialize(writer, value.ValueOrDefault, options); + var typeInfo = options.GetTypeInfo(typeof(T)); + if (typeInfo is JsonTypeInfo info) + { + JsonSerializer.Serialize(writer, value.ValueOrDefault, info); + } + else + { + JsonSerializer.Serialize(writer, value.ValueOrDefault, typeInfo); + } + break; } } /// - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026", Justification = "Public properties/fields are preserved")] public override Optional Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => new(reader.TokenType is JsonTokenType.Null ? default : JsonSerializer.Deserialize(ref reader, options)); + { + JsonTypeInfo typeInfo; + return new( + reader.TokenType is JsonTokenType.Null ? + default + : (typeInfo = options.GetTypeInfo(typeof(T))) is JsonTypeInfo + ? JsonSerializer.Deserialize(ref reader, (JsonTypeInfo)typeInfo) + : (T?)JsonSerializer.Deserialize(ref reader, typeInfo)); + } } \ No newline at end of file From 064546b74fb21c0744111f6e9df8a12940d24aaf Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 14:32:47 +0200 Subject: [PATCH 019/155] Cleanup projects --- src/DotNext.sln | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/DotNext.sln b/src/DotNext.sln index 9f4e169c6..0fddcf147 100644 --- a/src/DotNext.sln +++ b/src/DotNext.sln @@ -11,6 +11,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Benchmarks", "DotNe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Unsafe", "DotNext.Unsafe\DotNext.Unsafe.csproj", "{BEFBC460-FFF8-4685-8ACF-EC37530845FB}" EndProject +Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "VisualBasicElements", "VisualBasicElements\VisualBasicElements.vbproj", "{9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Metaprogramming", "DotNext.Metaprogramming\DotNext.Metaprogramming.csproj", "{C8415985-3BC5-44EB-BDB4-551777B73AB4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Threading", "DotNext.Threading\DotNext.Threading.csproj", "{956A2A3E-F151-4FA4-9932-862BC5CB2F87}" @@ -65,12 +67,6 @@ Global {D8438838-088B-4559-8605-7DEAC889902C}.Debug|Any CPU.Build.0 = Debug|Any CPU {D8438838-088B-4559-8605-7DEAC889902C}.Release|Any CPU.ActiveCfg = Release|Any CPU {D8438838-088B-4559-8605-7DEAC889902C}.Release|Any CPU.Build.0 = Release|Any CPU - {328F0106-4014-49E1-8DA0-35A37E8A0E96}.Bench|Any CPU.ActiveCfg = Release|Any CPU - {328F0106-4014-49E1-8DA0-35A37E8A0E96}.Bench|Any CPU.Build.0 = Release|Any CPU - {328F0106-4014-49E1-8DA0-35A37E8A0E96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {328F0106-4014-49E1-8DA0-35A37E8A0E96}.Debug|Any CPU.Build.0 = Debug|Any CPU - {328F0106-4014-49E1-8DA0-35A37E8A0E96}.Release|Any CPU.ActiveCfg = Release|Any CPU - {328F0106-4014-49E1-8DA0-35A37E8A0E96}.Release|Any CPU.Build.0 = Release|Any CPU {BEFBC460-FFF8-4685-8ACF-EC37530845FB}.Bench|Any CPU.ActiveCfg = Release|Any CPU {BEFBC460-FFF8-4685-8ACF-EC37530845FB}.Bench|Any CPU.Build.0 = Release|Any CPU {BEFBC460-FFF8-4685-8ACF-EC37530845FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU From 409062922734c1f5fc444dd48e4b307c2a28d420 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 18:01:08 +0200 Subject: [PATCH 020/155] AOT compat for JSON Serialization of Optional --- src/DotNext.Tests/DotNext.Tests.csproj | 1 + src/DotNext.Tests/Text/Json/OptionalConverterFactoryTests.cs | 2 ++ src/DotNext/Optional.cs | 3 +++ src/DotNext/Result.cs | 4 ++++ src/DotNext/Text/Json/OptionalConverter.cs | 4 ++-- 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/DotNext.Tests/DotNext.Tests.csproj b/src/DotNext.Tests/DotNext.Tests.csproj index cbe3e0325..f2ef0b166 100644 --- a/src/DotNext.Tests/DotNext.Tests.csproj +++ b/src/DotNext.Tests/DotNext.Tests.csproj @@ -16,6 +16,7 @@ https://github.com/dotnet/DotNext https://github.com/dotnet/DotNext.git git + true diff --git a/src/DotNext.Tests/Text/Json/OptionalConverterFactoryTests.cs b/src/DotNext.Tests/Text/Json/OptionalConverterFactoryTests.cs index f060d1ea5..2ee9f7c7e 100644 --- a/src/DotNext.Tests/Text/Json/OptionalConverterFactoryTests.cs +++ b/src/DotNext.Tests/Text/Json/OptionalConverterFactoryTests.cs @@ -7,6 +7,8 @@ public sealed class OptionalConverterFactoryTests : Test [Fact] public static void UndefinedValues() { + True(JsonSerializer.IsReflectionEnabledByDefault); + var expected = new TestJsonObject { IntegerValue = Optional.None(), diff --git a/src/DotNext/Optional.cs b/src/DotNext/Optional.cs index 35bd7c620..bbf913e64 100644 --- a/src/DotNext/Optional.cs +++ b/src/DotNext/Optional.cs @@ -4,6 +4,8 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Serialization; namespace DotNext; @@ -439,6 +441,7 @@ public T Value /// No value is present. [UnscopedRef] [DisallowNull] + [JsonIgnore] public ref readonly T ValueRef { get diff --git a/src/DotNext/Result.cs b/src/DotNext/Result.cs index fb438a761..e6873a436 100644 --- a/src/DotNext/Result.cs +++ b/src/DotNext/Result.cs @@ -3,6 +3,8 @@ using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; +using System.Text.Json; +using System.Text.Json.Serialization; namespace DotNext; @@ -159,6 +161,7 @@ public T Value /// The reference to the result. /// The result is unavailable. [UnscopedRef] + [JsonIgnore] public ref readonly T ValueRef { get @@ -426,6 +429,7 @@ public T Value /// The reference to the result. /// The value is unavailable. [UnscopedRef] + [JsonIgnore] public ref readonly T ValueRef { get diff --git a/src/DotNext/Text/Json/OptionalConverter.cs b/src/DotNext/Text/Json/OptionalConverter.cs index 216220c75..7a08a48d2 100644 --- a/src/DotNext/Text/Json/OptionalConverter.cs +++ b/src/DotNext/Text/Json/OptionalConverter.cs @@ -24,9 +24,9 @@ public override void Write(Utf8JsonWriter writer, Optional value, JsonSeriali break; default: var typeInfo = options.GetTypeInfo(typeof(T)); - if (typeInfo is JsonTypeInfo info) + if (typeInfo is JsonTypeInfo typed) { - JsonSerializer.Serialize(writer, value.ValueOrDefault, info); + JsonSerializer.Serialize(writer, value.ValueOrDefault, typed); } else { From cd74296466310326e31a784073e4233409bbbb4e Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 18:05:23 +0200 Subject: [PATCH 021/155] Reduced type size --- src/DotNext/Buffers/BufferWriterSlim.ByReference.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs b/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs index 16ffae3d9..14d69fbcd 100644 --- a/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs +++ b/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs @@ -12,11 +12,11 @@ public partial struct BufferWriterSlim internal readonly ref struct Ref { [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly ReadOnlySpan reference; + private readonly ref byte reference; internal Ref(ref BufferWriterSlim writer) { - reference = MemoryMarshal.CreateSpan(ref AsRef(ref writer), Size); + reference = ref AsRef(ref writer); [MethodImpl(MethodImplOptions.AggressiveInlining)] static ref byte AsRef(ref BufferWriterSlim writer) @@ -32,7 +32,7 @@ internal ref BufferWriterSlim Value [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return ref AsWriter(ref MemoryMarshal.GetReference(reference)); + return ref AsWriter(ref reference); [MethodImpl(MethodImplOptions.AggressiveInlining)] static ref BufferWriterSlim AsWriter(ref byte reference) From e7cf686a9f6f6b24e5979f5b7b84eda711014a5e Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 23:06:29 +0200 Subject: [PATCH 022/155] Migration to generic math --- src/DotNext.Tests/Numerics/BitVectorTests.cs | 268 ++--------- src/DotNext/Numerics/BitVector.cs | 440 +++--------------- .../Consensus/Raft/BufferedRaftLogEntry.cs | 12 +- .../TransportServices/LogEntryMetadata.cs | 4 +- 4 files changed, 116 insertions(+), 608 deletions(-) diff --git a/src/DotNext.Tests/Numerics/BitVectorTests.cs b/src/DotNext.Tests/Numerics/BitVectorTests.cs index 37df97b8b..b48badd2a 100644 --- a/src/DotNext.Tests/Numerics/BitVectorTests.cs +++ b/src/DotNext.Tests/Numerics/BitVectorTests.cs @@ -12,10 +12,10 @@ private static ReadOnlySpan CreateVector(int size, bool value) [Fact] public static void BitsToByte() { - Equal(0, BitVector.ToByte(ReadOnlySpan.Empty)); - Equal(3, BitVector.ToByte(stackalloc bool[] { true, true })); - Equal(8, BitVector.ToByte(stackalloc bool[] { false, false, false, true })); - Equal(byte.MaxValue, BitVector.ToByte(CreateVector(8, true))); + Equal(0, BitVector.FromBits([])); + Equal(3, BitVector.FromBits([true, true])); + Equal(8, BitVector.FromBits([false, false, false, true])); + Equal(byte.MaxValue, BitVector.FromBits(CreateVector(8, true))); } [Fact] @@ -24,53 +24,19 @@ public static void ByteToBits() var value = byte.MaxValue; var buffer = new bool[8]; - BitVector.FromByte(value, buffer); + BitVector.GetBits(value, buffer); Array.TrueForAll(buffer, static bit => bit); value = 3; Array.Clear(buffer); - BitVector.FromByte(value, buffer); + BitVector.GetBits(value, buffer); True(buffer[0]); True(buffer[1]); False(buffer[2]); value = 8; Array.Clear(buffer); - BitVector.FromByte(value, buffer); - False(buffer[0]); - False(buffer[1]); - False(buffer[2]); - True(buffer[3]); - } - - [Fact] - public static void BitsToSByte() - { - Equal(0, BitVector.ToSByte(ReadOnlySpan.Empty)); - Equal(3, BitVector.ToSByte(stackalloc bool[] { true, true })); - Equal(8, BitVector.ToSByte(stackalloc bool[] { false, false, false, true })); - Equal(-1, BitVector.ToSByte(CreateVector(8, true))); - } - - [Fact] - public static void SByteToBits() - { - sbyte value = -1; - var buffer = new bool[8]; - - BitVector.FromSByte(value, buffer); - Array.TrueForAll(buffer, static bit => bit); - - value = 3; - Array.Clear(buffer); - BitVector.FromSByte(value, buffer); - True(buffer[0]); - True(buffer[1]); - False(buffer[2]); - - value = 8; - Array.Clear(buffer); - BitVector.FromSByte(value, buffer); + BitVector.GetBits(value, buffer); False(buffer[0]); False(buffer[1]); False(buffer[2]); @@ -80,11 +46,11 @@ public static void SByteToBits() [Fact] public static void BitsToInt16() { - Equal(0, BitVector.ToInt16(ReadOnlySpan.Empty)); - Equal(3, BitVector.ToInt16(stackalloc bool[] { true, true })); - Equal(8, BitVector.ToInt16(stackalloc bool[] { false, false, false, true })); - Equal(short.MaxValue, BitVector.ToInt16(CreateVector(15, true))); - Equal(-1, BitVector.ToInt16(CreateVector(16, true))); + Equal(0, BitVector.FromBits([])); + Equal(3, BitVector.FromBits([true, true])); + Equal(8, BitVector.FromBits([false, false, false, true])); + Equal(short.MaxValue, BitVector.FromBits(CreateVector(15, true))); + Equal(-1, BitVector.FromBits(CreateVector(16, true))); } [Fact] @@ -93,53 +59,19 @@ public static void Int16ToBits() short value = -1; var buffer = new bool[16]; - BitVector.FromInt16(value, buffer); - Array.TrueForAll(buffer, static bit => bit); - - value = 3; - Array.Clear(buffer); - BitVector.FromInt16(value, buffer); - True(buffer[0]); - True(buffer[1]); - False(buffer[2]); - - value = 8; - Array.Clear(buffer); - BitVector.FromInt16(value, buffer); - False(buffer[0]); - False(buffer[1]); - False(buffer[2]); - True(buffer[3]); - } - - [Fact] - public static void BitsToUInt16() - { - Equal(0, BitVector.ToUInt16(ReadOnlySpan.Empty)); - Equal(3, BitVector.ToUInt16(stackalloc bool[] { true, true })); - Equal(8, BitVector.ToUInt16(stackalloc bool[] { false, false, false, true })); - Equal(ushort.MaxValue, BitVector.ToUInt16(CreateVector(16, true))); - } - - [Fact] - public static void UInt16ToBits() - { - var value = ushort.MaxValue; - var buffer = new bool[16]; - - BitVector.FromUInt16(value, buffer); + BitVector.GetBits(value, buffer); Array.TrueForAll(buffer, static bit => bit); value = 3; Array.Clear(buffer); - BitVector.FromUInt16(value, buffer); + BitVector.GetBits(value, buffer); True(buffer[0]); True(buffer[1]); False(buffer[2]); value = 8; Array.Clear(buffer); - BitVector.FromUInt16(value, buffer); + BitVector.GetBits(value, buffer); False(buffer[0]); False(buffer[1]); False(buffer[2]); @@ -149,11 +81,11 @@ public static void UInt16ToBits() [Fact] public static void BitsToInt32() { - Equal(0, BitVector.ToInt32(ReadOnlySpan.Empty)); - Equal(3, BitVector.ToInt32(stackalloc bool[] { true, true })); - Equal(8, BitVector.ToInt32(stackalloc bool[] { false, false, false, true })); - Equal(int.MaxValue, BitVector.ToInt32(CreateVector(31, true))); - Equal(-1, BitVector.ToInt32(CreateVector(32, true))); + Equal(0, BitVector.FromBits([])); + Equal(3, BitVector.FromBits([true, true])); + Equal(8, BitVector.FromBits([false, false, false, true])); + Equal(int.MaxValue, BitVector.FromBits(CreateVector(31, true))); + Equal(-1, BitVector.FromBits(CreateVector(32, true))); } [Fact] @@ -162,53 +94,19 @@ public static void Int32ToBits() int value = -1; var buffer = new bool[32]; - BitVector.FromInt32(value, buffer); - Array.TrueForAll(buffer, static bit => bit); - - value = 3; - Array.Clear(buffer); - BitVector.FromInt32(value, buffer); - True(buffer[0]); - True(buffer[1]); - False(buffer[2]); - - value = 8; - Array.Clear(buffer); - BitVector.FromInt32(value, buffer); - False(buffer[0]); - False(buffer[1]); - False(buffer[2]); - True(buffer[3]); - } - - [Fact] - public static void BitsToUInt32() - { - Equal(0U, BitVector.ToUInt32(ReadOnlySpan.Empty)); - Equal(3U, BitVector.ToUInt32(stackalloc bool[] { true, true })); - Equal(8U, BitVector.ToUInt32(stackalloc bool[] { false, false, false, true })); - Equal(uint.MaxValue, BitVector.ToUInt32(CreateVector(32, true))); - } - - [Fact] - public static void UInt32ToBits() - { - var value = uint.MaxValue; - var buffer = new bool[32]; - - BitVector.FromUInt32(value, buffer); + BitVector.GetBits(value, buffer); Array.TrueForAll(buffer, static bit => bit); value = 3; Array.Clear(buffer); - BitVector.FromUInt32(value, buffer); + BitVector.GetBits(value, buffer); True(buffer[0]); True(buffer[1]); False(buffer[2]); value = 8; Array.Clear(buffer); - BitVector.FromUInt32(value, buffer); + BitVector.GetBits(value, buffer); False(buffer[0]); False(buffer[1]); False(buffer[2]); @@ -218,11 +116,11 @@ public static void UInt32ToBits() [Fact] public static void BitsToInt64() { - Equal(0L, BitVector.ToInt64(ReadOnlySpan.Empty)); - Equal(3L, BitVector.ToInt64(stackalloc bool[] { true, true })); - Equal(8L, BitVector.ToInt64(stackalloc bool[] { false, false, false, true })); - Equal(long.MaxValue, BitVector.ToInt64(CreateVector(63, true))); - Equal(-1L, BitVector.ToInt32(CreateVector(64, true))); + Equal(0L, BitVector.FromBits([])); + Equal(3L, BitVector.FromBits([true, true])); + Equal(8L, BitVector.FromBits([false, false, false, true])); + Equal(long.MaxValue, BitVector.FromBits(CreateVector(63, true))); + Equal(-1L, BitVector.FromBits(CreateVector(64, true))); } [Fact] @@ -231,125 +129,27 @@ public static void Int64ToBits() long value = -1; var buffer = new bool[64]; - BitVector.FromInt64(value, buffer); - Array.TrueForAll(buffer, static bit => bit); - - value = 3; - Array.Clear(buffer); - BitVector.FromInt64(value, buffer); - True(buffer[0]); - True(buffer[1]); - False(buffer[2]); - - value = 8; - Array.Clear(buffer); - BitVector.FromInt64(value, buffer); - False(buffer[0]); - False(buffer[1]); - False(buffer[2]); - True(buffer[3]); - } - - [Fact] - public static void BitsToUInt64() - { - Equal(0UL, BitVector.ToUInt64(ReadOnlySpan.Empty)); - Equal(3UL, BitVector.ToUInt64(stackalloc bool[] { true, true })); - Equal(8UL, BitVector.ToUInt64(stackalloc bool[] { false, false, false, true })); - Equal(ulong.MaxValue, BitVector.ToUInt64(CreateVector(64, true))); - } - - [Fact] - public static void UInt64ToBits() - { - var value = ulong.MaxValue; - var buffer = new bool[64]; - - BitVector.FromUInt64(value, buffer); - Array.TrueForAll(buffer, static bit => bit); - - value = 3; - Array.Clear(buffer); - BitVector.FromUInt64(value, buffer); - True(buffer[0]); - True(buffer[1]); - False(buffer[2]); - - value = 8; - Array.Clear(buffer); - BitVector.FromUInt64(value, buffer); - False(buffer[0]); - False(buffer[1]); - False(buffer[2]); - True(buffer[3]); - } - - [Fact] - public static void BitsToNativeInt() - { - const nint minusOne = -1; - Equal((nint)0, BitVector.ToInt(ReadOnlySpan.Empty)); - Equal((nint)3, BitVector.ToInt(stackalloc bool[] { true, true })); - Equal((nint)8, BitVector.ToInt(stackalloc bool[] { false, false, false, true })); - Equal(minusOne, BitVector.ToInt(CreateVector(IntPtr.Size * 8, true))); - } - - [Fact] - public static void NativeIntToBits() - { - var value = nint.MaxValue; - var buffer = new bool[IntPtr.Size * 8]; - - BitVector.FromInt(value, buffer); + BitVector.GetBits(value, buffer); Array.TrueForAll(buffer, static bit => bit); value = 3; Array.Clear(buffer); - BitVector.FromInt(value, buffer); + BitVector.GetBits(value, buffer); True(buffer[0]); True(buffer[1]); False(buffer[2]); value = 8; Array.Clear(buffer); - BitVector.FromInt(value, buffer); + BitVector.GetBits(value, buffer); False(buffer[0]); False(buffer[1]); False(buffer[2]); True(buffer[3]); - } - [Fact] - public static void BitsToNativeUInt() - { - Equal((nuint)0, BitVector.ToUInt(ReadOnlySpan.Empty)); - Equal((nuint)3, BitVector.ToUInt(stackalloc bool[] { true, true })); - Equal((nuint)8, BitVector.ToUInt(stackalloc bool[] { false, false, false, true })); - Equal(nuint.MaxValue, BitVector.ToUInt(CreateVector(IntPtr.Size * 8, true))); - } - - [Fact] - public static void NativeUIntToBits() - { - var value = nuint.MaxValue; - var buffer = new bool[IntPtr.Size * 8]; - - BitVector.FromUInt(value, buffer); - Array.TrueForAll(buffer, static bit => bit); - - value = 3; + value = 1L << 62; Array.Clear(buffer); - BitVector.FromUInt(value, buffer); - True(buffer[0]); - True(buffer[1]); - False(buffer[2]); - - value = 8; - Array.Clear(buffer); - BitVector.FromUInt(value, buffer); - False(buffer[0]); - False(buffer[1]); - False(buffer[2]); - True(buffer[3]); + BitVector.GetBits(value, buffer); + True(buffer[62]); } } \ No newline at end of file diff --git a/src/DotNext/Numerics/BitVector.cs b/src/DotNext/Numerics/BitVector.cs index bbc259f06..7bcf0f5e8 100644 --- a/src/DotNext/Numerics/BitVector.cs +++ b/src/DotNext/Numerics/BitVector.cs @@ -1,7 +1,7 @@ +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; using Debug = System.Diagnostics.Debug; namespace DotNext.Numerics; @@ -11,181 +11,75 @@ namespace DotNext.Numerics; /// public static class BitVector { - // TODO: Rewrite using generic math - private interface IBitVector - where TValue : unmanaged - { - bool this[int position] { get; set; } - - TValue Value { get; set; } - } - - [StructLayout(LayoutKind.Auto)] - private struct UInt32Vector : IBitVector + /// + /// Converts bit vector to a value of type . + /// + /// The type of the result. + /// A vector of bits. + /// A value of type restored from the vector of bits. + public static TResult FromBits(this ReadOnlySpan bits) + where TResult : struct, IBinaryInteger { - private uint result; + var result = TResult.Zero; - bool IBitVector.this[int position] + for (var position = 0; position < bits.Length; position++) { - readonly get => ((result >> position) & 1U) != 0U; - set => result = (result & ~(1U << position)) | ((uint)value.ToInt32() << position); + if (bits[position]) + result |= TResult.One << position; } - uint IBitVector.Value - { - readonly get => result; - set => result = value; - } + return result; } - [StructLayout(LayoutKind.Auto)] - private struct UInt64Vector : IBitVector + /// + /// Converts a value to a set of bits. + /// + /// The type of the value to convert. + /// The value to convert. + /// A buffer to be modified. + /// has not enough length. + public static unsafe void GetBits(this T value, Span bits) + where T : unmanaged, IBinaryInteger { - private ulong result; + var sizeInBits = sizeof(T) * 8; + ArgumentOutOfRangeException.ThrowIfLessThan((uint)bits.Length, (uint)sizeInBits, nameof(bits)); - bool IBitVector.this[int position] + if (Vector256.IsHardwareAccelerated && (sizeInBits & 1) is 0) { - readonly get => ((result >> position) & 1UL) != 0UL; - set => result = (result & ~(1UL << position)) | ((ulong)value.ToInt32() << position); + Get16Bits(ref Unsafe.As(ref value), sizeof(T), ref MemoryMarshal.GetReference(bits)); } - - ulong IBitVector.Value + else if (Vector128.IsHardwareAccelerated) + { + Get8Bits(ref Unsafe.As(ref value), sizeof(T), ref MemoryMarshal.GetReference(bits)); + } + else { - readonly get => result; - set => result = value; + // software fallback + for (var position = 0; position < sizeInBits; position++) + { + bits[position] = (value & (T.One << position)) != T.Zero; + } } } - [StructLayout(LayoutKind.Auto)] - private struct UIntPtrVector : IBitVector + private static void Get8Bits(ref byte input, nint length, ref bool output) { - private const nuint One = 1; - private nuint result; + Debug.Assert(Vector128.IsHardwareAccelerated); + Debug.Assert(Vector64.IsHardwareAccelerated); - bool IBitVector.this[int position] + for (nint i = 0; i < length; i += sizeof(byte)) { - readonly get => ((result >> position) & 1UL) != 0UL; - set => result = (result & ~(One << position)) | ((nuint)value.ToInt32() << position); - } - - nuint IBitVector.Value - { - readonly get => result; - set => result = value; + Get8Bits( + Vector128.Create(Unsafe.Add(ref input, i)), + ref Unsafe.Add(ref output, i * 8)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static TValue VectorToScalar(ReadOnlySpan bits) - where TValue : unmanaged - where TVector : struct, IBitVector - { - var result = new TVector(); - - for (var position = 0; position < bits.Length; position++) - result[position] = bits[position]; - - return result.Value; - } - - /// - /// Converts bit vector to 8-bit unsigned integer. - /// - /// A sequence of bits. - /// 8-bit unsigned integer reconstructed from the bits. - public static byte ToByte(ReadOnlySpan bits) - => (byte)VectorToScalar(bits.TrimLength(8)); - - /// - /// Converts bit vector to 8-bit signed integer. - /// - /// A sequence of bits. - /// 8-bit unsigned integer reconstructed from the bits. - [CLSCompliant(false)] - public static sbyte ToSByte(ReadOnlySpan bits) - => (sbyte)VectorToScalar(bits.TrimLength(8)); - - /// - /// Converts bit vector to 16-bit signed integer. - /// - /// A sequence of bits. - /// 16-bit signed integer reconstructed from the bits. - public static short ToInt16(ReadOnlySpan bits) - => (short)VectorToScalar(bits.TrimLength(16)); - - /// - /// Converts bit vector to 16-bit unsigned integer. - /// - /// A sequence of bits. - /// 16-bit unsigned integer reconstructed from the bits. - [CLSCompliant(false)] - public static ushort ToUInt16(ReadOnlySpan bits) - => (ushort)VectorToScalar(bits.TrimLength(16)); - - /// - /// Converts bit vector to 32-bit signed integer. - /// - /// A sequence of bits. - /// 32-bit signed integer reconstructed from the bits. - public static int ToInt32(ReadOnlySpan bits) - => (int)VectorToScalar(bits.TrimLength(32)); - - /// - /// Converts bit vector to 32-bit unsigned integer. - /// - /// A sequence of bits. - /// 32-bit unsigned integer reconstructed from the bits. - [CLSCompliant(false)] - public static uint ToUInt32(ReadOnlySpan bits) - => VectorToScalar(bits.TrimLength(32)); - - /// - /// Converts bit vector to 64-bit signed integer. - /// - /// A sequence of bits. - /// 64-bit signed integer reconstructed from the bits. - public static long ToInt64(ReadOnlySpan bits) - => (long)VectorToScalar(bits.TrimLength(64)); - - /// - /// Converts bit vector to 64-bit unsigned integer. - /// - /// A sequence of bits. - /// 64-bit unsigned integer reconstructed from the bits. - [CLSCompliant(false)] - public static ulong ToUInt64(ReadOnlySpan bits) - => VectorToScalar(bits.TrimLength(64)); - - /// - /// Converts bit vector to platform-dependent signed integer. - /// - /// A sequence of bits. - /// Platform-dependent signed integer reconstructed from the bits. - public static nint ToInt(ReadOnlySpan bits) - => (nint)VectorToScalar(bits.TrimLength(IntPtr.Size << 3)); - - /// - /// Converts bit vector to platform-dependent unsigned integer. - /// - /// A sequence of bits. - /// Platform-dependent unsigned integer reconstructed from the bits. - [CLSCompliant(false)] - public static nuint ToUInt(ReadOnlySpan bits) - => VectorToScalar(bits.TrimLength(UIntPtr.Size << 3)); - - private static void GetBits(TValue value, Span bits) - where TValue : unmanaged - where TVector : struct, IBitVector - { - var vector = new TVector { Value = value }; - - for (var position = 0; position < bits.Length; position++) - bits[position] = vector[position]; - } - - private static void Get8Bits(byte input, ref bool output) + private static void Get8Bits(Vector128 input, ref bool output) { - Debug.Assert(Sse2.IsSupported); + Debug.Assert(Vector128.IsHardwareAccelerated); + Debug.Assert(Vector64.IsHardwareAccelerated); var onevec = Vector128.Create((byte)1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); var bitmask = Vector128.Create( @@ -206,17 +100,30 @@ private static void Get8Bits(byte input, ref bool output) 0, 0); - var result = Sse2.Min(Sse2.And(Vector128.Create(input), bitmask), onevec); - Unsafe.WriteUnaligned(ref Unsafe.As(ref output), result.GetLower()); + Vector128.Min(input & bitmask, onevec) + .GetLower().StoreUnsafe(ref Unsafe.As(ref output)); + } + + private static void Get16Bits(ref byte input, nint length, ref bool output) + { + Debug.Assert(Vector128.IsHardwareAccelerated); + + for (nint i = 0; i < length; i += sizeof(ushort)) + { + Get16Bits( + Vector256.Create(Unsafe.ReadUnaligned(ref Unsafe.Add(ref input, i))), + ref Unsafe.Add(ref output, i * 8)); + } } - private static void Get16Bits(ushort input, ref bool output) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Get16Bits(Vector256 input, ref bool output) { - Debug.Assert(Avx2.IsSupported); + Debug.Assert(Vector256.IsHardwareAccelerated); var onevec = Vector256.Create((ushort)1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); var bitmask = Vector256.Create( - (ushort)0B_0000_0000_0000_0001, + 0B_0000_0000_0000_0001, 0B_0000_0000_0000_0010, 0B_0000_0000_0000_0100, 0B_0000_0000_0000_1000, @@ -251,14 +158,14 @@ private static void Get16Bits(ushort input, ref bool output) byte.MaxValue, byte.MaxValue, byte.MaxValue, - 0, - 2, - 4, - 6, - 8, - 10, - 12, - 14, + 16, + 18, + 20, + 22, + 24, + 26, + 28, + 30, byte.MaxValue, byte.MaxValue, byte.MaxValue, @@ -268,204 +175,11 @@ private static void Get16Bits(ushort input, ref bool output) byte.MaxValue, byte.MaxValue); - var result = Avx2.Shuffle( - Avx2.Min(Avx2.And(Vector256.Create(input), bitmask), onevec).AsByte(), + var result = Vector256.Shuffle( + Vector256.Min(input & bitmask, onevec).AsByte(), shuffleMask); - Unsafe.WriteUnaligned(ref Unsafe.As(ref output), result.GetLower().GetLower()); - Unsafe.WriteUnaligned(ref Unsafe.Add(ref Unsafe.As(ref output), 8), result.GetUpper().GetLower()); - } - - /// - /// Extracts bits from 8-bit unsigned integer. - /// - /// The integer value. - /// The buffer for extracted bits. - public static void FromByte(byte value, Span bits) - { - bits = bits.TrimLength(8); - - if (Sse2.IsSupported) - { - Get8Bits(value, ref MemoryMarshal.GetReference(bits)); - } - else - { - GetBits(value, bits); - } - } - - /// - /// Extracts bits from 8-bit signed integer. - /// - /// The integer value. - /// The buffer for extracted bits. - [CLSCompliant(false)] - public static void FromSByte(sbyte value, Span bits) - => FromByte((byte)value, bits); - - /// - /// Extracts bits from 16-bit signed integer. - /// - /// The integer value. - /// The buffer for extracted bits. - public static void FromInt16(short value, Span bits) - => FromUInt16((ushort)value, bits); - - /// - /// Extracts bits from 16-bit unsigned integer. - /// - /// The integer value. - /// The buffer for extracted bits. - [CLSCompliant(false)] - public static void FromUInt16(ushort value, Span bits) - { - bits = bits.TrimLength(16); - - if (Avx2.IsSupported) - { - Get16Bits(value, ref MemoryMarshal.GetReference(bits)); - } - else if (Sse2.IsSupported) - { - ref var output = ref MemoryMarshal.GetReference(bits); - ref var input = ref Unsafe.As(ref value); - Get8Bits(input, ref output); - Get8Bits(Unsafe.Add(ref input, 1), ref Unsafe.Add(ref output, 8)); - } - else - { - GetBits(value, bits); - } - } - - /// - /// Extracts bits from 32-bit signed integer. - /// - /// The integer value. - /// The buffer for extracted bits. - public static void FromInt32(int value, Span bits) - => FromUInt32((uint)value, bits); - - /// - /// Extracts bits from 32-bit unsigned integer. - /// - /// The integer value. - /// The buffer for extracted bits. - [CLSCompliant(false)] - public static void FromUInt32(uint value, Span bits) - { - bits = bits.TrimLength(32); - - if (Avx2.IsSupported) - { - ref var output = ref MemoryMarshal.GetReference(bits); - ref var input = ref Unsafe.As(ref value); - Get16Bits(input, ref output); - Get16Bits(Unsafe.Add(ref input, 1), ref Unsafe.Add(ref output, 16)); - } - else if (Sse2.IsSupported) - { - ref var output = ref MemoryMarshal.GetReference(bits); - ref var input = ref Unsafe.As(ref value); - Get8Bits(input, ref output); - Get8Bits(Unsafe.Add(ref input, 1), ref Unsafe.Add(ref output, 8)); - Get8Bits(Unsafe.Add(ref input, 2), ref Unsafe.Add(ref output, 16)); - Get8Bits(Unsafe.Add(ref input, 3), ref Unsafe.Add(ref output, 24)); - } - else - { - GetBits(value, bits); - } - } - - /// - /// Extracts bits from 64-bit signed integer. - /// - /// The integer value. - /// The buffer for extracted bits. - public static void FromInt64(long value, Span bits) - => FromUInt64((ulong)value, bits); - - /// - /// Extracts bits from 64-bit unsigned integer. - /// - /// The integer value. - /// The buffer for extracted bits. - [CLSCompliant(false)] - public static void FromUInt64(ulong value, Span bits) - { - bits = bits.TrimLength(64); - - if (Avx2.IsSupported) - { - ref var output = ref MemoryMarshal.GetReference(bits); - ref var input = ref Unsafe.As(ref value); - - Get16Bits(input, ref output); - Get16Bits(Unsafe.Add(ref input, 1), ref Unsafe.Add(ref output, 16)); - Get16Bits(Unsafe.Add(ref input, 2), ref Unsafe.Add(ref output, 32)); - Get16Bits(Unsafe.Add(ref input, 3), ref Unsafe.Add(ref output, 48)); - } - else if (Sse2.IsSupported) - { - ref var output = ref MemoryMarshal.GetReference(bits); - ref var input = ref Unsafe.As(ref value); - Get8Bits(input, ref output); - Get8Bits(Unsafe.Add(ref input, 1), ref Unsafe.Add(ref output, 8)); - Get8Bits(Unsafe.Add(ref input, 2), ref Unsafe.Add(ref output, 16)); - Get8Bits(Unsafe.Add(ref input, 3), ref Unsafe.Add(ref output, 24)); - Get8Bits(Unsafe.Add(ref input, 4), ref Unsafe.Add(ref output, 32)); - Get8Bits(Unsafe.Add(ref input, 5), ref Unsafe.Add(ref output, 40)); - Get8Bits(Unsafe.Add(ref input, 6), ref Unsafe.Add(ref output, 48)); - Get8Bits(Unsafe.Add(ref input, 7), ref Unsafe.Add(ref output, 56)); - } - else - { - GetBits(value, bits); - } - } - - /// - /// Extracts bits from platform-dependent signed integer. - /// - /// The integer value. - /// The buffer for extracted bits. - public static void FromInt(nint value, Span bits) - { - switch (IntPtr.Size) - { - case sizeof(int): - FromInt32((int)value, bits); - break; - case sizeof(long): - FromInt64((long)value, bits); - break; - default: - GetBits((nuint)value, bits.TrimLength(IntPtr.Size << 3)); - break; - } - } - - /// - /// Extracts bits from platform-dependent unsigned integer. - /// - /// The integer value. - /// The buffer for extracted bits. - [CLSCompliant(false)] - public static void FromUInt(nuint value, Span bits) - { - switch (UIntPtr.Size) - { - case sizeof(uint): - FromUInt32((uint)value, bits); - break; - case sizeof(ulong): - FromUInt64((ulong)value, bits); - break; - default: - GetBits(value, bits.TrimLength(UIntPtr.Size << 3)); - break; - } + result.GetLower().GetLower().StoreUnsafe(ref Unsafe.As(ref output)); + result.GetUpper().GetLower().StoreUnsafe(ref Unsafe.Add(ref Unsafe.As(ref output), 8)); } } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs index 4a1d0dcd8..959625d3f 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs @@ -33,42 +33,38 @@ namespace DotNext.Net.Cluster.Consensus.Raft; private readonly int commandId; private readonly byte flags; - [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500", Justification = "False positive")] private BufferedRaftLogEntry(string fileName, int bufferSize, long term, DateTimeOffset timestamp, int? id, bool snapshot) { Term = term; Timestamp = timestamp; - flags = BitVector.ToByte(stackalloc bool[] { false, snapshot, id.HasValue }); + flags = BitVector.FromBits([false, snapshot, id.HasValue]); commandId = id.GetValueOrDefault(); content = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.None, bufferSize, FileOptions.SequentialScan | FileOptions.Asynchronous | FileOptions.DeleteOnClose); } - [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500", Justification = "False positive")] private BufferedRaftLogEntry(FileStream file, long term, DateTimeOffset timestamp, int? id, bool snapshot) { Term = term; Timestamp = timestamp; - flags = BitVector.ToByte(stackalloc bool[] { false, snapshot, id.HasValue }); + flags = BitVector.FromBits([false, snapshot, id.HasValue]); commandId = id.GetValueOrDefault(); content = file; } - [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500", Justification = "False positive")] private BufferedRaftLogEntry(IGrowableBuffer buffer, long term, DateTimeOffset timestamp, int? id, bool snapshot) { Term = term; Timestamp = timestamp; - flags = BitVector.ToByte(stackalloc bool[] { true, snapshot, id.HasValue }); + flags = BitVector.FromBits([true, snapshot, id.HasValue]); commandId = id.GetValueOrDefault(); content = buffer; } - [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500", Justification = "False positive")] private BufferedRaftLogEntry(long term, DateTimeOffset timestamp, int? id, bool snapshot) { Term = term; Timestamp = timestamp; - flags = BitVector.ToByte(stackalloc bool[] { true, snapshot, id.HasValue }); + flags = BitVector.FromBits([true, snapshot, id.HasValue]); commandId = id.GetValueOrDefault(); content = null; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs index 3046320b5..5ac86dd83 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices; @@ -18,12 +17,11 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices; private readonly byte flags; private readonly int identifier; - [SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1500", Justification = "False positive")] private LogEntryMetadata(long term, DateTimeOffset timestamp, bool isSnapshot, int? commandId, long? length) { Term = term; this.timestamp = timestamp.UtcTicks; - flags = BitVector.ToByte(stackalloc bool[] { commandId.HasValue, isSnapshot }); + flags = BitVector.FromBits([commandId.HasValue, isSnapshot]); identifier = commandId.GetValueOrDefault(); this.length = length.GetValueOrDefault(-1L); } From a58ca3de730be629f76db88caaf2424c6a27a948 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 23:08:57 +0200 Subject: [PATCH 023/155] Removed deleted project --- src/DotNext.sln | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/DotNext.sln b/src/DotNext.sln index 0fddcf147..a7ff60b4d 100644 --- a/src/DotNext.sln +++ b/src/DotNext.sln @@ -11,8 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Benchmarks", "DotNe EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Unsafe", "DotNext.Unsafe\DotNext.Unsafe.csproj", "{BEFBC460-FFF8-4685-8ACF-EC37530845FB}" EndProject -Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "VisualBasicElements", "VisualBasicElements\VisualBasicElements.vbproj", "{9AB8DBF7-D99B-47D8-B6B9-37F5B8D75C17}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Metaprogramming", "DotNext.Metaprogramming\DotNext.Metaprogramming.csproj", "{C8415985-3BC5-44EB-BDB4-551777B73AB4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNext.Threading", "DotNext.Threading\DotNext.Threading.csproj", "{956A2A3E-F151-4FA4-9932-862BC5CB2F87}" From fcd52bdc9a5b9d262ca112c3bf8e60ba45d483b8 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 23:11:31 +0200 Subject: [PATCH 024/155] Fixed compiler errors --- .../Numerics/BitVectorBenchmark.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/DotNext.Benchmarks/Numerics/BitVectorBenchmark.cs b/src/DotNext.Benchmarks/Numerics/BitVectorBenchmark.cs index 94242e877..9a7a377c4 100644 --- a/src/DotNext.Benchmarks/Numerics/BitVectorBenchmark.cs +++ b/src/DotNext.Benchmarks/Numerics/BitVectorBenchmark.cs @@ -12,28 +12,24 @@ public class BitVectorBenchmark [Benchmark(Description = "8 bits")] public void Get8Bits() { - Span bits = stackalloc bool[8]; - BitVector.FromByte(52, bits); + BitVector.GetBits(52, stackalloc bool[8]); } [Benchmark(Description = "16 bits")] public void Get16Bits() { - Span bits = stackalloc bool[16]; - BitVector.FromUInt16(52, bits); + BitVector.GetBits(52, stackalloc bool[16]); } [Benchmark(Description = "32 bits")] public void Get32Bits() { - Span bits = stackalloc bool[32]; - BitVector.FromUInt32(52U, bits); + BitVector.GetBits(52U, stackalloc bool[32]); } [Benchmark(Description = "64 bits")] public void Get64Bits() { - Span bits = stackalloc bool[64]; - BitVector.FromUInt64(52UL, bits); + BitVector.GetBits(52UL, stackalloc bool[64]); } } \ No newline at end of file From cb75ee2087958ffc21238d68d2f1bea7bf3c6419 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 23:26:21 +0200 Subject: [PATCH 025/155] Fixed parameter name --- src/DotNext/Buffers/SpanReader.cs | 6 +++--- src/DotNext/Buffers/SpanWriter.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/DotNext/Buffers/SpanReader.cs b/src/DotNext/Buffers/SpanReader.cs index 8980b6e7e..d537d759b 100644 --- a/src/DotNext/Buffers/SpanReader.cs +++ b/src/DotNext/Buffers/SpanReader.cs @@ -74,7 +74,7 @@ public int ConsumedCount readonly get => position; set { - ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)value, (uint)length); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)value, (uint)length, nameof(value)); position = value; } @@ -108,7 +108,7 @@ public readonly ReadOnlySpan RemainingSpan /// is greater than the available space in the rest of the memory block. public void Advance(int count) { - ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)RemainingCount); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)RemainingCount, nameof(count)); position += count; } @@ -120,7 +120,7 @@ public void Advance(int count) /// is less than zero or greater than . public void Rewind(int count) { - ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)position); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)position, nameof(count)); position -= count; } diff --git a/src/DotNext/Buffers/SpanWriter.cs b/src/DotNext/Buffers/SpanWriter.cs index 17afa18bb..ba6fb5f7c 100644 --- a/src/DotNext/Buffers/SpanWriter.cs +++ b/src/DotNext/Buffers/SpanWriter.cs @@ -79,7 +79,7 @@ public int WrittenCount readonly get => position; set { - ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)value, (uint)length); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)value, (uint)length, nameof(value)); position = value; } @@ -97,7 +97,7 @@ public int WrittenCount /// is greater than the available space in the rest of the memory block. public void Advance(int count) { - ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)FreeCapacity); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)FreeCapacity, nameof(count)); position += count; } @@ -109,7 +109,7 @@ public void Advance(int count) /// is less than zero or greater than . public void Rewind(int count) { - ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)FreeCapacity); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)count, (uint)FreeCapacity, nameof(count)); position -= count; } From 13ab9a5213a4e15dbbf12f4f811f4da0e5821431 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 23:39:33 +0200 Subject: [PATCH 026/155] Fixed BufferWriter interface --- src/DotNext.IO/IO/DataTransferObject.cs | 4 +- .../ApplicationMaintenanceInterfaceHost.cs | 6 +- .../CommandLine/IO/MaintenanceConsole.cs | 2 +- .../Buffers/BufferWriterTests.cs | 4 +- .../Buffers/MemoryWriterTests.cs | 14 +-- src/DotNext/Buffers/BufferWriter.cs | 25 ---- .../Buffers/PooledArrayBufferWriter.cs | 110 ++---------------- src/DotNext/Buffers/PooledBufferWriter.cs | 67 ++--------- .../Collections/Generic/Sequence.Async.cs | 2 +- .../Collections/Generic/Sequence.Buffer.cs | 6 +- .../Http/HttpPeerController.Shuffle.cs | 3 +- .../Consensus/Raft/BufferedRaftLogEntry.cs | 2 +- .../PersistentClusterConfigurationStorage.cs | 7 +- 13 files changed, 40 insertions(+), 212 deletions(-) diff --git a/src/DotNext.IO/IO/DataTransferObject.cs b/src/DotNext.IO/IO/DataTransferObject.cs index 18084a23e..681c5a810 100644 --- a/src/DotNext.IO/IO/DataTransferObject.cs +++ b/src/DotNext.IO/IO/DataTransferObject.cs @@ -39,9 +39,9 @@ ValueTask IDataTransferObject.ITransformation.TransformAsync(TRea // can return null if capacity == 0 private static BufferWriter? CreateBuffer(long? capacity, MemoryAllocator? allocator) => capacity switch { - null => new PooledBufferWriter { BufferAllocator = allocator }, + null => new PooledBufferWriter(allocator), 0L => null, - { } length when length <= Array.MaxLength => new PooledBufferWriter { BufferAllocator = allocator, Capacity = (int)length }, + { } length when length <= Array.MaxLength => new PooledBufferWriter(allocator) { Capacity = (int)length }, _ => throw new InsufficientMemoryException(), }; diff --git a/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs b/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs index 785c57c01..2bbf9e923 100644 --- a/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs +++ b/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs @@ -161,14 +161,12 @@ private async void ProcessRequestAsync(Socket clientSocket, CancellationToken to var outputBuffer = default(BufferWriter); try { - outputBuffer = new PooledBufferWriter + outputBuffer = new PooledBufferWriter(ByteBufferAllocator) { - BufferAllocator = ByteBufferAllocator, Capacity = bufferSize, }; - inputBuffer = new PooledBufferWriter + inputBuffer = new PooledBufferWriter(CharBufferAllocator) { - BufferAllocator = CharBufferAllocator, Capacity = bufferSize, }; diff --git a/src/DotNext.MaintenanceServices/Maintenance/CommandLine/IO/MaintenanceConsole.cs b/src/DotNext.MaintenanceServices/Maintenance/CommandLine/IO/MaintenanceConsole.cs index 5384438d9..24b75b86e 100644 --- a/src/DotNext.MaintenanceServices/Maintenance/CommandLine/IO/MaintenanceConsole.cs +++ b/src/DotNext.MaintenanceServices/Maintenance/CommandLine/IO/MaintenanceConsole.cs @@ -13,7 +13,7 @@ private sealed class BufferedStreamWriter : Disposable, IStandardStreamWriter private readonly PooledBufferWriter buffer; internal BufferedStreamWriter(int capacity, MemoryAllocator? allocator) - => buffer = new() { BufferAllocator = allocator, Capacity = capacity }; + => buffer = new(allocator) { Capacity = capacity }; internal IBufferWriter BufferWriter => buffer; diff --git a/src/DotNext.Tests/Buffers/BufferWriterTests.cs b/src/DotNext.Tests/Buffers/BufferWriterTests.cs index e1f5bd928..e6793167a 100644 --- a/src/DotNext.Tests/Buffers/BufferWriterTests.cs +++ b/src/DotNext.Tests/Buffers/BufferWriterTests.cs @@ -75,7 +75,7 @@ public static void ArrayBufferToString() public static IEnumerable CharWriters() { - yield return new object[] { new PooledBufferWriter { BufferAllocator = MemoryPool.Shared.ToAllocator() } }; + yield return new object[] { new PooledBufferWriter(MemoryPool.Shared.ToAllocator()) }; yield return new object[] { new PooledArrayBufferWriter() }; yield return new object[] { new SparseBufferWriter() }; yield return new object[] { new SparseBufferWriter(32) }; @@ -120,7 +120,7 @@ public static void MutableStringBuffer(TWriter writer) [Fact] public static void EncodeAsString() { - using (var writer = new PooledBufferWriter { BufferAllocator = MemoryPool.Shared.ToAllocator() }) + using (var writer = new PooledBufferWriter(MemoryPool.Shared.ToAllocator())) { EncodeDecode(writer, Encoding.UTF8); } diff --git a/src/DotNext.Tests/Buffers/MemoryWriterTests.cs b/src/DotNext.Tests/Buffers/MemoryWriterTests.cs index de6c538e5..2050c4e9a 100644 --- a/src/DotNext.Tests/Buffers/MemoryWriterTests.cs +++ b/src/DotNext.Tests/Buffers/MemoryWriterTests.cs @@ -72,9 +72,9 @@ private static void WriteReadUsingMemory(BufferWriter writer) public static void PooledBufferWriterDefaultCapacity() { var allocator = MemoryPool.Shared.ToAllocator(); - using (var writer = new PooledBufferWriter { BufferAllocator = allocator }) + using (var writer = new PooledBufferWriter(allocator)) WriteReadUsingSpan(writer); - using (var writer = new PooledBufferWriter { BufferAllocator = allocator }) + using (var writer = new PooledBufferWriter(allocator)) WriteReadUsingMemory(writer); } @@ -83,18 +83,18 @@ public static void PooledBufferWriterWithCapacity() { var allocator = MemoryPool.Shared.ToAllocator(); Throws(new Action(() => new PooledBufferWriter { Capacity = -1 })); - using (var writer = new PooledBufferWriter { BufferAllocator = allocator, Capacity = 30 }) + using (var writer = new PooledBufferWriter(allocator) { Capacity = 30 }) WriteReadUsingSpan(writer); - using (var writer = new PooledBufferWriter { BufferAllocator = allocator, Capacity = 20 }) + using (var writer = new PooledBufferWriter(allocator) { Capacity = 20 }) WriteReadUsingMemory(writer); } [Fact] public static void PooledArrayBufferWriterDefaultCapacity() { - using (var writer = new PooledArrayBufferWriter { BufferPool = ArrayPool.Shared }) + using (var writer = new PooledArrayBufferWriter()) WriteReadUsingSpan(writer); - using (var writer = new PooledArrayBufferWriter { BufferPool = ArrayPool.Shared }) + using (var writer = new PooledArrayBufferWriter()) WriteReadUsingMemory(writer); } @@ -188,7 +188,7 @@ public static void ReuseArrayWriter() [Fact] public static void ReuseMemoryWriter() { - using var writer = new PooledBufferWriter { BufferAllocator = MemoryPool.Shared.ToAllocator() }; + using var writer = new PooledBufferWriter(MemoryPool.Shared.ToAllocator()); Equal(0, writer.Capacity); var span = writer.GetSpan(10); span[0] = 20; diff --git a/src/DotNext/Buffers/BufferWriter.cs b/src/DotNext/Buffers/BufferWriter.cs index 5604f8515..32a91d50d 100644 --- a/src/DotNext/Buffers/BufferWriter.cs +++ b/src/DotNext/Buffers/BufferWriter.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -20,7 +19,6 @@ public abstract class BufferWriter : Disposable, IBufferWriter, ISupplier< private const string ElementTypeMeterAttribute = "dotnext.buffers.element"; private protected readonly TagList measurementTags; - private readonly object? diagObj; /// /// Represents position of write cursor. @@ -35,29 +33,6 @@ private protected BufferWriter() measurementTags = new() { { ElementTypeMeterAttribute, typeof(T).Name } }; } - /// - /// Sets the counter used to report allocation of internal buffer. - /// - [DisallowNull] - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public EventCounter? AllocationCounter - { - private protected get => diagObj as EventCounter; - init => diagObj = value; - } - - /// - /// Sets the callback used internally to report actual size - /// of the allocated buffer. - /// - [DisallowNull] - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public Action? BufferSizeCallback - { - private protected get => diagObj as Action; - init => diagObj = value; - } - /// /// Sets a list of tags to be associated with each measurement. /// diff --git a/src/DotNext/Buffers/PooledArrayBufferWriter.cs b/src/DotNext/Buffers/PooledArrayBufferWriter.cs index ae1b7f054..375cb6150 100644 --- a/src/DotNext/Buffers/PooledArrayBufferWriter.cs +++ b/src/DotNext/Buffers/PooledArrayBufferWriter.cs @@ -15,86 +15,14 @@ namespace DotNext.Buffers; /// This class provides additional methods for access to array segments in contrast to . /// /// The data type that can be written. -public sealed class PooledArrayBufferWriter : BufferWriter, ISupplier>, IList +/// +/// Initializes a new writer with the default initial capacity. +/// +/// The array pool. +public sealed class PooledArrayBufferWriter(ArrayPool? pool = null) : BufferWriter, ISupplier>, IList { - private readonly ArrayPool pool; - private T[] buffer; - - /// - /// Initializes a new writer with the specified initial capacity. - /// - /// The array pool. - /// The initial capacity of the writer. - /// is less than or equal to zero. - [Obsolete("Use init-only properties to set the capacity and allocator")] - public PooledArrayBufferWriter(ArrayPool pool, int initialCapacity) - { - if (initialCapacity <= 0) - throw new ArgumentOutOfRangeException(nameof(initialCapacity)); - this.pool = pool; - buffer = pool.Rent(initialCapacity); - } - - /// - /// Initializes a new writer with the default initial capacity. - /// - /// The array pool. - [Obsolete("Use init-only properties to set the capacity and pool")] - public PooledArrayBufferWriter(ArrayPool pool) - { - this.pool = pool; - buffer = Array.Empty(); - } - - /// - /// Initializes a new writer with the specified initial capacity and - /// as the array pooling mechanism. - /// - /// The initial capacity of the writer. - /// is less than or equal to zero. - [Obsolete("Use init-only properties to set the capacity and pool")] - public PooledArrayBufferWriter(int initialCapacity) - : this(ArrayPool.Shared, initialCapacity) - { - } - - /// - /// Initializes a new writer with the default initial capacity and - /// as the array pooling mechanism. - /// - /// - /// - public PooledArrayBufferWriter() - { - pool = ArrayPool.Shared; - buffer = Array.Empty(); - } - - /// - /// Sets the array pool that will be used to rent the internal buffer. - /// - /// - /// It is recommended to initialize this property before . - /// value is the same as . - /// - public ArrayPool? BufferPool - { - init - { - value ??= ArrayPool.Shared; - - var length = buffer.Length; - - // cover situation when Capacity initializer called before this initializer - if (length > 0) - { - pool.Return(buffer); // no need to clear fresh array - buffer = value.Rent(length); - } - - pool = value; - } - } + private readonly ArrayPool pool = pool ?? ArrayPool.Shared; + private T[] buffer = []; /// int ICollection.Count => WrittenCount; @@ -116,24 +44,12 @@ void ICollection.CopyTo(T[] array, int arrayIndex) /// The element at the specified index. /// the index is invalid. /// This writer has been disposed. - public new ref T this[int index] => ref this[(long)index]; - - /// - /// Gets the element at the specified index. - /// - /// The index of the element to retrieve. - /// The element at the specified index. - /// the index is invalid. - /// This writer has been disposed. - public ref T this[long index] + public new ref T this[int index] { get { ThrowIfDisposed(); - if ((ulong)index >= (ulong)position) - throw new ArgumentOutOfRangeException(nameof(index)); - - return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(buffer), (nint)index); + return ref buffer[index]; } } @@ -539,9 +455,6 @@ private protected override void Resize(int newSize) } buffer = newBuffer; -#pragma warning disable CS0618 - AllocationCounter?.WriteMetric(newBuffer.LongLength); -#pragma warning restore CS0618 PooledArrayBufferWriter.AllocationMeter.Record(newBuffer.LongLength, measurementTags); } @@ -550,13 +463,10 @@ protected override void Dispose(bool disposing) { if (disposing) { -#pragma warning disable CS0618 - BufferSizeCallback?.Invoke(buffer.Length); -#pragma warning restore CS0618 if (GetLength(buffer) > 0) { ReturnBuffer(); - buffer = Array.Empty(); + buffer = []; } } diff --git a/src/DotNext/Buffers/PooledBufferWriter.cs b/src/DotNext/Buffers/PooledBufferWriter.cs index 7c85e11ee..ceba669e3 100644 --- a/src/DotNext/Buffers/PooledBufferWriter.cs +++ b/src/DotNext/Buffers/PooledBufferWriter.cs @@ -8,55 +8,15 @@ namespace DotNext.Buffers; /// Represents memory writer that uses pooled memory. /// /// The data type that can be written. -public sealed class PooledBufferWriter : BufferWriter, IMemoryOwner +/// +/// Initializes a new writer with the default initial capacity. +/// +/// The allocator of internal buffer. +public sealed class PooledBufferWriter(MemoryAllocator? allocator = null) : BufferWriter, IMemoryOwner { - private readonly MemoryAllocator? allocator; + private readonly MemoryAllocator? allocator = allocator; private MemoryOwner buffer; - /// - /// Initializes a new writer with the specified initial capacity. - /// - /// The allocator of internal buffer. - /// The initial capacity of the writer. - /// is less than or equal to zero. - [Obsolete("Use init-only properties to set the capacity and allocator")] - public PooledBufferWriter(MemoryAllocator? allocator, int initialCapacity) - { - if (initialCapacity <= 0) - throw new ArgumentOutOfRangeException(nameof(initialCapacity)); - - this.allocator = allocator; - buffer = allocator.Invoke(initialCapacity, exactSize: false); - } - - /// - /// Initializes a new writer with the default initial capacity. - /// - /// The allocator of internal buffer. - [Obsolete("Use init-only properties to set the capacity and allocator")] - public PooledBufferWriter(MemoryAllocator? allocator) - => this.allocator = allocator; - - /// - /// Initializes a new empty writer. - /// - /// - /// - public PooledBufferWriter() - { - } - - /// - /// Sets the allocator of internal buffer. - /// - /// - /// It is recommended to initialize this property before . - /// - public MemoryAllocator? BufferAllocator - { - init => allocator = value; - } - /// public override int Capacity { @@ -113,10 +73,9 @@ public override void Clear(bool reuseBuffer = false) private ref readonly MemoryOwner GetBuffer(int sizeHint) { - if (sizeHint < 0) - throw new ArgumentOutOfRangeException(nameof(sizeHint)); - + ArgumentOutOfRangeException.ThrowIfNegative(sizeHint); ThrowIfDisposed(); + CheckAndResizeBuffer(sizeHint); return ref buffer; } @@ -153,22 +112,12 @@ public override MemoryOwner DetachBuffer() private protected override void Resize(int newSize) { buffer.Resize(newSize, false, allocator); -#pragma warning disable CS0618 - AllocationCounter?.WriteMetric(buffer.Length); -#pragma warning restore CS0618 PooledBufferWriter.AllocationMeter.Record(buffer.Length, measurementTags); } /// protected override void Dispose(bool disposing) { - if (disposing) - { -#pragma warning disable CS0618 - BufferSizeCallback?.Invoke(buffer.Length); -#pragma warning restore CS0618 - } - buffer.Dispose(); base.Dispose(disposing); } diff --git a/src/DotNext/Collections/Generic/Sequence.Async.cs b/src/DotNext/Collections/Generic/Sequence.Async.cs index 9b685825d..877758784 100644 --- a/src/DotNext/Collections/Generic/Sequence.Async.cs +++ b/src/DotNext/Collections/Generic/Sequence.Async.cs @@ -214,7 +214,7 @@ public static IAsyncEnumerable SkipNulls(this IAsyncEnumerable collect /// The operation has been canceled. public static async Task ToArrayAsync(this IAsyncEnumerable collection, int initialCapacity = 10, MemoryAllocator? allocator = null, CancellationToken token = default) { - using var buffer = new PooledBufferWriter { BufferAllocator = allocator, Capacity = initialCapacity }; + using var buffer = new PooledBufferWriter(allocator) { Capacity = initialCapacity }; await foreach (var item in collection.WithCancellation(token).ConfigureAwait(false)) { diff --git a/src/DotNext/Collections/Generic/Sequence.Buffer.cs b/src/DotNext/Collections/Generic/Sequence.Buffer.cs index 986728ac4..3a7b69a1d 100644 --- a/src/DotNext/Collections/Generic/Sequence.Buffer.cs +++ b/src/DotNext/Collections/Generic/Sequence.Buffer.cs @@ -81,11 +81,9 @@ static int GetSize(IEnumerable enumerable, int sizeHint) public static async Task> CopyAsync(this IAsyncEnumerable enumerable, int sizeHint = 0, MemoryAllocator? allocator = null, CancellationToken token = default) { ArgumentNullException.ThrowIfNull(enumerable); + ArgumentOutOfRangeException.ThrowIfNegative(sizeHint); - if (sizeHint < 0) - throw new ArgumentOutOfRangeException(nameof(sizeHint)); - - using var buffer = new PooledBufferWriter { BufferAllocator = allocator, Capacity = sizeHint }; + using var buffer = new PooledBufferWriter(allocator) { Capacity = sizeHint }; await foreach (var item in enumerable.WithCancellation(token).ConfigureAwait(false)) buffer.Add(item); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs index 9d395168c..179438d4b 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs @@ -101,9 +101,8 @@ private async Task ProcessShuffleRequestAsync(HttpRequest request, HttpResponse } else { - using var buffer = new PooledBufferWriter + using var buffer = new PooledBufferWriter(allocator) { - BufferAllocator = allocator, Capacity = payloadLength.Truncate(), }; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs index 959625d3f..dd34d3ced 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BufferedRaftLogEntry.cs @@ -135,7 +135,7 @@ private static async ValueTask CopyToMemoryOrFileAsync CopyToMemoryAsync(TEntry entry, int length, MemoryAllocator? allocator, CancellationToken token) where TEntry : notnull, IRaftLogEntry { - var writer = new PooledBufferWriter { BufferAllocator = allocator, Capacity = length }; + var writer = new PooledBufferWriter(allocator) { Capacity = length }; try { await entry.WriteToAsync(writer, token).ConfigureAwait(false); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs index cf8b99b7a..523466396 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs @@ -145,7 +145,7 @@ private PersistentClusterConfigurationStorage(DirectoryInfo storage, int fileBuf /// protected sealed override async ValueTask ProposeAsync(IClusterConfiguration configuration, CancellationToken token = default) { - using var writer = new PooledBufferWriter { BufferAllocator = allocator, Capacity = bufferSize }; + using var writer = new PooledBufferWriter(allocator) { Capacity = bufferSize }; writer.WriteInt64(configuration.Fingerprint, littleEndian: true); await configuration.WriteToAsync(writer, token).ConfigureAwait(false); @@ -179,11 +179,10 @@ private async ValueTask ApplyProposedAsync(CancellationToken token) /// protected sealed override async ValueTask LoadConfigurationAsync(CancellationToken token = default) { - var builder = ImmutableHashSet.CreateBuilder(comparer); + var builder = ImmutableHashSet.CreateBuilder(comparer); - using var buffer = new PooledBufferWriter + using var buffer = new PooledBufferWriter(allocator) { - BufferAllocator = allocator, Capacity = active.IsEmpty ? bufferSize : active.Length.Truncate(), }; From 9c6d9189d578ef794c71644f7de0de5d3fc17135 Mon Sep 17 00:00:00 2001 From: sakno Date: Mon, 20 Nov 2023 23:45:02 +0200 Subject: [PATCH 027/155] Removed duplicated methods --- src/DotNext.Tests/ComparableTests.cs | 8 -------- src/DotNext/Comparison.cs | 23 ----------------------- 2 files changed, 31 deletions(-) diff --git a/src/DotNext.Tests/ComparableTests.cs b/src/DotNext.Tests/ComparableTests.cs index 1d7d19afd..d839d1786 100644 --- a/src/DotNext.Tests/ComparableTests.cs +++ b/src/DotNext.Tests/ComparableTests.cs @@ -2,14 +2,6 @@ public sealed class ComparableTests : Test { - [Fact] - public static void ClampTest() - { - Equal(20M, 10M.Clamp(20M, 30M)); - Equal(25M, 25M.Clamp(20M, 30M)); - Equal(30M, 40M.Clamp(20M, 30M)); - } - [Fact] public static void BetweenTest() { diff --git a/src/DotNext/Comparison.cs b/src/DotNext/Comparison.cs index a19424e21..32a444bbc 100644 --- a/src/DotNext/Comparison.cs +++ b/src/DotNext/Comparison.cs @@ -7,29 +7,6 @@ namespace DotNext; /// public static class Comparison { - /// - /// Restricts a in specific range. - /// - /// Type of the values. - /// Value to be restricted. - /// Minimal range value. - /// Maximum range value. - /// - /// if ; - /// or if < ; - /// or if < . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Clamp(this T value, T min, T max) - where T : notnull, IComparable - { - if (value.CompareTo(min) < 0) - return min; - if (value.CompareTo(max) > 0) - return max; - return value; - } - /// /// Checks whether specified value is in range. /// From ce76bf645598f60143420ae2ea736ff0eef19522 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 00:20:15 +0200 Subject: [PATCH 028/155] Reduced IsOneOf overloaded methods --- src/DotNext.IO/IO/FileReader.Segment.cs | 4 +- .../IO/Pipelines/PipeExtensions.Readers.cs | 2 +- src/DotNext.IO/IO/StreamSegment.cs | 4 +- .../Runtime/CompilerServices/TaskType.cs | 2 +- .../TransportServices/TransportTestSuite.cs | 2 +- src/DotNext.Tests/ObjectExtensionsTests.cs | 7 --- src/DotNext.Tests/ValueTypeTests.cs | 7 +-- .../Runtime/InteropServices/Pointer.cs | 8 ++-- src/DotNext/EqualityComparerBuilder.cs | 2 +- src/DotNext/ObjectExtensions.cs | 35 ++------------ src/DotNext/Result.cs | 2 +- src/DotNext/ValueTypeExtensions.cs | 48 ------------------- .../Http/HttpPeerController.Shuffle.cs | 2 +- .../PersistentClusterConfigurationStorage.cs | 2 +- .../Raft/PersistentState.LogEntry.cs | 4 +- .../Consensus/Raft/RaftCluster.DefaultImpl.cs | 2 +- 16 files changed, 25 insertions(+), 108 deletions(-) diff --git a/src/DotNext.IO/IO/FileReader.Segment.cs b/src/DotNext.IO/IO/FileReader.Segment.cs index a3d8a5462..27fe90838 100644 --- a/src/DotNext.IO/IO/FileReader.Segment.cs +++ b/src/DotNext.IO/IO/FileReader.Segment.cs @@ -98,8 +98,8 @@ public bool Equals(SegmentLength other) public void SetSegmentLength(long? value) => length = new(value); private static ReadOnlyMemory TrimLength(ReadOnlyMemory buffer, SegmentLength length) - => length.IsInfinite ? buffer : buffer.TrimLength(ValueTypeExtensions.Truncate((long)length)); + => length.IsInfinite ? buffer : buffer.TrimLength(int.CreateSaturating((long)length)); private static Memory TrimLength(Memory buffer, SegmentLength length) - => length.IsInfinite ? buffer : buffer.TrimLength(ValueTypeExtensions.Truncate((long)length)); + => length.IsInfinite ? buffer : buffer.TrimLength(int.CreateSaturating((long)length)); } \ No newline at end of file diff --git a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs index e12ea2f29..0e08c1974 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs @@ -632,7 +632,7 @@ public static async ValueTask ReadBlockAsync(this PipeReader reader, for (int bytesToConsume; length > 0L && buffer.TryGet(ref consumed, out var block, false) && !block.IsEmpty; consumed = buffer.GetPosition(bytesToConsume, consumed), length -= bytesToConsume) { - bytesToConsume = Math.Min(block.Length, length.Truncate()); + bytesToConsume = Math.Min(block.Length, int.CreateSaturating(length)); await consumer.Invoke(block.Slice(0, bytesToConsume), token).ConfigureAwait(false); } } diff --git a/src/DotNext.IO/IO/StreamSegment.cs b/src/DotNext.IO/IO/StreamSegment.cs index d2d17b5bf..de7518431 100644 --- a/src/DotNext.IO/IO/StreamSegment.cs +++ b/src/DotNext.IO/IO/StreamSegment.cs @@ -114,7 +114,7 @@ public override int Read(byte[] buffer, int offset, int count) /// public override int Read(Span buffer) - => BaseStream.Read(buffer.TrimLength(RemainingBytes.Truncate())); + => BaseStream.Read(buffer.TrimLength(int.CreateSaturating(RemainingBytes))); /// public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) @@ -132,7 +132,7 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, Cancel /// public override ValueTask ReadAsync(Memory buffer, CancellationToken token = default) - => BaseStream.ReadAsync(buffer.TrimLength(RemainingBytes.Truncate()), token); + => BaseStream.ReadAsync(buffer.TrimLength(int.CreateSaturating(RemainingBytes)), token); /// public override long Seek(long offset, SeekOrigin origin) diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs index f2bddb9e4..80d312096 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs @@ -23,7 +23,7 @@ internal TaskType(Type? resultType, bool isValueTask) internal TaskType(Type taskType) { this.taskType = taskType; - if (taskType.IsOneOf(typeof(ValueTask), typeof(Task))) + if (taskType.IsOneOf([typeof(ValueTask), typeof(Task)])) { resultType = null; } diff --git a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs index c24bea3fa..c5afe30f4 100644 --- a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs +++ b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs @@ -80,7 +80,7 @@ internal LocalMember(bool smallAmountOfMetadata = false) async ValueTask ILocalMember.ProposeConfigurationAsync(Func, CancellationToken, ValueTask> configurationReader, long configurationLength, long fingerprint, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(configurationLength.Truncate(), true); + using var buffer = MemoryAllocator.Allocate(int.CreateSaturating(configurationLength), exactSize: true); await configurationReader(buffer.Memory, token).ConfigureAwait(false); Equal(42L, fingerprint); ReceivedConfiguration = buffer.Memory.ToArray(); diff --git a/src/DotNext.Tests/ObjectExtensionsTests.cs b/src/DotNext.Tests/ObjectExtensionsTests.cs index 51365a165..042028246 100644 --- a/src/DotNext.Tests/ObjectExtensionsTests.cs +++ b/src/DotNext.Tests/ObjectExtensionsTests.cs @@ -13,13 +13,6 @@ private sealed class UserDataSupport : UserDataStorage.IContainer object UserDataStorage.IContainer.Source => source; } - [Fact] - public static void OneOfCheck() - { - True("str".IsOneOf("a", "b", "str")); - True("str".IsOneOf(new List { "a", "b", "str" })); - } - [Fact] public static void UserDataStorage() { diff --git a/src/DotNext.Tests/ValueTypeTests.cs b/src/DotNext.Tests/ValueTypeTests.cs index 52dc8ecfb..9e885efab 100644 --- a/src/DotNext.Tests/ValueTypeTests.cs +++ b/src/DotNext.Tests/ValueTypeTests.cs @@ -135,11 +135,8 @@ public static void CustomHashCode() [Fact] public static void OneOfValues() { - True(2.IsOneOf(2, 5, 7)); - False(2.IsOneOf(3, 5, 7)); - - True(2.IsOneOf(new List { 2, 5, 7 })); - False(2.IsOneOf(new List { 3, 5, 7 })); + True(2.IsOneOf([2, 5, 7])); + False(2.IsOneOf([3, 5, 7])); } [Fact] diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs index f6e9de954..01d664d5e 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs @@ -428,7 +428,7 @@ static void WriteTo(byte* source, long length, Stream destination) { for (int count; length > 0; length -= count, source += count) { - destination.Write(new ReadOnlySpan(source, count = length.Truncate())); + destination.Write(new ReadOnlySpan(source, count = int.CreateSaturating(length))); } } } @@ -462,7 +462,7 @@ static async ValueTask WriteToAsync(nint source, long length, Stream destination { for (int count; length > 0; length -= count, source += count) { - using var manager = new MemorySource(source, count = length.Truncate()); + using var manager = new MemorySource(source, count = int.CreateSaturating(length)); await destination.WriteAsync(manager.Memory, token).ConfigureAwait(false); } } @@ -518,7 +518,7 @@ static long ReadFrom(Stream source, byte* destination, long length) var total = 0L; for (int bytesRead; length > 0L; total += bytesRead, length -= bytesRead) { - if ((bytesRead = source.Read(new Span(&destination[total], length.Truncate()))) is 0) + if ((bytesRead = source.Read(new Span(&destination[total], int.CreateSaturating(length)))) is 0) break; } @@ -572,7 +572,7 @@ static async ValueTask ReadFromStreamAsync(Stream source, IntPtr destinati var total = 0L; for (int bytesRead; length > 0L; length -= bytesRead, destination += bytesRead, total += bytesRead) { - using var manager = new MemorySource(destination, length.Truncate()); + using var manager = new MemorySource(destination, int.CreateSaturating(length)); if ((bytesRead = await source.ReadAsync(manager.Memory, token).ConfigureAwait(false)) is 0) break; } diff --git a/src/DotNext/EqualityComparerBuilder.cs b/src/DotNext/EqualityComparerBuilder.cs index bdd5b1e05..3ed9663f5 100644 --- a/src/DotNext/EqualityComparerBuilder.cs +++ b/src/DotNext/EqualityComparerBuilder.cs @@ -306,7 +306,7 @@ public IEqualityComparer Build() { var t = typeof(T); - return t.IsPrimitive || t.IsEnum || t.IsOneOf(typeof(nint), typeof(nuint), typeof(DateTime), typeof(Half), typeof(DateTimeOffset)) + return t.IsPrimitive || t.IsEnum || t.IsOneOf([typeof(nint), typeof(nuint), typeof(DateTime), typeof(Half), typeof(DateTimeOffset)]) ? EqualityComparer.Default : new ConstructedEqualityComparer(BuildEquals(), BuildGetHashCode()); } diff --git a/src/DotNext/ObjectExtensions.cs b/src/DotNext/ObjectExtensions.cs index 95c16f1ed..100015331 100644 --- a/src/DotNext/ObjectExtensions.cs +++ b/src/DotNext/ObjectExtensions.cs @@ -48,38 +48,13 @@ public static UserDataStorage GetUserData(this T obj) /// /// The type of object to compare. /// The object to compare with other. - /// Candidate objects. - /// , if is equal to one of . - public static bool IsOneOf(this T value, IEnumerable values) - where T : class - { - foreach (var v in values) - { - if (Equals(value, v)) - return true; - } - - return false; - } - - /// - /// Checks whether the specified object is equal to one - /// of the specified objects. - /// - /// - /// This method uses - /// to check equality between two objects. - /// - /// The type of object to compare. - /// The object to compare with other. - /// Candidate objects. - /// , if is equal to one of . - public static bool IsOneOf(this T value, params T?[] values) - where T : class + /// Candidate objects. + /// , if is equal to one of . + public static bool IsOneOf(this T value, ReadOnlySpan candidates) { - for (nint i = 0; i < Intrinsics.GetLength(values); i++) + foreach (var other in candidates) { - if (Equals(values[i], value)) + if (EqualityComparer.Default.Equals(value, other)) return true; } diff --git a/src/DotNext/Result.cs b/src/DotNext/Result.cs index e6873a436..971119bf4 100644 --- a/src/DotNext/Result.cs +++ b/src/DotNext/Result.cs @@ -51,7 +51,7 @@ public static class Result /// /// The type of . /// , if specified type is result type; otherwise, . - public static bool IsResult(this Type resultType) => resultType.IsConstructedGenericType && resultType.GetGenericTypeDefinition().IsOneOf(typeof(Result<>), typeof(Result<,>)); + public static bool IsResult(this Type resultType) => resultType.IsConstructedGenericType && resultType.GetGenericTypeDefinition().IsOneOf([typeof(Result<>), typeof(Result<,>)]); /// /// Returns the underlying type argument of the specified result type. diff --git a/src/DotNext/ValueTypeExtensions.cs b/src/DotNext/ValueTypeExtensions.cs index 81a378d0f..b5cb64c89 100644 --- a/src/DotNext/ValueTypeExtensions.cs +++ b/src/DotNext/ValueTypeExtensions.cs @@ -15,46 +15,6 @@ internal static TOutput ChangeType(this TInput input) where TOutput : struct, IConvertible => (TOutput)input.ToType(typeof(TOutput), InvariantCulture); - /// - /// Checks whether the specified value is equal to one - /// of the specified values. - /// - /// - /// This method uses - /// to check equality between two values. - /// - /// The type of object to compare. - /// The value to compare with other. - /// Candidate objects. - /// , if is equal to one of . - public static bool IsOneOf(this T value, IEnumerable values) - where T : struct, IEquatable - { - foreach (var v in values) - { - if (v.Equals(value)) - return true; - } - - return false; - } - - /// - /// Checks whether the specified value is equal to one - /// of the specified values. - /// - /// - /// This method uses - /// to check equality between two values. - /// - /// The type of object to compare. - /// The value to compare with other. - /// Candidate objects. - /// , if is equal to one of . - public static bool IsOneOf(this T value, params T[] values) - where T : struct, IEquatable - => values.AsSpan().Contains(value); - /// /// Attempts to get value from nullable container. /// @@ -110,14 +70,6 @@ internal static sbyte ToSByte(this bool value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool ToBoolean(this int value) => value is not 0; - /// - /// Truncates 64-bit signed integer. - /// - /// The value to truncate. - /// if is greater than ; otherwise, cast to . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Truncate(this long value) => value > int.MaxValue ? int.MaxValue : unchecked((int)value); - /// /// Normalizes value in the specified range. /// diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs index 179438d4b..fea9c22f5 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs @@ -103,7 +103,7 @@ private async Task ProcessShuffleRequestAsync(HttpRequest request, HttpResponse { using var buffer = new PooledBufferWriter(allocator) { - Capacity = payloadLength.Truncate(), + Capacity = int.CreateSaturating(payloadLength), }; await request.BodyReader.CopyToAsync(buffer, token).ConfigureAwait(false); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs index 523466396..958ba39e7 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs @@ -183,7 +183,7 @@ protected sealed override async ValueTask LoadConfigurationAsync(CancellationTok using var buffer = new PooledBufferWriter(allocator) { - Capacity = active.IsEmpty ? bufferSize : active.Length.Truncate(), + Capacity = active.IsEmpty ? bufferSize : int.CreateSaturating(active.Length), }; // restore active configuration diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs index c0c544d21..6a92be299 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs @@ -271,7 +271,7 @@ public IAsyncBinaryReader GetReader() [RequiresUnreferencedCode("JSON deserialization may be incompatible with IL trimming")] static async ValueTask DeserializeSlowAsync(IAsyncBinaryReader reader, LogEntryMetadata metadata, Func? typeLoader, JsonSerializerOptions? options, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(metadata.Length.Truncate(), true); + using var buffer = MemoryAllocator.Allocate(int.CreateSaturating(metadata.Length), true); await reader.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return JsonLogEntry.Deserialize(IAsyncBinaryReader.Create(buffer.Memory), typeLoader, options); } @@ -325,7 +325,7 @@ public IAsyncBinaryReader GetReader() static async ValueTask DeserializeSlowAsync(IAsyncBinaryReader reader, LogEntryMetadata metadata, Func typeLoader, JsonSerializerContext context, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(metadata.Length.Truncate(), true); + using var buffer = MemoryAllocator.Allocate(int.CreateSaturating(metadata.Length), exactSize: true); await reader.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return JsonLogEntry.Deserialize(IAsyncBinaryReader.Create(buffer.Memory), typeLoader, context); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs index d1bf59c0d..110e67db4 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs @@ -277,7 +277,7 @@ ValueTask ILocalMember.ResignAsync(CancellationToken token) [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] // hot path, avoid allocations async ValueTask ILocalMember.ProposeConfigurationAsync(Func, CancellationToken, ValueTask> configurationReader, long configurationLength, long fingerprint, CancellationToken token) { - var buffer = allocator.Invoke(configurationLength.Truncate(), true); + var buffer = allocator.Invoke(int.CreateSaturating(configurationLength), exactSize: true); await configurationReader(buffer.Memory, token).ConfigureAwait(false); cachedConfig.Clear(); cachedConfig.Update(buffer, fingerprint); From deabb4a2553c3baff574c32f2f1f676d86a13d9e Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 00:34:29 +0200 Subject: [PATCH 029/155] Improved code clarity --- src/DotNext/Numerics/BitVector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNext/Numerics/BitVector.cs b/src/DotNext/Numerics/BitVector.cs index 7bcf0f5e8..d50772a38 100644 --- a/src/DotNext/Numerics/BitVector.cs +++ b/src/DotNext/Numerics/BitVector.cs @@ -44,7 +44,7 @@ public static unsafe void GetBits(this T value, Span bits) var sizeInBits = sizeof(T) * 8; ArgumentOutOfRangeException.ThrowIfLessThan((uint)bits.Length, (uint)sizeInBits, nameof(bits)); - if (Vector256.IsHardwareAccelerated && (sizeInBits & 1) is 0) + if (Vector256.IsHardwareAccelerated && int.IsEvenInteger(sizeInBits)) { Get16Bits(ref Unsafe.As(ref value), sizeof(T), ref MemoryMarshal.GetReference(bits)); } From c12420ba5d09bf33c5eb9aef22b40ee0d0ac8b0e Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 00:50:30 +0200 Subject: [PATCH 030/155] Merge ValueTypeExtensions and ObjectExtensions --- ...nsionsTests.cs => BasicExtensionsTests.cs} | 57 +++++++- src/DotNext.Tests/ValueTypeTests.cs | 59 --------- ...ueTypeExtensions.cs => BasicExtensions.cs} | 122 ++++++++++++++---- src/DotNext/Converter.cs | 2 +- src/DotNext/DelegateHelpers.Unbind.cs | 4 +- src/DotNext/EnumConverter.cs | 8 +- src/DotNext/Func.cs | 8 +- src/DotNext/ObjectExtensions.cs | 86 ------------ src/DotNext/Predicate.cs | 6 +- src/DotNext/UserDataStorage.cs | 4 +- .../Http/HttpPeerController.Disconnect.cs | 2 +- .../Http/HttpPeerController.Neighbor.cs | 2 +- .../Raft/PersistentState.NodeState.cs | 2 +- .../ProtocolStreamExtensions.cs | 2 +- .../Server.LogEntryProducer.cs | 2 +- .../Datagram/ResignExchange.cs | 2 +- .../Datagram/SynchronizeExchange.cs | 2 +- .../Raft/TransportServices/Result.cs | 2 +- .../Net/EndPointFormatter.cs | 2 +- 19 files changed, 176 insertions(+), 198 deletions(-) rename src/DotNext.Tests/{ObjectExtensionsTests.cs => BasicExtensionsTests.cs} (77%) rename src/DotNext/{ValueTypeExtensions.cs => BasicExtensions.cs} (55%) delete mode 100644 src/DotNext/ObjectExtensions.cs diff --git a/src/DotNext.Tests/ObjectExtensionsTests.cs b/src/DotNext.Tests/BasicExtensionsTests.cs similarity index 77% rename from src/DotNext.Tests/ObjectExtensionsTests.cs rename to src/DotNext.Tests/BasicExtensionsTests.cs index 042028246..6c38bdda9 100644 --- a/src/DotNext.Tests/ObjectExtensionsTests.cs +++ b/src/DotNext.Tests/BasicExtensionsTests.cs @@ -1,6 +1,6 @@ namespace DotNext; -public sealed class ObjectExtensionsTests : Test +public sealed class BasicExtensionsTests : Test { private sealed class UserDataSupport : UserDataStorage.IContainer { @@ -164,4 +164,59 @@ public static void CaptureData() Equal(45U, capturedData[slot4.ToString()]); Equal((sbyte)46, capturedData[slot5.ToString()]); } + + [Fact] + public static void OneOfValues() + { + True(2.IsOneOf([2, 5, 7])); + False(2.IsOneOf([3, 5, 7])); + } + + [Fact] + public static void NormalizeToFloatingPoint() + { + Equal(1F, int.MaxValue.Normalize(int.MinValue, int.MaxValue)); + Equal(-1F, int.MinValue.Normalize(int.MinValue, int.MaxValue)); + + Equal(1D, int.MaxValue.Normalize(int.MinValue, int.MaxValue)); + Equal(-1D, int.MinValue.Normalize(int.MinValue, int.MaxValue)); + } + + [Fact] + public static void WeightOfUInt64() + { + var weight = 0UL.Normalize(); + Equal(0D, weight); + + weight = ulong.MaxValue.Normalize(); + Equal(0.9999999999999999D, weight); + + weight = (ulong.MaxValue - 1UL).Normalize(); + Equal(0.9999999999999998D, weight); + } + + [Fact] + public static void WeightOfInt64() + { + Equal(unchecked((ulong)long.MaxValue).Normalize(), long.MaxValue.Normalize()); + } + + [Fact] + public static void WeightOfUInt32() + { + var weight = 0U.Normalize(); + Equal(0F, weight); + + weight = uint.MaxValue.Normalize(); + Equal(0.99999994F, weight); + + weight = (uint.MaxValue - 1U).Normalize(); + Equal(0.9999999F, weight); + } + + [Fact] + public static void WeightOfInt32() + { + Equal(unchecked((uint)int.MaxValue).Normalize(), int.MaxValue.Normalize()); + } } \ No newline at end of file diff --git a/src/DotNext.Tests/ValueTypeTests.cs b/src/DotNext.Tests/ValueTypeTests.cs index 9e885efab..83dae5962 100644 --- a/src/DotNext.Tests/ValueTypeTests.cs +++ b/src/DotNext.Tests/ValueTypeTests.cs @@ -131,63 +131,4 @@ public static void CustomHashCode() result = BitwiseComparer.GetHashCode(new Guid(), 0, static (data, hash) => hash + 1, true); Equal(5, result); } - - [Fact] - public static void OneOfValues() - { - True(2.IsOneOf([2, 5, 7])); - False(2.IsOneOf([3, 5, 7])); - } - - [Fact] - public static void NormalizeToSingle() - { - Equal(1F, int.MaxValue.NormalizeToSingle(int.MinValue, int.MaxValue)); - Equal(-1F, int.MinValue.NormalizeToSingle(int.MinValue, int.MaxValue)); - } - - [Fact] - public static void NormalizeToDouble() - { - Equal(1F, int.MaxValue.NormalizeToDouble(int.MinValue, int.MaxValue)); - Equal(-1F, int.MinValue.NormalizeToDouble(int.MinValue, int.MaxValue)); - } - - [Fact] - public static void WeightOfUInt64() - { - var weight = 0UL.Normalize(); - Equal(0D, weight); - - weight = ulong.MaxValue.Normalize(); - Equal(0.9999999999999999D, weight); - - weight = (ulong.MaxValue - 1UL).Normalize(); - Equal(0.9999999999999998D, weight); - } - - [Fact] - public static void WeightOfInt64() - { - Equal(unchecked((ulong)long.MaxValue).Normalize(), long.MaxValue.Normalize()); - } - - [Fact] - public static void WeightOfUInt32() - { - var weight = 0U.Normalize(); - Equal(0F, weight); - - weight = uint.MaxValue.Normalize(); - Equal(0.99999994F, weight); - - weight = (uint.MaxValue - 1U).Normalize(); - Equal(0.9999999F, weight); - } - - [Fact] - public static void WeightOfInt32() - { - Equal(unchecked((uint)int.MaxValue).Normalize(), int.MaxValue.Normalize()); - } } \ No newline at end of file diff --git a/src/DotNext/ValueTypeExtensions.cs b/src/DotNext/BasicExtensions.cs similarity index 55% rename from src/DotNext/ValueTypeExtensions.cs rename to src/DotNext/BasicExtensions.cs index b5cb64c89..1b11f8ccd 100644 --- a/src/DotNext/ValueTypeExtensions.cs +++ b/src/DotNext/BasicExtensions.cs @@ -1,3 +1,4 @@ +using System.Numerics; using System.Runtime.CompilerServices; using static System.Globalization.CultureInfo; using static InlineIL.IL; @@ -6,10 +7,85 @@ namespace DotNext; /// -/// Various extensions for value types. +/// Various extension methods for core data types. /// -public static class ValueTypeExtensions +public static class BasicExtensions { + internal static bool IsNull(object? obj) => obj is null; + + internal static bool IsNotNull(object? obj) => obj is not null; + + internal static bool IsTypeOf(object? obj) => obj is T; + + internal static TOutput Identity(TInput input) + where TInput : TOutput + => input; + + /// + /// Provides ad-hoc approach to associate some data with the object + /// without modification of it. + /// + /// + /// This method allows to associate arbitrary user data with any object. + /// User data storage is not a part of object type declaration. + /// Modification of user data doesn't cause modification of internal state of the object. + /// The storage is associated with the object reference. + /// Any user data are transient and can't be passed across process boundaries (i.e. serialization is not supported). + /// + /// The type of the object. + /// Target object. + /// User data storage. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UserDataStorage GetUserData(this T obj) + where T : class + => new(obj); + + /// + /// Checks whether the specified object is equal to one + /// of the specified objects. + /// + /// + /// This method uses + /// to check equality between two objects. + /// + /// The type of object to compare. + /// The object to compare with other. + /// Candidate objects. + /// , if is equal to one of . + public static bool IsOneOf(this T value, ReadOnlySpan candidates) + { + foreach (var other in candidates) + { + if (EqualityComparer.Default.Equals(value, other)) + return true; + } + + return false; + } + + internal static bool IsContravariant(object? obj, Type type) => obj?.GetType().IsAssignableFrom(type) ?? false; + + /// + /// Reinterprets object reference. + /// + /// + /// This method can be used to get access to the explicitly implemented + /// interface methods. + /// + /// + /// + /// MemoryManager<T> manager; + /// manager.As<IDisposable>().Dispose(); + /// + /// + /// The target type. + /// The object reference to reinterpret. + /// The reinterpreted reference. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T As(this T obj) + where T : class? + => obj; + internal static TOutput ChangeType(this TInput input) where TInput : struct, IConvertible where TOutput : struct, IConvertible @@ -73,37 +149,29 @@ internal static sbyte ToSByte(this bool value) /// /// Normalizes value in the specified range. /// - /// The type of the value to be normalized. + /// The type of the input value and bounds. + /// The type of normalized value. /// The value to be normalized. Must be in range [min..max]. /// The lower bound of the value. /// The upper bound of the value. /// The normalized value in range [-1..1] for signed value and [0..1] for unsigned value. - [CLSCompliant(false)] - public static float NormalizeToSingle(this T value, T min, T max) - where T : struct, IConvertible, IComparable + public static TOutput Normalize(this TInput value, TInput min, TInput max) + where TInput : struct, INumberBase, IComparisonOperators + where TOutput : struct, IFloatingPoint { - var v = value.ToSingle(InvariantCulture); - return value.CompareTo(default) > 0 ? - v / max.ToSingle(InvariantCulture) : - -v / min.ToSingle(InvariantCulture); - } + var x = TOutput.CreateChecked(value); + TInput y; + if (value > TInput.Zero) + { + y = max; + } + else + { + y = min; + x = -x; + } - /// - /// Normalizes value in the specified range. - /// - /// The type of the value to be normalized. - /// The value to be normalized. Must be in range [min..max]. - /// The lower bound of the value. - /// The upper bound of the value. - /// The normalized value in range [-1..1] for signed value and [0..1] for unsigned value. - [CLSCompliant(false)] - public static double NormalizeToDouble(this T value, T min, T max) - where T : struct, IConvertible, IComparable - { - var v = value.ToDouble(InvariantCulture); - return value.CompareTo(default) > 0 ? - v / max.ToDouble(InvariantCulture) : - -v / min.ToDouble(InvariantCulture); + return x / TOutput.CreateChecked(y); } /// diff --git a/src/DotNext/Converter.cs b/src/DotNext/Converter.cs index bb66e2016..e64fe9f22 100644 --- a/src/DotNext/Converter.cs +++ b/src/DotNext/Converter.cs @@ -20,7 +20,7 @@ public static class Converter /// public static Converter Identity() where TInput : TOutput - => ObjectExtensions.Identity; + => BasicExtensions.Identity; /// /// The converter which returns input argument diff --git a/src/DotNext/DelegateHelpers.Unbind.cs b/src/DotNext/DelegateHelpers.Unbind.cs index 878ce94e9..17f338f51 100644 --- a/src/DotNext/DelegateHelpers.Unbind.cs +++ b/src/DotNext/DelegateHelpers.Unbind.cs @@ -6,8 +6,8 @@ private static T Unbind(this Delegate del, Type targetType) where T : MulticastDelegate => del.Target switch { - Closure closure when ObjectExtensions.IsContravariant(closure.Target, targetType) => ChangeType(closure.Delegate, default), - object target when ObjectExtensions.IsContravariant(target, targetType) => ChangeType(del, default), + Closure closure when BasicExtensions.IsContravariant(closure.Target, targetType) => ChangeType(closure.Delegate, default), + object target when BasicExtensions.IsContravariant(target, targetType) => ChangeType(del, default), _ => throw new InvalidOperationException(), }; diff --git a/src/DotNext/EnumConverter.cs b/src/DotNext/EnumConverter.cs index d50518aa0..5154212e5 100644 --- a/src/DotNext/EnumConverter.cs +++ b/src/DotNext/EnumConverter.cs @@ -97,7 +97,7 @@ public static T ToEnum(this short value) TypeCode.Empty => default(T), TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(ValueTypeExtensions.ToBoolean(value)), + TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), TypeCode.Int16 => ReinterpretCast(value), @@ -124,7 +124,7 @@ public static T ToEnum(this byte value) TypeCode.Empty => default(T), TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(ValueTypeExtensions.ToBoolean(value)), + TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), TypeCode.Byte => ReinterpretCast(value), TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), TypeCode.Int16 => ReinterpretCast(value), @@ -152,7 +152,7 @@ public static T ToEnum(this sbyte value) TypeCode.Empty => default(T), TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(ValueTypeExtensions.ToBoolean(value)), + TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), TypeCode.SByte => ReinterpretCast(value), TypeCode.Int16 => ReinterpretCast(value), @@ -180,7 +180,7 @@ public static T ToEnum(this ushort value) TypeCode.Empty => default(T), TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(ValueTypeExtensions.ToBoolean(value)), + TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), TypeCode.Int16 => ReinterpretCast(Convert.ToInt16(value)), diff --git a/src/DotNext/Func.cs b/src/DotNext/Func.cs index 17e7ecdf6..ec5448d0b 100644 --- a/src/DotNext/Func.cs +++ b/src/DotNext/Func.cs @@ -13,7 +13,7 @@ public static class Func /// /// The target type. /// The predicate instance. - public static Func IsTypeOf() => ObjectExtensions.IsTypeOf; + public static Func IsTypeOf() => BasicExtensions.IsTypeOf; /// /// Returns predicate implementing nullability check. @@ -25,7 +25,7 @@ public static class Func /// public static Func IsNull() where T : class? - => ObjectExtensions.IsNull; + => BasicExtensions.IsNull; /// /// Returns predicate checking that input argument @@ -38,7 +38,7 @@ public static Func IsNull() /// public static Func IsNotNull() where T : class? - => ObjectExtensions.IsNotNull; + => BasicExtensions.IsNotNull; /// /// The function which returns input argument @@ -52,7 +52,7 @@ public static Func IsNotNull() /// public static Func Identity() where TInput : TOutput - => ObjectExtensions.Identity; + => BasicExtensions.Identity; /// /// The converter which returns input argument diff --git a/src/DotNext/ObjectExtensions.cs b/src/DotNext/ObjectExtensions.cs deleted file mode 100644 index 100015331..000000000 --- a/src/DotNext/ObjectExtensions.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext; - -using Intrinsics = Runtime.Intrinsics; - -/// -/// Various extension methods for reference types. -/// -public static class ObjectExtensions -{ - internal static bool IsNull(object? obj) => obj is null; - - internal static bool IsNotNull(object? obj) => obj is not null; - - internal static bool IsTypeOf(object? obj) => obj is T; - - internal static TOutput Identity(TInput input) - where TInput : TOutput - => input; - - /// - /// Provides ad-hoc approach to associate some data with the object - /// without modification of it. - /// - /// - /// This method allows to associate arbitrary user data with any object. - /// User data storage is not a part of object type declaration. - /// Modification of user data doesn't cause modification of internal state of the object. - /// The storage is associated with the object reference. - /// Any user data are transient and can't be passed across process boundaries (i.e. serialization is not supported). - /// - /// The type of the object. - /// Target object. - /// User data storage. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static UserDataStorage GetUserData(this T obj) - where T : class - => new(obj); - - /// - /// Checks whether the specified object is equal to one - /// of the specified objects. - /// - /// - /// This method uses - /// to check equality between two objects. - /// - /// The type of object to compare. - /// The object to compare with other. - /// Candidate objects. - /// , if is equal to one of . - public static bool IsOneOf(this T value, ReadOnlySpan candidates) - { - foreach (var other in candidates) - { - if (EqualityComparer.Default.Equals(value, other)) - return true; - } - - return false; - } - - internal static bool IsContravariant(object? obj, Type type) => obj?.GetType().IsAssignableFrom(type) ?? false; - - /// - /// Reinterprets object reference. - /// - /// - /// This method can be used to get access to the explicitly implemented - /// interface methods. - /// - /// - /// - /// MemoryManager<T> manager; - /// manager.As<IDisposable>().Dispose(); - /// - /// - /// The target type. - /// The object reference to reinterpret. - /// The reinterpreted reference. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T As(this T obj) - where T : class? - => obj; -} \ No newline at end of file diff --git a/src/DotNext/Predicate.cs b/src/DotNext/Predicate.cs index b59ac422b..8cb5e3a87 100644 --- a/src/DotNext/Predicate.cs +++ b/src/DotNext/Predicate.cs @@ -13,7 +13,7 @@ public static class Predicate /// /// The target type. /// The predicate instance. - public static Predicate IsTypeOf() => ObjectExtensions.IsTypeOf; + public static Predicate IsTypeOf() => BasicExtensions.IsTypeOf; /// /// Returns predicate implementing nullability check. @@ -25,7 +25,7 @@ public static class Predicate /// public static Predicate IsNull() where T : class? - => ObjectExtensions.IsNull; + => BasicExtensions.IsNull; /// /// Returns predicate checking that input argument @@ -38,7 +38,7 @@ public static Predicate IsNull() /// public static Predicate IsNotNull() where T : class? - => ObjectExtensions.IsNotNull; + => BasicExtensions.IsNotNull; /// /// Returns predicate checking that input argument of value type diff --git a/src/DotNext/UserDataStorage.cs b/src/DotNext/UserDataStorage.cs index b177d2b92..0ba165a20 100644 --- a/src/DotNext/UserDataStorage.cs +++ b/src/DotNext/UserDataStorage.cs @@ -15,10 +15,10 @@ public readonly ref partial struct UserDataStorage { /// /// Implementation of this interface allows to customize behavior of - /// method. + /// method. /// /// - /// If runtime type of object passed to method + /// If runtime type of object passed to method /// provides implementation of this interface then actual /// depends on the implementation. /// It is recommended to implement this interface explicitly. diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs index 16d560818..e4c4dd992 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs @@ -35,7 +35,7 @@ private async Task ProcessDisconnectAsync(HttpRequest request, HttpResponse resp } private static (EndPoint, bool) DeserializeDisconnectRequest(ref SequenceReader reader) - => (reader.ReadEndPoint(), ValueTypeExtensions.ToBoolean(reader.Read())); + => (reader.ReadEndPoint(), BasicExtensions.ToBoolean(reader.Read())); private static (EndPoint, bool) DeserializeDisconnectRequest(ReadOnlyMemory buffer) { diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs index d3d161500..1d1400cf0 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs @@ -37,7 +37,7 @@ private async Task ProcessNeighborAsync(HttpRequest request, HttpResponse respon } private static (EndPoint, bool) DeserializeNeighborRequest(ref SequenceReader reader) - => (reader.ReadEndPoint(), ValueTypeExtensions.ToBoolean(reader.Read())); + => (reader.ReadEndPoint(), BasicExtensions.ToBoolean(reader.Read())); private static (EndPoint, bool) DeserializeNeighborRequest(ReadOnlyMemory buffer) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs index cfde6089a..7e6c454d2 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs @@ -88,7 +88,7 @@ private NodeState(string fileName, MemoryAllocator allocator, bool integri lastIndex = ReadInt64LittleEndian(bufferSpan.Slice(LastIndexOffset)); lastApplied = ReadInt64LittleEndian(bufferSpan.Slice(LastAppliedOffset)); snapshot = new(bufferSpan.Slice(SnapshotMetadataOffset)); - if (ValueTypeExtensions.ToBoolean(bufferSpan[LastVotePresenceOffset])) + if (BasicExtensions.ToBoolean(bufferSpan[LastVotePresenceOffset])) votedFor = BoxedClusterMemberId.Box(new ClusterMemberId(bufferSpan.Slice(LastVoteOffset))); this.integrityCheck = integrityCheck; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs index cfcc1bbc2..4838dc569 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs @@ -65,7 +65,7 @@ internal static ValueTask WriteBoolAsync(this ProtocolStream protocol, bool valu internal static async ValueTask ReadBoolAsync(this ProtocolStream protocol, CancellationToken token) { await protocol.ReadAsync(sizeof(byte), token).ConfigureAwait(false); - return ValueTypeExtensions.ToBoolean(protocol.WrittenBufferSpan[0]); + return BasicExtensions.ToBoolean(protocol.WrittenBufferSpan[0]); } internal static ValueTask WriteNullableInt64Async(this ProtocolStream protocol, in long? value, CancellationToken token) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs index 553f731f4..55ab3bed9 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs @@ -30,7 +30,7 @@ internal ReceivedLogEntries(ProtocolStream stream, MemoryAllocator allocat var reader = new SpanReader(stream.WrittenBufferSpan); (Id, Term, PrevLogIndex, PrevLogTerm, CommitIndex, entriesCount) = AppendEntriesMessage.Read(ref reader); - ApplyConfig = ValueTypeExtensions.ToBoolean(reader.Read()); + ApplyConfig = BasicExtensions.ToBoolean(reader.Read()); var fingerprint = reader.ReadInt64(true); var configLength = reader.ReadInt64(true); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs index 27223fc3a..40ead4d31 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs @@ -14,7 +14,7 @@ internal ResignExchange() public override ValueTask ProcessInboundMessageAsync(PacketHeaders headers, ReadOnlyMemory payload, CancellationToken token) { Debug.Assert(headers.Control == FlowControl.Ack); - TrySetResult(ValueTypeExtensions.ToBoolean(payload.Span[0])); + TrySetResult(BasicExtensions.ToBoolean(payload.Span[0])); return new(false); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs index f7b0aa837..64aeab1cf 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs @@ -18,7 +18,7 @@ public override ValueTask ProcessInboundMessageAsync(PacketHeaders headers Debug.Assert(headers.Control == FlowControl.Ack); var reader = new SpanReader(payload.Span); - var hasValue = ValueTypeExtensions.ToBoolean(reader.Read()); + var hasValue = BasicExtensions.ToBoolean(reader.Read()); var commitIndex = reader.ReadInt64(true); TrySetResult(hasValue ? commitIndex : null); return new(false); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs index 3812b08df..16e431630 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs @@ -48,7 +48,7 @@ internal static int WriteHeartbeatResult(Span output, in Result Read(ref SpanReader reader) => new() { Term = reader.ReadInt64(true), - Value = ValueTypeExtensions.ToBoolean(reader.Read()), + Value = BasicExtensions.ToBoolean(reader.Read()), }; internal static Result Read(ReadOnlySpan input) diff --git a/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs b/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs index 950381669..8eb47572c 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs @@ -213,7 +213,7 @@ private static DnsEndPoint DeserializeHost(ref SequenceReader reader) private static HttpEndPoint DeserializeHttp(ref SequenceReader reader) { - var secure = ValueTypeExtensions.ToBoolean(reader.Read()); + var secure = BasicExtensions.ToBoolean(reader.Read()); DeserializeHost(ref reader, out var hostName, out var port, out var family); return new(hostName, port, secure, family); } From a7279c5a3ae9db4401b6777eab1accd9467f33f3 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 00:55:16 +0200 Subject: [PATCH 031/155] Migration to throw helpers --- src/DotNext/DelegateHelpers.PtrToDelegate.cs | 111 +++++++------------ 1 file changed, 37 insertions(+), 74 deletions(-) diff --git a/src/DotNext/DelegateHelpers.PtrToDelegate.cs b/src/DotNext/DelegateHelpers.PtrToDelegate.cs index b070d537a..57f841617 100644 --- a/src/DotNext/DelegateHelpers.PtrToDelegate.cs +++ b/src/DotNext/DelegateHelpers.PtrToDelegate.cs @@ -21,8 +21,7 @@ public static partial class DelegateHelpers [CLSCompliant(false)] public static unsafe RefAction CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -45,8 +44,7 @@ public static unsafe RefAction CreateDelegate(delegate* CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -67,8 +65,7 @@ public static unsafe RefAction CreateDelegate(deleg [CLSCompliant(false)] public static unsafe RefFunc CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -92,8 +89,7 @@ public static unsafe RefFunc CreateDelegate CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -111,8 +107,7 @@ public static unsafe RefFunc CreateDelegate ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -133,8 +128,7 @@ public static unsafe Action CreateDelegate(delegate* ptr) public static unsafe Action CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -153,8 +147,7 @@ public static unsafe Action CreateDelegate(delegate* ptr, T obj) [CLSCompliant(false)] public static unsafe Action CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -176,8 +169,7 @@ public static unsafe Action CreateDelegate(delegate* ptr) public static unsafe Action CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -196,8 +188,7 @@ public static unsafe Action CreateDelegate(delegate* ptr [CLSCompliant(false)] public static unsafe Func CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -219,8 +210,7 @@ public static unsafe Func CreateDelegate(delegate* pt public static unsafe Func CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -240,8 +230,7 @@ public static unsafe Func CreateDelegate(delegate* CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -264,8 +253,7 @@ public static unsafe Action CreateDelegate(delegate* CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -276,8 +264,7 @@ public static unsafe Action CreateDelegate(delegate* CreateConverter(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -297,8 +284,7 @@ internal static unsafe Converter CreateConverter CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -321,8 +307,7 @@ public static unsafe Func CreateDelegate(delegate* CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -343,8 +328,7 @@ public static unsafe Func CreateDelegate(delega [CLSCompliant(false)] public static unsafe Action CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -368,8 +352,7 @@ public static unsafe Action CreateDelegate(delegate* CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -390,8 +373,7 @@ public static unsafe Action CreateDelegate(delegate*< [CLSCompliant(false)] public static unsafe Func CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -415,8 +397,7 @@ public static unsafe Func CreateDelegate(deleg public static unsafe Func CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -438,8 +419,7 @@ public static unsafe Func CreateDelegate(de [CLSCompliant(false)] public static unsafe Action CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -464,8 +444,7 @@ public static unsafe Action CreateDelegate(deleg public static unsafe Action CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -487,8 +466,7 @@ public static unsafe Action CreateDelegate(de [CLSCompliant(false)] public static unsafe Func CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -513,8 +491,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -537,8 +514,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -564,8 +540,7 @@ public static unsafe Action CreateDelegate CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -588,8 +563,7 @@ public static unsafe Action CreateDelegate CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -615,8 +589,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -640,8 +613,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -668,8 +640,7 @@ public static unsafe Action CreateDelegate CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -693,8 +664,7 @@ public static unsafe Action CreateDelegate CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -721,8 +691,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -747,8 +716,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate* ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -776,8 +744,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate* ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -797,8 +764,7 @@ public static unsafe Func CreateDelegate CreateDelegate(delegate*, TArg, void> ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -821,8 +787,7 @@ public static unsafe ReadOnlySpanAction CreateDelegate(delegat public static unsafe ReadOnlySpanAction CreateDelegate(delegate*, TArg, void> ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); @@ -842,8 +807,7 @@ public static unsafe ReadOnlySpanAction CreateDelegate CreateDelegate(delegate*, TArg, void> ptr) { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Ldnull(); Push(ptr); @@ -866,8 +830,7 @@ public static unsafe SpanAction CreateDelegate(delegate* CreateDelegate(delegate*, TArg, void> ptr, T obj) where T : class? { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); + ArgumentNullException.ThrowIfNull(ptr); Push(obj); Push(ptr); From 01e4cbdb77daa898a66260506bdd601580987a1e Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 21:18:57 +0200 Subject: [PATCH 032/155] Migration to generic math --- .../Runtime/Serialization/ISerializable.cs | 3 - .../Buffers/BufferWriterSlimTests.cs | 60 +++---- .../Buffers/SpanReaderWriterTests.cs | 56 +++---- .../Buffers/BufferHelpers.BufferWriterSlim.cs | 109 ++----------- .../Buffers/BufferHelpers.SpanReader.cs | 145 +++-------------- .../Buffers/BufferHelpers.SpanWriter.cs | 150 ++++++++---------- src/DotNext/Buffers/BufferWriterSlim.cs | 8 +- .../TransportServices/LogEntryMetadata.cs | 8 +- .../Http/HttpPeerController.ForwardJoin.cs | 2 +- .../Http/HttpPeerController.Shuffle.cs | 6 +- .../Net/Cluster/ClusterMemberId.cs | 14 +- .../Membership/ClusterConfigurationStorage.cs | 2 +- .../PersistentClusterConfigurationStorage.cs | 2 +- .../Raft/PersistentState.Internal.cs | 28 ++-- .../TransportServices/AppendEntriesMessage.cs | 20 +-- .../TransportServices/ConfigurationMessage.cs | 6 +- .../Client.AppendEntries.cs | 4 +- .../ConnectionOriented/Client.Synchronize.cs | 2 +- .../ProtocolStreamExtensions.cs | 4 +- .../Server.LogEntryProducer.cs | 6 +- .../Datagram/CorrelationId.cs | 16 +- .../Datagram/SynchronizeExchange.cs | 4 +- .../EmptyClusterConfiguration.cs | 4 +- .../TransportServices/HeartbeatMessage.cs | 16 +- .../TransportServices/LogEntryMetadata.cs | 24 ++- .../MetadataTransferObject.cs | 4 +- .../Raft/TransportServices/PreVoteMessage.cs | 12 +- .../Raft/TransportServices/Result.cs | 12 +- .../Raft/TransportServices/SnapshotMessage.cs | 8 +- .../Raft/TransportServices/VoteMessage.cs | 12 +- .../Messaging/Gossip/RumorTimestamp.cs | 8 +- .../Net/EndPointFormatter.cs | 12 +- 32 files changed, 262 insertions(+), 505 deletions(-) diff --git a/src/DotNext.IO/Runtime/Serialization/ISerializable.cs b/src/DotNext.IO/Runtime/Serialization/ISerializable.cs index 07d6afbbf..07d6ed595 100644 --- a/src/DotNext.IO/Runtime/Serialization/ISerializable.cs +++ b/src/DotNext.IO/Runtime/Serialization/ISerializable.cs @@ -1,5 +1,3 @@ -using System.Runtime.Versioning; - namespace DotNext.Runtime.Serialization; using IO; @@ -22,7 +20,6 @@ public interface ISerializable : IDataTransferObject /// The token that can be used to cancel the operation. /// The decoded object. /// The operation has been canceled. - [RequiresPreviewFeatures] public static abstract ValueTask ReadFromAsync(TReader reader, CancellationToken token) where TReader : notnull, IAsyncBinaryReader; } \ No newline at end of file diff --git a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs index 25cdad4ae..f639ad7e0 100644 --- a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs +++ b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs @@ -109,44 +109,32 @@ public static void ReadWritePrimitives() var builder = new BufferWriterSlim(stackalloc byte[512]); try { - builder.WriteInt16(short.MinValue, true); - builder.WriteInt16(short.MaxValue, false); - builder.WriteUInt16(42, true); - builder.WriteUInt16(ushort.MaxValue, false); - builder.WriteInt32(int.MaxValue, true); - builder.WriteInt32(int.MinValue, false); - builder.WriteUInt32(42, true); - builder.WriteUInt32(uint.MaxValue, false); - builder.WriteInt64(long.MaxValue, true); - builder.WriteInt64(long.MinValue, false); - builder.WriteUInt64(42, true); - builder.WriteUInt64(ulong.MaxValue, false); - builder.WriteSingle(float.MaxValue, true); - builder.WriteSingle(float.MinValue, false); - builder.WriteDouble(double.MaxValue, true); - builder.WriteDouble(double.MinValue, false); - builder.WriteHalf(Half.MaxValue, true); - builder.WriteHalf(Half.MinValue, false); + builder.WriteLittleEndian(short.MinValue); + builder.WriteBigEndian(short.MaxValue); + builder.WriteLittleEndian(42); + builder.WriteBigEndian(ushort.MaxValue); + builder.WriteLittleEndian(int.MaxValue); + builder.WriteBigEndian(int.MinValue); + builder.WriteLittleEndian(42U); + builder.WriteBigEndian(uint.MaxValue); + builder.WriteLittleEndian(long.MaxValue); + builder.WriteBigEndian(long.MinValue); + builder.WriteLittleEndian(42UL); + builder.WriteBigEndian(ulong.MaxValue); var reader = new SpanReader(builder.WrittenSpan); - Equal(short.MinValue, reader.ReadInt16(true)); - Equal(short.MaxValue, reader.ReadInt16(false)); - Equal(42, reader.ReadUInt16(true)); - Equal(ushort.MaxValue, reader.ReadUInt16(false)); - Equal(int.MaxValue, reader.ReadInt32(true)); - Equal(int.MinValue, reader.ReadInt32(false)); - Equal(42U, reader.ReadUInt32(true)); - Equal(uint.MaxValue, reader.ReadUInt32(false)); - Equal(long.MaxValue, reader.ReadInt64(true)); - Equal(long.MinValue, reader.ReadInt64(false)); - Equal(42UL, reader.ReadUInt64(true)); - Equal(ulong.MaxValue, reader.ReadUInt64(false)); - Equal(float.MaxValue, reader.ReadSingle(true)); - Equal(float.MinValue, reader.ReadSingle(false)); - Equal(double.MaxValue, reader.ReadDouble(true)); - Equal(double.MinValue, reader.ReadDouble(false)); - Equal(Half.MaxValue, reader.ReadHalf(true)); - Equal(Half.MinValue, reader.ReadHalf(false)); + Equal(short.MinValue, reader.ReadLittleEndian(isUnsigned: false)); + Equal(short.MaxValue, reader.ReadBigEndian(isUnsigned: false)); + Equal(42, reader.ReadLittleEndian(isUnsigned: true)); + Equal(ushort.MaxValue, reader.ReadBigEndian(isUnsigned: true)); + Equal(int.MaxValue, reader.ReadLittleEndian(isUnsigned: false)); + Equal(int.MinValue, reader.ReadBigEndian(isUnsigned: false)); + Equal(42U, reader.ReadLittleEndian(isUnsigned: true)); + Equal(uint.MaxValue, reader.ReadBigEndian(isUnsigned: true)); + Equal(long.MaxValue, reader.ReadLittleEndian(isUnsigned: false)); + Equal(long.MinValue, reader.ReadBigEndian(isUnsigned: false)); + Equal(42UL, reader.ReadLittleEndian(isUnsigned: true)); + Equal(ulong.MaxValue, reader.ReadBigEndian(isUnsigned: true)); } finally { diff --git a/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs b/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs index fb618a214..e898e81a4 100644 --- a/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs +++ b/src/DotNext.Tests/Buffers/SpanReaderWriterTests.cs @@ -183,40 +183,32 @@ public static void ReadWritePrimitives() { var buffer = new byte[1024]; var writer = new SpanWriter(buffer); - writer.WriteInt16(short.MinValue, true); - writer.WriteInt16(short.MaxValue, false); - writer.WriteUInt16(42, true); - writer.WriteUInt16(ushort.MaxValue, false); - writer.WriteInt32(int.MaxValue, true); - writer.WriteInt32(int.MinValue, false); - writer.WriteUInt32(42, true); - writer.WriteUInt32(uint.MaxValue, false); - writer.WriteInt64(long.MaxValue, true); - writer.WriteInt64(long.MinValue, false); - writer.WriteUInt64(42, true); - writer.WriteUInt64(ulong.MaxValue, false); - writer.WriteSingle(float.MaxValue, true); - writer.WriteSingle(float.MinValue, false); - writer.WriteDouble(double.MaxValue, true); - writer.WriteDouble(double.MinValue, false); + writer.WriteLittleEndian(short.MinValue); + writer.WriteBigEndian(short.MaxValue); + writer.WriteLittleEndian(42); + writer.WriteBigEndian(ushort.MaxValue); + writer.WriteLittleEndian(int.MaxValue); + writer.WriteBigEndian(int.MinValue); + writer.WriteLittleEndian(42U); + writer.WriteBigEndian(uint.MaxValue); + writer.WriteLittleEndian(long.MaxValue); + writer.WriteBigEndian(long.MinValue); + writer.WriteLittleEndian(42UL); + writer.WriteBigEndian(ulong.MaxValue); var reader = new SpanReader(buffer); - Equal(short.MinValue, reader.ReadInt16(true)); - Equal(short.MaxValue, reader.ReadInt16(false)); - Equal(42, reader.ReadUInt16(true)); - Equal(ushort.MaxValue, reader.ReadUInt16(false)); - Equal(int.MaxValue, reader.ReadInt32(true)); - Equal(int.MinValue, reader.ReadInt32(false)); - Equal(42U, reader.ReadUInt32(true)); - Equal(uint.MaxValue, reader.ReadUInt32(false)); - Equal(long.MaxValue, reader.ReadInt64(true)); - Equal(long.MinValue, reader.ReadInt64(false)); - Equal(42UL, reader.ReadUInt64(true)); - Equal(ulong.MaxValue, reader.ReadUInt64(false)); - Equal(float.MaxValue, reader.ReadSingle(true)); - Equal(float.MinValue, reader.ReadSingle(false)); - Equal(double.MaxValue, reader.ReadDouble(true)); - Equal(double.MinValue, reader.ReadDouble(false)); + Equal(short.MinValue, reader.ReadLittleEndian(isUnsigned: false)); + Equal(short.MaxValue, reader.ReadBigEndian(isUnsigned: false)); + Equal(42U, reader.ReadLittleEndian(isUnsigned: true)); + Equal(ushort.MaxValue, reader.ReadBigEndian(isUnsigned: true)); + Equal(int.MaxValue, reader.ReadLittleEndian(isUnsigned: false)); + Equal(int.MinValue, reader.ReadBigEndian(isUnsigned: false)); + Equal(42U, reader.ReadLittleEndian(isUnsigned: true)); + Equal(uint.MaxValue, reader.ReadBigEndian(isUnsigned: true)); + Equal(long.MaxValue, reader.ReadLittleEndian(isUnsigned: false)); + Equal(long.MinValue, reader.ReadBigEndian(isUnsigned: false)); + Equal(42UL, reader.ReadLittleEndian(isUnsigned: true)); + Equal(ulong.MaxValue, reader.ReadBigEndian(isUnsigned: true)); } [Fact] diff --git a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs index 91778fed2..fd00fc375 100644 --- a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs +++ b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs @@ -1,109 +1,30 @@ +using System.Numerics; using System.Runtime.CompilerServices; using System.Text; -using static System.Buffers.Binary.BinaryPrimitives; namespace DotNext.Buffers; public static partial class BufferHelpers { - [SkipLocalsInit] - private static unsafe void Write(ref BufferWriterSlim builder, delegate*, T, void> encoder, T value) - where T : unmanaged - { - Span memory = stackalloc byte[sizeof(T)]; - encoder(memory, value); - builder.Write(memory); - } - - /// - /// Encodes 16-bit signed integer as bytes. - /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteInt16(this ref BufferWriterSlim builder, short value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteInt16LittleEndian : &WriteInt16BigEndian, value); - - /// - /// Encodes 16-bit unsigned integer as bytes. - /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe void WriteUInt16(this ref BufferWriterSlim builder, ushort value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteUInt16LittleEndian : &WriteUInt16BigEndian, value); - - /// - /// Encodes 32-bit signed integer as bytes. - /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteInt32(this ref BufferWriterSlim builder, int value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteInt32LittleEndian : &WriteInt32BigEndian, value); - - /// - /// Encodes 32-bit unsigned integer as bytes. - /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe void WriteUInt32(this ref BufferWriterSlim builder, uint value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteUInt32LittleEndian : &WriteUInt32BigEndian, value); - /// - /// Encodes 64-bit signed integer as bytes. + /// Encodes a number as little-endian format. /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void WriteInt64(this ref BufferWriterSlim builder, long value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteInt64LittleEndian : &WriteInt64BigEndian, value); - - /// - /// Encodes 64-bit unsigned integer as bytes. - /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe void WriteUInt64(this ref BufferWriterSlim builder, ulong value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteUInt64LittleEndian : &WriteUInt64BigEndian, value); - - /// - /// Encodes single-precision floating-point number as bytes. - /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - public static unsafe void WriteSingle(this ref BufferWriterSlim builder, float value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteSingleLittleEndian : &WriteSingleBigEndian, value); - - /// - /// Encodes double-precision floating-point number as bytes. - /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - public static unsafe void WriteDouble(this ref BufferWriterSlim builder, double value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteDoubleLittleEndian : &WriteDoubleBigEndian, value); + /// The type of the value. + /// The byte buffer. + /// The value to encode. + public static void WriteLittleEndian(this ref BufferWriterSlim builder, T value) + where T : unmanaged, IBinaryInteger + => builder.Advance(value.WriteLittleEndian(builder.GetSpan(value.GetByteCount()))); /// - /// Encodes half-precision floating-point number as bytes. + /// Encodes a number as big-endian format. /// - /// The buffer writer. - /// The value to be encoded. - /// to use little-endian encoding; to use big-endian encoding. - public static unsafe void WriteHalf(this ref BufferWriterSlim builder, Half value, bool isLittleEndian) - => Write(ref builder, isLittleEndian ? &WriteHalfLittleEndian : &WriteHalfBigEndian, value); + /// The type of the value. + /// The byte buffer. + /// The value to encode. + public static void WriteBigEndian(this ref BufferWriterSlim builder, T value) + where T : unmanaged, IBinaryInteger + => builder.Advance(value.WriteLittleEndian(builder.GetSpan(value.GetByteCount()))); /// /// Writes the contents of string builder to the buffer. diff --git a/src/DotNext/Buffers/BufferHelpers.SpanReader.cs b/src/DotNext/Buffers/BufferHelpers.SpanReader.cs index 0b9801cf2..79b0e129e 100644 --- a/src/DotNext/Buffers/BufferHelpers.SpanReader.cs +++ b/src/DotNext/Buffers/BufferHelpers.SpanReader.cs @@ -1,3 +1,4 @@ +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static System.Buffers.Binary.BinaryPrimitives; @@ -43,134 +44,30 @@ public static unsafe T Read(this ref SpanReader reader) => Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(reader.Read(sizeof(T)))); /// - /// Decodes 16-bit signed integer. + /// Reads the value encoded in little-endian byte order. /// + /// The type of the value to decode. /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static short ReadInt16(this ref SpanReader reader, bool isLittleEndian) - { - var result = reader.Read(); - if (isLittleEndian != BitConverter.IsLittleEndian) - result = ReverseEndianness(result); - return result; - } - - /// - /// Decodes 16-bit unsigned integer. - /// - /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ushort ReadUInt16(this ref SpanReader reader, bool isLittleEndian) - { - var result = reader.Read(); - if (isLittleEndian != BitConverter.IsLittleEndian) - result = ReverseEndianness(result); - return result; - } - - /// - /// Decodes 32-bit signed integer. - /// - /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ReadInt32(this ref SpanReader reader, bool isLittleEndian) - { - var result = reader.Read(); - if (isLittleEndian != BitConverter.IsLittleEndian) - result = ReverseEndianness(result); - return result; - } - - /// - /// Decodes 32-bit unsigned integer. - /// - /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint ReadUInt32(this ref SpanReader reader, bool isLittleEndian) - { - var result = reader.Read(); - if (isLittleEndian != BitConverter.IsLittleEndian) - result = ReverseEndianness(result); - return result; - } - - /// - /// Decodes 64-bit signed integer. - /// - /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long ReadInt64(this ref SpanReader reader, bool isLittleEndian) - { - var result = reader.Read(); - if (isLittleEndian != BitConverter.IsLittleEndian) - result = ReverseEndianness(result); - return result; - } - - /// - /// Decodes 64-bit unsigned integer. - /// - /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong ReadUInt64(this ref SpanReader reader, bool isLittleEndian) - { - var result = reader.Read(); - if (isLittleEndian != BitConverter.IsLittleEndian) - result = ReverseEndianness(result); - return result; - } + /// + /// if source represents an unsigned two's complement number; + /// otherwise, to indicate it represents a signed two's complement number. + /// + /// Decoded value. + public static unsafe T ReadLittleEndian(this ref SpanReader reader, bool isUnsigned) + where T : unmanaged, IBinaryInteger + => T.ReadLittleEndian(reader.Read(sizeof(T)), isUnsigned); /// - /// Decodes single-precision floating-point number. + /// Reads the value encoded in big-endian byte order. /// + /// The type of the value to decode. /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float ReadSingle(this ref SpanReader reader, bool isLittleEndian) - => BitConverter.Int32BitsToSingle(reader.ReadInt32(isLittleEndian)); - - /// - /// Decodes double-precision floating-point number. - /// - /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double ReadDouble(this ref SpanReader reader, bool isLittleEndian) - => BitConverter.Int64BitsToDouble(reader.ReadInt64(isLittleEndian)); - - /// - /// Decodes half-precision floating-point number. - /// - /// The memory reader. - /// to use little-endian encoding; to use big-endian encoding. - /// The decoded value. - /// The end of memory block is reached. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Half ReadHalf(this ref SpanReader reader, bool isLittleEndian) - => BitConverter.Int16BitsToHalf(reader.ReadInt16(isLittleEndian)); + /// + /// if source represents an unsigned two's complement number; + /// otherwise, to indicate it represents a signed two's complement number. + /// + /// Decoded value. + public static unsafe T ReadBigEndian(this ref SpanReader reader, bool isUnsigned) + where T : unmanaged, IBinaryInteger + => T.ReadBigEndian(reader.Read(sizeof(T)), isUnsigned); } \ No newline at end of file diff --git a/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs b/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs index af7c39778..473ff4e4d 100644 --- a/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs +++ b/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs @@ -1,3 +1,4 @@ +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -29,111 +30,65 @@ public static bool TryWrite(this ref SpanWriter writer, scoped in T val /// The blittable type. /// Remaining space in the underlying span is not enough to place all bytes. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Write(this ref SpanWriter writer, scoped in T value) + public static unsafe void Write(this ref SpanWriter writer, in T value) where T : unmanaged - => Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(writer.Slide(sizeof(T))), value); + => Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(writer.Slide(sizeof(T))), value); /// - /// Writes 16-bit signed integer to the block of memory. + /// Writes a number in little-endian byte order. /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. - /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteInt16(this ref SpanWriter writer, short value, bool isLittleEndian) - => writer.Write(isLittleEndian == BitConverter.IsLittleEndian ? value : ReverseEndianness(value)); // TODO: Replace with generic numbers in .NET 8 - - /// - /// Writes 16-bit unsigned integer to the block of memory. - /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. - /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void WriteUInt16(this ref SpanWriter writer, ushort value, bool isLittleEndian) - => writer.Write(isLittleEndian == BitConverter.IsLittleEndian ? value : ReverseEndianness(value)); - - /// - /// Writes 32-bit signed integer to the block of memory. - /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. - /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteInt32(this ref SpanWriter writer, int value, bool isLittleEndian) - => writer.Write(isLittleEndian == BitConverter.IsLittleEndian ? value : ReverseEndianness(value)); - - /// - /// Writes 32-bit unsigned integer to the block of memory. - /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. + /// The type of the value. + /// The buffer writer. + /// The value to encode. /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void WriteUInt32(this ref SpanWriter writer, uint value, bool isLittleEndian) - => writer.Write(isLittleEndian == BitConverter.IsLittleEndian ? value : ReverseEndianness(value)); + public static void WriteLittleEndian(this ref SpanWriter writer, T value) + where T : unmanaged, IBinaryInteger + => writer.Advance(value.WriteLittleEndian(writer.RemainingSpan)); /// - /// Writes 64-bit signed integer to the block of memory. + /// Attempts to write a number in little-endian byte order. /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. - /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteInt64(this ref SpanWriter writer, long value, bool isLittleEndian) - => writer.Write(isLittleEndian == BitConverter.IsLittleEndian ? value : ReverseEndianness(value)); + /// The type of the value. + /// The buffer writer. + /// The value to encode. + /// if has enough space to place the value; otherwise, . + public static bool TryWriteLittleEndian(this ref SpanWriter writer, T value) + where T : unmanaged, IBinaryInteger + { + bool result; + if (result = value.TryWriteLittleEndian(writer.RemainingSpan, out var writtenBytes)) + writer.Advance(writtenBytes); - /// - /// Writes 64-bit unsigned integer to the block of memory. - /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. - /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void WriteUInt64(this ref SpanWriter writer, ulong value, bool isLittleEndian) - => writer.Write(isLittleEndian == BitConverter.IsLittleEndian ? value : ReverseEndianness(value)); + return result; + } /// - /// Writes single-precision floating-point number to the block of memory. + /// Writes a number in big-endian byte order. /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. + /// The type of the value. + /// The buffer writer. + /// The value to encode. /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteSingle(this ref SpanWriter writer, float value, bool isLittleEndian) - => writer.WriteInt32(BitConverter.SingleToInt32Bits(value), isLittleEndian); + public static void WriteBigEndian(this ref SpanWriter writer, T value) + where T : unmanaged, IBinaryInteger + => writer.Advance(value.WriteBigEndian(writer.RemainingSpan)); /// - /// Writes double-precision floating-point number to the block of memory. + /// Attempts to write a number in big-endian byte order. /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. - /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteDouble(this ref SpanWriter writer, double value, bool isLittleEndian) - => writer.WriteInt64(BitConverter.DoubleToInt64Bits(value), isLittleEndian); + /// The type of the value. + /// The buffer writer. + /// The value to encode. + /// if has enough space to place the value; otherwise, . + public static bool TryWriteBigEndian(this ref SpanWriter writer, T value) + where T : unmanaged, IBinaryInteger + { + bool result; + if (result = value.TryWriteLittleEndian(writer.RemainingSpan, out var writtenBytes)) + writer.Advance(writtenBytes); - /// - /// Writes half-precision floating-point number to the block of memory. - /// - /// Memory writer. - /// The value to be encoded in the block of memory. - /// to use little-endian encoding; to use big-endian encoding. - /// Remaining space in the underlying span is not enough to place the value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteHalf(this ref SpanWriter writer, Half value, bool isLittleEndian) - => writer.WriteInt16(BitConverter.HalfToInt16Bits(value), isLittleEndian); + return result; + } /// /// Writes the contents of a string builder to the buffer. @@ -163,4 +118,23 @@ public static void Write(this ref SpanWriter writer, T value, scoped Re writer.Advance(writtenCount); } + + /// + /// Converts the value to a set of characters and writes them to the buffer. + /// + /// The formattable type. + /// The buffer writer. + /// The value to be converted to a set of characters. + /// The format of the value. + /// The format provider. + /// if has enough space to place the value; otherwise, . + public static bool TryWrite(this ref SpanWriter writer, T value, scoped ReadOnlySpan format = default, IFormatProvider? provider = null) + where T : notnull, ISpanFormattable + { + bool result; + if (result = value.TryFormat(writer.RemainingSpan, out var writtenCount, format, provider)) + writer.Advance(writtenCount); + + return result; + } } \ No newline at end of file diff --git a/src/DotNext/Buffers/BufferWriterSlim.cs b/src/DotNext/Buffers/BufferWriterSlim.cs index 33be95cb1..42f332fe5 100644 --- a/src/DotNext/Buffers/BufferWriterSlim.cs +++ b/src/DotNext/Buffers/BufferWriterSlim.cs @@ -117,15 +117,9 @@ static void ThrowArgumentOutOfRangeException() /// The requested buffer size is not available. public Span GetSpan(int sizeHint = 0) { - if (sizeHint < 0) - ThrowArgumentOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfNegative(sizeHint); return InternalGetSpan(sizeHint); - - [DoesNotReturn] - [StackTraceHidden] - static void ThrowArgumentOutOfRangeException() - => throw new ArgumentOutOfRangeException(nameof(sizeHint)); } internal Span InternalGetSpan(int sizeHint) diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs index 72855795c..916b3335c 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs @@ -65,10 +65,10 @@ internal LogEntryMetadata(ReadOnlySequence input, out SequencePosition pos internal void Serialize(Span output) { var writer = new SpanWriter(output); - writer.WriteInt64(Term, true); - writer.WriteInt64(timestamp, true); + writer.WriteLittleEndian(Term); + writer.WriteLittleEndian(timestamp); writer.Add(flags); - writer.WriteInt32(identifier, true); - writer.WriteInt64(Length, true); + writer.WriteLittleEndian(identifier); + writer.WriteLittleEndian(Length); } } \ No newline at end of file diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs index 04e3e2f53..02dfcf2c3 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs @@ -70,7 +70,7 @@ private MemoryOwner SerializeForwardJoinRequest(EndPoint joinedPeer, int t { writer.WriteEndPoint(localNode); writer.WriteEndPoint(joinedPeer); - writer.WriteInt32(timeToLive, true); + writer.WriteLittleEndian(timeToLive); if (!writer.TryDetachBuffer(out result)) result = writer.WrittenSpan.Copy(allocator); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs index fea9c22f5..9dec68652 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs @@ -73,7 +73,7 @@ private MemoryOwner SerializeShuffleReply(IReadOnlyCollection pe try { - writer.WriteInt32(peers.Count, true); + writer.WriteLittleEndian(peers.Count); foreach (var peer in peers) writer.WriteEndPoint(peer); @@ -163,9 +163,9 @@ private MemoryOwner SerializeShuffleRequest(EndPoint origin, IReadOnlyColl { writer.WriteEndPoint(localNode); writer.WriteEndPoint(origin); - writer.WriteInt32(timeToLive, true); + writer.WriteLittleEndian(timeToLive); - writer.WriteInt32(peers.Count, true); + writer.WriteLittleEndian(peers.Count); foreach (var peer in peers) writer.WriteEndPoint(peer); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs index c974fb136..b360ccc15 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs @@ -14,7 +14,6 @@ namespace DotNext.Net.Cluster; /// /// Represents unique identifier of cluster member. /// -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Sequential)] public readonly struct ClusterMemberId : IEquatable, IBinaryFormattable { @@ -123,8 +122,8 @@ public ClusterMemberId(Random random) public ClusterMemberId(ref SpanReader reader) { address = new(reader.Read(16)); - lengthAndPort = reader.ReadUInt64(true); - family = reader.ReadInt32(true); + lengthAndPort = reader.ReadLittleEndian(isUnsigned: true); + family = reader.ReadLittleEndian(isUnsigned: false); } /// @@ -135,12 +134,12 @@ static ClusterMemberId IBinaryFormattable.Parse(ref SpanReader< /// Serializes the value as a sequence of bytes. /// /// The memory block writer. - /// is not large enough. + /// is not large enough. public void Format(ref SpanWriter writer) { address.TryWriteBytes(writer.Slide(16)); - writer.WriteUInt64(lengthAndPort, true); - writer.WriteInt32(family, true); + writer.WriteLittleEndian(lengthAndPort); + writer.WriteLittleEndian(family); } /// @@ -238,5 +237,4 @@ public static bool TryParse([NotNullWhen(true)] string? identifier, out ClusterM /// if both identifiers are not equal; otherwise, . public static bool operator !=(in ClusterMemberId x, in ClusterMemberId y) => !x.Equals(in y); -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/ClusterConfigurationStorage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/ClusterConfigurationStorage.cs index 41a3c2544..44e29882a 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/ClusterConfigurationStorage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/ClusterConfigurationStorage.cs @@ -55,7 +55,7 @@ private protected ClusterConfigurationStorage(IEqualityComparer? compa private protected void Encode(IReadOnlyCollection configuration, ref BufferWriterSlim output) { - output.WriteInt32(configuration.Count, true); + output.WriteLittleEndian(configuration.Count); foreach (var address in configuration) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs index 958ba39e7..d7aa74110 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs @@ -218,7 +218,7 @@ private MemoryOwner Encode(IReadOnlyCollection configuration, lo try { - writer.WriteInt64(fingerprint, true); + writer.WriteLittleEndian(fingerprint); Encode(configuration, ref writer); if (!writer.TryDetachBuffer(out result)) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs index 9ebb7b891..108139344 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Internal.cs @@ -50,12 +50,12 @@ internal LogEntryMetadata(DateTimeOffset timeStamp, long term, long offset, long // slow version if target architecture has BE byte order or pointer is not aligned private LogEntryMetadata(ref SpanReader reader) { - Term = reader.ReadInt64(true); - Timestamp = reader.ReadInt64(true); - Length = reader.ReadInt64(true); - Offset = reader.ReadInt64(true); - flags = (LogEntryFlags)reader.ReadUInt32(true); - identifier = reader.ReadInt32(true); + Term = reader.ReadLittleEndian(isUnsigned: false); + Timestamp = reader.ReadLittleEndian(isUnsigned: false); + Length = reader.ReadLittleEndian(isUnsigned: false); + Offset = reader.ReadLittleEndian(isUnsigned: false); + flags = (LogEntryFlags)reader.ReadLittleEndian(isUnsigned: true); + identifier = reader.ReadLittleEndian(isUnsigned: false); } internal LogEntryMetadata(ReadOnlySpan input) @@ -106,12 +106,12 @@ internal static LogEntryMetadata Create(in CachedLogEntry entry, long offset) private void FormatSlow(Span output) { var writer = new SpanWriter(output); - writer.WriteInt64(Term, true); - writer.WriteInt64(Timestamp, true); - writer.WriteInt64(Length, true); - writer.WriteInt64(Offset, true); - writer.WriteUInt32((uint)flags, true); - writer.WriteInt32(identifier, true); + writer.WriteLittleEndian(Term); + writer.WriteLittleEndian(Timestamp); + writer.WriteLittleEndian(Length); + writer.WriteLittleEndian(Offset); + writer.WriteLittleEndian((uint)flags); + writer.WriteLittleEndian(identifier); } public void Format(Span output) @@ -136,7 +136,7 @@ public void Format(Span output) else { // 32-bit LE case, the pointer may not be aligned to 8 bytes - Unsafe.WriteUnaligned(ref ptr, this); + Unsafe.WriteUnaligned(ref ptr, this); } } @@ -169,7 +169,7 @@ static long GetEndOfLogEntrySlow(ReadOnlySpan input) { var reader = new SpanReader(input); reader.Advance(sizeof(long) + sizeof(long)); // skip Term and Timestamp - return reader.ReadInt64(true) + reader.ReadInt64(true); // Length + Offset + return reader.ReadLittleEndian(isUnsigned: false) + reader.ReadLittleEndian(isUnsigned: false); // Length + Offset } } } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/AppendEntriesMessage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/AppendEntriesMessage.cs index 6a3a36247..83cdd1619 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/AppendEntriesMessage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/AppendEntriesMessage.cs @@ -9,20 +9,20 @@ internal static class AppendEntriesMessage internal static void Write(ref SpanWriter writer, in ClusterMemberId id, long term, long prevLogIndex, long prevLogTerm, long commitIndex, int entriesCount) { id.Format(ref writer); - writer.WriteInt64(term, true); - writer.WriteInt64(prevLogIndex, true); - writer.WriteInt64(prevLogTerm, true); - writer.WriteInt64(commitIndex, true); - writer.WriteInt32(entriesCount, true); + writer.WriteLittleEndian(term); + writer.WriteLittleEndian(prevLogIndex); + writer.WriteLittleEndian(prevLogTerm); + writer.WriteLittleEndian(commitIndex); + writer.WriteLittleEndian(entriesCount); } internal static (ClusterMemberId Id, long Term, long PrevLogIndex, long PrevLogTerm, long CommitIndex, int EntriesCount) Read(ref SpanReader reader) => new() { Id = new(ref reader), - Term = reader.ReadInt64(true), - PrevLogIndex = reader.ReadInt64(true), - PrevLogTerm = reader.ReadInt64(true), - CommitIndex = reader.ReadInt64(true), - EntriesCount = reader.ReadInt32(true), + Term = reader.ReadLittleEndian(isUnsigned: false), + PrevLogIndex = reader.ReadLittleEndian(isUnsigned: false), + PrevLogTerm = reader.ReadLittleEndian(isUnsigned: false), + CommitIndex = reader.ReadLittleEndian(isUnsigned: false), + EntriesCount = reader.ReadLittleEndian(isUnsigned: false), }; } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConfigurationMessage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConfigurationMessage.cs index afb5d8156..ee372aec0 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConfigurationMessage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConfigurationMessage.cs @@ -8,10 +8,10 @@ internal static class ConfigurationMessage internal static void Write(ref SpanWriter writer, long fingerprint, long length) { - writer.WriteInt64(fingerprint, true); - writer.WriteInt64(length, true); + writer.WriteLittleEndian(fingerprint); + writer.WriteLittleEndian(length); } internal static (long Fingerprint, long Length) Read(ref SpanReader reader) - => (reader.ReadInt64(true), reader.ReadInt64(true)); + => (reader.ReadLittleEndian(isUnsigned: false), reader.ReadLittleEndian(isUnsigned: false)); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs index 8157a5bb9..5aacf6e0b 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs @@ -68,8 +68,8 @@ protected int WriteHeaders(ProtocolStream protocol, in ClusterMemberId sender, i var writer = protocol.BeginRequestMessage(MessageType.AppendEntries); AppendEntriesMessage.Write(ref writer, in sender, term, prevLogIndex, prevLogTerm, commitIndex, entriesCount); writer.Add(applyConfig.ToByte()); - writer.WriteInt64(config.Fingerprint, true); - writer.WriteInt64(config.Length, true); + writer.WriteLittleEndian(config.Fingerprint); + writer.WriteLittleEndian(config.Length); return writer.WrittenCount; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs index 049dd7e8b..9c8f5b04c 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs @@ -20,7 +20,7 @@ internal partial class Client : RaftClusterMember ValueTask IClientExchange.RequestAsync(ILocalMember localMember, ProtocolStream protocol, Memory buffer, CancellationToken token) { var writer = protocol.BeginRequestMessage(MessageType.Synchronize); - writer.WriteInt64(commitIndex, true); + writer.WriteLittleEndian(commitIndex); protocol.AdvanceWriteCursor(writer.WrittenCount); return protocol.WriteToTransportAsync(token); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs index 4838dc569..35012fbda 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs @@ -73,7 +73,7 @@ internal static ValueTask WriteNullableInt64Async(this ProtocolStream protocol, protocol.Reset(); var writer = new SpanWriter(protocol.RemainingBufferSpan); writer.Add(value.HasValue.ToByte()); - writer.WriteInt64(value.GetValueOrDefault(), true); + writer.WriteLittleEndian(value.GetValueOrDefault()); protocol.AdvanceWriteCursor(writer.WrittenCount); return protocol.WriteToTransportAsync(token); } @@ -86,7 +86,7 @@ internal static ValueTask WriteNullableInt64Async(this ProtocolStream protocol, static long? Read(ReadOnlySpan responseData) { var reader = new SpanReader(responseData); - return reader.Read() is 0 ? null : reader.ReadInt64(true); + return reader.Read() is 0 ? null : reader.ReadLittleEndian(isUnsigned: false); } } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs index 55ab3bed9..c9e295402 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs @@ -32,11 +32,11 @@ internal ReceivedLogEntries(ProtocolStream stream, MemoryAllocator allocat (Id, Term, PrevLogIndex, PrevLogTerm, CommitIndex, entriesCount) = AppendEntriesMessage.Read(ref reader); ApplyConfig = BasicExtensions.ToBoolean(reader.Read()); - var fingerprint = reader.ReadInt64(true); - var configLength = reader.ReadInt64(true); + var fingerprint = reader.ReadLittleEndian(isUnsigned: false); + var configLength = reader.ReadLittleEndian(isUnsigned: false); Configuration = configLength > 0L - ? new(allocator(checked((int)configLength)), fingerprint) + ? new(allocator(int.CreateChecked(configLength)), fingerprint) : new(fingerprint); this.allocator = allocator; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/CorrelationId.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/CorrelationId.cs index 06565d12e..ecae8277d 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/CorrelationId.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/CorrelationId.cs @@ -1,11 +1,11 @@ using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.Datagram; using Buffers; -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Sequential)] internal readonly struct CorrelationId : IEquatable, IBinaryFormattable { @@ -32,8 +32,8 @@ internal CorrelationId(long applicationId, long streamId) internal CorrelationId(ref SpanReader reader) { - ApplicationId = reader.ReadInt64(true); - StreamId = reader.ReadInt64(true); + ApplicationId = reader.ReadLittleEndian(isUnsigned: false); + StreamId = reader.ReadLittleEndian(isUnsigned: false); } static CorrelationId IBinaryFormattable.Parse(ref SpanReader input) => new(ref input); @@ -42,8 +42,8 @@ internal CorrelationId(ref SpanReader reader) public void Format(ref SpanWriter output) { - output.WriteInt64(ApplicationId, true); - output.WriteInt64(StreamId, true); + output.WriteLittleEndian(ApplicationId); + output.WriteLittleEndian(StreamId); } private bool Equals(in CorrelationId other) @@ -51,7 +51,8 @@ private bool Equals(in CorrelationId other) public bool Equals(CorrelationId other) => Equals(in other); - public override bool Equals([NotNullWhen(true)] object? other) => other is CorrelationId id && Equals(in id); + public override bool Equals([NotNullWhen(true)] object? other) + => other is CorrelationId id && Equals(in Unsafe.Unbox(other)); public override int GetHashCode() => HashCode.Combine(ApplicationId, StreamId); @@ -62,5 +63,4 @@ private bool Equals(in CorrelationId other) public static bool operator !=(in CorrelationId x, in CorrelationId y) => !x.Equals(in y); -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs index 64aeab1cf..e508296fc 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs @@ -19,7 +19,7 @@ public override ValueTask ProcessInboundMessageAsync(PacketHeaders headers var reader = new SpanReader(payload.Span); var hasValue = BasicExtensions.ToBoolean(reader.Read()); - var commitIndex = reader.ReadInt64(true); + var commitIndex = reader.ReadLittleEndian(isUnsigned: false); TrySetResult(hasValue ? commitIndex : null); return new(false); } @@ -28,7 +28,7 @@ internal static int WriteResponse(Span output, long? commitIndex) { var writer = new SpanWriter(output); writer.Add(commitIndex.HasValue.ToByte()); - writer.WriteInt64(commitIndex.GetValueOrDefault(), true); + writer.WriteLittleEndian(commitIndex.GetValueOrDefault()); return writer.WrittenCount; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/EmptyClusterConfiguration.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/EmptyClusterConfiguration.cs index f156c5be4..e3885f3d3 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/EmptyClusterConfiguration.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/EmptyClusterConfiguration.cs @@ -41,13 +41,13 @@ internal static void WriteTo(in EmptyClusterConfiguration? configuration, ref Sp } writer.Add(configState); - writer.WriteInt64(fingerprint, true); + writer.WriteLittleEndian(fingerprint); } internal static EmptyClusterConfiguration? ReadFrom(ref SpanReader reader) { var configState = reader.Read(); - var fingerprint = reader.ReadInt64(true); + var fingerprint = reader.ReadLittleEndian(isUnsigned: false); return configState switch { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/HeartbeatMessage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/HeartbeatMessage.cs index e592d92d8..37dddea60 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/HeartbeatMessage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/HeartbeatMessage.cs @@ -8,19 +8,19 @@ internal static class HeartbeatMessage internal static void Write(ref SpanWriter writer, long term, long prevLogIndex, long prevLogTerm, long commitIndex, in EmptyClusterConfiguration? configuration) { - writer.WriteInt64(term, true); - writer.WriteInt64(prevLogIndex, true); - writer.WriteInt64(prevLogTerm, true); - writer.WriteInt64(commitIndex, true); + writer.WriteLittleEndian(term); + writer.WriteLittleEndian(prevLogIndex); + writer.WriteLittleEndian(prevLogTerm); + writer.WriteLittleEndian(commitIndex); EmptyClusterConfiguration.WriteTo(in configuration, ref writer); } internal static (long Term, long PrevLogIndex, long PrevLogTerm, long CommitIndex, EmptyClusterConfiguration? Configuration) Read(ref SpanReader reader) => new() { - Term = reader.ReadInt64(true), - PrevLogIndex = reader.ReadInt64(true), - PrevLogTerm = reader.ReadInt64(true), - CommitIndex = reader.ReadInt64(true), + Term = reader.ReadLittleEndian(isUnsigned: false), + PrevLogIndex = reader.ReadLittleEndian(isUnsigned: false), + PrevLogTerm = reader.ReadLittleEndian(isUnsigned: false), + CommitIndex = reader.ReadLittleEndian(isUnsigned: false), Configuration = EmptyClusterConfiguration.ReadFrom(ref reader), }; } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs index 5ac86dd83..d931fa8ba 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/LogEntryMetadata.cs @@ -5,7 +5,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices; using Buffers; using BitVector = Numerics.BitVector; -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Auto)] internal readonly struct LogEntryMetadata : IBinaryFormattable { @@ -32,11 +31,11 @@ internal static LogEntryMetadata Create(TEntry entry) internal LogEntryMetadata(ref SpanReader reader) { - Term = reader.ReadInt64(true); - timestamp = reader.ReadInt64(true); + Term = reader.ReadLittleEndian(isUnsigned: false); + timestamp = reader.ReadLittleEndian(isUnsigned: false); flags = reader.Read(); - identifier = reader.ReadInt32(true); - length = reader.ReadInt64(true); + identifier = reader.ReadLittleEndian(isUnsigned: false); + length = reader.ReadLittleEndian(isUnsigned: false); } static int IBinaryFormattable.Size => Size; @@ -49,17 +48,16 @@ internal LogEntryMetadata(ref SpanReader reader) internal long? Length => length >= 0L ? length : null; - internal int? CommandId => (flags & IdentifierFlag) != 0 ? identifier : null; + internal int? CommandId => (flags & IdentifierFlag) is not 0 ? identifier : null; - internal bool IsSnapshot => (flags & SnapshotFlag) != 0; + internal bool IsSnapshot => (flags & SnapshotFlag) is not 0; public void Format(ref SpanWriter writer) { - writer.WriteInt64(Term, true); - writer.WriteInt64(timestamp, true); + writer.WriteLittleEndian(Term); + writer.WriteLittleEndian(timestamp); writer.Add(flags); - writer.WriteInt32(identifier, true); - writer.WriteInt64(length, true); + writer.WriteLittleEndian(identifier); + writer.WriteLittleEndian(length); } -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs index 6d68ae21c..4ec7462e0 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs @@ -11,7 +11,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices; using Text; using Dictionary = Collections.Generic.Dictionary; -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Auto)] internal readonly struct MetadataTransferObject : ISerializable { @@ -164,5 +163,4 @@ public static ValueTask ReadFromAsync(TReader r return result; } -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/PreVoteMessage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/PreVoteMessage.cs index 7cb3c2650..ac1230cea 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/PreVoteMessage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/PreVoteMessage.cs @@ -9,16 +9,16 @@ internal static class PreVoteMessage internal static void Write(ref SpanWriter writer, in ClusterMemberId id, long term, long lastLogIndex, long lastLogTerm) { id.Format(ref writer); - writer.WriteInt64(term, true); - writer.WriteInt64(lastLogIndex, true); - writer.WriteInt64(lastLogTerm, true); + writer.WriteLittleEndian(term); + writer.WriteLittleEndian(lastLogIndex); + writer.WriteLittleEndian(lastLogTerm); } internal static (ClusterMemberId Id, long Term, long LastLogIndex, long LastLogTerm) Read(ref SpanReader reader) => new() { Id = new(ref reader), - Term = reader.ReadInt64(true), - LastLogIndex = reader.ReadInt64(true), - LastLogTerm = reader.ReadInt64(true), + Term = reader.ReadLittleEndian(isUnsigned: false), + LastLogIndex = reader.ReadLittleEndian(isUnsigned: false), + LastLogTerm = reader.ReadLittleEndian(isUnsigned: false), }; } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs index 16e431630..899be8b9f 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs @@ -8,7 +8,7 @@ internal static class Result internal static void Write(ref SpanWriter writer, in Result result) { - writer.WriteInt64(result.Term, true); + writer.WriteLittleEndian(result.Term); writer.Add(result.Value.ToByte()); } @@ -21,7 +21,7 @@ internal static int Write(Span output, in Result result) internal static void WritePreVoteResult(ref SpanWriter writer, in Result result) { - writer.WriteInt64(result.Term, true); + writer.WriteLittleEndian(result.Term); writer.Add((byte)result.Value); } @@ -34,7 +34,7 @@ internal static int WritePreVoteResult(Span output, in Result writer, in Result result) { - writer.WriteInt64(result.Term, true); + writer.WriteLittleEndian(result.Term); writer.Add((byte)result.Value); } @@ -47,7 +47,7 @@ internal static int WriteHeartbeatResult(Span output, in Result Read(ref SpanReader reader) => new() { - Term = reader.ReadInt64(true), + Term = reader.ReadLittleEndian(isUnsigned: false), Value = BasicExtensions.ToBoolean(reader.Read()), }; @@ -59,7 +59,7 @@ internal static Result Read(ReadOnlySpan input) internal static Result ReadPreVoteResult(ref SpanReader reader) => new() { - Term = reader.ReadInt64(true), + Term = reader.ReadLittleEndian(isUnsigned: false), Value = (PreVoteResult)reader.Read(), }; @@ -71,7 +71,7 @@ internal static Result ReadPreVoteResult(ReadOnlySpan input internal static Result ReadHeartbeatResult(ref SpanReader reader) => new() { - Term = reader.ReadInt64(true), + Term = reader.ReadLittleEndian(isUnsigned: false), Value = (HeartbeatResult)reader.Read(), }; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/SnapshotMessage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/SnapshotMessage.cs index 1e93236f1..2b795586c 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/SnapshotMessage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/SnapshotMessage.cs @@ -9,16 +9,16 @@ internal static class SnapshotMessage internal static void Write(ref SpanWriter writer, in ClusterMemberId id, long term, long snapshotIndex, IRaftLogEntry snapshot) { id.Format(ref writer); - writer.WriteInt64(term, true); - writer.WriteInt64(snapshotIndex, true); + writer.WriteLittleEndian(term); + writer.WriteLittleEndian(snapshotIndex); LogEntryMetadata.Create(snapshot).Format(ref writer); } internal static (ClusterMemberId Id, long Term, long SnapshotIndex, LogEntryMetadata SnapshotMetadata) Read(ref SpanReader reader) => new() { Id = new(ref reader), - Term = reader.ReadInt64(true), - SnapshotIndex = reader.ReadInt64(true), + Term = reader.ReadLittleEndian(isUnsigned: false), + SnapshotIndex = reader.ReadLittleEndian(isUnsigned: false), SnapshotMetadata = new(ref reader), }; } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/VoteMessage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/VoteMessage.cs index 965a1e1da..d7915ff37 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/VoteMessage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/VoteMessage.cs @@ -9,16 +9,16 @@ internal static class VoteMessage internal static void Write(ref SpanWriter writer, in ClusterMemberId id, long term, long lastLogIndex, long lastLogTerm) { id.Format(ref writer); - writer.WriteInt64(term, true); - writer.WriteInt64(lastLogIndex, true); - writer.WriteInt64(lastLogTerm, true); + writer.WriteLittleEndian(term); + writer.WriteLittleEndian(lastLogIndex); + writer.WriteLittleEndian(lastLogTerm); } internal static (ClusterMemberId Id, long Term, long LastLogIndex, long LastLogTerm) Read(ref SpanReader reader) => new() { Id = new(ref reader), - Term = reader.ReadInt64(true), - LastLogIndex = reader.ReadInt64(true), - LastLogTerm = reader.ReadInt64(true), + Term = reader.ReadLittleEndian(isUnsigned: false), + LastLogIndex = reader.ReadLittleEndian(isUnsigned: false), + LastLogTerm = reader.ReadLittleEndian(isUnsigned: false), }; } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs index ccc47949f..148ae891f 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs @@ -58,8 +58,8 @@ public RumorTimestamp() private RumorTimestamp(ref SpanReader reader) { - timestamp = reader.ReadInt64(true); - sequenceNumber = reader.ReadUInt64(true); + timestamp = reader.ReadLittleEndian(isUnsigned: false); + sequenceNumber = reader.ReadLittleEndian(isUnsigned: true); } private RumorTimestamp(long timestamp, ulong sequenceNumber) @@ -74,8 +74,8 @@ private RumorTimestamp(long timestamp, ulong sequenceNumber) /// The buffer writer. public void Format(ref SpanWriter writer) { - writer.WriteInt64(timestamp, true); - writer.WriteUInt64(sequenceNumber, true); + writer.WriteLittleEndian(timestamp); + writer.WriteLittleEndian(sequenceNumber); } /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs b/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs index 8eb47572c..b16a8379c 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs @@ -79,7 +79,7 @@ public static void WriteEndPoint(this ref BufferWriterSlim writer, EndPoin // number of address bytes, N = 1 byte // address bytes = N bytes writer.Add(IPEndPointPrefix); - writer.WriteInt32(ip.Port, true); + writer.WriteLittleEndian(ip.Port); Serialize(ip.Address, ref writer); break; case HttpEndPoint http: @@ -92,8 +92,8 @@ public static void WriteEndPoint(this ref BufferWriterSlim writer, EndPoin // host name = N bytes writer.Add(HttpEndPointPrefix); writer.Add(http.IsSecure.ToByte()); - writer.WriteInt32(http.Port, true); - writer.WriteInt32((int)http.AddressFamily, true); + writer.WriteLittleEndian(http.Port); + writer.WriteLittleEndian((int)http.AddressFamily); Serialize(http.Host, ref writer); break; case DnsEndPoint dns: @@ -104,8 +104,8 @@ public static void WriteEndPoint(this ref BufferWriterSlim writer, EndPoin // host name length, N = 4 bytes // host name = N bytes writer.Add(DnsEndPointPrefix); - writer.WriteInt32(dns.Port, true); - writer.WriteInt32((int)dns.AddressFamily, true); + writer.WriteLittleEndian(dns.Port); + writer.WriteLittleEndian((int)dns.AddressFamily); Serialize(dns.Host, ref writer); break; case UnixDomainSocketEndPoint domainSocket: @@ -144,7 +144,7 @@ private static void Serialize(IPAddress address, ref BufferWriterSlim writ private static void Serialize(ReadOnlySpan hostName, ref BufferWriterSlim writer) { var count = HostNameEncoding.GetByteCount(hostName); - writer.WriteInt32(count, true); + writer.WriteLittleEndian(count); HostNameEncoding.GetBytes(hostName, writer.GetSpan(count)); writer.Advance(count); From db7b8fac3f90d65aa20574b3fd8ed4adef0802b6 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 22:13:32 +0200 Subject: [PATCH 033/155] Fixed naming for extension methods --- .../Buffers/StringBuildingBenchmark.cs | 4 +- src/DotNext.IO/IO/EncodingTextWriter.cs | 2 +- src/DotNext.IO/IO/TextBufferWriter.cs | 2 +- .../InterpolatedStringTemplateBuilder.cs | 4 +- .../Buffers/BufferWriterSlimTests.cs | 32 +++++++------- .../Buffers/BufferWriterTests.cs | 2 +- .../Logging/AdvancedDebugProvider.cs | 2 +- .../Buffers/BufferHelpers.BufferWriterSlim.cs | 42 +++---------------- 8 files changed, 30 insertions(+), 60 deletions(-) diff --git a/src/DotNext.Benchmarks/Buffers/StringBuildingBenchmark.cs b/src/DotNext.Benchmarks/Buffers/StringBuildingBenchmark.cs index c82a7297d..9b1af3b39 100644 --- a/src/DotNext.Benchmarks/Buffers/StringBuildingBenchmark.cs +++ b/src/DotNext.Benchmarks/Buffers/StringBuildingBenchmark.cs @@ -69,7 +69,7 @@ public string BuildStringOnStackNoPreallocatedBuffer() for (var i = 0; i < 100; i++) { writer.Write(StringValue); - writer.WriteFormattable(int.MaxValue); + writer.Write(int.MaxValue); writer.WriteLine(); } @@ -90,7 +90,7 @@ public string BuildStringOnStack() for (var i = 0; i < 100; i++) { writer.Write(StringValue); - writer.WriteFormattable(int.MaxValue); + writer.Write(int.MaxValue); writer.WriteLine(); } diff --git a/src/DotNext.IO/IO/EncodingTextWriter.cs b/src/DotNext.IO/IO/EncodingTextWriter.cs index 78106037d..e2e7ddc08 100644 --- a/src/DotNext.IO/IO/EncodingTextWriter.cs +++ b/src/DotNext.IO/IO/EncodingTextWriter.cs @@ -27,7 +27,7 @@ private void WriteFormattable(T value) var writer = new BufferWriterSlim(stackalloc char[ConversionBufferSize]); try { - writer.WriteFormattable(value, provider: FormatProvider); + writer.Write(value, provider: FormatProvider); Write(writer.WrittenSpan); } finally diff --git a/src/DotNext.IO/IO/TextBufferWriter.cs b/src/DotNext.IO/IO/TextBufferWriter.cs index cd25db9ea..0eeb96c64 100644 --- a/src/DotNext.IO/IO/TextBufferWriter.cs +++ b/src/DotNext.IO/IO/TextBufferWriter.cs @@ -41,7 +41,7 @@ public sealed override void Flush() } } - public Task FlushAsync(CancellationToken token) + public override Task FlushAsync(CancellationToken token) { if (flushAsync is null) { diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs index 9743b6dce..e8d216a7b 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/InterpolatedStringTemplateBuilder.cs @@ -77,12 +77,12 @@ internal void WriteTo(scoped ref int position, scoped ref BufferWriterSlim } output.Add('{'); - output.WriteFormattable(position++, provider: InvariantCulture); + output.Write(position++, provider: InvariantCulture); if (alignment is not 0) { output.Add(','); - output.WriteFormattable(alignment, provider: InvariantCulture); + output.Write(alignment, provider: InvariantCulture); } if (literalOrFormat is { Length: > 0 }) diff --git a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs index f639ad7e0..f200447b3 100644 --- a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs +++ b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs @@ -73,27 +73,27 @@ public static void MutableOnStackWriter(int initialBufferSize) writer.Write("Hello, world"); writer.Add('!'); writer.WriteLine("!!"); - writer.WriteFormattable(42, provider: InvariantCulture); - writer.WriteFormattable(56U, provider: InvariantCulture); - writer.WriteFormattable(10, provider: InvariantCulture); - writer.WriteFormattable(22, provider: InvariantCulture); - writer.WriteFormattable(88, provider: InvariantCulture); - writer.WriteFormattable(99, provider: InvariantCulture); - writer.WriteFormattable(77L, provider: InvariantCulture); - writer.WriteFormattable(66UL, provider: InvariantCulture); + writer.Write(42, provider: InvariantCulture); + writer.Write(56U, provider: InvariantCulture); + writer.Write(10, provider: InvariantCulture); + writer.Write(22, provider: InvariantCulture); + writer.Write(88, provider: InvariantCulture); + writer.Write(99, provider: InvariantCulture); + writer.Write(77L, provider: InvariantCulture); + writer.Write(66UL, provider: InvariantCulture); var guid = Guid.NewGuid(); - writer.WriteFormattable(guid); + writer.Write(guid); var dt = DateTime.Now; - writer.WriteFormattable(dt, provider: InvariantCulture); + writer.Write(dt, provider: InvariantCulture); var dto = DateTimeOffset.Now; - writer.WriteFormattable(dto, provider: InvariantCulture); + writer.Write(dto, provider: InvariantCulture); - writer.WriteFormattable(42.5M, provider: InvariantCulture); - writer.WriteFormattable(32.2F, provider: InvariantCulture); - writer.WriteFormattable(56.6D, provider: InvariantCulture); + writer.Write(42.5M, provider: InvariantCulture); + writer.Write(32.2F, provider: InvariantCulture); + writer.Write(56.6D, provider: InvariantCulture); Equal("Hello, world!!!" + Environment.NewLine + "4256102288997766" + guid + dt.ToString(InvariantCulture) + dto.ToString(InvariantCulture) + "42.532.256.6", writer.ToString()); } @@ -167,11 +167,11 @@ public static void FormatValues() try { const string expectedString = "Hello, world!"; - Equal(expectedString.Length, writer.WriteAsString(expectedString)); + Equal(expectedString.Length, writer.Write(expectedString)); Equal(expectedString, writer.ToString()); writer.Clear(); - Equal(2, writer.WriteAsString(56, provider: InvariantCulture)); + Equal(2, writer.Write(56)); Equal("56", writer.ToString()); } finally diff --git a/src/DotNext.Tests/Buffers/BufferWriterTests.cs b/src/DotNext.Tests/Buffers/BufferWriterTests.cs index e6793167a..69e814eaf 100644 --- a/src/DotNext.Tests/Buffers/BufferWriterTests.cs +++ b/src/DotNext.Tests/Buffers/BufferWriterTests.cs @@ -264,7 +264,7 @@ public static void WriteInterpolatedStringToBufferWriterSlim(int x, int y) { // TODO: Return try-finally block when 'scoped' keyword will be introduced in the next version of C# var buffer = new BufferWriterSlim(stackalloc char[4]); - buffer.WriteString($"{x,4:X} = {y,-3:X}"); + buffer.Interpolate($"{x,4:X} = {y,-3:X}"); Equal($"{x,4:X} = {y,-3:X}", buffer.ToString()); buffer.Dispose(); } diff --git a/src/DotNext.Tests/Extensions/Logging/AdvancedDebugProvider.cs b/src/DotNext.Tests/Extensions/Logging/AdvancedDebugProvider.cs index 63d73aede..4ccd0ae23 100644 --- a/src/DotNext.Tests/Extensions/Logging/AdvancedDebugProvider.cs +++ b/src/DotNext.Tests/Extensions/Logging/AdvancedDebugProvider.cs @@ -45,7 +45,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except return; var buffer = new BufferWriterSlim(stackalloc char[128]); - buffer.WriteString($"[{prefix}]({new Timestamp()}){logLevel}: {message}"); + buffer.Interpolate($"[{prefix}]({new Timestamp()}){logLevel}: {message}"); if (exception is not null) { diff --git a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs index fd00fc375..4eac902ea 100644 --- a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs +++ b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs @@ -44,7 +44,7 @@ public static void Write(this ref BufferWriterSlim writer, StringBuilder i /// The formatting provider. /// The handler of the interpolated string. /// The number of written characters. - public static int WriteString(this ref BufferWriterSlim writer, IFormatProvider? provider, [InterpolatedStringHandlerArgument(nameof(writer), nameof(provider))] scoped ref BufferWriterSlimInterpolatedStringHandler handler) + public static int Interpolate(this ref BufferWriterSlim writer, IFormatProvider? provider, [InterpolatedStringHandlerArgument(nameof(writer), nameof(provider))] scoped ref BufferWriterSlimInterpolatedStringHandler handler) => handler.WrittenCount; /// @@ -53,8 +53,8 @@ public static int WriteString(this ref BufferWriterSlim writer, IFormatPro /// The buffer writer. /// The handler of the interpolated string. /// The number of written characters. - public static int WriteString(this ref BufferWriterSlim writer, [InterpolatedStringHandlerArgument(nameof(writer))] scoped ref BufferWriterSlimInterpolatedStringHandler handler) - => WriteString(ref writer, null, ref handler); + public static int Interpolate(this ref BufferWriterSlim writer, [InterpolatedStringHandlerArgument(nameof(writer))] scoped ref BufferWriterSlimInterpolatedStringHandler handler) + => Interpolate(ref writer, null, ref handler); /// /// Writes the value as a string. @@ -65,39 +65,9 @@ public static int WriteString(this ref BufferWriterSlim writer, [Interpola /// The format of the value. /// The format provider. /// The number of written characters. - public static int WriteAsString(this ref BufferWriterSlim writer, T value, string? format = null, IFormatProvider? provider = null) + public static int Write(this ref BufferWriterSlim writer, T value, string? format = null, IFormatProvider? provider = null) => BufferWriterSlimInterpolatedStringHandler.AppendFormatted(ref writer, value, format, provider); - /// - /// Writes the value as a sequence of characters. - /// - /// The type of the value to convert. - /// The buffer writer. - /// The value to convert. - /// The format of the value. - /// The format provider. - /// The number of written characters. - public static int WriteFormattable(this ref BufferWriterSlim writer, T value, scoped ReadOnlySpan format = default, IFormatProvider? provider = null) - where T : notnull, ISpanFormattable - { - int charsWritten; - const int maxBufferSize = int.MaxValue / 2; - - for (int bufferSize = 0; ;) - { - var buffer = writer.InternalGetSpan(bufferSize); - if (value.TryFormat(buffer, out charsWritten, format, provider)) - { - writer.Advance(charsWritten); - break; - } - - bufferSize = bufferSize <= maxBufferSize ? buffer.Length << 1 : throw new InsufficientMemoryException(); - } - - return charsWritten; - } - /// /// Writes line termination symbols to the buffer. /// @@ -173,7 +143,7 @@ public static void Concat(this ref BufferWriterSlim writer, scoped ReadOnl /// The type of formattable value. /// The buffer writer. /// The value to convert. - public static void WriteFormattable(this ref BufferWriterSlim writer, T value) + public static void Write(this ref BufferWriterSlim writer, T value) where T : notnull, IBinaryFormattable { var output = new SpanWriter(writer.GetSpan(T.Size)); @@ -187,7 +157,7 @@ public static void WriteFormattable(this ref BufferWriterSlim writer, T /// The type of formattable value. /// The buffer writer. /// A sequence of values to convert. - public static void WriteFormattable(this ref BufferWriterSlim writer, scoped ReadOnlySpan values) + public static void Write(this ref BufferWriterSlim writer, scoped ReadOnlySpan values) where T : notnull, IBinaryFormattable { if (values.IsEmpty) From 894d6d088f3253bac8d4107d230ff2ba0ae39ca0 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 22:30:56 +0200 Subject: [PATCH 034/155] Added support of checked operators --- .../PhiAccrualFailureDetectorTests.cs | 2 +- .../Diagnostics/TimestampTests.cs | 7 +++ src/DotNext/Diagnostics/Timestamp.cs | 51 +++++++++++++++---- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/DotNext.Tests/Diagnostics/PhiAccrualFailureDetectorTests.cs b/src/DotNext.Tests/Diagnostics/PhiAccrualFailureDetectorTests.cs index e3e64be12..8c6c83c6f 100644 --- a/src/DotNext.Tests/Diagnostics/PhiAccrualFailureDetectorTests.cs +++ b/src/DotNext.Tests/Diagnostics/PhiAccrualFailureDetectorTests.cs @@ -114,7 +114,7 @@ public static void RegressionIssue151() for (var i = 0; i < 50; i++) { - detector.ReportHeartbeat(ts + TimeSpan.FromMilliseconds(i)); + detector.ReportHeartbeat(checked(ts + TimeSpan.FromMilliseconds(i))); } } } \ No newline at end of file diff --git a/src/DotNext.Tests/Diagnostics/TimestampTests.cs b/src/DotNext.Tests/Diagnostics/TimestampTests.cs index 87d88b54b..5d549c099 100644 --- a/src/DotNext.Tests/Diagnostics/TimestampTests.cs +++ b/src/DotNext.Tests/Diagnostics/TimestampTests.cs @@ -67,6 +67,13 @@ public static void ArithmeticOperators() Equal(TimeSpan.FromMilliseconds(100), current.Value - result.Value); } + [Fact] + public static void CheckedArithmeticOperators() + { + Throws(static () => checked(new Timestamp() - TimeSpan.MaxValue)); + Throws(static () => checked(new Timestamp() + TimeSpan.MaxValue)); + } + [Fact] public static void DefaultTimestamp() { diff --git a/src/DotNext/Diagnostics/Timestamp.cs b/src/DotNext/Diagnostics/Timestamp.cs index ba4516ca7..b55d6272d 100644 --- a/src/DotNext/Diagnostics/Timestamp.cs +++ b/src/DotNext/Diagnostics/Timestamp.cs @@ -1,17 +1,21 @@ +using System.Numerics; using System.Runtime.CompilerServices; using static System.Diagnostics.Stopwatch; namespace DotNext.Diagnostics; -using static Threading.AtomicInt64; - /// /// Represents timestamp. /// /// /// This class can be used as allocation-free alternative to . /// -public readonly record struct Timestamp : IEquatable, IComparable +public readonly record struct Timestamp : + IEquatable, + IComparable, + IComparisonOperators, + IAdditionOperators, + ISubtractionOperators { private static readonly double TickFrequency = (double)TimeSpan.TicksPerSecond / Frequency; private readonly long ticks; @@ -87,7 +91,7 @@ private static long FromTimeSpan(TimeSpan value) /// /// Gets the total elapsed time measured by the current instance, in milliseconds. /// - public double ElapsedMilliseconds => ((double)ElapsedTicks / Frequency) * 1_000D; + public double ElapsedMilliseconds => (double)ElapsedTicks / Frequency * 1_000D; /// /// Gets a difference between two timestamps, in milliseconds. @@ -95,7 +99,7 @@ private static long FromTimeSpan(TimeSpan value) /// The timestamp in the past. /// The number of milliseconds since . public double ElapsedSince(Timestamp past) - => ((double)(ticks - past.ticks) / Frequency) * 1_000D; + => (double)(ticks - past.ticks) / Frequency * 1_000D; /// /// Gets representing the given timestamp. @@ -158,12 +162,24 @@ public double ElapsedSince(Timestamp past) /// The delta. /// The modified timestamp. /// is too large. - public static Timestamp operator +(Timestamp x, TimeSpan y) + public static Timestamp operator checked +(Timestamp x, TimeSpan y) { var ticks = checked(x.ticks + FromTimeSpan(y)); return ticks >= 0L ? new(ticks) : throw new OverflowException(); } + /// + /// Adds the specified duration to the timestamp. + /// + /// The timestamp value. + /// The delta. + /// The modified timestamp. + public static Timestamp operator +(Timestamp x, TimeSpan y) + { + var ticks = x.ticks + FromTimeSpan(y); + return ticks > 0L ? new(ticks) : default; + } + /// /// Subtracts the specified duration from the timestamp. /// @@ -171,19 +187,32 @@ public double ElapsedSince(Timestamp past) /// The delta. /// The modified timestamp. /// is too large. - public static Timestamp operator -(Timestamp x, TimeSpan y) // TODO: Convert to checked operator in C# 11 + public static Timestamp operator checked -(Timestamp x, TimeSpan y) { var ticks = checked(x.ticks - FromTimeSpan(y)); return ticks >= 0L ? new(ticks) : throw new OverflowException(); } + /// + /// Subtracts the specified duration from the timestamp. + /// + /// The timestamp value. + /// The delta. + /// The modified timestamp. + /// is too large. + public static Timestamp operator -(Timestamp x, TimeSpan y) + { + var ticks = x.ticks - FromTimeSpan(y); + return ticks > 0L ? new(ticks) : default; + } + /// /// Reads the timestamp and prevents the processor from reordering memory operations. /// /// The managed pointer to the timestamp. /// The value at the specified location. - public static Timestamp VolatileRead(ref Timestamp location) - => new(location.ticks.VolatileRead()); + public static Timestamp VolatileRead(ref readonly Timestamp location) + => new(Volatile.Read(in location.ticks)); /// /// Writes the timestamp and prevents the proces from reordering memory operations. @@ -191,12 +220,12 @@ public static Timestamp VolatileRead(ref Timestamp location) /// The managed pointer to the timestamp. /// The value to write. public static void VolatileWrite(ref Timestamp location, Timestamp newValue) - => Unsafe.AsRef(in location.ticks).VolatileWrite(newValue.ticks); + => Volatile.Write(ref Unsafe.AsRef(in location.ticks), newValue.ticks); /// /// Updates the timestamp to the current point in time and prevents the proces from reordering memory operations. /// /// The location of the timestampt to update. public static void Refresh(ref Timestamp location) - => Unsafe.AsRef(in location.ticks).VolatileWrite(Math.Max(1L, GetTimestamp())); + => Volatile.Write(ref Unsafe.AsRef(in location.ticks), Math.Max(1L, GetTimestamp())); } \ No newline at end of file From e9bea8fc58293b189d2c8e267ca23adf7fb382b8 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 22:41:06 +0200 Subject: [PATCH 035/155] Migration to simplified arg validation --- src/DotNext/Buffers/MemoryOwner.cs | 19 ++----------------- src/DotNext/Buffers/MemoryRental.cs | 4 ++-- 2 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/DotNext/Buffers/MemoryOwner.cs b/src/DotNext/Buffers/MemoryOwner.cs index 89607c422..077b31485 100644 --- a/src/DotNext/Buffers/MemoryOwner.cs +++ b/src/DotNext/Buffers/MemoryOwner.cs @@ -1,7 +1,6 @@ using System.Buffers; using System.ComponentModel; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -320,31 +319,17 @@ internal readonly ref T First /// The index of the element in memory. /// The managed pointer to the item. /// is invalid. - public readonly ref T this[nint index] + public readonly ref T this[int index] { get { AssertValid(); - - if ((nuint)index >= (nuint)length) - ThrowArgumentOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)index, (uint)length, nameof(index)); return ref Unsafe.Add(ref First, index); - - [DoesNotReturn] - [StackTraceHidden] - static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(nameof(index)); } } - /// - /// Gets managed pointer to the item in the rented memory. - /// - /// The index of the element in memory. - /// The managed pointer to the item. - /// is invalid. - public readonly ref T this[int index] => ref this[(nint)index]; - internal readonly void Clear(bool clearBuffer) { if (array is null) diff --git a/src/DotNext/Buffers/MemoryRental.cs b/src/DotNext/Buffers/MemoryRental.cs index 40b4f0c8e..4d2c43d4b 100644 --- a/src/DotNext/Buffers/MemoryRental.cs +++ b/src/DotNext/Buffers/MemoryRental.cs @@ -73,8 +73,8 @@ public MemoryRental(Span span, int length) public MemoryRental(MemoryPool pool, int minBufferSize, bool exactSize = true) { ArgumentNullException.ThrowIfNull(pool); - if (minBufferSize <= 0) - throw new ArgumentOutOfRangeException(nameof(minBufferSize)); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(minBufferSize); + var owner = pool.Rent(minBufferSize); memory = owner.Memory.Span; if (exactSize) From 23844ee6e20721ee6a0be6a4f559cb248bffb356 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 22:48:31 +0200 Subject: [PATCH 036/155] Removed dead code --- src/DotNext/Buffers/BufferWriterSlim.ByReference.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs b/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs index 14d69fbcd..2e071987a 100644 --- a/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs +++ b/src/DotNext/Buffers/BufferWriterSlim.ByReference.cs @@ -44,14 +44,4 @@ static ref BufferWriterSlim AsWriter(ref byte reference) } } } - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private static int Size - { - get - { - Sizeof(typeof(BufferWriterSlim)); - return Return(); - } - } } \ No newline at end of file From 257119299ca3c11ea3fc32a38272434005f777f1 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 22:56:20 +0200 Subject: [PATCH 037/155] Removed Reference type as unused --- .../InteropServices/PinnedArrayTests.cs | 13 --- .../Runtime/InteropServices/PointerTests.cs | 12 --- src/DotNext.Tests/Runtime/ReferenceTests.cs | 90 ------------------- .../Runtime/InteropServices/PinnedArray.cs | 7 -- .../Runtime/InteropServices/Pointer.cs | 20 ----- 5 files changed, 142 deletions(-) delete mode 100644 src/DotNext.Tests/Runtime/ReferenceTests.cs diff --git a/src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs b/src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs index 9e57ea197..58cb6788e 100644 --- a/src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs +++ b/src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs @@ -145,17 +145,4 @@ public static void StreamInterop() Equal(30, ms.Read()); Equal(40, ms.Read()); } - - [Fact] - public static void ArrayElementHandle() - { - var array = new PinnedArray(4); - Reference handle = array.GetReference(0); - - array[0] = 42; - Equal(42, handle.Target); - - handle.Target = 52; - Equal(52, array[0]); - } } \ No newline at end of file diff --git a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs index 823ec61a2..6f706f301 100644 --- a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs +++ b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs @@ -549,18 +549,6 @@ public static unsafe void PointerReflection() Equal((IntPtr)ptr.Address, new IntPtr(Pointer.Unbox(obj))); } - [Fact] - public static unsafe void PointerToHandle() - { - var value = 42; - Reference handle = new Pointer(&value); - - Equal(42, handle.Target); - - handle.Target = 52; - Equal(52, handle.Target); - } - [Fact] public static void CompareToMethod() { diff --git a/src/DotNext.Tests/Runtime/ReferenceTests.cs b/src/DotNext.Tests/Runtime/ReferenceTests.cs deleted file mode 100644 index 2d9137977..000000000 --- a/src/DotNext.Tests/Runtime/ReferenceTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace DotNext.Runtime; - -public sealed class ReferenceTests : Test -{ - private static long staticField; - - [Fact] - public static void AllocateStorage() - { - var handle = Reference.Allocate(string.Empty); - True(handle.IsValid); - Empty(handle.Target); - - handle.Target = "Hello, world!"; - Equal("Hello, world!", handle.Target); - } - - [Fact] - public static void ArrayElementAccess() - { - int[] array = { 10, 20, 30 }; - var handle = Reference.ArrayElement(array, 1); - True(handle.IsValid); - - Equal(array[1], handle.Target); - - array[1] = 42; - Equal(42, handle.Target); - - handle.Target = 43; - Equal(43, array[1]); - } - - [Fact] - public static unsafe void StaticFieldAccess() - { - var handle = Reference.Create(&GetStaticFieldRef); - True(handle.IsValid); - - handle.Target = 42; - Equal(42, staticField); - Equal(42, handle.Target); - - static ref long GetStaticFieldRef() => ref staticField; - } - - [Fact] - public static void BoxedValueAccess() - { - var handle = Reference.Unbox(42); - Equal(42, handle.Target); - } - - [Fact] - public static unsafe void PointerAccess() - { - var value = 42; - var handle = Reference.FromPointer(&value); - True(handle.IsValid); - - Equal(42, handle.Target); - - handle.Target = 43; - Equal(43, value); - } - - [Fact] - public static void SpanAccess() - { - var handle = Reference.Allocate(42); - Equal(42, handle.Span[0]); - } - - [Fact] - public static void InvalidHandle() - { - var handle = default(Reference); - False(handle.IsValid); - Null(handle.ToString()); - Throws(() => handle.Target); - } - - [Fact] - public static unsafe void InvalidArrayElementRef() - { - Throws(static () => Reference.Create(Array.Empty(), &GetElementRef)); - - static ref string GetElementRef(string[] array) => ref array[0]; - } -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs b/src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs index 7ef3ac0e4..74768600b 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs @@ -60,13 +60,6 @@ public PinnedArray Clone() /// The managed pointer to the array element. public ref T this[nint index] => ref Array[index]; - /// - /// Gets a reference to the array element. - /// - /// The index of the array element. - /// The reference to the array element. - public Reference GetReference(nint index) => Reference.ArrayElement(Array, index); - /// T IList.this[int index] { diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs index 01d664d5e..583fd3dd4 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs @@ -117,19 +117,6 @@ public unsafe Pointer(nuint ptr) [StackTraceHidden] private static void ThrowNullPointerException() => throw new NullPointerException(); - /// - /// Converts this pointer to . - /// - /// The reference to the memory location identified by this pointer. - /// This pointer is zero. - public unsafe Reference GetReference() - { - if (IsNull) - ThrowNullPointerException(); - - return Reference.FromPointer(value); - } - /// /// Gets boxed pointer. /// @@ -1405,13 +1392,6 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) [CLSCompliant(false)] public static unsafe implicit operator nuint(Pointer ptr) => (nuint)ptr.value; - /// - /// Obtains the reference to the memory location identified by the pointer. - /// - /// The pointer to the memory location. - /// The reference to the memory location identified by the pointer. - public static implicit operator Reference(Pointer ptr) => ptr.GetReference(); - /// unsafe UIntPtr ISupplier.Invoke() => (nuint)value; From 25dfd56cdc510c9c415a7573505f489e0eb20169 Mon Sep 17 00:00:00 2001 From: sakno Date: Tue, 21 Nov 2023 23:18:31 +0200 Subject: [PATCH 038/155] Require constant for better inlining --- src/DotNext/Buffers/MemoryAllocator.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/DotNext/Buffers/MemoryAllocator.cs b/src/DotNext/Buffers/MemoryAllocator.cs index bcc9c2925..f8159f779 100644 --- a/src/DotNext/Buffers/MemoryAllocator.cs +++ b/src/DotNext/Buffers/MemoryAllocator.cs @@ -1,4 +1,5 @@ using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace DotNext.Buffers; @@ -65,7 +66,7 @@ public static MemoryAllocator ToAllocator(this Func> /// The type of the items in the memory pool. /// The allocated memory. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Invoke(this MemoryAllocator? allocator, int length, bool exactSize) + public static MemoryOwner Invoke(this MemoryAllocator? allocator, int length, [ConstantExpected] bool exactSize) { MemoryOwner result; if (allocator is null) @@ -84,14 +85,6 @@ public static MemoryOwner Invoke(this MemoryAllocator? allocator, int l return result; } - /// - /// Returns array allocator. - /// - /// The type of elements in the array. - /// The array allocator. - [Obsolete("Use GetArrayAllocator() method instead.")] - public static MemoryAllocator CreateArrayAllocator() => GetArrayAllocator(); - /// /// Returns array allocator. /// @@ -130,6 +123,6 @@ static MemoryOwner AllocateArray(int length) /// /// The allocated memory. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Allocate(int length, bool exactSize) + public static MemoryOwner Allocate(int length, [ConstantExpected] bool exactSize) => new(ArrayPool.Shared, length, exactSize); } \ No newline at end of file From 0989497f526171faa8a47791062d41be257a1396 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 00:12:19 +0200 Subject: [PATCH 039/155] Removed InlineIL dependency --- src/DotNext.IO/DotNext.IO.csproj | 2 -- src/DotNext.IO/FodyWeavers.xml | 4 --- .../IO/FileBufferingWriter.Sequence.cs | 36 ++++++------------- 3 files changed, 11 insertions(+), 31 deletions(-) delete mode 100644 src/DotNext.IO/FodyWeavers.xml diff --git a/src/DotNext.IO/DotNext.IO.csproj b/src/DotNext.IO/DotNext.IO.csproj index 2b67c3836..b334f92eb 100644 --- a/src/DotNext.IO/DotNext.IO.csproj +++ b/src/DotNext.IO/DotNext.IO.csproj @@ -52,8 +52,6 @@ - - diff --git a/src/DotNext.IO/FodyWeavers.xml b/src/DotNext.IO/FodyWeavers.xml deleted file mode 100644 index 1bc8bbd95..000000000 --- a/src/DotNext.IO/FodyWeavers.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs index d3a74806d..0c433a97d 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs @@ -1,13 +1,10 @@ using System.Buffers; -using static InlineIL.IL; -using static InlineIL.IL.Emit; -using static InlineIL.MethodRef; -using static InlineIL.TypeRef; using Debug = System.Diagnostics.Debug; using SafeFileHandle = Microsoft.Win32.SafeHandles.SafeFileHandle; namespace DotNext.IO; +using System.Diagnostics.CodeAnalysis; using Buffers; using IReadOnlySequenceSource = Buffers.IReadOnlySequenceSource; @@ -15,21 +12,11 @@ public partial class FileBufferingWriter { private sealed class TailSegment : ReadOnlySequenceSegment { - private static readonly Action, ReadOnlySequenceSegment> SegmentSetter; - - static TailSegment() - { - Ldnull(); - Ldftn(PropertySet(Type>(), nameof(Next))); - Newobj(Constructor(Type, ReadOnlySequenceSegment>>(), Type(), Type())); - Pop(out SegmentSetter); - } - - internal TailSegment(ReadOnlySequenceSegment previous, Memory memory) + internal TailSegment(LazySegment previous, Memory memory) { Memory = memory; RunningIndex = previous.RunningIndex + previous.Memory.Length; - SegmentSetter(previous, this); + previous.Next(this); } } @@ -76,6 +63,8 @@ private LazySegment(IMemorySegmentProvider cursor, int length) { } + internal new void Next(ReadOnlySequenceSegment value) => base.Next = value; + private new LazySegment Next(int length) { var index = RunningIndex; @@ -87,7 +76,7 @@ private LazySegment(IMemorySegmentProvider cursor, int length) return segment; } - internal static void AddSegment(ReadOnlySequenceSource cursor, int length, ref LazySegment? first, ref LazySegment? last) + internal static void AddSegment(ReadOnlySequenceSource cursor, int length, [AllowNull] ref LazySegment first, [AllowNull] ref LazySegment last) { if (first is null || last is null) first = last = new(cursor, length) { RunningIndex = 0L }; @@ -159,9 +148,8 @@ MemoryHandle IMemorySegmentProvider.Pin(in Segment window, int elementIndex) return buffer.Memory.Pin(); } - private (ReadOnlySequenceSegment, ReadOnlySequenceSegment) BuildSegments() + private void BuildSegments([AllowNull] ref LazySegment first, [AllowNull] ref LazySegment last) { - LazySegment? first = null, last = null; for (var remainingLength = RandomAccess.GetLength(handle); remainingLength > 0;) { var segmentLength = (int)Math.Min(this.segmentLength, remainingLength); @@ -171,7 +159,6 @@ MemoryHandle IMemorySegmentProvider.Pin(in Segment window, int elementIndex) Debug.Assert(first is not null); Debug.Assert(last is not null); - return (first, last); } /// @@ -185,11 +172,10 @@ public ReadOnlySequence Sequence { ThrowIfDisposed(); - var (first, last) = BuildSegments(); - if (!tail.IsEmpty) - last = new TailSegment(last, tail); - - return new ReadOnlySequence(first, 0, last, last.Memory.Length); + LazySegment? first = null, last = null; + BuildSegments(ref first, ref last); + ReadOnlySequenceSegment tail = this.tail.IsEmpty ? last : new TailSegment(last, this.tail); + return new(first, 0, tail, tail.Memory.Length); } } From e76f693a4b0122f10273066b1073474acb1a3b2d Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 00:13:38 +0200 Subject: [PATCH 040/155] Simplify arg check --- src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs index 0c433a97d..cbb486931 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs @@ -233,8 +233,8 @@ protected override void Dispose(bool disposing) /// The memory manager is already obtained but not disposed. public IReadOnlySequenceSource GetWrittenContent(int segmentSize) { - if (segmentSize <= 0) - throw new ArgumentOutOfRangeException(nameof(segmentSize)); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(segmentSize); + if (IsReading) throw new InvalidOperationException(ExceptionMessages.WriterInReadMode); From 397ac1320cbe64d42dc94d1100e7db6873a33840 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 08:22:40 +0200 Subject: [PATCH 041/155] Moved random extensions to RandomExtensions type --- src/DotNext.Tests/BasicExtensionsTests.cs | 16 ++ .../Collections/Generic/CollectionTests.cs | 22 --- .../Collections/Generic/ListTests.cs | 16 -- .../Collections/Generic/SequenceTests.cs | 45 ------ .../Specialized/SingletonListTests.cs | 2 +- src/DotNext.Tests/ComparableTests.cs | 20 --- src/DotNext.Tests/DelegateHelpersTests.cs | 1 - src/DotNext.Tests/RandomTests.cs | 62 ++++++++ src/DotNext.Tests/SpanTests.cs | 113 +++----------- src/DotNext.Tests/StringExtensionsTests.cs | 39 +---- src/DotNext/BasicExtensions.cs | 28 +++- src/DotNext/Collections/Generic/Collection.cs | 33 ---- src/DotNext/Collections/Generic/List.cs | 43 +----- src/DotNext/Collections/Generic/Sequence.cs | 145 ------------------ .../Collections/Specialized/SingletonList.cs | 33 +--- src/DotNext/Comparison.cs | 26 ---- src/DotNext/OneDimensionalArray.cs | 8 - src/DotNext/RandomExtensions.cs | 94 +++++++++++- src/DotNext/Span.Hex.cs | 47 ------ src/DotNext/Span.cs | 51 +----- src/DotNext/TupleExtensions.cs | 24 +-- .../HyParView/PeerController.Disconnect.cs | 2 +- .../HyParView/PeerController.ForwardJoin.cs | 2 +- .../HyParView/PeerController.Shuffle.cs | 4 +- .../Discovery/HyParView/PeerController.cs | 6 +- 25 files changed, 251 insertions(+), 631 deletions(-) delete mode 100644 src/DotNext.Tests/ComparableTests.cs delete mode 100644 src/DotNext/Comparison.cs delete mode 100644 src/DotNext/Span.Hex.cs diff --git a/src/DotNext.Tests/BasicExtensionsTests.cs b/src/DotNext.Tests/BasicExtensionsTests.cs index 6c38bdda9..e9c0fb473 100644 --- a/src/DotNext.Tests/BasicExtensionsTests.cs +++ b/src/DotNext.Tests/BasicExtensionsTests.cs @@ -219,4 +219,20 @@ public static void WeightOfInt32() { Equal(unchecked((uint)int.MaxValue).Normalize(), int.MaxValue.Normalize()); } + + [Fact] + public static void Range() + { + True(15M.IsBetween(10M, 20M)); + False(10M.IsBetween(10M, 20M, BoundType.Open)); + True(10M.IsBetween(10M, 20M, BoundType.LeftClosed)); + False(15M.IsBetween(10M, 12M)); + } + + [Fact] + public static void LeftGreaterThanRight() + { + False(4L.IsBetween(4L, 3L, BoundType.Closed)); + False(4L.IsBetween(4L, 4L, BoundType.LeftClosed)); + } } \ No newline at end of file diff --git a/src/DotNext.Tests/Collections/Generic/CollectionTests.cs b/src/DotNext.Tests/Collections/Generic/CollectionTests.cs index 80df54a32..97860d975 100644 --- a/src/DotNext.Tests/Collections/Generic/CollectionTests.cs +++ b/src/DotNext.Tests/Collections/Generic/CollectionTests.cs @@ -42,26 +42,4 @@ public static void ReadOnlyView() NotEmpty(view); All(view, static value => True(value.IsBetween(0, 3, BoundType.Closed))); } - - [Fact] - public static void PeekRandomFromEmptyCollection() - { - False(Array.Empty().PeekRandom(Random.Shared).HasValue); - } - - [Fact] - public static void PeekRandomFromSingletonCollection() - { - Equal(5, new int[] { 5 }.PeekRandom(Random.Shared)); - } - - [Fact] - public static void PeekRandomFromCollection() - { - IReadOnlyCollection collection = new int[] { 10, 20, 30 }; - All(Enumerable.Range(0, collection.Count), i => - { - True(collection.PeekRandom(Random.Shared).Value is 10 or 20 or 30); - }); - } } \ No newline at end of file diff --git a/src/DotNext.Tests/Collections/Generic/ListTests.cs b/src/DotNext.Tests/Collections/Generic/ListTests.cs index a1d1199ce..a2a57d027 100644 --- a/src/DotNext.Tests/Collections/Generic/ListTests.cs +++ b/src/DotNext.Tests/Collections/Generic/ListTests.cs @@ -114,22 +114,6 @@ public static void InsertRemove() Equal(2, list.Count); } - [Fact] - public static void ShuffleArray() - { - var array = new int[] { 1, 2, 3, 4, 5, 6, 7 }; - array.Shuffle(Random.Shared); - True(array[0] != 1 || array[1] != 2 || array[2] != 3 || array[3] != 4 || array[4] != 5); - } - - [Fact] - public static void ShuffleList() - { - var array = new List { 1, 2, 3, 4, 5, 6, 7 }; - array.Shuffle(Random.Shared); - True(array[0] != 1 || array[1] != 2 || array[2] != 3 || array[3] != 4 || array[4] != 5); - } - [Fact] public static void ArraySlice() { diff --git a/src/DotNext.Tests/Collections/Generic/SequenceTests.cs b/src/DotNext.Tests/Collections/Generic/SequenceTests.cs index 70f4acf3b..157b51445 100644 --- a/src/DotNext.Tests/Collections/Generic/SequenceTests.cs +++ b/src/DotNext.Tests/Collections/Generic/SequenceTests.cs @@ -41,17 +41,6 @@ public static async Task ForEachTestAsync() Equal(5, counter.value); } - [Fact] - public static void FirstOrNullTest() - { - var array = new long[0]; - var element = array.FirstOrNull(); - Null(element); - array = new long[] { 10, 20 }; - element = array.FirstOrNull(); - Equal(10, element); - } - [Fact] public static async Task FirstOrNullTestAsync() { @@ -201,40 +190,6 @@ public static void LimitedSequence() False(enumerator.MoveNext()); } - [Fact] - public static void Iteration() - { - IEnumerable collection = Array.Empty(); - Null(collection.FirstOrNull()); - Equal(Optional.None, collection.FirstOrNone()); - Equal(Optional.None, collection.FirstOrNone(Predicate.Constant(true))); - - collection = new int[] { 42 }; - Equal(42, collection.FirstOrNull()); - Equal(42, collection.FirstOrNone()); - Equal(42, collection.FirstOrNone(Predicate.Constant(true))); - - Equal('a', "ab".FirstOrNone()); - False(string.Empty.FirstOrNone().HasValue); - Equal('a', "ab".FirstOrNone(Predicate.Constant(true))); - False(string.Empty.FirstOrNone(Predicate.Constant(true)).HasValue); - } - - [Fact] - public static void Iteration2() - { - IEnumerable collection = Array.Empty(); - Null(collection.LastOrNull()); - Equal(Optional.None, collection.LastOrNone()); - - collection = new int[] { 42 }; - Equal(42, collection.LastOrNull()); - Equal(42, collection.LastOrNone()); - - Equal('b', "ab".LastOrNone()); - False(string.Empty.LastOrNone().HasValue); - } - [Fact] public static async Task IterationAsync() { diff --git a/src/DotNext.Tests/Collections/Specialized/SingletonListTests.cs b/src/DotNext.Tests/Collections/Specialized/SingletonListTests.cs index 30da5e656..78b73c47c 100644 --- a/src/DotNext.Tests/Collections/Specialized/SingletonListTests.cs +++ b/src/DotNext.Tests/Collections/Specialized/SingletonListTests.cs @@ -5,7 +5,7 @@ public sealed class SingletonListTests : Test [Fact] public static void ListInterop() { - IList list = new SingletonList(42); + IList list = new SingletonList { Item = 42 }; Equal(42, list[0]); True(list.IsReadOnly); Single(list); diff --git a/src/DotNext.Tests/ComparableTests.cs b/src/DotNext.Tests/ComparableTests.cs deleted file mode 100644 index d839d1786..000000000 --- a/src/DotNext.Tests/ComparableTests.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace DotNext; - -public sealed class ComparableTests : Test -{ - [Fact] - public static void BetweenTest() - { - True(15M.IsBetween(10M, 20M)); - False(10M.IsBetween(10M, 20M, BoundType.Open)); - True(10M.IsBetween(10M, 20M, BoundType.LeftClosed)); - False(15M.IsBetween(10M, 12M)); - } - - [Fact] - public static void LeftGreaterThanRight() - { - False(4L.IsBetween(4L, 3L, BoundType.Closed)); - False(4L.IsBetween(4L, 4L, BoundType.LeftClosed)); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/DelegateHelpersTests.cs b/src/DotNext.Tests/DelegateHelpersTests.cs index f80ad8603..a3dc6e15e 100644 --- a/src/DotNext.Tests/DelegateHelpersTests.cs +++ b/src/DotNext.Tests/DelegateHelpersTests.cs @@ -1,7 +1,6 @@ using System.Globalization; using System.Linq.Expressions; using System.Reflection; -using System.Text; namespace DotNext; diff --git a/src/DotNext.Tests/RandomTests.cs b/src/DotNext.Tests/RandomTests.cs index 3ecebbf7b..2de52ae72 100644 --- a/src/DotNext.Tests/RandomTests.cs +++ b/src/DotNext.Tests/RandomTests.cs @@ -54,4 +54,66 @@ public static void MakeRandomGuids() All(buffer, static v => NotEqual(Guid.Empty, v)); } + + [Theory] + [InlineData("abcd123456789", 6)] + [InlineData("abcd123456789", 7)] + [InlineData("0123456789ABCDEF", 12)] // allowedChars.Length is pow of 2 + public static void RandomString(string allowedChars, int length) + { + var str = Random.Shared.NextString(allowedChars, length); + Equal(length, str.Length); + All(str, ch => True(allowedChars.Contains(ch))); + + using var generator = RandomNumberGenerator.Create(); + str = generator.NextString(allowedChars, length); + Equal(length, str.Length); + All(str, ch => True(allowedChars.Contains(ch))); + } + + [Fact] + public static void RandomChars() + { + const string AllowedChars = "abcd123456789"; + var str = new char[6]; + + Random.Shared.NextChars(AllowedChars, str); + All(str, static ch => True(AllowedChars.Contains(ch))); + + using var generator = RandomNumberGenerator.Create(); + Array.Clear(str); + generator.NextChars(AllowedChars, str); + + All(str, static ch => True(AllowedChars.Contains(ch))); + } + + [Fact] + public static void PeekRandomFromEmptyCollection() + { + False(Random.Shared.Peek(Array.Empty()).HasValue); + } + + [Fact] + public static void PeekRandomFromSingletonCollection() + { + Equal(5, Random.Shared.Peek(new int[] { 5 })); + } + + [Fact] + public static void PeekRandomFromCollection() + { + IReadOnlyCollection collection = new int[] { 10, 20, 30 }; + All(Enumerable.Range(0, collection.Count), i => + { + True(Random.Shared.Peek(collection).Value is 10 or 20 or 30); + }); + } + + [Fact] + public static void ShuffleList() + { + var list = new List { 1, 2, 3, 4, 5, 6, 7 }; + Random.Shared.Shuffle(list); + NotEqual([1, 2, 3, 4, 5, 6, 7], list.ToArray()); + } } \ No newline at end of file diff --git a/src/DotNext.Tests/SpanTests.cs b/src/DotNext.Tests/SpanTests.cs index 10147ac17..fd93332ad 100644 --- a/src/DotNext.Tests/SpanTests.cs +++ b/src/DotNext.Tests/SpanTests.cs @@ -9,8 +9,8 @@ public sealed class SpanTests : Test [Fact] public static void BitwiseEquality() { - Span array1 = new Guid[] { Guid.Empty, Guid.NewGuid(), Guid.NewGuid() }; - Span array2 = new Guid[] { Guid.Empty, array1[1], array1[2] }; + Span array1 = [Guid.Empty, Guid.NewGuid(), Guid.NewGuid()]; + Span array2 = [Guid.Empty, array1[1], array1[2]]; True(array1.SequenceEqual(array2)); True(array1.BitwiseEquals(array2)); array2[1] = Guid.Empty; @@ -21,8 +21,8 @@ public static void BitwiseEquality() [Fact] public static void BitwiseComparison() { - Span array1 = new Guid[] { Guid.Empty, Guid.NewGuid(), Guid.NewGuid() }; - Span array2 = new Guid[] { Guid.Empty, array1[1], array1[2] }; + Span array1 = [Guid.Empty, Guid.NewGuid(), Guid.NewGuid()]; + Span array2 = [Guid.Empty, array1[1], array1[2]]; Equal(0, array1.BitwiseCompare(array2)); array2[1] = Guid.Empty; True(array1.BitwiseCompare(array2) > 0); @@ -89,73 +89,6 @@ public static void TrimByLength2() private static string ToHexSlow(byte[] data, bool lowercased) => string.Join(string.Empty, Array.ConvertAll(data, i => i.ToString(lowercased ? "x2" : "X2", null))); - [Theory] - [InlineData(0, true)] - [InlineData(7, true)] - [InlineData(10, true)] - [InlineData(128, true)] - [InlineData(2048, true)] - [InlineData(0, false)] - [InlineData(7, false)] - [InlineData(10, false)] - [InlineData(128, false)] - [InlineData(2048, false)] - [Obsolete] - public static void ToHexConversion(int arraySize, bool lowercased) - { - var data = RandomBytes(arraySize); - Equal(ToHexSlow(data, lowercased), new ReadOnlySpan(data).ToHex(lowercased)); - } - - [Fact] - [Obsolete] - public static void ToHexConversionVarLength() - { - ReadOnlySpan data = new byte[] { 1, 2 }; - char[] encoded = new char[1]; - Equal(0, data.ToHex(encoded)); - encoded = new char[2]; - Equal(2, data.ToHex(encoded)); - Equal('0', encoded[0]); - Equal('1', encoded[1]); - } - - [Fact] - [Obsolete] - public static void FromHexConversionVarLength() - { - ReadOnlySpan data = new char[] { 'F', 'F', 'A' }; - var decoded = new byte[1]; - Equal(1, data.FromHex(decoded)); - Equal(byte.MaxValue, decoded[0]); - data = "ABBA".AsSpan(); - decoded = new byte[2]; - Equal(2, data.FromHex(decoded)); - Equal(0xAB, decoded[0]); - Equal(0xBA, decoded[1]); - data = "abba".AsSpan(); - Equal(2, data.FromHex(decoded)); - Equal(0xAB, decoded[0]); - Equal(0xBA, decoded[1]); - data = default; - Equal(0, data.FromHex(decoded)); - } - - [Theory] - [InlineData(0, true)] - [InlineData(128, true)] - [InlineData(2048, true)] - [InlineData(0, false)] - [InlineData(128, false)] - [InlineData(2048, false)] - [Obsolete] - public static void FromHexConversion(int arraySize, bool lowercased) - { - var data = RandomBytes(arraySize); - ReadOnlySpan hex = ToHexSlow(data, lowercased); - Equal(data, hex.FromHex()); - } - public static IEnumerable TestAllocators() { yield return new object[] { null }; @@ -505,7 +438,7 @@ public static void SpanContravariance() [Fact] public static void SplitSpanByLength() { - Span chars = new char[] { 'a', 'b', 'c', 'd' }; + Span chars = ['a', 'b', 'c', 'd']; var head = chars.TrimLength(2, out var rest); Equal("ab", head.ToString()); Equal("cd", rest.ToString()); @@ -534,39 +467,39 @@ public static void SwapElements() public static void TransformElements() { // left < right - Span input = new int[] { 1, 2, 3, 4, 5, 6 }; + Span input = [1, 2, 3, 4, 5, 6]; input.Swap(0..2, 3..6); Equal(new int[] { 4, 5, 6, 3, 1, 2 }, input.ToArray()); // left > right - input = new int[] { 1, 2, 3, 4, 5, 6 }; + input = [1, 2, 3, 4, 5, 6]; input.Swap(0..3, 4..6); - Equal(new int[] { 5, 6, 4, 1, 2, 3 }, input.ToArray()); + Equal([5, 6, 4, 1, 2, 3], input.ToArray()); // left is zero length - input = new int[] { 1, 2, 3, 4, 5, 6 }; + input = [1, 2, 3, 4, 5, 6]; input.Swap(1..1, 3..6); - Equal(new int[] { 1, 4, 5, 6, 2, 3 }, input.ToArray()); + Equal([1, 4, 5, 6, 2, 3], input.ToArray()); // right is zero length - input = new int[] { 1, 2, 3, 4, 5, 6 }; + input = [1, 2, 3, 4, 5, 6]; input.Swap(0..2, 3..3); - Equal(new int[] { 3, 1, 2, 4, 5, 6 }, input.ToArray()); + Equal([3, 1, 2, 4, 5, 6], input.ToArray()); // no space between ranges - input = new int[] { 1, 2, 3, 4, 5, 6 }; + input = [1, 2, 3, 4, 5, 6]; input.Swap(0..2, 2..6); - Equal(new int[] { 3, 4, 5, 6, 1, 2 }, input.ToArray()); + Equal([3, 4, 5, 6, 1, 2], input.ToArray()); // left == right - input = new int[] { 1, 2, 3, 4, 5, 6 }; + input = [1, 2, 3, 4, 5, 6]; input.Swap(0..3, 3..6); - Equal(new int[] { 4, 5, 6, 1, 2, 3 }, input.ToArray()); + Equal([4, 5, 6, 1, 2, 3], input.ToArray()); // left and right are empty - input = new int[] { 1, 2, 3, 4, 5, 6 }; + input = [1, 2, 3, 4, 5, 6]; input.Swap(1..1, 5..5); - Equal(new int[] { 1, 2, 3, 4, 5, 6 }, input.ToArray()); + Equal([1, 2, 3, 4, 5, 6], input.ToArray()); // overlapping Throws(() => new int[] { 1, 2, 3, 4, 5, 6 }.AsSpan().Swap(0..2, 1..3)); @@ -576,18 +509,18 @@ public static void TransformElements() public static void MoveRange() { // move from left to right - Span input = new int[] { 1, 2, 3, 4, 5, 6 }; + Span input = [1, 2, 3, 4, 5, 6]; input.Move(0..2, 3); - Equal(new int[] { 3, 1, 2, 4, 5, 6 }, input.ToArray()); + Equal([3, 1, 2, 4, 5, 6], input.ToArray()); // move from left to right - input = new int[] { 1, 2, 3, 4, 5, 6 }; + input = [1, 2, 3, 4, 5, 6]; input.Move(1..3, 6); - Equal(new int[] { 1, 4, 5, 6, 2, 3 }, input.ToArray()); + Equal([1, 4, 5, 6, 2, 3], input.ToArray()); // move from right to left input.Move(4..6, 1); - Equal(new int[] { 1, 2, 3, 4, 5, 6 }, input.ToArray()); + Equal([1, 2, 3, 4, 5, 6], input.ToArray()); // out of range Throws(() => new int[] { 1, 2, 3, 4, 5, 6 }.AsSpan().Move(0..2, 1)); diff --git a/src/DotNext.Tests/StringExtensionsTests.cs b/src/DotNext.Tests/StringExtensionsTests.cs index e4de48286..04fc6a7ea 100644 --- a/src/DotNext.Tests/StringExtensionsTests.cs +++ b/src/DotNext.Tests/StringExtensionsTests.cs @@ -1,5 +1,4 @@ -using System.Security.Cryptography; -using System.Text; +using System.Text; namespace DotNext; @@ -14,42 +13,6 @@ public static void IfNullOrEmptyTest() Equal("b", "b".IfNullOrEmpty("a")); } - [Theory] - [InlineData("abcd123456789", 6)] - [InlineData("abcd123456789", 7)] - [InlineData("0123456789ABCDEF", 12)] // allowedChars.Length is pow of 2 - public static void RandomStringTest(string allowedChars, int length) - { - var str = Random.Shared.NextString(allowedChars, length); - Equal(length, str.Length); - All(str, ch => True(allowedChars.Contains(ch))); - - using (var generator = RandomNumberGenerator.Create()) - { - str = generator.NextString(allowedChars, length); - Equal(length, str.Length); - All(str, ch => True(allowedChars.Contains(ch))); - } - } - - [Fact] - public static void RandomChars() - { - const string AllowedChars = "abcd123456789"; - var str = new char[6]; - - Random.Shared.NextChars(AllowedChars, str); - All(str, static ch => True(AllowedChars.Contains(ch))); - - using (var generator = RandomNumberGenerator.Create()) - { - Array.Clear(str); - generator.NextChars(AllowedChars, str); - - All(str, static ch => True(AllowedChars.Contains(ch))); - } - } - [Fact] public static void ReverseTest() { diff --git a/src/DotNext/BasicExtensions.cs b/src/DotNext/BasicExtensions.cs index 1b11f8ccd..03d1f412b 100644 --- a/src/DotNext/BasicExtensions.cs +++ b/src/DotNext/BasicExtensions.cs @@ -1,4 +1,5 @@ -using System.Numerics; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; using System.Runtime.CompilerServices; using static System.Globalization.CultureInfo; using static InlineIL.IL; @@ -215,4 +216,29 @@ public static float Normalize(this uint value) /// The normalized value in range [0..1). public static float Normalize(this int value) => Normalize(unchecked((uint)value)); + + /// + /// Checks whether specified value is in range. + /// + /// Type of value to check. + /// Value to check. + /// Range left bound. + /// Range right bound. + /// Range endpoints bound type. + /// , if is in its bounds. + public static bool IsBetween(this T value, T left, T right, [ConstantExpected] BoundType boundType = BoundType.Open) + where T : notnull, IComparable + { + int l = value.CompareTo(left), r = value.CompareTo(right); + return (l > 0 || (l is 0 && (boundType & BoundType.LeftClosed) is not 0)) + && (r < 0 || (r is 0 && (boundType & BoundType.RightClosed) is not 0)); + } + + /// + /// Indicates that array is or empty. + /// + /// The array to check. + /// , if array is or empty. + public static bool IsNullOrEmpty([NotNullWhen(false)] this Array? array) + => array is null || Runtime.Intrinsics.GetLength(array) is 0; } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Collection.cs b/src/DotNext/Collections/Generic/Collection.cs index 5edf50a1e..77a7350f1 100644 --- a/src/DotNext/Collections/Generic/Collection.cs +++ b/src/DotNext/Collections/Generic/Collection.cs @@ -80,37 +80,4 @@ public static void AddAll(this ICollection collection, IEnumerable item break; } } - - /// - /// Gets the random element from the collection. - /// - /// The type of elements in the collection. - /// The collection to get the random element. - /// The random numbers source. - /// The random element from the collection; or if collection is empty. - public static Optional PeekRandom(this IReadOnlyCollection collection, Random random) - { - return collection.Count switch - { - 0 => Optional.None, - 1 => collection.FirstOrNone(), - _ when collection is T[] array => Span.PeekRandom(array, random), - _ when collection is List list => Span.PeekRandom(CollectionsMarshal.AsSpan(list), random), - _ => PeekRandomSlow(collection, random), - }; - - [MethodImpl(MethodImplOptions.NoInlining)] - static Optional PeekRandomSlow(IReadOnlyCollection collection, Random random) - { - var index = random.Next(collection.Count); - using var enumerator = collection.GetEnumerator(); - for (var i = 0; enumerator.MoveNext(); i++) - { - if (i == index) - return enumerator.Current; - } - - return Optional.None; - } - } } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/List.cs b/src/DotNext/Collections/Generic/List.cs index 6a6088bc1..ec5ff06ae 100644 --- a/src/DotNext/Collections/Generic/List.cs +++ b/src/DotNext/Collections/Generic/List.cs @@ -196,7 +196,7 @@ public static ReadOnlyListView Convert(this IR /// An item to be placed into list. /// Type of list items. /// Read-only list containing single item. - public static IReadOnlyList Singleton(T item) => new Specialized.SingletonList(item); + public static IReadOnlyList Singleton(T item) => new Specialized.SingletonList { Item = item }; /// /// Inserts the item into sorted list. @@ -214,7 +214,7 @@ public static ReadOnlyListView Convert(this IR /// The comparer function. /// The actual index of the inserted item. public static int InsertOrdered(this List list, T item, TComparer comparer) - where TComparer : IComparer + where TComparer : notnull, IComparer { var span = CollectionsMarshal.AsSpan(list); var low = 0; @@ -245,7 +245,7 @@ public static int InsertOrdered(this List list, T item, TCompar /// The comparer function. /// The actual index of the inserted item. public static int InsertOrdered(this IList list, T item, TComparer comparer) - where TComparer : IComparer + where TComparer : notnull, IComparer { var low = 0; for (var high = list.Count; low < high;) @@ -336,41 +336,4 @@ public static void RemoveAt(this IList list, Index index) /// The section of the list. public static ListSegment Slice(this IList list, Range range) => new(list, range); - - /// - /// Randomizes elements in the list. - /// - /// The type of items in the list. - /// The list to shuffle. - /// The source of random values. - public static void Shuffle(this IList list, Random random) - { - Span span; - switch (list) - { - case List typedList: - span = CollectionsMarshal.AsSpan(typedList); - break; - case T[] array: - span = array; - break; - default: - ShuffleSlow(list, random); - return; - } - - random.Shuffle(span); - - [MethodImpl(MethodImplOptions.NoInlining)] - static void ShuffleSlow(IList list, Random random) - { - for (var i = list.Count - 1; i > 0; i--) - { - var randomIndex = random.Next(i + 1); - T item = list[randomIndex]; - list[randomIndex] = list[i]; - list[i] = item; - } - } - } } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.cs b/src/DotNext/Collections/Generic/Sequence.cs index 747dd0690..8431d5f3d 100644 --- a/src/DotNext/Collections/Generic/Sequence.cs +++ b/src/DotNext/Collections/Generic/Sequence.cs @@ -89,151 +89,6 @@ public static async ValueTask ForEachAsync(this IEnumerable collection, Fu await action.Invoke(item, token).ConfigureAwait(false); } - /// - /// Obtains first value type in the sequence; or - /// if sequence is empty. - /// - /// Type of elements in the sequence. - /// A sequence to check. Cannot be . - /// First element in the sequence; or if sequence is empty. - public static T? FirstOrNull(this IEnumerable seq) - where T : struct - => FirstOrNone(seq).OrNull(); - - /// - /// Obtains the last value type in the sequence; or - /// if sequence is empty. - /// - /// Type of elements in the sequence. - /// A sequence to check. Cannot be . - /// The last element in the sequence; or if sequence is empty. - public static T? LastOrNull(this IEnumerable seq) - where T : struct - => LastOrNone(seq).OrNull(); - - /// - /// Obtains first value in the sequence; or - /// if sequence is empty. - /// - /// Type of elements in the sequence. - /// A sequence to check. Cannot be . - /// The first element in the sequence; or if sequence is empty. - [Obsolete("Use FirstOrNone() extension method instead")] - public static Optional FirstOrEmpty(this IEnumerable seq) - => FirstOrNone(seq); - - /// - /// Obtains first value in the sequence; or - /// if sequence is empty. - /// - /// Type of elements in the sequence. - /// A sequence to check. Cannot be . - /// The first element in the sequence; or if sequence is empty. - public static Optional FirstOrNone(this IEnumerable seq) - { - return seq switch - { - List list => Span.FirstOrNone(CollectionsMarshal.AsSpan(list)), - T[] array => Span.FirstOrNone(array), - string str => ReinterpretCast, Optional>(str.AsSpan().FirstOrNone()), // Workaround for https://github.com/dotnet/runtime/issues/57484 - IList list => list.Count > 0 ? list[0] : Optional.None, - IReadOnlyList list => list.Count > 0 ? list[0] : Optional.None, - _ => FirstOrNoneSlow(seq), - }; - - [MethodImpl(MethodImplOptions.NoInlining)] - static Optional FirstOrNoneSlow(IEnumerable seq) - { - using var enumerator = seq.GetEnumerator(); - return enumerator.MoveNext() ? enumerator.Current : Optional.None; - } - } - - /// - /// Obtains first value in the sequence; or - /// if sequence is empty. - /// - /// Type of elements in the sequence. - /// A sequence to check. Cannot be . - /// The first element in the sequence; or if sequence is empty. - public static Optional LastOrNone(this IEnumerable seq) - { - return seq switch - { - List list => Span.LastOrNone(CollectionsMarshal.AsSpan(list)), - T[] array => Span.LastOrNone(array), - string str => ReinterpretCast, Optional>(str.AsSpan().LastOrNone()), // Workaround for https://github.com/dotnet/runtime/issues/57484 - IList list => list.Count > 0 ? list[list.Count - 1] : Optional.None, - IReadOnlyList list => list.Count > 0 ? list[list.Count - 1] : Optional.None, - _ => LastOrNoneSlow(seq), - }; - - [MethodImpl(MethodImplOptions.NoInlining)] - static Optional LastOrNoneSlow(IEnumerable seq) - { - var result = Optional.None; - foreach (var item in seq) - result = item; - - return result; - } - } - - /// - /// Returns the first element in a sequence that satisfies a specified condition. - /// - /// The type of the elements of source. - /// A collection to return an element from. - /// A function to test each element for a condition. - /// The first element in the sequence that matches to the specified filter; or . - [Obsolete("Use FirstOrNone() extension method instead")] - public static Optional FirstOrEmpty(this IEnumerable seq, Predicate filter) - => FirstOrNone(seq, filter); - - /// - /// Returns the first element in a sequence that satisfies a specified condition. - /// - /// The type of the elements of source. - /// A collection to return an element from. - /// A function to test each element for a condition. - /// The first element in the sequence that matches to the specified filter; or . - public static Optional FirstOrNone(this IEnumerable seq, Predicate filter) - { - return seq switch - { - List list => Span.FirstOrNone(CollectionsMarshal.AsSpan(list), filter), - T[] array => Span.FirstOrNone(array, filter), - string str => ReinterpretCast, Optional>(str.AsSpan().FirstOrNone(Unsafe.As>(filter))), - LinkedList list => FindInLinkedList(list, filter), - _ => FirstOrNoneSlow(seq, filter) - }; - - [MethodImpl(MethodImplOptions.NoInlining)] - static Optional FindInLinkedList(LinkedList list, Predicate filter) - { - for (var node = list.First; node is not null; node = node.Next) - { - var value = node.Value; - if (filter(value)) - return value; - } - - return Optional.None; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static Optional FirstOrNoneSlow(IEnumerable seq, Predicate filter) - { - foreach (var item in seq) - { - if (filter.Invoke(item)) - return item; - } - - return Optional.None; - } - } - /// /// Bypasses a specified number of elements in a sequence. /// diff --git a/src/DotNext/Collections/Specialized/SingletonList.cs b/src/DotNext/Collections/Specialized/SingletonList.cs index 4e8e372bb..b380e1c44 100644 --- a/src/DotNext/Collections/Specialized/SingletonList.cs +++ b/src/DotNext/Collections/Specialized/SingletonList.cs @@ -1,7 +1,5 @@ using System.Collections; using System.ComponentModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -48,7 +46,7 @@ internal Enumerator(T item) /// if the enumerator advanced successfully; otherwise, . public bool MoveNext() { - if (state == NotRequestedState) + if (state is NotRequestedState) { state = RequestedState; return true; @@ -62,25 +60,15 @@ public bool MoveNext() /// public void Reset() { - if (state == RequestedState) + if (state is RequestedState) state = NotRequestedState; } } /// - /// Initializes a new list with one element. + /// The item of the list. /// - /// The element in the list. - public SingletonList(T item) => Item = item; - - /// - /// Gets or sets the item in this list. - /// - public T Item - { - readonly get; - set; - } + required public T Item; /// /// Gets or sets the item in this list. @@ -94,26 +82,19 @@ public T this[int index] { readonly get { - if (index != 0) - ThrowIndexOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0); return Item; } set { - if (index != 0) - ThrowIndexOutOfRangeException(); + ArgumentOutOfRangeException.ThrowIfNotEqual(index, 0); Item = value; } } - [DoesNotReturn] - [StackTraceHidden] - private static void ThrowIndexOutOfRangeException() - => throw new IndexOutOfRangeException(ExceptionMessages.IndexShouldBeZero); - /// readonly object? ITuple.this[int index] => this[index]; @@ -171,5 +152,5 @@ readonly void ICollection.CopyTo(T[] array, int arrayIndex) /// /// The item in the list. /// The collection containing the list. - public static implicit operator SingletonList(T item) => new(item); + public static implicit operator SingletonList(T item) => new() { Item = item }; } \ No newline at end of file diff --git a/src/DotNext/Comparison.cs b/src/DotNext/Comparison.cs deleted file mode 100644 index 32a444bbc..000000000 --- a/src/DotNext/Comparison.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext; - -/// -/// Provides generic methods to work with comparable values. -/// -public static class Comparison -{ - /// - /// Checks whether specified value is in range. - /// - /// Type of value to check. - /// Value to check. - /// Range left bound. - /// Range right bound. - /// Range endpoints bound type. - /// , if is in its bounds. - public static bool IsBetween(this T value, T left, T right, BoundType boundType = BoundType.Open) - where T : notnull, IComparable - { - int l = value.CompareTo(left), r = value.CompareTo(right); - return (l > 0 || (l is 0 && (boundType & BoundType.LeftClosed) is not 0)) - && (r < 0 || (r is 0 && (boundType & BoundType.RightClosed) is not 0)); - } -} \ No newline at end of file diff --git a/src/DotNext/OneDimensionalArray.cs b/src/DotNext/OneDimensionalArray.cs index 107cae271..abc2c9e5f 100644 --- a/src/DotNext/OneDimensionalArray.cs +++ b/src/DotNext/OneDimensionalArray.cs @@ -36,14 +36,6 @@ public static T[] Concat(this T[] left, T[] right, long startIndex) return result; } - /// - /// Indicates that array is or empty. - /// - /// The array to check. - /// , if array is or empty. - public static bool IsNullOrEmpty([NotNullWhen(false)] this Array? array) - => array is null || Intrinsics.GetLength(array) == 0; - /// /// Applies specific action to each array element. /// diff --git a/src/DotNext/RandomExtensions.cs b/src/DotNext/RandomExtensions.cs index c3eac2f6e..8474d2d99 100644 --- a/src/DotNext/RandomExtensions.cs +++ b/src/DotNext/RandomExtensions.cs @@ -178,9 +178,7 @@ private static unsafe void GetItems(TRandom random, Span values) public static string NextString(this Random random, ReadOnlySpan allowedChars, int length) { ArgumentNullException.ThrowIfNull(random); - - if (length < 0) - throw new ArgumentOutOfRangeException(nameof(length)); + ArgumentOutOfRangeException.ThrowIfNegative(length); string result; if (length is 0 || allowedChars.IsEmpty) @@ -364,8 +362,6 @@ public static T Next(this RandomNumberGenerator random) return result; } - /// - /// Fills the buffer with random values of the specified type. /// /// The blittable type. /// The source of random numbers. @@ -385,4 +381,92 @@ public static void GetItems(this Random random, Span buffer) public static void GetItems(this RandomNumberGenerator random, Span buffer) where T : unmanaged => GetItems(random ?? throw new ArgumentNullException(nameof(random)), buffer); + + /// + /// Randomizes elements in the list. + /// + /// The type of items in the list. + /// The source of random values. + /// The list to shuffle. + public static void Shuffle(this Random random, IList list) + { + Span span; + switch (list) + { + case List typedList: + span = CollectionsMarshal.AsSpan(typedList); + break; + case T[] array: + span = array; + break; + default: + ShuffleSlow(random, list); + return; + } + + random.Shuffle(span); + + [MethodImpl(MethodImplOptions.NoInlining)] + static void ShuffleSlow(Random random, IList list) + { + for (var i = list.Count - 1; i > 0; i--) + { + var randomIndex = random.Next(i + 1); + (list[i], list[randomIndex]) = (list[randomIndex], list[i]); + } + } + } + + /// + /// Gets the random element from the collection. + /// + /// The type of elements in the collection. + /// The random numbers source. + /// The collection to get the random element. + /// The random element from the collection; or if collection is empty. + public static Optional Peek(this Random random, IReadOnlyCollection collection) + { + return collection switch + { + null => throw new ArgumentNullException(nameof(collection)), + { Count: 0 } => Optional.None, + { Count: 1 } => collection.ElementAt(0), + T[] array => random.Peek(array.AsSpan()), + List list => random.Peek(CollectionsMarshal.AsSpan(list)), + _ => PeekRandomSlow(random, collection), + }; + + [MethodImpl(MethodImplOptions.NoInlining)] + static Optional PeekRandomSlow(Random random, IReadOnlyCollection collection) + { + var index = random.Next(collection.Count); + using var enumerator = collection.GetEnumerator(); + for (var i = 0; enumerator.MoveNext(); i++) + { + if (i == index) + return enumerator.Current; + } + + return Optional.None; + } + } + + /// + /// Chooses the random element in the span. + /// + /// The type of elements in the span. + /// The source of random values. + /// The span of elements. + /// Randomly selected element from the span; or if span is empty. + public static Optional Peek(this Random random, ReadOnlySpan span) + { + var length = span.Length; + + return length switch + { + 0 => Optional.None, + 1 => MemoryMarshal.GetReference(span), + _ => span[random.Next(length)], + }; + } } \ No newline at end of file diff --git a/src/DotNext/Span.Hex.cs b/src/DotNext/Span.Hex.cs deleted file mode 100644 index a92be8020..000000000 --- a/src/DotNext/Span.Hex.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace DotNext; - -using Hex = Buffers.Text.Hex; - -public static partial class Span -{ - /// - /// Converts set of bytes into hexadecimal representation. - /// - /// The bytes to convert. - /// The buffer used to write hexadecimal representation of bytes. - /// to return lowercased hex string; to return uppercased hex string. - /// The actual number of characters in written by the method. - [Obsolete("Use DotNext.Buffers.Text.Hex.EncodeToUtf16 method instead.")] - public static int ToHex(this ReadOnlySpan bytes, Span output, bool lowercased = false) - => Hex.EncodeToUtf16(bytes, output, lowercased); - - /// - /// Converts set of bytes into hexadecimal representation. - /// - /// The bytes to convert. - /// to return lowercased hex string; to return uppercased hex string. - /// The hexadecimal representation of bytes. - [Obsolete("Use DotNext.Buffers.Text.Hex.EncodeToUtf16 method instead.")] - public static string ToHex(this ReadOnlySpan bytes, bool lowercased = false) - => Hex.EncodeToUtf16(bytes, lowercased); - - /// - /// Decodes hexadecimal representation of bytes. - /// - /// The hexadecimal representation of bytes. - /// The output buffer used to write decoded bytes. - /// The actual number of bytes in written by the method. - /// contain invalid hexadecimal symbol. - [Obsolete("Use DotNext.Buffers.Text.Hex.DecodeFromUtf16 method instead.")] - public static int FromHex(this ReadOnlySpan chars, Span output) - => Hex.DecodeFromUtf16(chars, output); - - /// - /// Decodes hexadecimal representation of bytes. - /// - /// The characters containing hexadecimal representation of bytes. - /// The decoded array of bytes. - /// contain invalid hexadecimal symbol. - [Obsolete("Use Convert.FromHexString() method instead")] - public static byte[] FromHex(this ReadOnlySpan chars) => Convert.FromHexString(chars); -} \ No newline at end of file diff --git a/src/DotNext/Span.cs b/src/DotNext/Span.cs index bf2156fdc..e6fdd8857 100644 --- a/src/DotNext/Span.cs +++ b/src/DotNext/Span.cs @@ -320,7 +320,7 @@ public static Span TrimLength(this Span span, int maxLength, out Span.Empty; + return default; default: return TrimLengthCore(span, maxLength, out rest); } @@ -451,7 +451,7 @@ public static unsafe Span AsBytes(ref T value) /// The type of the pointer. /// The span of contiguous memory. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan AsReadOnlyBytes(in T value) + public static ReadOnlySpan AsReadOnlyBytes(ref readonly T value) where T : unmanaged => AsBytes(ref AsRef(in value)); @@ -579,36 +579,6 @@ public static void CopyTo(this ReadOnlySpan source, Span destination, o public static void CopyTo(this Span source, Span destination, out int writtenCount) => CopyTo((ReadOnlySpan)source, destination, out writtenCount); - /// - /// Gets first element in the span. - /// - /// The type of elements in the span. - /// The span of elements. - /// The first element in the span; or if span is empty. - [Obsolete("Use FirstOrNone() extension method instead")] - public static Optional FirstOrEmpty(this ReadOnlySpan span) - => FirstOrNone(span); - - /// - /// Gets first element in the span. - /// - /// The type of elements in the span. - /// The span of elements. - /// The first element in the span; or if span is empty. - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010", Justification = "False positive")] - public static Optional FirstOrNone(this ReadOnlySpan span) // TODO: Remove in the next version because of list pattern matching - => span is [var result, ..] ? result : Optional.None; - - /// - /// Gets the last element in the span. - /// - /// The type of elements in the span. - /// The span of elements. - /// The last element in the span; or if span is empty. - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010", Justification = "False positive")] - public static Optional LastOrNone(this ReadOnlySpan span) - => span is [.., var result] ? result : Optional.None; - /// /// Returns the first element in a span that satisfies a specified condition. /// @@ -631,20 +601,6 @@ public static Optional FirstOrNone(this ReadOnlySpan span, Predicate return Optional.None; } - /// - /// Chooses the random element in the span. - /// - /// The type of elements in the span. - /// The span of elements. - /// The source of random values. - /// Randomly selected element from the span; or if span is empty. - public static Optional PeekRandom(this ReadOnlySpan span, Random random) => span.Length switch - { - 0 => Optional.None, - 1 => MemoryMarshal.GetReference(span), - int length => span[random.Next(length)], // cannot use MemoryMarshal here because Random.Next is virtual so bounds check required for security reasons - }; - internal static bool ElementAt(ReadOnlySpan span, int index, [MaybeNullWhen(false)] out T element) { if ((uint)index < (uint)span.Length) @@ -749,8 +705,7 @@ public static ReadOnlySpan Contravariance(this ReadOnlySpan /// overlaps with . public static void Swap(this Span x, Span y) { - if (x.Length != y.Length) - throw new ArgumentOutOfRangeException(nameof(y)); + ArgumentOutOfRangeException.ThrowIfNotEqual(x.Length, y.Length, nameof(y)); if (x.Overlaps(y)) throw new ArgumentException(ExceptionMessages.OverlappedRange, nameof(y)); diff --git a/src/DotNext/TupleExtensions.cs b/src/DotNext/TupleExtensions.cs index ad53b1e3f..c656162fb 100644 --- a/src/DotNext/TupleExtensions.cs +++ b/src/DotNext/TupleExtensions.cs @@ -28,7 +28,7 @@ public static class TupleExtensions } else { - result = Array.Empty(); + result = []; } return result; @@ -45,7 +45,7 @@ private static Span TupleToSpan(ref TTuple tuple) /// The type of items in the tuple. /// The span over items in the tuple. public static Span AsSpan(this ref ValueTuple tuple) - => Span.Empty; + => []; /// /// Obtains read-only span over tuple items. @@ -54,7 +54,7 @@ public static Span AsSpan(this ref ValueTuple tuple) /// The type of items in the tuple. /// The span over items in the tuple. public static ReadOnlySpan AsReadOnlySpan(this in ValueTuple tuple) - => ReadOnlySpan.Empty; + => []; /// /// Obtains a span over tuple items. @@ -63,7 +63,7 @@ public static ReadOnlySpan AsReadOnlySpan(this in ValueTuple tuple) /// The type of items in the tuple. /// The span over items in the tuple. public static Span AsSpan(this ref ValueTuple tuple) - => TupleToSpan>(ref tuple); + => new(ref tuple.Item1); /// /// Obtains read-only span over tuple items. @@ -71,8 +71,8 @@ public static Span AsSpan(this ref ValueTuple tuple) /// The tuple. /// The type of items in the tuple. /// The span over items in the tuple. - public static ReadOnlySpan AsReadOnlySpan(this in ValueTuple tuple) - => TupleToSpan>(ref AsRef(in tuple)); + public static ReadOnlySpan AsReadOnlySpan(this ref readonly ValueTuple tuple) + => new(in tuple.Item1); /// /// Obtains a span over tuple items. @@ -89,7 +89,7 @@ public static Span AsSpan(this ref (T, T) tuple) /// The tuple. /// The type of items in the tuple. /// The span over items in the tuple. - public static ReadOnlySpan AsReadOnlySpan(this in (T, T) tuple) + public static ReadOnlySpan AsReadOnlySpan(this ref readonly (T, T) tuple) => TupleToSpan>(ref AsRef(in tuple)); /// @@ -107,7 +107,7 @@ public static Span AsSpan(this ref (T, T, T) tuple) /// The tuple. /// The type of items in the tuple. /// The span over items in the tuple. - public static ReadOnlySpan AsReadOnlySpan(this in (T, T, T) tuple) + public static ReadOnlySpan AsReadOnlySpan(this ref readonly (T, T, T) tuple) => TupleToSpan(ref AsRef(in tuple)); /// @@ -125,7 +125,7 @@ public static Span AsSpan(this ref (T, T, T, T) tuple) /// The tuple. /// The type of items in the tuple. /// The span over items in the tuple. - public static ReadOnlySpan AsReadOnlySpan(this in (T, T, T, T) tuple) + public static ReadOnlySpan AsReadOnlySpan(this ref readonly (T, T, T, T) tuple) => TupleToSpan(ref AsRef(in tuple)); /// @@ -143,7 +143,7 @@ public static Span AsSpan(this ref (T, T, T, T, T) tuple) /// The tuple. /// The type of items in the tuple. /// The span over items in the tuple. - public static ReadOnlySpan AsReadOnlySpan(this in (T, T, T, T, T) tuple) + public static ReadOnlySpan AsReadOnlySpan(this ref readonly (T, T, T, T, T) tuple) => TupleToSpan(ref AsRef(in tuple)); /// @@ -161,7 +161,7 @@ public static Span AsSpan(this ref (T, T, T, T, T, T) tuple) /// The tuple. /// The type of items in the tuple. /// The span over items in the tuple. - public static ReadOnlySpan AsReadOnlySpan(this in (T, T, T, T, T, T) tuple) + public static ReadOnlySpan AsReadOnlySpan(this ref readonly (T, T, T, T, T, T) tuple) => TupleToSpan(ref AsRef(in tuple)); /// @@ -179,6 +179,6 @@ public static Span AsSpan(this ref (T, T, T, T, T, T, T) tuple) /// The tuple. /// The type of items in the tuple. /// The span over items in the tuple. - public static ReadOnlySpan AsReadOnlySpan(this in (T, T, T, T, T, T, T) tuple) + public static ReadOnlySpan AsReadOnlySpan(this ref readonly (T, T, T, T, T, T, T) tuple) => TupleToSpan(ref AsRef(in tuple)); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Disconnect.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Disconnect.cs index a36e3d997..be454a3ea 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Disconnect.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Disconnect.cs @@ -57,7 +57,7 @@ private async Task ProcessDisconnectAsync(EndPoint sender, bool isAlive) OnPeerGone(sender); // move random peer from passive view to active view - while (passiveView.PeekRandom(random).TryGet(out var candidate)) + while (random.Peek(passiveView).TryGet(out var candidate)) { try { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.ForwardJoin.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.ForwardJoin.cs index 928abb3ff..0d744d480 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.ForwardJoin.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.ForwardJoin.cs @@ -41,7 +41,7 @@ private async Task ProcessForwardJoinAsync(EndPoint sender, EndPoint joinedPeer, if (timeToLive == passiveRandomWalkLength) await AddPeerToPassiveViewAsync(joinedPeer).ConfigureAwait(false); - await (activeView.Except(new[] { sender, joinedPeer }).PeekRandom(random).TryGet(out var randomActivePeer) + await (random.Peek(activeView.Except(new[] { sender, joinedPeer })).TryGet(out var randomActivePeer) ? ForwardJoinAsync(randomActivePeer, joinedPeer, timeToLive - 1, LifecycleToken) : AddPeerToActiveViewAsync(joinedPeer, true)).ConfigureAwait(false); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs index c8bd4dd72..904d3e984 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Shuffle.cs @@ -25,7 +25,7 @@ static async ValueTask SuspendAsync(Task queueCompletion, TimeSpan period, private async Task ProcessShuffleAsync() { - if (activeView.PeekRandom(random).TryGet(out var activePeer)) + if (random.Peek(activeView).TryGet(out var activePeer)) { PooledArrayBufferWriter peersToSend; @@ -128,7 +128,7 @@ private async Task ProcessShuffleAsync(EndPoint sender, EndPoint origin, IReadOn await AddPeersToPassiveViewAsync(announcement).ConfigureAwait(false); } - else if (activeView.Except(new[] { sender, origin }).PeekRandom(random).TryGet(out var activePeer)) + else if (random.Peek(activeView.Except(new[] { sender, origin })).TryGet(out var activePeer)) { // resend announcement await ShuffleAsync(activePeer, origin, announcement, ttl - 1, LifecycleToken).ConfigureAwait(false); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.cs index fdec3b7c9..138c29c2d 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.cs @@ -258,7 +258,7 @@ private ValueTask AddPeerToPassiveViewAsync(EndPoint peer) if (activeView.Contains(peer) || passiveView.Contains(peer) || IsLocalNode(peer)) goto exit; - if (passiveView.Count >= passiveViewCapacity && passiveView.PeekRandom(random).TryGet(out var removedPeer)) + if (passiveView.Count >= passiveViewCapacity && random.Peek(passiveView).TryGet(out var removedPeer)) { passiveView = passiveView.Remove(removedPeer); result = removedPeer; @@ -278,7 +278,7 @@ private async ValueTask AddPeersToPassiveViewAsync(IEnumerable peers) if (activeView.Contains(peer) || passiveViewCopy.Contains(peer)) continue; - if (passiveViewCopy.Count >= passiveViewCapacity && passiveViewCopy.PeekRandom(random).TryGet(out var removedPeer)) + if (passiveViewCopy.Count >= passiveViewCapacity && random.Peek(passiveViewCopy).TryGet(out var removedPeer)) { passiveViewCopy.Remove(removedPeer); await DestroyAsync(removedPeer).ConfigureAwait(false); @@ -328,7 +328,7 @@ private async Task AddPeerToActiveViewAsync(EndPoint peer, bool highPriority) passiveView = passiveView.Remove(peer); // allocate space in active view if it is full - if (activeView.Count >= activeViewCapacity && activeView.PeekRandom(random).TryGet(out var removedPeer)) + if (activeView.Count >= activeViewCapacity && random.Peek(activeView).TryGet(out var removedPeer)) { activeView = activeView.Remove(removedPeer); try From 5805ec8354500582a7d978b5da9cc4a5bb9271e6 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 09:14:57 +0200 Subject: [PATCH 042/155] Fixed documentation --- src/DotNext/RandomExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/DotNext/RandomExtensions.cs b/src/DotNext/RandomExtensions.cs index 8474d2d99..a64907b57 100644 --- a/src/DotNext/RandomExtensions.cs +++ b/src/DotNext/RandomExtensions.cs @@ -362,6 +362,8 @@ public static T Next(this RandomNumberGenerator random) return result; } + /// + /// Fills the buffer with random values of the specified type. /// /// The blittable type. /// The source of random numbers. From a3dabee5ce87ba9f02e2625abdd73efe96c34dd2 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 09:54:16 +0200 Subject: [PATCH 043/155] Merge Sequence static class with other static classes --- src/DotNext.IO/IO/StreamExtensions.cs | 4 +- .../Linq/Expressions/ForEachExpression.cs | 4 +- .../Linq/Expressions/ForExpression.cs | 4 +- .../Linq/Expressions/LockExpression.cs | 4 +- .../Linq/Expressions/NullSafetyExpression.cs | 4 +- .../Linq/Expressions/UsingExpression.cs | 4 +- .../Linq/Expressions/WithExpression.cs | 4 +- .../Metaprogramming/AsyncLambdaExpression.cs | 4 +- .../Metaprogramming/LambdaExpression.cs | 4 +- .../Metaprogramming/MatchBuilder.cs | 12 +- .../Metaprogramming/SwitchBuilder.cs | 4 +- .../AsyncStateMachineBuilder.cs | 2 +- .../Generic/AsyncEnumerableTests.cs | 70 ++++ .../Collections/Generic/EnumeratorTests.cs | 172 ++++++++++ .../Collections/Generic/ListTests.cs | 8 + .../Collections/Generic/SequenceTests.cs | 288 +--------------- .../Metaprogramming/LoopTests.cs | 2 +- src/DotNext.Tests/OneDimensionalArrayTests.cs | 15 - .../Channels/PersistentChannelTests.cs | 2 +- .../Tasks/TaskCompletionPipeTests.cs | 2 +- src/DotNext/Buffers/BufferWriter.cs | 4 +- .../Buffers/ReadOnlySequencePartitioner.cs | 4 +- .../Buffers/SparseBufferWriter.Reader.cs | 4 +- .../Generic/AsyncEnumerable.Buffer.cs | 34 ++ .../Generic/AsyncEnumerable.Empty.cs | 25 ++ ...s => AsyncEnumerable.NotNullEnumerable.cs} | 6 +- .../Generic/AsyncEnumerable.Proxy.cs | 44 +++ .../{Sequence.Async.cs => AsyncEnumerable.cs} | 14 +- ...equence.Buffer.cs => Collection.Buffer.cs} | 31 +- ...e.cs => Collection.ConsumingEnumerable.cs} | 2 +- ...ble.cs => Collection.NotNullEnumerable.cs} | 2 +- src/DotNext/Collections/Generic/Collection.cs | 213 +++++++++++- .../Collections/Generic/Dictionary.Empty.cs | 45 +++ src/DotNext/Collections/Generic/Dictionary.cs | 2 +- .../Collections/Generic/EmptyDictionary.cs | 39 --- ...tor.cs => Enumerator.LimitedEnumerator.cs} | 2 +- src/DotNext/Collections/Generic/Enumerator.cs | 124 +++++++ .../Generic/ReadOnlyCollectionView.cs | 24 +- .../Generic/ReadOnlyDictionaryView.cs | 24 +- .../Collections/Generic/ReadOnlyListView.cs | 24 +- .../Generic/Sequence.AsyncEnumerator.cs | 70 ---- .../Generic/Sequence.AsyncGenerator.cs | 78 ----- .../Generic/Sequence.EmptyEnumerator.cs | 46 --- .../Collections/Generic/Sequence.Generator.cs | 74 ---- src/DotNext/Collections/Generic/Sequence.cs | 324 ------------------ .../Collections/Specialized/InvocationList.cs | 9 +- src/DotNext/EqualityComparerBuilder.cs | 14 +- 47 files changed, 828 insertions(+), 1067 deletions(-) create mode 100644 src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs create mode 100644 src/DotNext.Tests/Collections/Generic/EnumeratorTests.cs create mode 100644 src/DotNext/Collections/Generic/AsyncEnumerable.Buffer.cs create mode 100644 src/DotNext/Collections/Generic/AsyncEnumerable.Empty.cs rename src/DotNext/Collections/Generic/{Sequence.AsyncNotNullEnumerable.cs => AsyncEnumerable.NotNullEnumerable.cs} (88%) create mode 100644 src/DotNext/Collections/Generic/AsyncEnumerable.Proxy.cs rename src/DotNext/Collections/Generic/{Sequence.Async.cs => AsyncEnumerable.cs} (96%) rename src/DotNext/Collections/Generic/{Sequence.Buffer.cs => Collection.Buffer.cs} (64%) rename src/DotNext/Collections/Generic/{Sequence.ConsumingEnumerable.cs => Collection.ConsumingEnumerable.cs} (98%) rename src/DotNext/Collections/Generic/{Sequence.NotNullEnumerable.cs => Collection.NotNullEnumerable.cs} (97%) create mode 100644 src/DotNext/Collections/Generic/Dictionary.Empty.cs delete mode 100644 src/DotNext/Collections/Generic/EmptyDictionary.cs rename src/DotNext/Collections/Generic/{Sequence.LimitedEnumerator.cs => Enumerator.LimitedEnumerator.cs} (97%) create mode 100644 src/DotNext/Collections/Generic/Enumerator.cs delete mode 100644 src/DotNext/Collections/Generic/Sequence.AsyncEnumerator.cs delete mode 100644 src/DotNext/Collections/Generic/Sequence.AsyncGenerator.cs delete mode 100644 src/DotNext/Collections/Generic/Sequence.EmptyEnumerator.cs delete mode 100644 src/DotNext/Collections/Generic/Sequence.Generator.cs delete mode 100644 src/DotNext/Collections/Generic/Sequence.cs diff --git a/src/DotNext.IO/IO/StreamExtensions.cs b/src/DotNext.IO/IO/StreamExtensions.cs index b0a067692..a9558a567 100644 --- a/src/DotNext.IO/IO/StreamExtensions.cs +++ b/src/DotNext.IO/IO/StreamExtensions.cs @@ -1,5 +1,4 @@ using System.Buffers; -using System.Diagnostics; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -12,7 +11,6 @@ namespace DotNext.IO; using Buffers; using Text; using static Buffers.BufferReader; -using static Collections.Generic.Sequence; /// /// Represents high-level read/write methods for the stream. @@ -1230,7 +1228,7 @@ public static void CopyTo(this Stream source, IBufferWriter destination, i /// A collection of streams. /// An object that represents multiple streams as one logical stream. public static Stream Combine(this Stream stream, params Stream[] others) - => others.IsNullOrEmpty() ? stream : new SparseStream(Singleton(stream).Concat(others)); + => others is { Length: > 0 } ? new SparseStream(others.Prepend(stream)) : stream; /// /// Combines multiple readable streams. diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs index bc9477810..791ab5057 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs @@ -7,7 +7,7 @@ namespace DotNext.Linq.Expressions; using static Reflection.CollectionType; using static Reflection.DisposableType; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents iteration over collection elements as expression. @@ -193,7 +193,7 @@ private Expression Reduce(Expression moveNextCall, Expression? disposeCall) Assign(enumeratorVar, Default(enumeratorVar.Type)) : Block(disposeCall, Assign(enumeratorVar, Default(enumeratorVar.Type))); loopBody = TryFinally(loopBody, @finally); - return Block(Type, Seq.Singleton(enumeratorVar), enumeratorAssignment, loopBody); + return Block(Type, List.Singleton(enumeratorVar), enumeratorAssignment, loopBody); } /// diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ForExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ForExpression.cs index f697f7aa7..336a7f967 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ForExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ForExpression.cs @@ -2,7 +2,7 @@ namespace DotNext.Linq.Expressions; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents for loop as expression. @@ -196,6 +196,6 @@ public override Expression Reduce() { Expression body = Condition(Test, Body, Goto(BreakLabel), typeof(void)); body = Loop(body, BreakLabel); - return Block(typeof(void), Seq.Singleton(LoopVar), Assign(LoopVar, Initialization), body); + return Block(typeof(void), List.Singleton(LoopVar), Assign(LoopVar, Initialization), body); } } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs index 3d7b6f6a8..36252f1fb 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/LockExpression.cs @@ -4,7 +4,7 @@ namespace DotNext.Linq.Expressions; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents synchronized block of code. @@ -101,6 +101,6 @@ public override Expression Reduce() var body = TryFinally(Body, Call(monitorExit, SyncRoot)); return assignment is null ? Block(Call(monitorEnter, SyncRoot), body) : - Block(Seq.Singleton(SyncRoot), assignment, Call(monitorEnter, SyncRoot), body); + Block(List.Singleton(SyncRoot), assignment, Call(monitorEnter, SyncRoot), body); } } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/NullSafetyExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/NullSafetyExpression.cs index 7f999d4d5..07376d3a2 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/NullSafetyExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/NullSafetyExpression.cs @@ -2,7 +2,7 @@ namespace DotNext.Linq.Expressions; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents expression that is protected by null check, e.g. safe navigation operator (?. in C#). @@ -96,6 +96,6 @@ public override Expression Reduce() Expression conditional = Condition(Target.IsNotNull(), body, Default(body.Type)); return assignment is null ? conditional : - Block(body.Type, Seq.Singleton(Target), assignment, conditional); + Block(body.Type, List.Singleton(Target), assignment, conditional); } } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/UsingExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/UsingExpression.cs index 81ccc16f8..a4dba6817 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/UsingExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/UsingExpression.cs @@ -4,7 +4,7 @@ namespace DotNext.Linq.Expressions; using static Reflection.DisposableType; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents using or await using expression. @@ -156,6 +156,6 @@ public override Expression Reduce() return assignment is null ? MakeTry(Type, Body, Block(typeof(void), disposeCall, Assign(Resource, Default(Resource.Type))), null, null) : - Block(Type, Seq.Singleton(Resource), assignment, TryFinally(Body, disposeCall)); + Block(Type, List.Singleton(Resource), assignment, TryFinally(Body, disposeCall)); } } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/WithExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/WithExpression.cs index 57491c8a5..65094c0dd 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/WithExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/WithExpression.cs @@ -2,7 +2,7 @@ namespace DotNext.Linq.Expressions; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Provides an expression refer to a single object or structure so @@ -89,5 +89,5 @@ public Expression Body /// /// Translated expression. public override Expression Reduce() - => assignment is null ? Body : Block(Seq.Singleton(Variable), assignment, Body); + => assignment is null ? Body : Block(List.Singleton(Variable), assignment, Body); } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs b/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs index e21c51c8f..7c0b3ecc1 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs @@ -5,7 +5,7 @@ namespace DotNext.Metaprogramming; using Linq.Expressions; using Runtime.CompilerServices; using static Reflection.DelegateType; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; internal sealed class AsyncLambdaExpression : LambdaExpression, ILexicalScope, Action>, ILexicalScope, Action> where TDelegate : Delegate @@ -72,7 +72,7 @@ internal override Expression Return(Expression? result) { lambda = Expression.Lambda( Expression.Block( - Seq.Singleton(recursion), + List.Singleton(recursion), Expression.Assign(recursion, lambda), Expression.Invoke(recursion, Parameters)), Parameters); } diff --git a/src/DotNext.Metaprogramming/Metaprogramming/LambdaExpression.cs b/src/DotNext.Metaprogramming/Metaprogramming/LambdaExpression.cs index 8ac241b6d..dc434fe34 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/LambdaExpression.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/LambdaExpression.cs @@ -3,7 +3,7 @@ namespace DotNext.Metaprogramming; using static Reflection.DelegateType; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents lambda function builder. @@ -112,7 +112,7 @@ internal override Expression Return(Expression? result) if (recursion is not null) { body = Expression.Block( - Seq.Singleton(recursion), + List.Singleton(recursion), Expression.Assign(recursion, Expression.Lambda(body, tailCall, Parameters)), Expression.Invoke(recursion, Parameters)); } diff --git a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs index c74209ff1..90331ece9 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs @@ -5,7 +5,7 @@ namespace DotNext.Metaprogramming; using Linq.Expressions; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents pattern matcher. @@ -84,7 +84,7 @@ static PatternMatch MatchByType(ParameterExpression value, Type expectedType, TB var test = value.InstanceOf(expectedType); var typedValue = Expression.Variable(expectedType); var body = builder.Invoke(typedValue); - return endOfMatch => Expression.IfThen(test, Expression.Block(Seq.Singleton(typedValue), typedValue.Assign(value.Convert(expectedType)), endOfMatch.Goto(body))); + return endOfMatch => Expression.IfThen(test, Expression.Block(List.Singleton(typedValue), typedValue.Assign(value.Convert(expectedType)), endOfMatch.Goto(body))); } } @@ -101,7 +101,7 @@ static PatternMatch MatchByType(ParameterExpression value, Type expectedType, Pa var typedVarInit = typedVar.Assign(value.Convert(expectedType)); var body = builder.Invoke(typedVar); var test2 = condition(typedVar); - return endOfMatch => Expression.IfThen(test, Expression.Block(Seq.Singleton(typedVar), typedVarInit, Expression.IfThen(test2, endOfMatch.Goto(body)))); + return endOfMatch => Expression.IfThen(test, Expression.Block(List.Singleton(typedVar), typedVarInit, Expression.IfThen(test2, endOfMatch.Goto(body)))); } } @@ -180,7 +180,7 @@ private MatchBuilder Case(IEnumerable<(string, Expression)> structPattern, CaseS /// The action to be executed if object matches to the pattern. /// this builder. public MatchBuilder Case(string memberName, Expression memberValue, Func body) - => Case(StructuralPattern(Seq.Singleton((memberName, memberValue))), value => body(Expression.PropertyOrField(value, memberName))); + => Case(StructuralPattern(List.Singleton((memberName, memberValue))), value => body(Expression.PropertyOrField(value, memberName))); /// /// Defines pattern matching based on structural matching. @@ -409,7 +409,7 @@ private protected override BlockExpression Build() // setup label as last instruction instructions.Add(Expression.Label(endOfMatch, Expression.Default(endOfMatch.Type))); - return assignment is null ? Expression.Block(instructions) : Expression.Block(Seq.Singleton(value), instructions); + return assignment is null ? Expression.Block(instructions) : Expression.Block(List.Singleton(value), instructions); } private protected override void Cleanup() @@ -501,7 +501,7 @@ internal MatchByMemberStatement(MatchBuilder builder, string memberName, Express private protected override MatchBuilder Build(MatchBuilder builder, Action scope) { - var pattern = StructuralPattern(Seq.Singleton((memberName, memberValue))); + var pattern = StructuralPattern(List.Singleton((memberName, memberValue))); return builder.MatchByCondition(pattern, new CaseStatementBuilder(this, memberName, scope)); } } diff --git a/src/DotNext.Metaprogramming/Metaprogramming/SwitchBuilder.cs b/src/DotNext.Metaprogramming/Metaprogramming/SwitchBuilder.cs index e945f6d99..92661d856 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/SwitchBuilder.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/SwitchBuilder.cs @@ -2,7 +2,7 @@ namespace DotNext.Metaprogramming; -using Seq = Collections.Generic.Sequence; +using List = Collections.Generic.List; /// /// Represents selection statement that chooses a single section to execute from a @@ -55,7 +55,7 @@ public SwitchBuilder Case(IEnumerable testValues, Action body) /// Single test value. /// The expression to be returned from selection statement. /// this builder. - public SwitchBuilder Case(Expression test, Expression body) => Case(Seq.Singleton(test), body); + public SwitchBuilder Case(Expression test, Expression body) => Case(List.Singleton(test), body); /// /// Specifies a pattern to compare to the match expression diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs index cf8026f16..c58001ef1 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs @@ -10,7 +10,7 @@ namespace DotNext.Runtime.CompilerServices; using Linq.Expressions; using Reflection; using static Collections.Generic.Dictionary; -using static Collections.Generic.Sequence; +using static Collections.Generic.Collection; using static Reflection.TypeExtensions; /// diff --git a/src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs b/src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs new file mode 100644 index 000000000..46670698f --- /dev/null +++ b/src/DotNext.Tests/Collections/Generic/AsyncEnumerableTests.cs @@ -0,0 +1,70 @@ +namespace DotNext.Collections.Generic; + +public sealed class AsyncEnumerableTests : Test +{ + [Fact] + public static async Task EmptyAsyncEnumerable() + { + var count = 0; + + await foreach (var item in AsyncEnumerable.Empty()) + count++; + + Equal(0, count); + } + + [Fact] + public static async Task ElementAtIndexAsync() + { + var list = new LinkedList(); + list.AddLast(10); + list.AddLast(40); + list.AddLast(100); + + var asyncList = list.ToAsyncEnumerable(); + Equal(100, await asyncList.ElementAtAsync(2)); + Equal(10, await asyncList.ElementAtAsync(0)); + } + + [Fact] + public static async Task ForEachTestAsync() + { + var list = new List { 1, 10, 20 }.ToAsyncEnumerable(); + var counter = new SequenceTests.Counter(); + await list.ForEachAsync(counter.Accept); + Equal(3, counter.value); + counter.value = 0; + + list = new int[] { 1, 2, 10, 11, 15 }.ToAsyncEnumerable(); + await list.ForEachAsync(counter.Accept); + Equal(5, counter.value); + } + + [Fact] + public static async Task FirstOrNullTestAsync() + { + var array = new long[0].ToAsyncEnumerable(); + var element = await array.FirstOrNullAsync(); + Null(element); + array = new long[] { 10, 20 }.ToAsyncEnumerable(); + element = await array.FirstOrNullAsync(); + Equal(10, element); + } + + [Fact] + public static async Task CopyListAsync() + { + using var copy = await new List { 10, 20, 30 }.ToAsyncEnumerable().CopyAsync(sizeHint: 4); + Equal(3, copy.Length); + Equal(10, copy[0]); + Equal(20, copy[1]); + Equal(30, copy[2]); + } + + [Fact] + public static async Task CopyEmptCollectionAsync() + { + using var copy = await Enumerable.Empty().ToAsyncEnumerable().CopyAsync(); + True(copy.IsEmpty); + } +} \ No newline at end of file diff --git a/src/DotNext.Tests/Collections/Generic/EnumeratorTests.cs b/src/DotNext.Tests/Collections/Generic/EnumeratorTests.cs new file mode 100644 index 000000000..4ed9835b4 --- /dev/null +++ b/src/DotNext.Tests/Collections/Generic/EnumeratorTests.cs @@ -0,0 +1,172 @@ +using System.Buffers; + +namespace DotNext.Collections.Generic; + +using Buffers; + +public sealed class EnumeratorTests : Test +{ + [Fact] + public static void EmptyMemoryEnumerator() + { + using var enumerator = Enumerator.ToEnumerator(ReadOnlyMemory.Empty); + False(enumerator.MoveNext()); + } + + [Fact] + public static void ArrayMemoryEnumerator() + { + using var enumerator = Enumerator.ToEnumerator(new ReadOnlyMemory([1, 2, 3])); + + True(enumerator.MoveNext()); + Equal(1, enumerator.Current); + + True(enumerator.MoveNext()); + Equal(2, enumerator.Current); + + True(enumerator.MoveNext()); + Equal(3, enumerator.Current); + + False(enumerator.MoveNext()); + } + + [Fact] + public static void NativeMemoryEnumerator() + { + using var owner = UnmanagedMemoryAllocator.Allocate(3); + owner[(nint)0] = 10; + owner[(nint)1] = 20; + owner[(nint)2] = 30; + + using var enumerator = Enumerator.ToEnumerator(owner.Memory); + + True(enumerator.MoveNext()); + Equal(10, enumerator.Current); + + True(enumerator.MoveNext()); + Equal(20, enumerator.Current); + + True(enumerator.MoveNext()); + Equal(30, enumerator.Current); + + False(enumerator.MoveNext()); + } + + [Fact] + public static void EmptySequenceEnumerator() + { + using var enumerator = Enumerator.ToEnumerator(ReadOnlySequence.Empty); + False(enumerator.MoveNext()); + } + + [Fact] + public static void ArraySequenceEnumerator() + { + using var enumerator = Enumerator.ToEnumerator(new ReadOnlySequence(new ReadOnlyMemory(new int[] { 1, 2, 3 }))); + True(enumerator.MoveNext()); + Equal(1, enumerator.Current); + + True(enumerator.MoveNext()); + Equal(2, enumerator.Current); + + True(enumerator.MoveNext()); + Equal(3, enumerator.Current); + + False(enumerator.MoveNext()); + } + + [Fact] + public static void SequenceEnumerator() + { + var bytes = RandomBytes(64); + using var enumerator = Enumerator.ToEnumerator(ToReadOnlySequence(bytes, 32)); + + var i = 0; + while (enumerator.MoveNext()) + { + Equal(bytes[i++], enumerator.Current); + } + } + + [Fact] + public static async Task CanceledAsyncEnumerator() + { + await using var enumerator = new int[] { 10, 20, 30 }.GetAsyncEnumerator(new CancellationToken(true)); + await ThrowsAsync(enumerator.MoveNextAsync().AsTask); + } + + [Fact] + public static async Task ConversionToAsyncEnumerator() + { + await using var enumerator = new int[] { 10, 20, 30 }.GetAsyncEnumerator(); + for (int index = 0; await enumerator.MoveNextAsync(); index++) + { + switch (index) + { + case 0: + Equal(10, enumerator.Current); + break; + case 1: + Equal(20, enumerator.Current); + break; + case 2: + Equal(30, enumerator.Current); + break; + default: + Fail("Unexpected enumerator state"); + break; + } + } + } + + [Fact] + public static void SkipValueEnumerator() + { + var list = new List { 10L, 20L, 30L }; + var enumerator = list.GetEnumerator(); + True(enumerator.Skip.Enumerator, long>(2)); + True(enumerator.MoveNext()); + Equal(30L, enumerator.Current); + enumerator.Dispose(); + } + + [Fact] + public static void LimitedSequence() + { + var range = Enumerable.Range(0, 10); + using var enumerator = range.GetEnumerator().Limit(3); + True(enumerator.MoveNext()); + Equal(0, enumerator.Current); + True(enumerator.MoveNext()); + Equal(1, enumerator.Current); + True(enumerator.MoveNext()); + Equal(2, enumerator.Current); + False(enumerator.MoveNext()); + } + + [Fact] + public static async Task SkipAsync() + { + var range = Enumerable.Range(0, 10); + await using var enumerator = range.GetAsyncEnumerator(); + True(await enumerator.SkipAsync(8)); + True(await enumerator.MoveNextAsync()); + Equal(8, enumerator.Current); + True(await enumerator.MoveNextAsync()); + Equal(9, enumerator.Current); + False(await enumerator.MoveNextAsync()); + } + + [Fact] + public static void Skip() + { + var range = Enumerable.Range(0, 10); + using var enumerator = range.GetEnumerator(); + True(enumerator.Skip(8)); + True(enumerator.MoveNext()); + Equal(8, enumerator.Current); + True(enumerator.MoveNext()); + Equal(9, enumerator.Current); + False(enumerator.MoveNext()); + } +} \ No newline at end of file diff --git a/src/DotNext.Tests/Collections/Generic/ListTests.cs b/src/DotNext.Tests/Collections/Generic/ListTests.cs index a2a57d027..116fcabdb 100644 --- a/src/DotNext.Tests/Collections/Generic/ListTests.cs +++ b/src/DotNext.Tests/Collections/Generic/ListTests.cs @@ -140,4 +140,12 @@ public static void ListSlice() Equal(10, span[0]); Equal(20, span[1]); } + + [Fact] + public static void SingletonCollection() + { + var collection = List.Singleton(42); + NotEmpty(collection); + Equal(42, collection.First()); + } } \ No newline at end of file diff --git a/src/DotNext.Tests/Collections/Generic/SequenceTests.cs b/src/DotNext.Tests/Collections/Generic/SequenceTests.cs index 157b51445..fd7b7f0ac 100644 --- a/src/DotNext.Tests/Collections/Generic/SequenceTests.cs +++ b/src/DotNext.Tests/Collections/Generic/SequenceTests.cs @@ -3,11 +3,9 @@ namespace DotNext.Collections.Generic; -using Buffers; - public sealed class SequenceTests : Test { - private sealed class Counter + internal sealed class Counter { public int value; @@ -27,31 +25,6 @@ public static void ForEachTest() Equal(5, counter.value); } - [Fact] - public static async Task ForEachTestAsync() - { - var list = new List { 1, 10, 20 }.ToAsyncEnumerable(); - var counter = new Counter(); - await list.ForEachAsync(counter.Accept); - Equal(3, counter.value); - counter.value = 0; - - list = new int[] { 1, 2, 10, 11, 15 }.ToAsyncEnumerable(); - await list.ForEachAsync(counter.Accept); - Equal(5, counter.value); - } - - [Fact] - public static async Task FirstOrNullTestAsync() - { - var array = new long[0].ToAsyncEnumerable(); - var element = await array.FirstOrNullAsync(); - Null(element); - array = new long[] { 10, 20 }.ToAsyncEnumerable(); - element = await array.FirstOrNullAsync(); - Equal(10, element); - } - [Fact] public static void ElementAtIndex() { @@ -65,19 +38,6 @@ public static void ElementAtIndex() Equal(10, element); } - [Fact] - public static async Task ElementAtIndexAsync() - { - var list = new LinkedList(); - list.AddLast(10); - list.AddLast(40); - list.AddLast(100); - - var asyncList = list.ToAsyncEnumerable(); - Equal(100, await asyncList.ElementAtAsync(2)); - Equal(10, await asyncList.ElementAtAsync(0)); - } - [Fact] public static void SkipNullsTest() { @@ -139,57 +99,6 @@ public static void Append() Equal("Four", items.Last()); } - [Fact] - public static void Skip() - { - var range = Enumerable.Range(0, 10); - using var enumerator = range.GetEnumerator(); - True(enumerator.Skip(8)); - True(enumerator.MoveNext()); - Equal(8, enumerator.Current); - True(enumerator.MoveNext()); - Equal(9, enumerator.Current); - False(enumerator.MoveNext()); - } - - [Fact] - public static async Task SkipAsync() - { - var range = Enumerable.Range(0, 10); - await using var enumerator = range.GetAsyncEnumerator(); - True(await enumerator.SkipAsync(8)); - True(await enumerator.MoveNextAsync()); - Equal(8, enumerator.Current); - True(await enumerator.MoveNextAsync()); - Equal(9, enumerator.Current); - False(await enumerator.MoveNextAsync()); - } - - [Fact] - public static void SkipValueEnumerator() - { - var list = new List { 10L, 20L, 30L }; - var enumerator = list.GetEnumerator(); - True(enumerator.Skip.Enumerator, long>(2)); - True(enumerator.MoveNext()); - Equal(30L, enumerator.Current); - enumerator.Dispose(); - } - - [Fact] - public static void LimitedSequence() - { - var range = Enumerable.Range(0, 10); - using var enumerator = range.GetEnumerator().Limit(3); - True(enumerator.MoveNext()); - Equal(0, enumerator.Current); - True(enumerator.MoveNext()); - Equal(1, enumerator.Current); - True(enumerator.MoveNext()); - Equal(2, enumerator.Current); - False(enumerator.MoveNext()); - } - [Fact] public static async Task IterationAsync() { @@ -238,85 +147,10 @@ public static async Task ConversionToAsyncEnumerable() } } - [Fact] - public static async Task ConversionToAsyncEnumerator() - { - await using var enumerator = new int[] { 10, 20, 30 }.GetAsyncEnumerator(); - for (int index = 0; await enumerator.MoveNextAsync(); index++) - { - switch (index) - { - case 0: - Equal(10, enumerator.Current); - break; - case 1: - Equal(20, enumerator.Current); - break; - case 2: - Equal(30, enumerator.Current); - break; - default: - Fail("Unexpected enumerator state"); - break; - } - } - } - - [Fact] - public static async Task CanceledAsyncEnumerator() - { - await using var enumerator = new int[] { 10, 20, 30 }.GetAsyncEnumerator(new CancellationToken(true)); - await ThrowsAsync(enumerator.MoveNextAsync().AsTask); - } - - [Fact] - public static void GeneratorMethod() - { - int i = 0; - Func> generator = () => i < 3 ? i++ : Optional.None; - var list = new List(); - foreach (var item in generator.ToEnumerable()) - list.Add(item); - - NotEmpty(list); - Equal(3, list.Count); - Equal(0, list[0]); - Equal(1, list[1]); - Equal(2, list[2]); - - list.Clear(); - foreach (var item in new Sequence.Generator()) - list.Add(item); - - Empty(list); - } - - [Fact] - public static async Task AsyncGeneratorMethod() - { - int i = 0; - Func>> generator = token => new ValueTask>(i < 3 ? i++ : Optional.None); - var list = new List(); - await foreach (var item in generator.ToAsyncEnumerable()) - list.Add(item); - - NotEmpty(list); - Equal(3, list.Count); - Equal(0, list[0]); - Equal(1, list[1]); - Equal(2, list[2]); - - list.Clear(); - await foreach (var item in new Sequence.AsyncGenerator()) - list.Add(item); - - Empty(list); - } - [Fact] public static void EmptyConsumingEnumerable() { - var enumerable = new Sequence.ConsumingEnumerable(); + var enumerable = new Collection.ConsumingEnumerable(); Empty(enumerable); } @@ -396,128 +230,10 @@ public static void CopyEmptyCollection() True(copy.IsEmpty); } - [Fact] - public static async Task CopyEmptCollectionAsync() - { - using var copy = await Enumerable.Empty().ToAsyncEnumerable().CopyAsync(); - True(copy.IsEmpty); - } - - [Fact] - public static async Task CopyListAsync() - { - using var copy = await new List { 10, 20, 30 }.ToAsyncEnumerable().CopyAsync(sizeHint: 4); - Equal(3, copy.Length); - Equal(10, copy[0]); - Equal(20, copy[1]); - Equal(30, copy[2]); - } - [Fact] public static void CopyString() { using var copy = "abcd".Copy(); Equal("abcd", copy.Memory.ToString()); } - - [Fact] - public static void SingletonCollection() - { - var collection = Sequence.Singleton(42); - NotEmpty(collection); - Equal(42, collection.First()); - } - - [Fact] - public static void EmptyMemoryEnumerator() - { - using var enumerator = Sequence.ToEnumerator(ReadOnlyMemory.Empty); - False(enumerator.MoveNext()); - } - - [Fact] - public static void ArrayMemoryEnumerator() - { - using var enumerator = Sequence.ToEnumerator(new ReadOnlyMemory(new int[] { 1, 2, 3 })); - - True(enumerator.MoveNext()); - Equal(1, enumerator.Current); - - True(enumerator.MoveNext()); - Equal(2, enumerator.Current); - - True(enumerator.MoveNext()); - Equal(3, enumerator.Current); - - False(enumerator.MoveNext()); - } - - [Fact] - public static void NativeMemoryEnumerator() - { - using var owner = UnmanagedMemoryAllocator.Allocate(3); - owner[(nint)0] = 10; - owner[(nint)1] = 20; - owner[(nint)2] = 30; - - using var enumerator = Sequence.ToEnumerator(owner.Memory); - - True(enumerator.MoveNext()); - Equal(10, enumerator.Current); - - True(enumerator.MoveNext()); - Equal(20, enumerator.Current); - - True(enumerator.MoveNext()); - Equal(30, enumerator.Current); - - False(enumerator.MoveNext()); - } - - [Fact] - public static void EmptySequenceEnumerator() - { - using var enumerator = Sequence.ToEnumerator(ReadOnlySequence.Empty); - False(enumerator.MoveNext()); - } - - [Fact] - public static void ArraySequenceEnumerator() - { - using var enumerator = Sequence.ToEnumerator(new ReadOnlySequence(new ReadOnlyMemory(new int[] { 1, 2, 3 }))); - True(enumerator.MoveNext()); - Equal(1, enumerator.Current); - - True(enumerator.MoveNext()); - Equal(2, enumerator.Current); - - True(enumerator.MoveNext()); - Equal(3, enumerator.Current); - - False(enumerator.MoveNext()); - } - - [Fact] - public static void SequenceEnumerator() - { - var bytes = RandomBytes(64); - using var enumerator = Sequence.ToEnumerator(ToReadOnlySequence(bytes, 32)); - - var i = 0; - while (enumerator.MoveNext()) - { - Equal(bytes[i++], enumerator.Current); - } - } - - [Fact] - public static async Task EmptyAsyncEnumerable() - { - var count = 0; - - await foreach (var item in Sequence.GetEmptyAsyncEnumerable()) - count++; - - Equal(0, count); - } } \ No newline at end of file diff --git a/src/DotNext.Tests/Metaprogramming/LoopTests.cs b/src/DotNext.Tests/Metaprogramming/LoopTests.cs index 783514c52..59daf464e 100644 --- a/src/DotNext.Tests/Metaprogramming/LoopTests.cs +++ b/src/DotNext.Tests/Metaprogramming/LoopTests.cs @@ -3,7 +3,7 @@ namespace DotNext.Metaprogramming; using Linq.Expressions; -using static Collections.Generic.Sequence; +using static Collections.Generic.Collection; using static CodeGenerator; public sealed class LoopTests : Test diff --git a/src/DotNext.Tests/OneDimensionalArrayTests.cs b/src/DotNext.Tests/OneDimensionalArrayTests.cs index aa61b8d1d..b2ba987a2 100644 --- a/src/DotNext.Tests/OneDimensionalArrayTests.cs +++ b/src/DotNext.Tests/OneDimensionalArrayTests.cs @@ -1,7 +1,5 @@ namespace DotNext; -using static Collections.Generic.Sequence; - public sealed class OneDimensionalArrayTests : Test { public sealed class Equatable @@ -15,19 +13,6 @@ public sealed class Equatable public override int GetHashCode() => value.GetHashCode(); } - [Fact] - public static void ArrayEquality2() - { - var array1 = new[] { new Equatable("a"), new Equatable("b") }; - var array2 = new[] { new Equatable("a"), new Equatable("b") }; - True(array1.SequenceEqual(array2)); - True(array1.SequenceEqual(array2, true)); - Equal(array1.SequenceHashCode(), array2.SequenceHashCode()); - array2[0] = new Equatable("c"); - False(array1.SequenceEqual(array2)); - False(array1.SequenceEqual(array2, true)); - } - [Fact] public static void Insert() { diff --git a/src/DotNext.Tests/Threading/Channels/PersistentChannelTests.cs b/src/DotNext.Tests/Threading/Channels/PersistentChannelTests.cs index 6ae8c0d30..51d021f85 100644 --- a/src/DotNext.Tests/Threading/Channels/PersistentChannelTests.cs +++ b/src/DotNext.Tests/Threading/Channels/PersistentChannelTests.cs @@ -2,7 +2,7 @@ namespace DotNext.Threading.Channels; -using static Collections.Generic.Sequence; +using static Collections.Generic.AsyncEnumerable; using static IO.StreamExtensions; public sealed class PersistentChannelTests : Test diff --git a/src/DotNext.Tests/Threading/Tasks/TaskCompletionPipeTests.cs b/src/DotNext.Tests/Threading/Tasks/TaskCompletionPipeTests.cs index 53e3612e2..713bc3ff4 100644 --- a/src/DotNext.Tests/Threading/Tasks/TaskCompletionPipeTests.cs +++ b/src/DotNext.Tests/Threading/Tasks/TaskCompletionPipeTests.cs @@ -1,6 +1,6 @@ namespace DotNext.Threading.Tasks; -using static Collections.Generic.Sequence; +using static Collections.Generic.AsyncEnumerable; public class TaskCompletionPipeTests : Test { diff --git a/src/DotNext/Buffers/BufferWriter.cs b/src/DotNext/Buffers/BufferWriter.cs index 32a91d50d..2a9e77c37 100644 --- a/src/DotNext/Buffers/BufferWriter.cs +++ b/src/DotNext/Buffers/BufferWriter.cs @@ -7,7 +7,7 @@ namespace DotNext.Buffers; -using Seq = Collections.Generic.Sequence; +using Enumerator = Collections.Generic.Enumerator; /// /// Represents memory-backed output sink which data can be written. @@ -296,7 +296,7 @@ protected override void Dispose(bool disposing) /// Gets enumerator over all written elements. /// /// The enumerator over all written elements. - public IEnumerator GetEnumerator() => Seq.ToEnumerator(WrittenMemory); + public IEnumerator GetEnumerator() => Enumerator.ToEnumerator(WrittenMemory); /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs index 21606583e..efd7ec4d6 100644 --- a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs +++ b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs @@ -6,7 +6,7 @@ namespace DotNext.Buffers; -using Sequence = Collections.Generic.Sequence; +using Enumerator = Collections.Generic.Enumerator; internal sealed class ReadOnlySequencePartitioner : OrderablePartitioner { @@ -127,7 +127,7 @@ public override IList> GetPartitions(int partitionCount) { var length = i < remainder ? quotient + 1 : quotient; var sliced = sequence.Slice(startIndex, length); - partitions[i] = Sequence.ToEnumerator(in sliced); + partitions[i] = Enumerator.ToEnumerator(in sliced); startIndex = sliced.End; } diff --git a/src/DotNext/Buffers/SparseBufferWriter.Reader.cs b/src/DotNext/Buffers/SparseBufferWriter.Reader.cs index f5f2a5199..97e76cd83 100644 --- a/src/DotNext/Buffers/SparseBufferWriter.Reader.cs +++ b/src/DotNext/Buffers/SparseBufferWriter.Reader.cs @@ -7,8 +7,6 @@ namespace DotNext.Buffers; -using Sequence = Collections.Generic.Sequence; - public partial class SparseBufferWriter : IEnumerable> { /// @@ -252,7 +250,7 @@ public Enumerator GetEnumerator() /// IEnumerator> IEnumerable>.GetEnumerator() - => first is null ? Sequence.GetEmptyEnumerator>() : GetEnumerator(); + => first is null ? Enumerable.Empty>().GetEnumerator() : GetEnumerator(); /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/DotNext/Collections/Generic/AsyncEnumerable.Buffer.cs b/src/DotNext/Collections/Generic/AsyncEnumerable.Buffer.cs new file mode 100644 index 000000000..a18361613 --- /dev/null +++ b/src/DotNext/Collections/Generic/AsyncEnumerable.Buffer.cs @@ -0,0 +1,34 @@ +namespace DotNext.Collections.Generic; + +using Buffers; + +/// +/// Provides extension methods for interface. +/// +public static partial class AsyncEnumerable +{ + /// + /// Creates a copy of the elements from the collection. + /// + /// The type of elements in the collection. + /// The collection to copy. + /// The approximate size of the collection, if known. + /// The allocator of the memory block used to place copied elements. + /// The token that can be used to cancel the enumeration. + /// Rented memory block containing a copy of the elements from the collection. + /// is . + /// is less than zero. + /// The operation has been canceled. + public static async Task> CopyAsync(this IAsyncEnumerable enumerable, int sizeHint = 0, MemoryAllocator? allocator = null, CancellationToken token = default) + { + ArgumentNullException.ThrowIfNull(enumerable); + ArgumentOutOfRangeException.ThrowIfNegative(sizeHint); + + using var buffer = new PooledBufferWriter(allocator) { Capacity = sizeHint }; + + await foreach (var item in enumerable.WithCancellation(token).ConfigureAwait(false)) + buffer.Add(item); + + return buffer.DetachBuffer(); + } +} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/AsyncEnumerable.Empty.cs b/src/DotNext/Collections/Generic/AsyncEnumerable.Empty.cs new file mode 100644 index 000000000..2a1793830 --- /dev/null +++ b/src/DotNext/Collections/Generic/AsyncEnumerable.Empty.cs @@ -0,0 +1,25 @@ +using System.Diagnostics; + +namespace DotNext.Collections.Generic; + +public static partial class AsyncEnumerable +{ + [DebuggerDisplay("Count = 0")] + private sealed class EmptyEnumerator : IAsyncEnumerator, IAsyncEnumerable + { + internal static readonly EmptyEnumerator Instance = new(); + + private EmptyEnumerator() + { + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public T Current => throw new InvalidOperationException(); + + ValueTask IAsyncEnumerator.MoveNextAsync() => new(false); + + IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) => this; + + ValueTask IAsyncDisposable.DisposeAsync() => ValueTask.CompletedTask; + } +} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.AsyncNotNullEnumerable.cs b/src/DotNext/Collections/Generic/AsyncEnumerable.NotNullEnumerable.cs similarity index 88% rename from src/DotNext/Collections/Generic/Sequence.AsyncNotNullEnumerable.cs rename to src/DotNext/Collections/Generic/AsyncEnumerable.NotNullEnumerable.cs index 11c214e43..597edc245 100644 --- a/src/DotNext/Collections/Generic/Sequence.AsyncNotNullEnumerable.cs +++ b/src/DotNext/Collections/Generic/AsyncEnumerable.NotNullEnumerable.cs @@ -1,8 +1,8 @@ namespace DotNext.Collections.Generic; -public static partial class Sequence +public static partial class AsyncEnumerable { - private sealed class AsyncNotNullEnumerable : IAsyncEnumerable + private sealed class NotNullEnumerable : IAsyncEnumerable where T : class { private sealed class Enumerator : IAsyncEnumerator @@ -39,7 +39,7 @@ public ValueTask DisposeAsync() private readonly IAsyncEnumerable enumerable; - internal AsyncNotNullEnumerable(IAsyncEnumerable enumerable) + internal NotNullEnumerable(IAsyncEnumerable enumerable) => this.enumerable = enumerable; public IAsyncEnumerator GetAsyncEnumerator(CancellationToken token) diff --git a/src/DotNext/Collections/Generic/AsyncEnumerable.Proxy.cs b/src/DotNext/Collections/Generic/AsyncEnumerable.Proxy.cs new file mode 100644 index 000000000..33c3cc838 --- /dev/null +++ b/src/DotNext/Collections/Generic/AsyncEnumerable.Proxy.cs @@ -0,0 +1,44 @@ +namespace DotNext.Collections.Generic; + +public static partial class AsyncEnumerable +{ + internal sealed class Proxy : IAsyncEnumerable + { + internal sealed class Enumerator : Disposable, IAsyncEnumerator + { + private readonly IEnumerator enumerator; + private readonly CancellationToken token; + + internal Enumerator(IEnumerable enumerable, CancellationToken token) + { + enumerator = enumerable.GetEnumerator(); + this.token = token; + } + + public T Current => enumerator.Current; + + public ValueTask MoveNextAsync() + => token.IsCancellationRequested ? ValueTask.FromCanceled(token) : new(enumerator.MoveNext()); + + protected override void Dispose(bool disposing) + { + if (disposing) + { + enumerator.Dispose(); + } + + base.Dispose(disposing); + } + + public new ValueTask DisposeAsync() => base.DisposeAsync(); + } + + private readonly IEnumerable enumerable; + + internal Proxy(IEnumerable enumerable) + => this.enumerable = enumerable; + + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken token) + => new Enumerator(enumerable, token); + } +} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.Async.cs b/src/DotNext/Collections/Generic/AsyncEnumerable.cs similarity index 96% rename from src/DotNext/Collections/Generic/Sequence.Async.cs rename to src/DotNext/Collections/Generic/AsyncEnumerable.cs index 877758784..81fd4a04b 100644 --- a/src/DotNext/Collections/Generic/Sequence.Async.cs +++ b/src/DotNext/Collections/Generic/AsyncEnumerable.cs @@ -2,7 +2,10 @@ namespace DotNext.Collections.Generic; using Buffers; -public static partial class Sequence +/// +/// Provides extension methods for interface. +/// +public static partial class AsyncEnumerable { /// /// Applies specified action to each collection element asynchronously. @@ -200,7 +203,7 @@ public static async ValueTask> ElementAtAsync(this IAsyncEnumerab /// Modified lazy collection without values. public static IAsyncEnumerable SkipNulls(this IAsyncEnumerable collection) where T : class - => new AsyncNotNullEnumerable(collection); + => new NotNullEnumerable(collection); /// /// Converts asynchronous collection to the array. @@ -223,4 +226,11 @@ public static async Task ToArrayAsync(this IAsyncEnumerable collectio return buffer.WrittenMemory.ToArray(); } + + /// + /// Gets empty asynchronous collection. + /// + /// The type of the elements in the collection. + /// Empty asynchronous collection. + public static IAsyncEnumerable Empty() => EmptyEnumerator.Instance; } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.Buffer.cs b/src/DotNext/Collections/Generic/Collection.Buffer.cs similarity index 64% rename from src/DotNext/Collections/Generic/Sequence.Buffer.cs rename to src/DotNext/Collections/Generic/Collection.Buffer.cs index 3a7b69a1d..cf19a7317 100644 --- a/src/DotNext/Collections/Generic/Sequence.Buffer.cs +++ b/src/DotNext/Collections/Generic/Collection.Buffer.cs @@ -8,7 +8,7 @@ namespace DotNext.Collections.Generic; using Buffers; using static Runtime.Intrinsics; -public static partial class Sequence +public static partial class Collection { /// /// Creates a copy of the elements from the collection. @@ -23,9 +23,7 @@ public static partial class Sequence public static MemoryOwner Copy(this IEnumerable enumerable, int sizeHint = 0, MemoryAllocator? allocator = null) { ArgumentNullException.ThrowIfNull(enumerable); - - if (sizeHint < 0) - throw new ArgumentOutOfRangeException(nameof(sizeHint)); + ArgumentOutOfRangeException.ThrowIfNegative(sizeHint); return enumerable switch { @@ -65,29 +63,4 @@ static MemoryOwner CopySlow(IEnumerable enumerable, int sizeHint, MemoryAl static int GetSize(IEnumerable enumerable, int sizeHint) => enumerable.TryGetNonEnumeratedCount(out var result) ? result : sizeHint; } - - /// - /// Creates a copy of the elements from the collection. - /// - /// The type of elements in the collection. - /// The collection to copy. - /// The approximate size of the collection, if known. - /// The allocator of the memory block used to place copied elements. - /// The token that can be used to cancel the enumeration. - /// Rented memory block containing a copy of the elements from the collection. - /// is . - /// is less than zero. - /// The operation has been canceled. - public static async Task> CopyAsync(this IAsyncEnumerable enumerable, int sizeHint = 0, MemoryAllocator? allocator = null, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(enumerable); - ArgumentOutOfRangeException.ThrowIfNegative(sizeHint); - - using var buffer = new PooledBufferWriter(allocator) { Capacity = sizeHint }; - - await foreach (var item in enumerable.WithCancellation(token).ConfigureAwait(false)) - buffer.Add(item); - - return buffer.DetachBuffer(); - } } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.ConsumingEnumerable.cs b/src/DotNext/Collections/Generic/Collection.ConsumingEnumerable.cs similarity index 98% rename from src/DotNext/Collections/Generic/Sequence.ConsumingEnumerable.cs rename to src/DotNext/Collections/Generic/Collection.ConsumingEnumerable.cs index 856d131f1..39d7e0595 100644 --- a/src/DotNext/Collections/Generic/Sequence.ConsumingEnumerable.cs +++ b/src/DotNext/Collections/Generic/Collection.ConsumingEnumerable.cs @@ -4,7 +4,7 @@ namespace DotNext.Collections.Generic; -public static partial class Sequence +public static partial class Collection { /// /// Represents a wrapped for method diff --git a/src/DotNext/Collections/Generic/Sequence.NotNullEnumerable.cs b/src/DotNext/Collections/Generic/Collection.NotNullEnumerable.cs similarity index 97% rename from src/DotNext/Collections/Generic/Sequence.NotNullEnumerable.cs rename to src/DotNext/Collections/Generic/Collection.NotNullEnumerable.cs index d16484a0d..8f98a5bfa 100644 --- a/src/DotNext/Collections/Generic/Sequence.NotNullEnumerable.cs +++ b/src/DotNext/Collections/Generic/Collection.NotNullEnumerable.cs @@ -2,7 +2,7 @@ namespace DotNext.Collections.Generic; -public static partial class Sequence +public static partial class Collection { private sealed class NotNullEnumerable : IEnumerable where T : class diff --git a/src/DotNext/Collections/Generic/Collection.cs b/src/DotNext/Collections/Generic/Collection.cs index 77a7350f1..b40a35944 100644 --- a/src/DotNext/Collections/Generic/Collection.cs +++ b/src/DotNext/Collections/Generic/Collection.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -6,7 +7,7 @@ namespace DotNext.Collections.Generic; /// /// Provides utility methods to work with collections. /// -public static class Collection +public static partial class Collection { /// /// Returns lazily converted read-only collection. @@ -80,4 +81,214 @@ public static void AddAll(this ICollection collection, IEnumerable item break; } } + + /// + /// Computes hash code for the sequence of objects. + /// + /// Type of the elements in the sequence. + /// The sequence of elements. + /// to include randomized salt data into hashing; to use data from memory only. + /// The hash code computed from each element in the sequence. + public static int SequenceHashCode(this IEnumerable sequence, bool salted = true) + { + const int hashSalt = -1521134295; + var hashCode = sequence.Aggregate(-910176598, static (hash, obj) => (hash * hashSalt) + (obj is null ? 0 : EqualityComparer.Default.GetHashCode(obj))); + return salted ? (hashCode * hashSalt) + RandomExtensions.BitwiseHashSalt : hashCode; + } + + internal static bool SequenceEqual(IEnumerable? first, IEnumerable? second) + => first is null ? second is null : second is not null && Enumerable.SequenceEqual(first, second); + + /// + /// Applies specified action to each collection element. + /// + /// Type of elements in the collection. + /// A collection to enumerate. Cannot be . + /// An action to applied for each element. + public static void ForEach(this IEnumerable collection, Action action) + { + switch (collection) + { + case List list: + Span.ForEach(CollectionsMarshal.AsSpan(list), action); + break; + case T[] array: + Array.ForEach(array, action); + break; + case ArraySegment segment: + Span.ForEach(segment.AsSpan(), action); + break; + case LinkedList list: + ForEachNode(list, action); + break; + case string str: + Span.ForEach(str.AsSpan(), Unsafe.As>(action)); + break; + default: + ForEachSlow(collection, action); + break; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void ForEachSlow(IEnumerable collection, Action action) + { + foreach (var item in collection) + action(item); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void ForEachNode(LinkedList list, Action action) + { + for (var node = list.First; node is not null; node = node.Next) + action(node.Value); + } + } + + /// + /// Applies the specified asynchronous action to each collection element. + /// + /// Type of elements in the collection. + /// A collection to enumerate. Cannot be . + /// An action to applied for each element. + /// The token that can be used to cancel the enumeration. + /// The task representing asynchronous execution of this method. + /// The enumeration has been canceled. + public static async ValueTask ForEachAsync(this IEnumerable collection, Func action, CancellationToken token = default) + { + foreach (var item in collection) + await action.Invoke(item, token).ConfigureAwait(false); + } + + /// + /// Obtains element at the specified index in the sequence. + /// + /// + /// This method is optimized for types + /// and . + /// + /// Type of elements in the sequence. + /// Source collection. + /// Index of the element to read. + /// Obtained element. + /// , if element is available in the collection and obtained successfully; otherwise, . + public static bool ElementAt(this IEnumerable collection, int index, [MaybeNullWhen(false)] out T element) + { + return collection switch + { + List list => Span.ElementAt(CollectionsMarshal.AsSpan(list), index, out element), + T[] array => Span.ElementAt(array, index, out element), + LinkedList list => NodeValueAt(list, index, out element), + IList list => ListElementAt(list, index, out element), + IReadOnlyList readOnlyList => ReadOnlyListElementAt(readOnlyList, index, out element), + _ => ElementAtSlow(collection, index, out element), + }; + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool NodeValueAt(LinkedList list, int matchIndex, [MaybeNullWhen(false)] out T element) + { + // slow but no memory allocation + var index = 0; + for (var node = list.First; node is not null; node = node.Next) + { + if (index++ == matchIndex) + { + element = node.Value; + return true; + } + } + + element = default; + return false; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static bool ElementAtSlow(IEnumerable collection, int index, [MaybeNullWhen(false)] out T element) + { + using var enumerator = collection.GetEnumerator(); + enumerator.Skip(index); + if (enumerator.MoveNext()) + { + element = enumerator.Current; + return true; + } + + element = default!; + return false; + } + + static bool ListElementAt(IList list, int index, [MaybeNullWhen(false)] out T element) + { + if ((uint)index < (uint)list.Count) + { + element = list[index]; + return true; + } + + element = default!; + return false; + } + + static bool ReadOnlyListElementAt(IReadOnlyList list, int index, [MaybeNullWhen(false)] out T element) + { + if ((uint)index < (uint)list.Count) + { + element = list[index]; + return true; + } + + element = default!; + return false; + } + } + + /// + /// Skip values in the collection. + /// + /// Type of elements in the collection. + /// A collection to check. Cannot be . + /// Modified lazy collection without values. + public static IEnumerable SkipNulls(this IEnumerable collection) + where T : class + => new NotNullEnumerable(collection); + + /// + /// Concatenates each element from the collection into single string. + /// + /// Type of array elements. + /// Collection to convert. Cannot be . + /// Delimiter between elements in the final string. + /// A string to be returned if collection has no elements. + /// Converted collection into string. + public static string ToString(this IEnumerable collection, string delimiter, string ifEmpty = "") + => string.Join(delimiter, collection) is { Length: > 0 } result ? result : ifEmpty; + + /// + /// Adds to the beginning of . + /// + /// The type of items in the collection. + /// The collection to be concatenated with the items. + /// The items to be added to the beginning of the collection. + /// The concatenated collection. + public static IEnumerable Prepend(this IEnumerable collection, params T[] items) + => items.Concat(collection); + + /// + /// Adds to the end of . + /// + /// The type of items in the collection. + /// The collection to be concatenated with the items. + /// The items to be added to the end of the collection. + /// The concatenated collection. + public static IEnumerable Append(this IEnumerable collection, params T[] items) + => collection.Concat(items); + + /// + /// Converts synchronous collection of elements to asynchronous. + /// + /// The collection of elements. + /// The type of the elements in the collection. + /// The asynchronous wrapper over synchronous collection of elements. + /// is . + public static IAsyncEnumerable ToAsyncEnumerable(this IEnumerable enumerable) + => new AsyncEnumerable.Proxy(enumerable ?? throw new ArgumentNullException(nameof(enumerable))); } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Dictionary.Empty.cs b/src/DotNext/Collections/Generic/Dictionary.Empty.cs new file mode 100644 index 000000000..c23c7c026 --- /dev/null +++ b/src/DotNext/Collections/Generic/Dictionary.Empty.cs @@ -0,0 +1,45 @@ +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace DotNext.Collections.Generic; + +public static partial class Dictionary +{ + [DebuggerDisplay("Count = 0")] + private sealed class EmptyDictionary : IReadOnlyDictionary + { + internal static readonly EmptyDictionary Instance = new(); + + private EmptyDictionary() + { + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + int IReadOnlyCollection>.Count => 0; + + TValue IReadOnlyDictionary.this[TKey key] => throw new KeyNotFoundException(); + + bool IReadOnlyDictionary.ContainsKey(TKey key) => false; + + bool IReadOnlyDictionary.TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + { + value = default; + return false; + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + IEnumerable IReadOnlyDictionary.Keys => Array.Empty(); + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + IEnumerable IReadOnlyDictionary.Values => Array.Empty(); + + private static IEnumerator> EmptyEnumerator + => Enumerable.Empty>().GetEnumerator(); + + IEnumerator> IEnumerable>.GetEnumerator() + => EmptyEnumerator; + + IEnumerator IEnumerable.GetEnumerator() => EmptyEnumerator; + } +} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Dictionary.cs b/src/DotNext/Collections/Generic/Dictionary.cs index 82d8f8c95..2fd03a388 100644 --- a/src/DotNext/Collections/Generic/Dictionary.cs +++ b/src/DotNext/Collections/Generic/Dictionary.cs @@ -12,7 +12,7 @@ namespace DotNext.Collections.Generic; /// Represents various extensions for types /// and . /// -public static class Dictionary +public static partial class Dictionary { /// /// Provides strongly-typed access to dictionary indexer. diff --git a/src/DotNext/Collections/Generic/EmptyDictionary.cs b/src/DotNext/Collections/Generic/EmptyDictionary.cs deleted file mode 100644 index 041cbf032..000000000 --- a/src/DotNext/Collections/Generic/EmptyDictionary.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace DotNext.Collections.Generic; - -[DebuggerDisplay("Count = 0")] -internal sealed class EmptyDictionary : IReadOnlyDictionary -{ - internal static readonly EmptyDictionary Instance = new(); - - private EmptyDictionary() - { - } - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - int IReadOnlyCollection>.Count => 0; - - TValue IReadOnlyDictionary.this[TKey key] => throw new KeyNotFoundException(); - - bool IReadOnlyDictionary.ContainsKey(TKey key) => false; - - bool IReadOnlyDictionary.TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) - { - value = default; - return false; - } - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - IEnumerable IReadOnlyDictionary.Keys => Array.Empty(); - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - IEnumerable IReadOnlyDictionary.Values => Array.Empty(); - - IEnumerator> IEnumerable>.GetEnumerator() - => Sequence.GetEmptyEnumerator>(); - - IEnumerator IEnumerable.GetEnumerator() => Sequence.GetEmptyEnumerator>(); -} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.LimitedEnumerator.cs b/src/DotNext/Collections/Generic/Enumerator.LimitedEnumerator.cs similarity index 97% rename from src/DotNext/Collections/Generic/Sequence.LimitedEnumerator.cs rename to src/DotNext/Collections/Generic/Enumerator.LimitedEnumerator.cs index e142ac2b6..b0842d2fe 100644 --- a/src/DotNext/Collections/Generic/Sequence.LimitedEnumerator.cs +++ b/src/DotNext/Collections/Generic/Enumerator.LimitedEnumerator.cs @@ -3,7 +3,7 @@ namespace DotNext.Collections.Generic; -public static partial class Sequence +public static partial class Enumerator { /// /// Wrapped for the enumerator which is limited by count. diff --git a/src/DotNext/Collections/Generic/Enumerator.cs b/src/DotNext/Collections/Generic/Enumerator.cs new file mode 100644 index 000000000..c451ddfff --- /dev/null +++ b/src/DotNext/Collections/Generic/Enumerator.cs @@ -0,0 +1,124 @@ +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DotNext.Collections.Generic; + +/// +/// Various methods to work with classes implementing interface. +/// +public static partial class Enumerator +{ + /// + /// Bypasses a specified number of elements in a sequence. + /// + /// The type of the elements in the sequence. + /// Enumerator to modify. Cannot be . + /// The number of elements to skip. + /// , if current element is available; otherwise, . + public static bool Skip(this IEnumerator enumerator, int count) + { + while (count > 0) + { + if (!enumerator.MoveNext()) + return false; + + count--; + } + + return true; + } + + /// + /// Bypasses a specified number of elements in a sequence. + /// + /// The type of the sequence. + /// The type of the elements in the sequence. + /// Enumerator to modify. + /// The number of elements to skip. + /// , if current element is available; otherwise, . + public static bool Skip(this ref TEnumerator enumerator, int count) + where TEnumerator : struct, IEnumerator + { + while (count > 0) + { + if (!enumerator.MoveNext()) + return false; + + count--; + } + + return true; + } + + /// + /// Limits the number of the elements in the sequence. + /// + /// The type of items in the sequence. + /// The sequence of the elements. + /// The maximum number of the elements in the returned sequence. + /// to dispose ; otherwise, . + /// The enumerator which is limited by count. + public static LimitedEnumerator Limit(this IEnumerator enumerator, int count, bool leaveOpen = false) + => new(enumerator, count, leaveOpen); + + /// + /// Gets enumerator over all elements in the memory. + /// + /// The memory block to be converted. + /// The type of elements in the memory. + /// The enumerator over all elements in the memory. + /// + public static IEnumerator ToEnumerator(ReadOnlyMemory memory) + { + return memory.IsEmpty + ? Enumerable.Empty().GetEnumerator() + : MemoryMarshal.TryGetArray(memory, out var segment) + ? segment.GetEnumerator() + : ToEnumeratorSlow(memory); + + static IEnumerator ToEnumeratorSlow(ReadOnlyMemory memory) + { + for (nint i = 0; i < memory.Length; i++) + yield return Unsafe.Add(ref MemoryMarshal.GetReference(memory.Span), i); + } + } + + /// + /// Gets enumerator over all elements in the sequence. + /// + /// The type of elements in the sequence. + /// A sequence of elements. + /// The enumerator over all elements in the sequence. + public static IEnumerator ToEnumerator(in ReadOnlySequence sequence) + { + return sequence.IsEmpty + ? Enumerable.Empty().GetEnumerator() + : sequence.IsSingleSegment + ? ToEnumerator(sequence.First) + : ToEnumeratorSlow(sequence.GetEnumerator()); + + static IEnumerator ToEnumeratorSlow(ReadOnlySequence.Enumerator enumerator) + { + while (enumerator.MoveNext()) + { + var segment = enumerator.Current; + + for (nint i = 0; i < segment.Length; i++) + yield return Unsafe.Add(ref MemoryMarshal.GetReference(segment.Span), i); + } + } + } + + /// + /// Obtains asynchronous enumerator over the sequence of elements. + /// + /// The collection of elements. + /// The token that can be used by consumer to cancel the enumeration. + /// The type of the elements in the collection. + /// The asynchronous wrapper over synchronous enumerator. + /// is . + public static IAsyncEnumerator GetAsyncEnumerator(this IEnumerable enumerable, CancellationToken token = default) + => new AsyncEnumerable.Proxy.Enumerator(enumerable ?? throw new ArgumentNullException(nameof(enumerable)), token); +} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/ReadOnlyCollectionView.cs b/src/DotNext/Collections/Generic/ReadOnlyCollectionView.cs index c882007c3..c3d887843 100644 --- a/src/DotNext/Collections/Generic/ReadOnlyCollectionView.cs +++ b/src/DotNext/Collections/Generic/ReadOnlyCollectionView.cs @@ -9,22 +9,16 @@ namespace DotNext.Collections.Generic; /// /// Type of items in the source collection. /// Type of items in the converted collection. +/// +/// Initializes a new lazily converted view. +/// +/// Read-only collection to convert. +/// Collection items converter. [StructLayout(LayoutKind.Auto)] -public readonly struct ReadOnlyCollectionView : IReadOnlyCollection, IEquatable> +public readonly struct ReadOnlyCollectionView(IReadOnlyCollection collection, Func mapper) : IReadOnlyCollection, IEquatable> { - private readonly IReadOnlyCollection? source; - private readonly Func mapper; - - /// - /// Initializes a new lazily converted view. - /// - /// Read-only collection to convert. - /// Collection items converter. - public ReadOnlyCollectionView(IReadOnlyCollection collection, Func mapper) - { - source = collection ?? throw new ArgumentNullException(nameof(collection)); - this.mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - } + private readonly IReadOnlyCollection? source = collection ?? throw new ArgumentNullException(nameof(collection)); + private readonly Func mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); /// /// Initializes a new lazily converted view. @@ -46,7 +40,7 @@ public ReadOnlyCollectionView(IReadOnlyCollection collection, Converter< /// /// The enumerator over converted items. public IEnumerator GetEnumerator() - => source is null || mapper is null || source.Count == 0 ? Sequence.GetEmptyEnumerator() : source.Select(mapper).GetEnumerator(); + => source is null or { Count: 0 } || mapper is null ? Enumerable.Empty().GetEnumerator() : source.Select(mapper).GetEnumerator(); /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs b/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs index cad0d7b61..dbfc83fdd 100644 --- a/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs +++ b/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs @@ -11,22 +11,16 @@ namespace DotNext.Collections.Generic; /// Type of dictionary keys. /// Type of values in the source dictionary. /// Type of values in the converted dictionary. +/// +/// Initializes a new lazily converted view. +/// +/// Read-only dictionary to convert. +/// Value converter. [StructLayout(LayoutKind.Auto)] -public readonly struct ReadOnlyDictionaryView : IReadOnlyDictionary, IEquatable> +public readonly struct ReadOnlyDictionaryView(IReadOnlyDictionary dictionary, Func mapper) : IReadOnlyDictionary, IEquatable> { - private readonly IReadOnlyDictionary? source; - private readonly Func mapper; - - /// - /// Initializes a new lazily converted view. - /// - /// Read-only dictionary to convert. - /// Value converter. - public ReadOnlyDictionaryView(IReadOnlyDictionary dictionary, Func mapper) - { - source = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); - this.mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - } + private readonly IReadOnlyDictionary? source = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); + private readonly Func mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); /// /// Initializes a new lazily converted view. @@ -91,7 +85,7 @@ private static IEnumerator> GetEnumerator(IReadOnlyD /// /// The enumerator over key/value pairs. public IEnumerator> GetEnumerator() - => source is null || mapper is null || source.Count == 0 ? Sequence.GetEmptyEnumerator>() : GetEnumerator(source, mapper); + => source is null or { Count: 0 } || mapper is null ? Dictionary.Empty().GetEnumerator() : GetEnumerator(source, mapper); /// /// Returns the converted value associated with the specified key. diff --git a/src/DotNext/Collections/Generic/ReadOnlyListView.cs b/src/DotNext/Collections/Generic/ReadOnlyListView.cs index f2f0b15c5..904d31237 100644 --- a/src/DotNext/Collections/Generic/ReadOnlyListView.cs +++ b/src/DotNext/Collections/Generic/ReadOnlyListView.cs @@ -9,22 +9,16 @@ namespace DotNext.Collections.Generic; /// /// Type of items in the source list. /// Type of items in the converted list. +/// +/// Initializes a new lazily converted view. +/// +/// Read-only list to convert. +/// List items converter. [StructLayout(LayoutKind.Auto)] -public readonly struct ReadOnlyListView : IReadOnlyList, IEquatable> +public readonly struct ReadOnlyListView(IReadOnlyList list, Func mapper) : IReadOnlyList, IEquatable> { - private readonly IReadOnlyList? source; - private readonly Func mapper; - - /// - /// Initializes a new lazily converted view. - /// - /// Read-only list to convert. - /// List items converter. - public ReadOnlyListView(IReadOnlyList list, Func mapper) - { - source = list ?? throw new ArgumentNullException(nameof(list)); - this.mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); - } + private readonly IReadOnlyList? source = list ?? throw new ArgumentNullException(nameof(list)); + private readonly Func mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); /// /// Initializes a new lazily converted view. @@ -63,7 +57,7 @@ public TOutput this[int index] /// /// The enumerator over converted items. public IEnumerator GetEnumerator() - => source is null || mapper is null || source.Count == 0 ? Sequence.GetEmptyEnumerator() : source.Select(mapper).GetEnumerator(); + => source is null or { Count: 0 } || mapper is null ? Enumerable.Empty().GetEnumerator() : source.Select(mapper).GetEnumerator(); /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/DotNext/Collections/Generic/Sequence.AsyncEnumerator.cs b/src/DotNext/Collections/Generic/Sequence.AsyncEnumerator.cs deleted file mode 100644 index 75ba707a2..000000000 --- a/src/DotNext/Collections/Generic/Sequence.AsyncEnumerator.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace DotNext.Collections.Generic; - -public static partial class Sequence -{ - private sealed class AsyncEnumerable : IAsyncEnumerable - { - internal sealed class Enumerator : Disposable, IAsyncEnumerator - { - private readonly IEnumerator enumerator; - private readonly CancellationToken token; - - internal Enumerator(IEnumerable enumerable, CancellationToken token) - { - enumerator = enumerable.GetEnumerator(); - this.token = token; - } - - public T Current => enumerator.Current; - - public ValueTask MoveNextAsync() - => token.IsCancellationRequested ? ValueTask.FromCanceled(token) : new(enumerator.MoveNext()); - - protected override void Dispose(bool disposing) - { - if (disposing) - { - enumerator.Dispose(); - } - - base.Dispose(disposing); - } - - public new ValueTask DisposeAsync() => base.DisposeAsync(); - } - - private readonly IEnumerable enumerable; - - internal AsyncEnumerable(IEnumerable enumerable) - => this.enumerable = enumerable; - - public IAsyncEnumerator GetAsyncEnumerator(CancellationToken token) - => new Enumerator(enumerable, token); - } - - /// - /// Converts synchronous collection of elements to asynchronous. - /// - /// The collection of elements. - /// The type of the elements in the collection. - /// The asynchronous wrapper over synchronous collection of elements. - public static IAsyncEnumerable ToAsyncEnumerable(this IEnumerable enumerable) - => new AsyncEnumerable(enumerable ?? throw new ArgumentNullException(nameof(enumerable))); - - /// - /// Obtains asynchronous enumerator over the sequence of elements. - /// - /// The collection of elements. - /// The token that can be used by consumer to cancel the enumeration. - /// The type of the elements in the collection. - /// The asynchronous wrapper over synchronous enumerator. - public static IAsyncEnumerator GetAsyncEnumerator(this IEnumerable enumerable, CancellationToken token = default) - => new AsyncEnumerable.Enumerator(enumerable ?? throw new ArgumentNullException(nameof(enumerable)), token); - - /// - /// Gets empty asynchronous collection. - /// - /// The type of the elements in the collection. - /// Empty asynchronous collection. - public static IAsyncEnumerable GetEmptyAsyncEnumerable() => EmptyEnumerator.Instance; -} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.AsyncGenerator.cs b/src/DotNext/Collections/Generic/Sequence.AsyncGenerator.cs deleted file mode 100644 index 9dc1a0eaf..000000000 --- a/src/DotNext/Collections/Generic/Sequence.AsyncGenerator.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Collections.Generic; - -public static partial class Sequence -{ - /// - /// Gets enumerable collection created from generator method. - /// - /// The type of the elements returned by generator method. - [StructLayout(LayoutKind.Auto)] - public readonly struct AsyncGenerator : IAsyncEnumerable - { - private sealed class Enumerator : IAsyncEnumerator - { - private readonly Func>>? generator; - private readonly CancellationToken token; - private Optional current; - - internal Enumerator(Func>>? generator, CancellationToken token) - { - this.generator = generator; - this.token = token; - } - - /// - /// Gets the element in the collection at the current position of the enumerator. - /// - /// The enumerator is empty. - public T Current - => current.TryGet(out var result, out var isNull) || isNull ? result! : throw new InvalidOperationException(); - - /// - /// Advances the enumerator to the next element. - /// - /// if the enumerator was successfully advanced to the next element; if - /// the enumerator has passed the end of the collection. - public async ValueTask MoveNextAsync() - { - Optional current; - if (generator is null) - current = default; - else - this.current = current = await generator(token).ConfigureAwait(false); - - return !current.IsUndefined; - } - - ValueTask IAsyncDisposable.DisposeAsync() - { - current = default; - return default; - } - } - - private readonly Func>>? generator; - - internal AsyncGenerator(Func>> generator) - => this.generator = generator; - - /// - /// Gets enumerator over elements to be returned by generator method. - /// - /// The token that can be used to cancel the enumeration. - /// The enumerator over elements. - public IAsyncEnumerator GetAsyncEnumerator(CancellationToken token = default) => new Enumerator(generator, token); - } - - /// - /// Converts generator function to enumerable collection. - /// - /// Stateful generator function. - /// The type of the elements in the collection. - /// The enumerable collection. - /// is . - public static AsyncGenerator ToAsyncEnumerable(this Func>> generator) - => new(generator ?? throw new ArgumentNullException(nameof(generator))); -} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.EmptyEnumerator.cs b/src/DotNext/Collections/Generic/Sequence.EmptyEnumerator.cs deleted file mode 100644 index 0c1a64b23..000000000 --- a/src/DotNext/Collections/Generic/Sequence.EmptyEnumerator.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections; -using System.Diagnostics; - -namespace DotNext.Collections.Generic; - -public static partial class Sequence -{ - [DebuggerDisplay("Count = 0")] - private sealed class EmptyEnumerator : IEnumerator, IAsyncEnumerator, IAsyncEnumerable - { - internal static readonly EmptyEnumerator Instance = new(); - - private EmptyEnumerator() - { - } - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - public T Current => throw new InvalidOperationException(); - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - object? IEnumerator.Current => Current; - - bool IEnumerator.MoveNext() => false; - - ValueTask IAsyncEnumerator.MoveNextAsync() => new(false); - - IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken) => this; - - void IEnumerator.Reset() - { - } - - void IDisposable.Dispose() - { - } - - ValueTask IAsyncDisposable.DisposeAsync() => ValueTask.CompletedTask; - } - - /// - /// Gets empty enumerator. - /// - /// The type of elements in the enumerator. - /// Empty enumerator. - public static IEnumerator GetEmptyEnumerator() => EmptyEnumerator.Instance; -} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.Generator.cs b/src/DotNext/Collections/Generic/Sequence.Generator.cs deleted file mode 100644 index 13d124c4d..000000000 --- a/src/DotNext/Collections/Generic/Sequence.Generator.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Collections.Generic; - -public static partial class Sequence -{ - /// - /// Gets enumerable collection created from generator method. - /// - /// The type of the elements returned by generator method. - [StructLayout(LayoutKind.Auto)] - public readonly struct Generator - { - /// - /// The enumerator over elements returned by generator method. - /// - [StructLayout(LayoutKind.Auto)] - public struct Enumerator - { - private readonly Func>? generator; - private Optional current; - - internal Enumerator(Func>? generator) - { - this.generator = generator; - current = Optional.None; - } - - /// - /// Gets the element in the collection at the current position of the enumerator. - /// - /// The enumerator is empty. - public readonly T Current - => current.TryGet(out var result, out var isNull) || isNull ? result! : throw new InvalidOperationException(); - - /// - /// Advances the enumerator to the next element. - /// - /// if the enumerator was successfully advanced to the next element; if - /// the enumerator has passed the end of the collection. - public bool MoveNext() - { - Optional current; - if (generator is null) - current = default; - else - this.current = current = generator(); - - return !current.IsUndefined; - } - } - - private readonly Func>? generator; - - internal Generator(Func> generator) - => this.generator = generator; - - /// - /// Gets enumerator over elements to be returned by generator method. - /// - /// The enumerator over elements. - public Enumerator GetEnumerator() => new(generator); - } - - /// - /// Converts generator function to enumerable collection. - /// - /// Stateful generator function. - /// The type of the elements in the collection. - /// The enumerable collection. - /// is . - public static Generator ToEnumerable(this Func> generator) - => new(generator ?? throw new ArgumentNullException(nameof(generator))); -} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Sequence.cs b/src/DotNext/Collections/Generic/Sequence.cs deleted file mode 100644 index 8431d5f3d..000000000 --- a/src/DotNext/Collections/Generic/Sequence.cs +++ /dev/null @@ -1,324 +0,0 @@ -using System.Buffers; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DotNext.Collections.Generic; - -using static Runtime.Intrinsics; - -/// -/// Various methods to work with classes implementing interface. -/// -public static partial class Sequence -{ - /// - /// Computes hash code for the sequence of objects. - /// - /// Type of the elements in the sequence. - /// The sequence of elements. - /// to include randomized salt data into hashing; to use data from memory only. - /// The hash code computed from each element in the sequence. - public static int SequenceHashCode(this IEnumerable sequence, bool salted = true) - { - const int hashSalt = -1521134295; - var hashCode = sequence.Aggregate(-910176598, static (hash, obj) => (hash * hashSalt) + (obj is null ? 0 : EqualityComparer.Default.GetHashCode(obj))); - return salted ? (hashCode * hashSalt) + RandomExtensions.BitwiseHashSalt : hashCode; - } - - internal static bool SequenceEqual(IEnumerable? first, IEnumerable? second) - => first is null ? second is null : second is not null && Enumerable.SequenceEqual(first, second); - - /// - /// Applies specified action to each collection element. - /// - /// Type of elements in the collection. - /// A collection to enumerate. Cannot be . - /// An action to applied for each element. - public static void ForEach(this IEnumerable collection, Action action) - { - switch (collection) - { - case List list: - Span.ForEach(CollectionsMarshal.AsSpan(list), action); - break; - case T[] array: - Array.ForEach(array, action); - break; - case ArraySegment segment: - Span.ForEach(segment.AsSpan(), action); - break; - case LinkedList list: - ForEachNode(list, action); - break; - case string str: - Span.ForEach(str.AsSpan(), Unsafe.As>(action)); - break; - default: - ForEachSlow(collection, action); - break; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static void ForEachSlow(IEnumerable collection, Action action) - { - foreach (var item in collection) - action(item); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static void ForEachNode(LinkedList list, Action action) - { - for (var node = list.First; node is not null; node = node.Next) - action(node.Value); - } - } - - /// - /// Applies the specified asynchronous action to each collection element. - /// - /// Type of elements in the collection. - /// A collection to enumerate. Cannot be . - /// An action to applied for each element. - /// The token that can be used to cancel the enumeration. - /// The task representing asynchronous execution of this method. - /// The enumeration has been canceled. - public static async ValueTask ForEachAsync(this IEnumerable collection, Func action, CancellationToken token = default) - { - foreach (var item in collection) - await action.Invoke(item, token).ConfigureAwait(false); - } - - /// - /// Bypasses a specified number of elements in a sequence. - /// - /// The type of the elements in the sequence. - /// Enumerator to modify. Cannot be . - /// The number of elements to skip. - /// , if current element is available; otherwise, . - public static bool Skip(this IEnumerator enumerator, int count) - { - while (count > 0) - { - if (!enumerator.MoveNext()) - return false; - - count--; - } - - return true; - } - - /// - /// Bypasses a specified number of elements in a sequence. - /// - /// The type of the sequence. - /// The type of the elements in the sequence. - /// Enumerator to modify. - /// The number of elements to skip. - /// , if current element is available; otherwise, . - public static bool Skip(this ref TEnumerator enumerator, int count) - where TEnumerator : struct, IEnumerator - { - while (count > 0) - { - if (!enumerator.MoveNext()) - return false; - - count--; - } - - return true; - } - - /// - /// Obtains element at the specified index in the sequence. - /// - /// - /// This method is optimized for types - /// and . - /// - /// Type of elements in the sequence. - /// Source collection. - /// Index of the element to read. - /// Obtained element. - /// , if element is available in the collection and obtained successfully; otherwise, . - public static bool ElementAt(this IEnumerable collection, int index, [MaybeNullWhen(false)] out T element) - { - return collection switch - { - List list => Span.ElementAt(CollectionsMarshal.AsSpan(list), index, out element), - T[] array => Span.ElementAt(array, index, out element), - LinkedList list => NodeValueAt(list, index, out element), - IList list => ListElementAt(list, index, out element), - IReadOnlyList readOnlyList => ReadOnlyListElementAt(readOnlyList, index, out element), - _ => ElementAtSlow(collection, index, out element), - }; - - [MethodImpl(MethodImplOptions.NoInlining)] - static bool NodeValueAt(LinkedList list, int matchIndex, [MaybeNullWhen(false)] out T element) - { - // slow but no memory allocation - var index = 0; - for (var node = list.First; node is not null; node = node.Next) - { - if (index++ == matchIndex) - { - element = node.Value; - return true; - } - } - - element = default; - return false; - } - - [MethodImpl(MethodImplOptions.NoInlining)] - static bool ElementAtSlow(IEnumerable collection, int index, [MaybeNullWhen(false)] out T element) - { - using var enumerator = collection.GetEnumerator(); - enumerator.Skip(index); - if (enumerator.MoveNext()) - { - element = enumerator.Current; - return true; - } - - element = default!; - return false; - } - - static bool ListElementAt(IList list, int index, [MaybeNullWhen(false)] out T element) - { - if ((uint)index < (uint)list.Count) - { - element = list[index]; - return true; - } - - element = default!; - return false; - } - - static bool ReadOnlyListElementAt(IReadOnlyList list, int index, [MaybeNullWhen(false)] out T element) - { - if ((uint)index < (uint)list.Count) - { - element = list[index]; - return true; - } - - element = default!; - return false; - } - } - - /// - /// Skip values in the collection. - /// - /// Type of elements in the collection. - /// A collection to check. Cannot be . - /// Modified lazy collection without values. - public static IEnumerable SkipNulls(this IEnumerable collection) - where T : class - => new NotNullEnumerable(collection); - - /// - /// Concatenates each element from the collection into single string. - /// - /// Type of array elements. - /// Collection to convert. Cannot be . - /// Delimiter between elements in the final string. - /// A string to be returned if collection has no elements. - /// Converted collection into string. - public static string ToString(this IEnumerable collection, string delimiter, string ifEmpty = "") - => string.Join(delimiter, collection) is { Length: > 0 } result ? result : ifEmpty; - - /// - /// Constructs a sequence from the single element. - /// - /// Type of element. - /// An item to be placed into sequence. - /// Sequence of single element. - public static IEnumerable Singleton(T item) - => List.Singleton(item); - - /// - /// Adds to the beginning of . - /// - /// The type of items in the collection. - /// The collection to be concatenated with the items. - /// The items to be added to the beginning of the collection. - /// The concatenated collection. - public static IEnumerable Prepend(this IEnumerable collection, params T[] items) - => items.Concat(collection); - - /// - /// Adds to the end of . - /// - /// The type of items in the collection. - /// The collection to be concatenated with the items. - /// The items to be added to the end of the collection. - /// The concatenated collection. - public static IEnumerable Append(this IEnumerable collection, params T[] items) - => collection.Concat(items); - - /// - /// Limits the number of the elements in the sequence. - /// - /// The type of items in the sequence. - /// The sequence of the elements. - /// The maximum number of the elements in the returned sequence. - /// to dispose ; otherwise, . - /// The enumerator which is limited by count. - public static LimitedEnumerator Limit(this IEnumerator enumerator, int count, bool leaveOpen = false) - => new(enumerator, count, leaveOpen); - - /// - /// Gets enumerator over all elements in the memory. - /// - /// The memory block to be converted. - /// The type of elements in the memory. - /// The enumerator over all elements in the memory. - /// - public static IEnumerator ToEnumerator(ReadOnlyMemory memory) - { - return memory.IsEmpty - ? GetEmptyEnumerator() - : MemoryMarshal.TryGetArray(memory, out var segment) - ? segment.GetEnumerator() - : ToEnumeratorSlow(memory); - - static IEnumerator ToEnumeratorSlow(ReadOnlyMemory memory) - { - for (nint i = 0; i < memory.Length; i++) - yield return Unsafe.Add(ref MemoryMarshal.GetReference(memory.Span), i); - } - } - - /// - /// Gets enumerator over all elements in the sequence. - /// - /// The type of elements in the sequence. - /// A sequence of elements. - /// The enumerator over all elements in the sequence. - public static IEnumerator ToEnumerator(in ReadOnlySequence sequence) - { - return sequence.IsEmpty - ? GetEmptyEnumerator() - : sequence.IsSingleSegment - ? ToEnumerator(sequence.First) - : ToEnumeratorSlow(sequence.GetEnumerator()); - - static IEnumerator ToEnumeratorSlow(ReadOnlySequence.Enumerator enumerator) - { - while (enumerator.MoveNext()) - { - var segment = enumerator.Current; - - for (nint i = 0; i < segment.Length; i++) - yield return Unsafe.Add(ref MemoryMarshal.GetReference(segment.Span), i); - } - } - } -} diff --git a/src/DotNext/Collections/Specialized/InvocationList.cs b/src/DotNext/Collections/Specialized/InvocationList.cs index b23f65a4a..893b2d7ea 100644 --- a/src/DotNext/Collections/Specialized/InvocationList.cs +++ b/src/DotNext/Collections/Specialized/InvocationList.cs @@ -5,8 +5,6 @@ namespace DotNext.Collections.Specialized; -using Collections.Generic; - /// /// Represents immutable list of delegates. /// @@ -91,9 +89,8 @@ public bool MoveNext() private InvocationList(TDelegate[] array, TDelegate d) { - Array.Resize(ref array, array.Length + 1); - array[array.Length - 1] = d; - list = array; + TDelegate[] list = [.. array, d]; + this.list = list; } private InvocationList(TDelegate d1, TDelegate d2) @@ -200,7 +197,7 @@ public InvocationList Remove(TDelegate? d) private IEnumerator GetEnumeratorCore() => list switch { - null => Sequence.GetEmptyEnumerator(), + null => Enumerable.Empty().GetEnumerator(), TDelegate d => new SingletonList.Enumerator(d), _ => Unsafe.As>(list).GetEnumerator(), }; diff --git a/src/DotNext/EqualityComparerBuilder.cs b/src/DotNext/EqualityComparerBuilder.cs index 3ed9663f5..c810be1de 100644 --- a/src/DotNext/EqualityComparerBuilder.cs +++ b/src/DotNext/EqualityComparerBuilder.cs @@ -6,9 +6,9 @@ namespace DotNext; +using Collections.Generic; using Reflection; using Intrinsics = Runtime.Intrinsics; -using Seq = Collections.Generic.Sequence; /// /// Generates hash code and equality check functions for the particular type. @@ -141,12 +141,12 @@ private static MethodInfo EqualsMethodForArrayElementType(Type itemType) if (itemType.IsValueType) { - return typeof(Seq) - .GetMethod(nameof(Seq.SequenceEqual), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)! + return typeof(Collection) + .GetMethod(nameof(Collection.SequenceEqual), BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly)! .MakeGenericMethod(itemType); } - return new Func?, IEnumerable?, bool>(Seq.SequenceEqual).Method; + return new Func?, IEnumerable?, bool>(Collection.SequenceEqual).Method; } [RequiresUnreferencedCode("Dynamic code generation may be incompatible with IL trimming")] @@ -164,11 +164,11 @@ private static MethodInfo HashCodeMethodForArrayElementType(Type itemType) { var arrayType = Type.MakeGenericMethodParameter(0).MakeArrayType(); return typeof(OneDimensionalArray) - .GetMethod(nameof(OneDimensionalArray.BitwiseHashCode), 1, PublicStaticFlags, null, new[] { arrayType, typeof(bool) }, null)! + .GetMethod(nameof(OneDimensionalArray.BitwiseHashCode), 1, PublicStaticFlags, null, [arrayType, typeof(bool)], null)! .MakeGenericMethod(itemType); } - return typeof(Seq).GetMethod(nameof(Seq.SequenceHashCode), PublicStaticFlags)! + return typeof(Collection).GetMethod(nameof(Collection.SequenceHashCode), PublicStaticFlags)! .MakeGenericMethod(itemType); } @@ -278,7 +278,7 @@ private Func BuildGetHashCode() } expressions.Add(hashCodeTemp); - expr = Expression.Block(typeof(int), Seq.Singleton(hashCodeTemp), expressions); + expr = Expression.Block(typeof(int), List.Singleton(hashCodeTemp), expressions); return Expression.Lambda>(expr, false, inputParam).Compile(); } From b167337c59b5f1ee2fb6c54d45e44f627fe62f2a Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 10:02:02 +0200 Subject: [PATCH 044/155] Migration to Empty dictionary from .NET library --- .../Collections/Generic/DictionaryTests.cs | 11 ----- .../Collections/Generic/Dictionary.Empty.cs | 45 ------------------- src/DotNext/Collections/Generic/Dictionary.cs | 9 ---- .../Generic/ReadOnlyDictionaryView.cs | 2 +- .../Collections/Specialized/InvocationList.cs | 5 +-- src/DotNext/UserDataStorage.DebugSupport.cs | 3 +- .../MetadataTransferObject.cs | 4 +- 7 files changed, 6 insertions(+), 73 deletions(-) delete mode 100644 src/DotNext/Collections/Generic/Dictionary.Empty.cs diff --git a/src/DotNext.Tests/Collections/Generic/DictionaryTests.cs b/src/DotNext.Tests/Collections/Generic/DictionaryTests.cs index 9017e34ba..7090686e2 100644 --- a/src/DotNext.Tests/Collections/Generic/DictionaryTests.cs +++ b/src/DotNext.Tests/Collections/Generic/DictionaryTests.cs @@ -154,15 +154,4 @@ public static void KeysAndValuesAsDelegate() Equal(new HashSet(new[] { "one", "two" }), Enumerable.ToHashSet(dict.KeysGetter().Invoke())); Equal(new HashSet(new[] { 1, 2 }), Enumerable.ToHashSet(dict.ValuesGetter().Invoke())); } - - [Fact] - public static void EmptyDictionary() - { - var dict = Dictionary.Empty(); - Empty(dict); - False(dict.ContainsKey(string.Empty)); - False(dict.TryGetValue(string.Empty, out _)); - Empty(dict.Keys); - Empty(dict.Values); - } } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Dictionary.Empty.cs b/src/DotNext/Collections/Generic/Dictionary.Empty.cs deleted file mode 100644 index c23c7c026..000000000 --- a/src/DotNext/Collections/Generic/Dictionary.Empty.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace DotNext.Collections.Generic; - -public static partial class Dictionary -{ - [DebuggerDisplay("Count = 0")] - private sealed class EmptyDictionary : IReadOnlyDictionary - { - internal static readonly EmptyDictionary Instance = new(); - - private EmptyDictionary() - { - } - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - int IReadOnlyCollection>.Count => 0; - - TValue IReadOnlyDictionary.this[TKey key] => throw new KeyNotFoundException(); - - bool IReadOnlyDictionary.ContainsKey(TKey key) => false; - - bool IReadOnlyDictionary.TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) - { - value = default; - return false; - } - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - IEnumerable IReadOnlyDictionary.Keys => Array.Empty(); - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - IEnumerable IReadOnlyDictionary.Values => Array.Empty(); - - private static IEnumerator> EmptyEnumerator - => Enumerable.Empty>().GetEnumerator(); - - IEnumerator> IEnumerable>.GetEnumerator() - => EmptyEnumerator; - - IEnumerator IEnumerable.GetEnumerator() => EmptyEnumerator; - } -} \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Dictionary.cs b/src/DotNext/Collections/Generic/Dictionary.cs index 2fd03a388..6661a719a 100644 --- a/src/DotNext/Collections/Generic/Dictionary.cs +++ b/src/DotNext/Collections/Generic/Dictionary.cs @@ -352,13 +352,4 @@ public static Optional TryGetValue(IReadOnlyDictionaryRead-only view of the dictionary where each value is converted in lazy manner. public static ReadOnlyDictionaryView ConvertValues(this IReadOnlyDictionary dictionary, Converter mapper) => new(dictionary, mapper); - - /// - /// Gets empty read-only dictionary. - /// - /// The type of the key. - /// The type of the value. - /// The empty dictionary. - public static IReadOnlyDictionary Empty() - => EmptyDictionary.Instance; } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs b/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs index dbfc83fdd..5670d26c3 100644 --- a/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs +++ b/src/DotNext/Collections/Generic/ReadOnlyDictionaryView.cs @@ -85,7 +85,7 @@ private static IEnumerator> GetEnumerator(IReadOnlyD /// /// The enumerator over key/value pairs. public IEnumerator> GetEnumerator() - => source is null or { Count: 0 } || mapper is null ? Dictionary.Empty().GetEnumerator() : GetEnumerator(source, mapper); + => source is null or { Count: 0 } || mapper is null ? Enumerable.Empty>().GetEnumerator() : GetEnumerator(source, mapper); /// /// Returns the converted value associated with the specified key. diff --git a/src/DotNext/Collections/Specialized/InvocationList.cs b/src/DotNext/Collections/Specialized/InvocationList.cs index 893b2d7ea..1fa16fd2d 100644 --- a/src/DotNext/Collections/Specialized/InvocationList.cs +++ b/src/DotNext/Collections/Specialized/InvocationList.cs @@ -88,10 +88,7 @@ public bool MoveNext() public InvocationList(TDelegate d) => list = d; private InvocationList(TDelegate[] array, TDelegate d) - { - TDelegate[] list = [.. array, d]; - this.list = list; - } + => list = (TDelegate[])[.. array, d]; private InvocationList(TDelegate d1, TDelegate d2) => list = new TDelegate[] { d1, d2 }; diff --git a/src/DotNext/UserDataStorage.DebugSupport.cs b/src/DotNext/UserDataStorage.DebugSupport.cs index 00629bbe0..8dcc5d9d7 100644 --- a/src/DotNext/UserDataStorage.DebugSupport.cs +++ b/src/DotNext/UserDataStorage.DebugSupport.cs @@ -1,3 +1,4 @@ +using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -29,5 +30,5 @@ private readonly struct DebugView /// The copy of all custom data. [EditorBrowsable(EditorBrowsableState.Advanced)] public IReadOnlyDictionary Capture() - => GetStorage()?.Dump() ?? Dictionary.Empty(); + => GetStorage()?.Dump() ?? ReadOnlyDictionary.Empty; } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs index 4ec7462e0..08e1c35a3 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs @@ -1,4 +1,5 @@ using System.Buffers; +using System.Collections.ObjectModel; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -9,7 +10,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices; using IO; using Runtime.Serialization; using Text; -using Dictionary = Collections.Generic.Dictionary; [StructLayout(LayoutKind.Auto)] internal readonly struct MetadataTransferObject : ISerializable @@ -22,7 +22,7 @@ internal MetadataTransferObject(IReadOnlyDictionary metadata) private static Encoding Encoding => Encoding.UTF8; - internal IReadOnlyDictionary Metadata => metadata ?? Dictionary.Empty(); + internal IReadOnlyDictionary Metadata => metadata ?? ReadOnlyDictionary.Empty; long? IDataTransferObject.Length => null; From a12489e1aeb59b88c8ed0a4c2a920c5e92f2a610 Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 12:52:24 +0200 Subject: [PATCH 045/155] Migration to new Span ctor with ref parameter only --- src/DotNext/CharComparer.cs | 87 ++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/src/DotNext/CharComparer.cs b/src/DotNext/CharComparer.cs index 0ecc98692..d500b377b 100644 --- a/src/DotNext/CharComparer.cs +++ b/src/DotNext/CharComparer.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; -using static System.Runtime.InteropServices.MemoryMarshal; namespace DotNext; @@ -36,7 +35,7 @@ protected CharComparer() /// The comparison type. /// if both characters are equal; otherwise, . public static bool Equals(char x, char y, StringComparison comparisonType) - => CreateReadOnlySpan(ref x, 1).Equals(CreateReadOnlySpan(ref y, 1), comparisonType); + => MemoryExtensions.Equals(new(ref x), new(ref y), comparisonType); /// /// Compares two characters and returns an indication of their relative sort order. @@ -54,7 +53,7 @@ public static bool Equals(char x, char y, StringComparison comparisonType) /// The comparison type. /// A number indicating relative sort order of the characters. public static int Compare(char x, char y, StringComparison comparisonType) - => CreateReadOnlySpan(ref x, 1).CompareTo(CreateReadOnlySpan(ref y, 1), comparisonType); + => MemoryExtensions.CompareTo(new(ref x), new(ref y), comparisonType); /// /// Gets the hash code for the specified character. @@ -70,7 +69,7 @@ public static int Compare(char x, char y, StringComparison comparisonType) /// The comparison type. /// A hash code of the character. public static int GetHashCode(char ch, StringComparison comparisonType) - => string.GetHashCode(CreateReadOnlySpan(ref ch, 1), comparisonType); + => string.GetHashCode(new(ref ch), comparisonType); /// /// Converts to . @@ -106,60 +105,60 @@ public static CharComparer Create(CultureInfo culture, CompareOptions options) return new CultureSpecificCharComparer(culture, options); } -} -internal sealed class DefaultCharComparer : CharComparer -{ - private readonly StringComparison comparisonType; - - internal DefaultCharComparer(StringComparison comparison) - => comparisonType = comparison; + private sealed class DefaultCharComparer : CharComparer + { + private readonly StringComparison comparisonType; - public override bool Equals(char x, char y) - => Equals(x, y, comparisonType); + internal DefaultCharComparer(StringComparison comparison) + => comparisonType = comparison; - public override int Compare(char x, char y) - => Compare(x, y, comparisonType); + public override bool Equals(char x, char y) + => Equals(x, y, comparisonType); - public override int GetHashCode(char ch) - => GetHashCode(ch, comparisonType); + public override int Compare(char x, char y) + => Compare(x, y, comparisonType); - public override bool Equals([NotNullWhen(true)] object? other) - => other is DefaultCharComparer comparer && comparisonType == comparer.comparisonType; + public override int GetHashCode(char ch) + => GetHashCode(ch, comparisonType); - public override int GetHashCode() => (int)comparisonType; + public override bool Equals([NotNullWhen(true)] object? other) + => other is DefaultCharComparer comparer && comparisonType == comparer.comparisonType; - public override string ToString() => comparisonType.ToString(); -} + public override int GetHashCode() => (int)comparisonType; -internal sealed class CultureSpecificCharComparer : CharComparer -{ - private readonly CompareInfo comparison; - private readonly CompareOptions options; + public override string ToString() => comparisonType.ToString(); + } - internal CultureSpecificCharComparer(CultureInfo culture, CompareOptions options) + private sealed class CultureSpecificCharComparer : CharComparer { - comparison = culture.CompareInfo; - this.options = options; - } + private readonly CompareInfo comparison; + private readonly CompareOptions options; - public override bool Equals(char x, char y) - => Compare(x, y) is 0; + internal CultureSpecificCharComparer(CultureInfo culture, CompareOptions options) + { + comparison = culture.CompareInfo; + this.options = options; + } - public override int Compare(char x, char y) - => comparison.Compare(CreateReadOnlySpan(ref x, 1), CreateReadOnlySpan(ref y, 1), options); + public override bool Equals(char x, char y) + => Compare(x, y) is 0; - public override int GetHashCode(char ch) - => comparison.GetHashCode(CreateReadOnlySpan(ref ch, 1), options); + public override int Compare(char x, char y) + => comparison.Compare(new ReadOnlySpan(ref x), new(ref y), options); - public override bool Equals([NotNullWhen(true)] object? other) - => other is CultureSpecificCharComparer comparer && Equals(comparison, comparer.comparison) && options == comparer.options; + public override int GetHashCode(char ch) + => comparison.GetHashCode(new ReadOnlySpan(ref ch), options); - public override int GetHashCode() - { - var result = new HashCode(); - result.Add(comparison); - result.Add(options); - return result.ToHashCode(); + public override bool Equals([NotNullWhen(true)] object? other) + => other is CultureSpecificCharComparer comparer && Equals(comparison, comparer.comparison) && options == comparer.options; + + public override int GetHashCode() + { + var result = new HashCode(); + result.Add(comparison); + result.Add(options); + return result.ToHashCode(); + } } } \ No newline at end of file From ff1d536dab0a694e8c5681d1565de0a80f8f70bf Mon Sep 17 00:00:00 2001 From: sakno Date: Wed, 22 Nov 2023 20:11:38 +0200 Subject: [PATCH 046/155] Added hashing library --- src/Directory.Packages.props | 1 + src/DotNext/DotNext.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 36c68cb07..da6b99f13 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -13,6 +13,7 @@ + diff --git a/src/DotNext/DotNext.csproj b/src/DotNext/DotNext.csproj index bed98ce7c..bfefac274 100644 --- a/src/DotNext/DotNext.csproj +++ b/src/DotNext/DotNext.csproj @@ -53,6 +53,7 @@ + From 6d7d3a9df05a7d81cf998982affe8928ed36fecf Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 23 Nov 2023 00:45:22 +0200 Subject: [PATCH 047/155] Expose FNV-1a implementation to public --- src/DotNext.Tests/DotNext.Tests.csproj | 1 + src/DotNext.Tests/IO/Hashing/FNV1aTests.cs | 66 +++++++ src/DotNext/IO/Hashing/FNV1a.cs | 219 +++++++++++++++++++++ src/DotNext/IO/Hashing/FNV1a128.cs | 8 + src/DotNext/IO/Hashing/FNV1a32.cs | 8 + src/DotNext/IO/Hashing/FNV1a64.cs | 8 + src/DotNext/IO/Hashing/FNV1aParameters.cs | 37 ++++ src/DotNext/IO/Hashing/IFNV1aParameters.cs | 19 ++ src/DotNext/Runtime/Intrinsics.cs | 21 ++ 9 files changed, 387 insertions(+) create mode 100644 src/DotNext.Tests/IO/Hashing/FNV1aTests.cs create mode 100644 src/DotNext/IO/Hashing/FNV1a.cs create mode 100644 src/DotNext/IO/Hashing/FNV1a128.cs create mode 100644 src/DotNext/IO/Hashing/FNV1a32.cs create mode 100644 src/DotNext/IO/Hashing/FNV1a64.cs create mode 100644 src/DotNext/IO/Hashing/FNV1aParameters.cs create mode 100644 src/DotNext/IO/Hashing/IFNV1aParameters.cs diff --git a/src/DotNext.Tests/DotNext.Tests.csproj b/src/DotNext.Tests/DotNext.Tests.csproj index f2ef0b166..b46e043e8 100644 --- a/src/DotNext.Tests/DotNext.Tests.csproj +++ b/src/DotNext.Tests/DotNext.Tests.csproj @@ -40,6 +40,7 @@ + diff --git a/src/DotNext.Tests/IO/Hashing/FNV1aTests.cs b/src/DotNext.Tests/IO/Hashing/FNV1aTests.cs new file mode 100644 index 000000000..ab9c00c77 --- /dev/null +++ b/src/DotNext.Tests/IO/Hashing/FNV1aTests.cs @@ -0,0 +1,66 @@ +using System.Numerics; + +namespace DotNext.IO.Hashing; + +using System.Runtime.InteropServices; +using Collections.Generic; + +public sealed class FNV1aTests : Test +{ + private static void HashTest(FNV1a algorithm) + where THash : unmanaged, IBinaryNumber + where TParameters : notnull, IFNV1aParameters + { + ReadOnlySpan chunk1 = [1, 2, 3, 4, 5]; + ReadOnlySpan chunk2 = [6, 7, 8, 9, 10]; + ReadOnlySpan data = [.. chunk1, .. chunk2]; + + algorithm.Append(chunk1); + algorithm.Append(chunk2); + var hash = algorithm.GetCurrentHash(); + + algorithm.Reset(); + algorithm.Append(data); + Equal(algorithm.GetCurrentHash(), hash); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void Hash32(bool salted) => HashTest(new FNV1a32(salted)); + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void Hash64(bool salted) => HashTest(new FNV1a64(salted)); + + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void Hash128(bool salted) => HashTest(new FNV1a64(salted)); + + [Fact] + public static void HashList() + { + List list = [1, 2, 3, 4]; + var hash1 = FNV1a32.Hash, int>(List.Indexer.Getter, list.Count, list); + var hash2 = FNV1a32.Hash(CollectionsMarshal.AsSpan(list)); + var hash3 = FNV1a32.Hash(MemoryMarshal.Cast(CollectionsMarshal.AsSpan(list))); + + Equal(hash1, hash2); + Equal(hash1, hash3); + } + + [Fact] + public static void HashGuid() + { + Span elements = stackalloc Guid[3]; + Random.Shared.GetItems(elements); + + var algorithm = new FNV1a32(); + algorithm.Append(elements); + var hash1 = algorithm.GetCurrentHash(); + + Equal(FNV1a32.Hash(elements), algorithm.GetCurrentHash()); + } +} \ No newline at end of file diff --git a/src/DotNext/IO/Hashing/FNV1a.cs b/src/DotNext/IO/Hashing/FNV1a.cs new file mode 100644 index 000000000..015b92fca --- /dev/null +++ b/src/DotNext/IO/Hashing/FNV1a.cs @@ -0,0 +1,219 @@ +using System.Diagnostics; +using System.IO.Hashing; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DotNext.IO.Hashing; + +using Intrinsics = Runtime.Intrinsics; + +/// +/// Represents FNV-1a hash algorithm. +/// +/// The type representing hash value. +/// The parameters of the algorithm. +/// FNV-1a +/// to include randomized salt data into hashing; to use data from memory only. +public class FNV1a(bool salted = false) : NonCryptographicHashAlgorithm(Unsafe.SizeOf()), IResettable + where THash : unmanaged, IBinaryNumber + where TParameters : notnull, IFNV1aParameters +{ + private State state = new(salted); + + /// + public sealed override void Reset() => state = new(salted); + + /// + public sealed override void Append(ReadOnlySpan source) + => state.Append(source); + + /// + /// Appends the contents of source to the data already processed for the current hash computation. + /// + /// The type of the span elements. + /// The data to process. + public void Append(ReadOnlySpan source) + where T : unmanaged + => Hash(ref state, source); + + /// + protected sealed override void GetCurrentHashCore(Span destination) + => state.GetValue(destination); + + /// + /// Gets the current computed hash value without modifying accumulated state. + /// + /// The hash value for the data already provided. + public new THash GetCurrentHash() => state.Value; + + private static void Hash(ref State hash, ReadOnlySpan data) + where T : unmanaged + { + if (typeof(T) == typeof(byte)) + { + hash.Append(Intrinsics.ReinterpretCast(data)); + } + else if (Intrinsics.AreCompatible()) + { + hash.Append(Intrinsics.ReinterpretCast(data)); + } + else + { + hash.Append(data); + } + } + + /// + /// Computes hash code over the span of elements. + /// + /// The type of the elements. + /// The data to be hashed. + /// to include randomized salt data into hashing; to use data from memory only. + /// The computed FNV-1a hash. + public static THash Hash(ReadOnlySpan data, bool salted = false) + where T : unmanaged + { + var hash = new State(salted); + Hash(ref hash, data); + return hash.Value; + } + + /// + /// Computes hash code over elements returned by vector accessor. + /// + /// The type of the elements in the vector. + /// The type that is used as identifier of the elements in the vector. + /// The delegate that provided access to the element of type at the given index. + /// The number of elements in the vector. + /// The argument to be passed to the accessor. + /// to include randomized salt data into hashing; to use data from memory only. + /// The computed FNV-1a hash. + public static THash Hash(Func accessor, TIndex count, T arg, bool salted = false) + where TIndex : notnull, IComparisonOperators, IAdditiveIdentity, IIncrementOperators + { + ArgumentNullException.ThrowIfNull(accessor); + + var hash = new State(salted); + for (var i = TIndex.AdditiveIdentity; i < count; i++) + { + hash.Append(accessor(arg, i)); + } + + return hash.Value; + } + + [StructLayout(LayoutKind.Auto)] + private unsafe struct State + { + private int bufferSize; + private THash buffer, hash; + + internal State(bool salted) + { + hash = TParameters.Offset; + + if (salted) + Append(ref hash, THash.CreateTruncating(RandomExtensions.BitwiseHashSalt)); + } + + internal readonly THash Value + { + get + { + var result = hash; + if (HasBufferedData) + Append(ref result, buffer); + + return result; + } + } + + internal readonly void GetValue(Span output) + { + var hash = Value; + Span.AsReadOnlyBytes(in hash).CopyTo(output); + } + + private Span RemainingBuffer + { + get + { + ref var bufferPtr = ref Unsafe.Add(ref Unsafe.As(ref buffer), bufferSize); + return MemoryMarshal.CreateSpan(ref bufferPtr, sizeof(THash) - bufferSize); + } + } + + private void Bufferize(ReadOnlySpan data) + { + data.CopyTo(RemainingBuffer); + bufferSize += data.Length; + } + + private void Append(ref byte data, int length) + { + var remaining = RemainingBuffer; + if (remaining.Length < sizeof(THash) && remaining.Length <= length) + { + data = ref Unsafe.Add(ref data, Append(remaining, ref data, ref length)); + } + + for (; length >= sizeof(THash); length -= sizeof(THash), data = ref Unsafe.Add(ref data, sizeof(THash))) + { + Append(Unsafe.ReadUnaligned(in data)); + } + + if (length > 0) + { + Debug.Assert(length < sizeof(THash)); + + Bufferize(MemoryMarshal.CreateReadOnlySpan(ref data, length)); + } + } + + private int Append(Span remaining, ref byte data, ref int length) + { + var input = MemoryMarshal.CreateReadOnlySpan(ref data, remaining.Length); + input.CopyTo(remaining); + Flush(); + length -= input.Length; + return input.Length; + } + + internal void Append(ReadOnlySpan data) + => Append(ref MemoryMarshal.GetReference(data), data.Length); + + internal void Append(ReadOnlySpan data) + { + foreach (var element in data) + Append(element); + } + + internal unsafe void Append(ReadOnlySpan data) + where T : unmanaged + { + for (int maxSize = int.MaxValue / sizeof(T), size; !data.IsEmpty; data = data.Slice(size)) + { + size = Math.Min(maxSize, data.Length); + Append(ref Unsafe.As(ref MemoryMarshal.GetReference(data)), size * sizeof(T)); + } + } + + private readonly bool HasBufferedData => bufferSize > 0; + + private void Flush() + { + Debug.Assert(HasBufferedData); + + Append(buffer); + bufferSize = 0; + buffer = default; + } + + private static void Append(ref THash hash, THash data) + => hash = (hash ^ data) * TParameters.Prime; + + internal void Append(THash data) + => Append(ref hash, data); + } +} \ No newline at end of file diff --git a/src/DotNext/IO/Hashing/FNV1a128.cs b/src/DotNext/IO/Hashing/FNV1a128.cs new file mode 100644 index 000000000..2b359218e --- /dev/null +++ b/src/DotNext/IO/Hashing/FNV1a128.cs @@ -0,0 +1,8 @@ +namespace DotNext.IO.Hashing; + +/// +/// Represents FNV-1a 128-bit hash algorithm. +/// +public sealed class FNV1a128(bool salted = false) : FNV1a(salted) +{ +} \ No newline at end of file diff --git a/src/DotNext/IO/Hashing/FNV1a32.cs b/src/DotNext/IO/Hashing/FNV1a32.cs new file mode 100644 index 000000000..3fefbd34c --- /dev/null +++ b/src/DotNext/IO/Hashing/FNV1a32.cs @@ -0,0 +1,8 @@ +namespace DotNext.IO.Hashing; + +/// +/// Represents FNV-1a 64-bit hash algorithm. +/// +public sealed class FNV1a32(bool salted = false) : FNV1a(salted) +{ +} \ No newline at end of file diff --git a/src/DotNext/IO/Hashing/FNV1a64.cs b/src/DotNext/IO/Hashing/FNV1a64.cs new file mode 100644 index 000000000..22172d533 --- /dev/null +++ b/src/DotNext/IO/Hashing/FNV1a64.cs @@ -0,0 +1,8 @@ +namespace DotNext.IO.Hashing; + +/// +/// Represents FNV-1a 64-bit hash algorithm. +/// +public sealed class FNV1a64(bool salted = false) : FNV1a(salted) +{ +} \ No newline at end of file diff --git a/src/DotNext/IO/Hashing/FNV1aParameters.cs b/src/DotNext/IO/Hashing/FNV1aParameters.cs new file mode 100644 index 000000000..82ab3e759 --- /dev/null +++ b/src/DotNext/IO/Hashing/FNV1aParameters.cs @@ -0,0 +1,37 @@ +namespace DotNext.IO.Hashing; + +/// +/// Represents parameters of FNV-1a hash algorithm for 32, 64, and 128 bits variations. +/// +public readonly struct FNV1aParameters : IFNV1aParameters, IFNV1aParameters, IFNV1aParameters +{ + /// + /// Gets offset basis for 32-bit version of FNV-1a hash algorithm. + /// + static int IFNV1aParameters.Offset => unchecked((int)2166136261); + + /// + /// Gets prime number for 32-bit version of FNV-1a hash algorithm. + /// + static int IFNV1aParameters.Prime => 16777619; + + /// + /// Gets offset basis for 64-bit version of FNV-1a hash algorithm. + /// + static long IFNV1aParameters.Offset => unchecked((long)14695981039346656037); + + /// + /// Gets prime number for 64-bit version of FNV-1a hash algorithm. + /// + static long IFNV1aParameters.Prime => 1099511628211; + + /// + /// Gets offset basis for 128-bit version of FNV-1a hash algorithm. + /// + static Int128 IFNV1aParameters.Offset { get; } = Int128.Parse("144066263297769815596495629667062367629"); + + /// + /// Gets prime number for 128-bit version of FNV-1a hash algorithm. + /// + static Int128 IFNV1aParameters.Prime { get; } = Int128.Parse("309485009821345068724781371"); +} \ No newline at end of file diff --git a/src/DotNext/IO/Hashing/IFNV1aParameters.cs b/src/DotNext/IO/Hashing/IFNV1aParameters.cs new file mode 100644 index 000000000..b31bc76a3 --- /dev/null +++ b/src/DotNext/IO/Hashing/IFNV1aParameters.cs @@ -0,0 +1,19 @@ +namespace DotNext.IO.Hashing; + +/// +/// Represents parameters of FNV-1a hash algorithm. +/// +/// The type representing a hash. +public interface IFNV1aParameters + where THash : unmanaged +{ + /// + /// Gets offset basis. + /// + static abstract THash Offset { get; } + + /// + /// Gets prime number. + /// + static abstract THash Prime { get; } +} \ No newline at end of file diff --git a/src/DotNext/Runtime/Intrinsics.cs b/src/DotNext/Runtime/Intrinsics.cs index 26153fce1..cfcec9af2 100644 --- a/src/DotNext/Runtime/Intrinsics.cs +++ b/src/DotNext/Runtime/Intrinsics.cs @@ -1012,6 +1012,16 @@ internal static Span ReinterpretCast(Span inpu return MemoryMarshal.CreateSpan(ref Unsafe.As(ref MemoryMarshal.GetReference(input)), input.Length); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ReadOnlySpan ReinterpretCast(ReadOnlySpan input) + where TInput : unmanaged + where TOutput : unmanaged + { + Debug.Assert(Unsafe.SizeOf() == Unsafe.SizeOf()); + + return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(input)), input.Length); + } + /// /// Explicitly invokes object finalizer. /// @@ -1068,4 +1078,15 @@ public static int AlignOf() [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsAtomic() => AlignOf() == Unsafe.SizeOf() && Unsafe.SizeOf() <= UIntPtr.Size; + + /// + /// Determines whether the two types are binary compatible, i.e. both types have the same + /// size and memory alignment. + /// + /// The first type to compare. + /// The second type to compare. + /// if both types are binary compatible; otherwise, . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool AreCompatible() + => Unsafe.SizeOf() == Unsafe.SizeOf() && AlignOf() == AlignOf(); } \ No newline at end of file From 2610bbfcc24cd5613db3d2b1dda9e9b9477fcf7d Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 23 Nov 2023 15:03:50 +0200 Subject: [PATCH 048/155] Added hashing support for unmanaged memory --- src/DotNext/IO/Hashing/FNV1a.cs | 42 ++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/src/DotNext/IO/Hashing/FNV1a.cs b/src/DotNext/IO/Hashing/FNV1a.cs index 015b92fca..c047e1729 100644 --- a/src/DotNext/IO/Hashing/FNV1a.cs +++ b/src/DotNext/IO/Hashing/FNV1a.cs @@ -37,6 +37,15 @@ public void Append(ReadOnlySpan source) where T : unmanaged => Hash(ref state, source); + /// + /// Appends the contents of unmanaged memory to the data already processed for the current hash computation. + /// + /// The address of the unmanaged memory. + /// The length of the unmanaged memory block, in bytes. + [CLSCompliant(false)] + public unsafe void Append(void* address, nuint length) + => state.Append(ref Unsafe.AsRef(address), length); + /// protected sealed override void GetCurrentHashCore(Span destination) => state.GetValue(destination); @@ -103,6 +112,21 @@ public static THash Hash(Func accessor, TIndex coun return hash.Value; } + /// + /// Computes hash code for the specified block of unmanaged memory. + /// + /// The address of the unmanaged memory. + /// The length of the unmanaged memory block, in bytes. + /// to include randomized salt data into hashing; to use data from memory only. + /// The computed FNV-1a hash. + [CLSCompliant(false)] + public static unsafe THash Hash(void* address, nuint length, bool salted = false) + { + var hash = new State(salted); + hash.Append(ref Unsafe.AsRef(address), length); + return hash.Value; + } + [StructLayout(LayoutKind.Auto)] private unsafe struct State { @@ -150,38 +174,38 @@ private void Bufferize(ReadOnlySpan data) bufferSize += data.Length; } - private void Append(ref byte data, int length) + internal void Append(ref byte data, nuint length) { var remaining = RemainingBuffer; - if (remaining.Length < sizeof(THash) && remaining.Length <= length) + if (remaining.Length < sizeof(THash) && (uint)remaining.Length <= length) { data = ref Unsafe.Add(ref data, Append(remaining, ref data, ref length)); } - for (; length >= sizeof(THash); length -= sizeof(THash), data = ref Unsafe.Add(ref data, sizeof(THash))) + for (; length >= (uint)sizeof(THash); length -= (uint)sizeof(THash), data = ref Unsafe.Add(ref data, sizeof(THash))) { Append(Unsafe.ReadUnaligned(in data)); } if (length > 0) { - Debug.Assert(length < sizeof(THash)); + Debug.Assert(length < (uint)sizeof(THash)); - Bufferize(MemoryMarshal.CreateReadOnlySpan(ref data, length)); + Bufferize(MemoryMarshal.CreateReadOnlySpan(ref data, (int)length)); } } - private int Append(Span remaining, ref byte data, ref int length) + private int Append(Span remaining, ref byte data, ref nuint length) { var input = MemoryMarshal.CreateReadOnlySpan(ref data, remaining.Length); input.CopyTo(remaining); Flush(); - length -= input.Length; + length -= (uint)input.Length; return input.Length; } internal void Append(ReadOnlySpan data) - => Append(ref MemoryMarshal.GetReference(data), data.Length); + => Append(ref MemoryMarshal.GetReference(data), (nuint)data.Length); internal void Append(ReadOnlySpan data) { @@ -195,7 +219,7 @@ internal unsafe void Append(ReadOnlySpan data) for (int maxSize = int.MaxValue / sizeof(T), size; !data.IsEmpty; data = data.Slice(size)) { size = Math.Min(maxSize, data.Length); - Append(ref Unsafe.As(ref MemoryMarshal.GetReference(data)), size * sizeof(T)); + Append(ref Unsafe.As(ref MemoryMarshal.GetReference(data)), (uint)size * (uint)sizeof(T)); } } From 86fc58e9f139bdcf1ea0f6973a90fa071feeea80 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 23 Nov 2023 15:06:55 +0200 Subject: [PATCH 049/155] Fixed location for binary transformation --- .../Buffers/Binary/BinaryTransformationsTests.cs | 16 ++++++++++++++++ src/DotNext.Tests/Runtime/IntrinsicsTests.cs | 16 ---------------- .../Buffers/Binary/BinaryTransformations.cs | 9 +++++++++ src/DotNext/Runtime/Intrinsics.cs | 9 --------- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs b/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs index a7b17ad39..c3dabc9ea 100644 --- a/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs +++ b/src/DotNext.Tests/Buffers/Binary/BinaryTransformationsTests.cs @@ -141,4 +141,20 @@ static uint[] OnesComplementSlow(ReadOnlySpan x) return result; } } + + [Fact] + public static void ReverseUInt32() + { + uint i = uint.MaxValue >> 1, tmp = i; + BinaryTransformations.Reverse(ref i); + + if (BitConverter.IsLittleEndian) + { + Equal(BinaryPrimitives.ReadUInt32BigEndian(Span.AsReadOnlyBytes(in tmp)), i); + } + else + { + Equal(BinaryPrimitives.ReadUInt32LittleEndian(Span.AsReadOnlyBytes(in tmp)), i); + } + } } \ No newline at end of file diff --git a/src/DotNext.Tests/Runtime/IntrinsicsTests.cs b/src/DotNext.Tests/Runtime/IntrinsicsTests.cs index 91b97ed60..cc491ca8e 100644 --- a/src/DotNext.Tests/Runtime/IntrinsicsTests.cs +++ b/src/DotNext.Tests/Runtime/IntrinsicsTests.cs @@ -304,22 +304,6 @@ public static void ThrowOnNullReference() True(thrown); } - [Fact] - public static void ReverseUInt32() - { - uint i = uint.MaxValue >> 1, tmp = i; - Intrinsics.Reverse(ref i); - - if (BitConverter.IsLittleEndian) - { - Equal(BinaryPrimitives.ReadUInt32BigEndian(Span.AsReadOnlyBytes(in tmp)), i); - } - else - { - Equal(BinaryPrimitives.ReadUInt32LittleEndian(Span.AsReadOnlyBytes(in tmp)), i); - } - } - [Fact] public static void HasFinalizer() { diff --git a/src/DotNext/Buffers/Binary/BinaryTransformations.cs b/src/DotNext/Buffers/Binary/BinaryTransformations.cs index 65f1910da..591ea7a65 100644 --- a/src/DotNext/Buffers/Binary/BinaryTransformations.cs +++ b/src/DotNext/Buffers/Binary/BinaryTransformations.cs @@ -16,4 +16,13 @@ private interface IBinaryTransformation { public static abstract T Transform(T x, T y); } + + /// + /// Reverse bytes in the specified value of blittable type. + /// + /// Blittable type. + /// The value which bytes should be reversed. + public static void Reverse(ref T value) + where T : unmanaged + => Span.AsBytes(ref value).Reverse(); } \ No newline at end of file diff --git a/src/DotNext/Runtime/Intrinsics.cs b/src/DotNext/Runtime/Intrinsics.cs index cfcec9af2..2e93b2d82 100644 --- a/src/DotNext/Runtime/Intrinsics.cs +++ b/src/DotNext/Runtime/Intrinsics.cs @@ -907,15 +907,6 @@ public static unsafe int GetHashCode32([In] void* source, nuint length, bool sal => GetHashCode32Unaligned(ref *((byte*)source), length, salted); #endregion - /// - /// Reverse bytes in the specified value of blittable type. - /// - /// Blittable type. - /// The value which bytes should be reversed. - public static void Reverse(ref T value) - where T : unmanaged - => Span.AsBytes(ref value).Reverse(); - /// /// Checks whether the specified object is exactly of the specified type. /// From 65c1e10748e127765814c3899bdff3d6830b2ce3 Mon Sep 17 00:00:00 2001 From: sakno Date: Thu, 23 Nov 2023 15:50:36 +0200 Subject: [PATCH 050/155] Added ability to hash scalar values --- src/DotNext/IO/Hashing/FNV1a.cs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/DotNext/IO/Hashing/FNV1a.cs b/src/DotNext/IO/Hashing/FNV1a.cs index c047e1729..6ad2d0bd7 100644 --- a/src/DotNext/IO/Hashing/FNV1a.cs +++ b/src/DotNext/IO/Hashing/FNV1a.cs @@ -46,6 +46,13 @@ public void Append(ReadOnlySpan source) public unsafe void Append(void* address, nuint length) => state.Append(ref Unsafe.AsRef(address), length); + /// + /// Appends the value to the data already processed for the current hash computation. + /// + /// The value to be hashed. + public void Append(THash value) + => state.Append(value); + /// protected sealed override void GetCurrentHashCore(Span destination) => state.GetValue(destination); @@ -127,6 +134,17 @@ public static unsafe THash Hash(void* address, nuint length, bool salted = false return hash.Value; } + /// + /// Computes a hash for a value of type . + /// + /// The data to be hashed. + /// The computed FNV-1a hash. + public static THash Hash(THash data) + { + State.Append(ref data, data); + return data; + } + [StructLayout(LayoutKind.Auto)] private unsafe struct State { @@ -234,7 +252,7 @@ private void Flush() buffer = default; } - private static void Append(ref THash hash, THash data) + internal static void Append(ref THash hash, THash data) => hash = (hash ^ data) * TParameters.Prime; internal void Append(THash data) From 0bd3f559885267d8ce266092df0475b5e8e581d8 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 07:45:37 +0200 Subject: [PATCH 051/155] Removed OneDimensionalArray static class and bitwise operations from Intrinsics --- .../ArrayEqualityBenchmark.cs | 58 -- .../GenericEnumBenchmark.cs | 12 - .../Linq/Expressions/ExpressionBuilder.cs | 4 +- .../Linq/Expressions/MetaExpression.cs | 6 +- .../Metaprogramming/CodeGenerator.cs | 5 +- .../ReadOnlySequencePartitionerTests.cs | 2 +- .../Buffers/UnmanagedMemoryPoolTests.cs | 45 +- src/DotNext.Tests/DelegateHelpersTests.cs | 2 +- .../Raft/DiskBasedStateMachineTests.cs | 2 +- .../Raft/MemoryBasedStateMachineTests.cs | 16 +- src/DotNext.Tests/OneDimensionalArrayTests.cs | 122 ---- .../InteropServices/PinnedArrayTests.cs | 148 ---- .../Runtime/InteropServices/PointerTests.cs | 21 +- src/DotNext.Tests/Runtime/IntrinsicsTests.cs | 129 +--- src/DotNext.Tests/SpanTests.cs | 37 +- .../Threading/AsyncDelegateTests.cs | 2 +- src/DotNext.Tests/ValueTypeTests.cs | 9 - .../Buffers/UnmanagedMemoryOwner.cs | 2 +- .../MemoryMappedDirectAccessor.cs | 5 +- .../InteropServices/IUnmanagedArray.cs | 118 ---- .../InteropServices/IUnmanagedMemory.cs | 18 +- .../Runtime/InteropServices/PinnedArray.cs | 258 ------- .../Runtime/InteropServices/Pointer.cs | 546 +++++--------- src/DotNext/BitwiseComparer.cs | 127 +--- .../Buffers/PooledArrayBufferWriter.cs | 36 +- .../Buffers/ReadOnlySequencePartitioner.cs | 10 +- .../Collections/Generic/Collection.Buffer.cs | 4 +- src/DotNext/Collections/Generic/Collection.cs | 4 +- .../ConcurrentTypeMap.Enumerator.cs | 10 +- .../Collections/Specialized/InvocationList.cs | 14 +- .../Specialized/TypeMap.Enumerator.cs | 10 +- src/DotNext/Disposable.cs | 2 +- src/DotNext/EqualityComparerBuilder.cs | 10 +- src/DotNext/IO/Hashing/FNV1a32.cs | 3 + src/DotNext/OneDimensionalArray.cs | 473 ------------- src/DotNext/Runtime/Intrinsics.cs | 665 +----------------- src/DotNext/Runtime/Reference.cs | 268 ------- src/DotNext/Span.cs | 312 ++------ src/DotNext/Threading/Atomic.cs | 2 +- .../Net/Cluster/ClusterMemberId.cs | 28 +- .../Raft/Commands/CommandInterpreter.cs | 2 +- .../Consensus/Raft/ConsensusOnlyState.cs | 24 +- 42 files changed, 424 insertions(+), 3147 deletions(-) delete mode 100644 src/DotNext.Benchmarks/ArrayEqualityBenchmark.cs delete mode 100644 src/DotNext.Tests/OneDimensionalArrayTests.cs delete mode 100644 src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs delete mode 100644 src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs delete mode 100644 src/DotNext/OneDimensionalArray.cs delete mode 100644 src/DotNext/Runtime/Reference.cs diff --git a/src/DotNext.Benchmarks/ArrayEqualityBenchmark.cs b/src/DotNext.Benchmarks/ArrayEqualityBenchmark.cs deleted file mode 100644 index 0465a05a4..000000000 --- a/src/DotNext.Benchmarks/ArrayEqualityBenchmark.cs +++ /dev/null @@ -1,58 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Order; -using System; - - -namespace DotNext; - -[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] -[Orderer(SummaryOrderPolicy.FastestToSlowest)] -public class ArrayEqualityBenchmark -{ - private static readonly Guid[] ShortGuidArray1 = new Guid[10]; - private static readonly Guid[] ShortGuidArray2 = ShortGuidArray1.Clone() as Guid[]; - - private static readonly Guid[] LongGuidArray1 = new Guid[100]; - private static readonly Guid[] LongGuidArray2 = LongGuidArray1.Clone() as Guid[]; - - static ArrayEqualityBenchmark() - { - for (var i = default(long); i < ShortGuidArray1.LongLength; i++) - ShortGuidArray1[i] = ShortGuidArray2[i] = Guid.NewGuid(); - for (var i = default(long); i < LongGuidArray1.LongLength; i++) - LongGuidArray1[i] = LongGuidArray2[i] = Guid.NewGuid(); - } - - [Benchmark(Description = "Guid[].BitwiseEquals, small array (~10 elements)")] - public bool ShortGuidArrayBitwiseEquals() - => ShortGuidArray1.BitwiseEquals(ShortGuidArray2); - - [Benchmark(Description = "ReadOnlySpan.SequenceEqual, small array (~10 elements)")] - public bool ShortGuidArraySequenceEqual() - => new ReadOnlySpan(ShortGuidArray1).SequenceEqual(ShortGuidArray2); - - [Benchmark(Description = "for loop, small array, (~10 elements)")] - public void ShortGuidArrayForEachEqual() - { - for (var i = default(long); i < ShortGuidArray1.LongLength; i++) - if (ShortGuidArray1[i] != ShortGuidArray2[i]) - return; - } - - [Benchmark(Description = "Guid[].BitwiseEquals, large array (~100 elements)")] - public bool LongGuidArrayBitwiseEquals() - => LongGuidArray1.BitwiseEquals(LongGuidArray2); - - [Benchmark(Description = "ReadOnlySpan.SequenceEqual, large array (~100 elements)")] - public bool LongGuidArraySequenceEqual() - => new ReadOnlySpan(LongGuidArray1).SequenceEqual(LongGuidArray2); - - [Benchmark(Description = "for loop, large array, (~100 elements)")] - public void LongGuidArrayForEachEqual() - { - for (var i = default(long); i < LongGuidArray1.LongLength; i++) - if (LongGuidArray1[i] != LongGuidArray2[i]) - return; - } -} \ No newline at end of file diff --git a/src/DotNext.Benchmarks/GenericEnumBenchmark.cs b/src/DotNext.Benchmarks/GenericEnumBenchmark.cs index 965b116c2..42b2ef207 100644 --- a/src/DotNext.Benchmarks/GenericEnumBenchmark.cs +++ b/src/DotNext.Benchmarks/GenericEnumBenchmark.cs @@ -5,8 +5,6 @@ namespace DotNext; -using Intrinsics = Runtime.Intrinsics; - [SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] [Orderer(SummaryOrderPolicy.FastestToSlowest)] public class GenericEnumBenchmark @@ -19,13 +17,6 @@ private static long ToInt64(T value) where T : struct, IConvertible => value.ToInt64(null); - private static T ToEnum(int value) - where T : unmanaged, Enum - { - Intrinsics.Bitcast(value, out T result); - return result; - } - [Benchmark] public int ToInt32UsingConstrainedCall() => ToInt32(EnvironmentVariableTarget.Machine); @@ -38,9 +29,6 @@ private static T ToEnum(int value) [Benchmark] public long ToInt64UsingGenericConverter() => EnvironmentVariableTarget.Machine.ToInt64(); - [Benchmark] - public EnvironmentVariableTarget ToEnumUsingBitcast() => ToEnum(2); - [Benchmark] public EnvironmentVariableTarget ToEnumUsingGenericConverter() => 2.ToEnum(); } \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs index dde8db18e..19e552909 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs @@ -133,7 +133,7 @@ public static BinaryExpression Add(this Expression left, Expression right) /// Other strings to concatenate. /// An expression presenting concatenation. public static MethodCallExpression Concat(this Expression first, params Expression[] other) - => Concat(other.Insert(first, 0L)); + => Concat([first, .. other]); /// /// Constructs binary arithmetic subtraction expression. @@ -899,7 +899,7 @@ public static MemberExpression Property(this Expression instance, string propert /// The rest of the indexer arguments. /// Property access expression. public static IndexExpression Property(this Expression instance, string propertyName, Expression index0, params Expression[] indicies) - => Expression.Property(instance, propertyName, indicies.Insert(index0, 0L)); + => Expression.Property(instance, propertyName, [index0, .. indicies]); /// /// Constructs instance field access expression. diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs index fdccdb970..da56a86fd 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs @@ -73,15 +73,15 @@ private static Expression ToExpression(DynamicMetaObject arg, out BindingRestric return Expression.Call(typeof(ExpressionBuilder), nameof(ExpressionBuilder.Const), new[] { arg.Expression.Type }, arg.Expression); } - private static IEnumerable ToExpressions(DynamicMetaObject[] args, out BindingRestrictions restrictions) + private static IReadOnlyList ToExpressions(DynamicMetaObject[] args, out BindingRestrictions restrictions) { restrictions = BindingRestrictions.Empty; - if (Intrinsics.GetLength(args) == 0) + if (Intrinsics.GetLength(args) is 0) return Array.Empty(); var result = new Expression[args.LongLength]; - for (nint i = 0; i < Intrinsics.GetLength(args); i++) + for (nuint i = 0; i < Intrinsics.GetLength(args); i++) { result[i] = ToExpression(args[i], out var r); restrictions = restrictions.Merge(r); diff --git a/src/DotNext.Metaprogramming/Metaprogramming/CodeGenerator.cs b/src/DotNext.Metaprogramming/Metaprogramming/CodeGenerator.cs index 3b2d42571..e175e6830 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/CodeGenerator.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/CodeGenerator.cs @@ -1063,10 +1063,9 @@ public static Expression AsyncLambda(bool usePooling, Acti private static LambdaExpressionTree AsyncLambda(Type[] parameters, Type returnType, AsyncLambdaFlags flags, TScope scope) where TScope : MulticastDelegate { - var args = parameters.Concat(new Type[] { new TaskType(returnType, (flags & AsyncLambdaFlags.UseValueTask) != 0) }, parameters.LongLength); - var type = LambdaExpressionTree.GetDelegateType(args); + var type = LambdaExpressionTree.GetDelegateType([.. parameters, new TaskType(returnType, flags.HasFlag(AsyncLambdaFlags.UseValueTask))]); type = typeof(AsyncLambdaExpression<>).MakeGenericType(type); - using var expression = (LambdaExpression?)Activator.CreateInstance(type, new object[] { (flags & AsyncLambdaFlags.UseTaskPooling) != 0 }); + using var expression = (LambdaExpression?)Activator.CreateInstance(type, [flags.HasFlag(AsyncLambdaFlags.UseTaskPooling)]); Debug.Assert(expression is ILexicalScope); return ((ILexicalScope)expression).Build(scope); } diff --git a/src/DotNext.Tests/Buffers/ReadOnlySequencePartitionerTests.cs b/src/DotNext.Tests/Buffers/ReadOnlySequencePartitionerTests.cs index 00b0f4866..b7ed1f6f6 100644 --- a/src/DotNext.Tests/Buffers/ReadOnlySequencePartitionerTests.cs +++ b/src/DotNext.Tests/Buffers/ReadOnlySequencePartitionerTests.cs @@ -10,7 +10,7 @@ public class ReadOnlySequencePartitionerTests : Test public static void ParallelProcessing(bool splitOnSegments, int chunkSize) { var values = new int[1025]; - values.ForEach(static (ref int element, nint index) => element = (int)index); + values.AsSpan().ForEach(static (ref int element, int index) => element = index); var partitioner = ToReadOnlySequence(values, chunkSize).CreatePartitioner(splitOnSegments); diff --git a/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs b/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs index c6cfce8c2..e5beea791 100644 --- a/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs +++ b/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs @@ -12,19 +12,15 @@ public static void ReadWriteTest() array[0] = 10; array[1] = 20; array[2] = 30; - Equal(new ushort[] { 10, 20, 30 }, owner.ToArray()); - Equal(0, owner.Span.BitwiseCompare(new ushort[] { 10, 20, 30 })); - True(owner.Span.BitwiseEquals(new ushort[] { 10, 20, 30 })); - False(owner.Span.BitwiseEquals(new ushort[] { 10, 20, 40 })); - True(owner.Span.BitwiseCompare(new ushort[] { 10, 20, 40 }) < 0); + Equal([10, 20, 30], owner.ToArray()); Equal(3, array.Length); Equal(3, owner.Length); Equal(6, owner.Size); Equal(10, array[0]); Equal(20, array[1]); Equal(30, array[2]); - var managedArray = System.Linq.Enumerable.ToArray(owner); - Equal(new ushort[] { 10, 20, 30 }, managedArray); + var managedArray = Enumerable.ToArray(owner); + Equal([10, 20, 30], managedArray); array.Clear(); Equal(0, array[0]); Equal(0, array[1]); @@ -66,37 +62,6 @@ public static unsafe void UnmanagedMemoryAllocation() Equal(30, dest[2]); } - [Fact] - public static void BitwiseOperationsTest() - { - using var owner1 = UnmanagedMemoryAllocator.Allocate(3); - using var owner2 = UnmanagedMemoryAllocator.Allocate(3); - Span array1 = owner1.Span; - Span array2 = owner2.Span; - - array1[0] = 10; - array1[1] = 20; - array1[2] = 30; - - - array2[0] = 10; - array2[1] = 20; - array2[2] = 30; - - True(array1.BitwiseEquals(array2)); - True(owner1.BitwiseEquals(owner2)); - Equal(0, owner1.BitwiseCompare(owner2)); - True(owner1.BitwiseEquals(owner2.Pointer)); - Equal(0, array1.BitwiseCompare(array2)); - - array2[1] = 50; - False(array1.BitwiseEquals(array2)); - False(owner1.BitwiseEquals(owner2)); - True(owner1.BitwiseCompare(owner2) < 0); - False(owner1.BitwiseEquals(owner2.Pointer)); - NotEqual(0, array1.BitwiseCompare(array2)); - } - [Fact] public static void ResizeTest() { @@ -217,7 +182,7 @@ public static async Task StreamInteropAsync() await memory.WriteToAsync(ms); Equal(6L, ms.Length); True(ms.TryGetBuffer(out var buffer)); - buffer.Array.ForEach((ref byte value, nint _) => + buffer.AsSpan().ForEach((ref byte value, int _) => { if (value is 1) value = 20; @@ -236,7 +201,7 @@ public static void StreamInterop() memory.WriteTo(ms); Equal(6L, ms.Length); True(ms.TryGetBuffer(out var buffer)); - buffer.Array.ForEach((ref byte value, nint _) => + buffer.AsSpan().ForEach((ref byte value, int _) => { if (value is 1) value = 20; diff --git a/src/DotNext.Tests/DelegateHelpersTests.cs b/src/DotNext.Tests/DelegateHelpersTests.cs index a3dc6e15e..84ba23f81 100644 --- a/src/DotNext.Tests/DelegateHelpersTests.cs +++ b/src/DotNext.Tests/DelegateHelpersTests.cs @@ -160,7 +160,7 @@ static MethodInfo GetMethod(int argCount) types[argCount] = typeof(int); var funcType = Expression.GetFuncType(types); var parameters = new ParameterExpression[argCount]; - parameters.ForEach(static (ref ParameterExpression p, nint _) => p = Expression.Parameter(typeof(string))); + parameters.AsSpan().ForEach(static (ref ParameterExpression p, int _) => p = Expression.Parameter(typeof(string))); //prepare args var args = new object[parameters.LongLength + 1]; Array.Fill(args, string.Empty); diff --git a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/DiskBasedStateMachineTests.cs b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/DiskBasedStateMachineTests.cs index b450cd86d..d7261deae 100644 --- a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/DiskBasedStateMachineTests.cs +++ b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/DiskBasedStateMachineTests.cs @@ -251,7 +251,7 @@ public static async Task PartitionOverflow(bool useCaching) public static async Task SnapshotInstallation(bool useCaching) { var entries = new Int64LogEntry[SimpleStateMachine.RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Func, long?, CancellationToken, ValueTask> checker; using (var state = new SimpleStateMachine(dir, new DiskBasedStateMachine.Options { UseCaching = useCaching })) diff --git a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/MemoryBasedStateMachineTests.cs b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/MemoryBasedStateMachineTests.cs index cd262acbe..5fa1a8199 100644 --- a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/MemoryBasedStateMachineTests.cs +++ b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/MemoryBasedStateMachineTests.cs @@ -463,7 +463,7 @@ public static async Task Commit(bool useCaching) public static async Task SnapshotInstallation(bool useCaching) { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Func, long?, CancellationToken, ValueTask> checker; using (var state = new PersistentStateWithSnapshot(dir, useCaching)) @@ -532,7 +532,7 @@ public static async Task RewriteLogEntry() public static async Task ClearLog() { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Func, long?, CancellationToken, ValueTask> checker; using (var state = new PersistentStateWithSnapshot(dir, useCaching: false)) @@ -567,7 +567,7 @@ public static async Task ClearLog() public static async Task AppendAndCommitAsync(MemoryBasedStateMachine.CompactionMode compaction) { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); using (var state = new PersistentStateWithSnapshot(dir, true, compaction)) { @@ -591,7 +591,7 @@ public static async Task AppendAndCommitAsync(MemoryBasedStateMachine.Compaction public static async Task SequentialCompaction(bool useCaching) { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Func, long?, CancellationToken, ValueTask> checker; using (var state = new PersistentStateWithSnapshot(dir, useCaching, MemoryBasedStateMachine.CompactionMode.Sequential)) @@ -639,7 +639,7 @@ public static async Task SequentialCompaction(bool useCaching) public static async Task BackgroundCompaction(bool useCaching) { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Func, long?, CancellationToken, ValueTask> checker; using (var state = new PersistentStateWithSnapshot(dir, useCaching, MemoryBasedStateMachine.CompactionMode.Background)) @@ -703,7 +703,7 @@ public static async Task BackgroundCompaction(bool useCaching) public static async Task ForegroundCompaction(bool useCaching) { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Func, long?, CancellationToken, ValueTask> checker; using (var state = new PersistentStateWithSnapshot(dir, useCaching, MemoryBasedStateMachine.CompactionMode.Foreground)) @@ -751,7 +751,7 @@ public static async Task ForegroundCompaction(bool useCaching) public static async Task IncrementalCompaction(bool useCaching) { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Func, long?, CancellationToken, ValueTask> checker; using (var state = new PersistentStateWithSnapshot(dir, useCaching, MemoryBasedStateMachine.CompactionMode.Incremental)) @@ -853,7 +853,7 @@ public static async Task RestoreBackup() public static async Task Reconstruction() { var entries = new Int64LogEntry[RecordsPerPartition * 2 + 1]; - entries.ForEach((ref Int64LogEntry entry, nint index) => entry = new Int64LogEntry(42L + index) { Term = index }); + entries.AsSpan().ForEach((ref Int64LogEntry entry, int index) => entry = new Int64LogEntry(42L + index) { Term = index }); var dir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); using (var state = new PersistentStateWithSnapshot(dir, true)) { diff --git a/src/DotNext.Tests/OneDimensionalArrayTests.cs b/src/DotNext.Tests/OneDimensionalArrayTests.cs deleted file mode 100644 index b2ba987a2..000000000 --- a/src/DotNext.Tests/OneDimensionalArrayTests.cs +++ /dev/null @@ -1,122 +0,0 @@ -namespace DotNext; - -public sealed class OneDimensionalArrayTests : Test -{ - public sealed class Equatable - { - private readonly string value; - - public Equatable(string value) => this.value = value; - - public override bool Equals(object other) => other is Equatable equ && value == equ.value; - - public override int GetHashCode() => value.GetHashCode(); - } - - [Fact] - public static void Insert() - { - int[] array = { 1, 2, 3 }; - Equal(new[] { 1, 4, 2, 3 }, array.Insert(4, (Index)1)); - Equal(new[] { 0, 1, 2, 3 }, array.Insert(0, (Index)0)); - Equal(new[] { 1, 2, 3, 4 }, array.Insert(4, (Index)3)); - } - - [Fact] - public static void ArrayEquality() - { - var array1 = new[] { Guid.Empty, Guid.NewGuid(), Guid.NewGuid() }; - var array2 = new[] { Guid.Empty, array1[1], array1[2] }; - False(array1.Equals(array2)); - True(array1.SequenceEqual(array2)); - True(array1.BitwiseEquals(array2)); - array2[1] = Guid.Empty; - False(array1.Equals(array2)); - False(array1.SequenceEqual(array2)); - False(array1.BitwiseEquals(array2)); - } - - [Fact] - public static void BitwiseComparison() - { - var array1 = new[] { Guid.Empty, Guid.NewGuid(), Guid.NewGuid() }; - var array2 = new[] { Guid.Empty, array1[1], array1[2] }; - Equal(0, array1.BitwiseCompare(array2)); - array2[1] = Guid.Empty; - True(array1.BitwiseCompare(array2) > 0); - } - - [Fact] - public static void Slice() - { - var array = new[] { 1, 2, 3, 4 }; - array = array.Slice(1, 2); - Equal(2, array.LongLength); - Equal(2, array[0]); - Equal(3, array[1]); - - array = new[] { 1, 2, 3, 4 }; - array = array.Slice(0, 2); - Equal(2, array.LongLength); - Equal(1, array[0]); - Equal(2, array[1]); - - array = new[] { 1, 2, 3, 4 }; - array = array.Slice(2, 3); - Equal(2, array.LongLength); - Equal(3, array[0]); - Equal(4, array[1]); - } - - [Fact] - public static void View() - { - var array = new[] { 1, 2, 3, 4 }; - var view = array.Slice(1..3); - Equal(2, view.Count); - Equal(2, view[0]); - Equal(3, view[1]); - - view = array.Slice(0..2); - Equal(2, view.Count); - Equal(1, view[0]); - Equal(2, view[1]); - - view = array.Slice(2..); - Equal(2, view.Count); - Equal(3, view[0]); - Equal(4, view[1]); - } - - [Fact] - public static void Concatenation() - { - int[] array1 = { 1, 3, 5 }; - int[] array2 = { 7, 9 }; - Equal(new int[] { 1, 3, 5, 7, 9 }, array1.Concat(array2, array1.Length)); - Equal(array2, array1.Concat(array2, 0)); - Equal(new int[] { 1, 7, 9 }, array1.Concat(array2, 1)); - Equal(Array.Empty(), array1.Concat(Array.Empty(), 0)); - Equal(array2, Array.Empty().Concat(array2, 0)); - } - - [Fact] - public static void RemoveElement() - { - long[] array = { 1, 3, 10 }; - Equal(new[] { 1L, 3L }, array.RemoveAt((Index)2)); - Equal(new[] { 1L }, array.RemoveLast(2)); - Equal(new[] { 10L }, array.RemoveFirst(2)); - Equal(Array.Empty(), array.RemoveFirst(3)); - Equal(Array.Empty(), array.RemoveLast(3)); - } - - [Fact] - public static unsafe void ForEachUsingPointer() - { - int[] array = { 1, 2, 3 }; - array.ForEach(&Exists, array); - - static void Exists(ref int item, int[] array) => Contains(item, array); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs b/src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs deleted file mode 100644 index 58cb6788e..000000000 --- a/src/DotNext.Tests/Runtime/InteropServices/PinnedArrayTests.cs +++ /dev/null @@ -1,148 +0,0 @@ -namespace DotNext.Runtime.InteropServices; - -using IO; - -public sealed class PinnedArrayTests : Test -{ - private static void CheckEmptyArray(IUnmanagedArray array) - { - Equal(0, array.Length); - Equal(Array.Empty(), array); - Equal(Array.Empty(), array.ToArray()); - Equal(Stream.Null, array.AsStream()); - True(array.Span.IsEmpty); - Empty(array); - True(array.Pointer.IsNull); - array = (IUnmanagedArray)array.Clone(); - Equal(0, array.Length); - Equal(Array.Empty(), array); - Same(Array.Empty(), array.ToArray()); - Same(Stream.Null, array.AsStream()); - } - - [Fact] - public static void EmptyArray() - { - var def = new PinnedArray(); - var empty = new PinnedArray(0); - CheckEmptyArray(def); - CheckEmptyArray(empty); - True(empty == def); - False(empty != def); - Equal(def.GetHashCode(), empty.GetHashCode()); - True(def.Equals(Array.Empty())); - True(def.Span.IsEmpty); - } - - [Fact] - public static void Cloning() - { - var array = new PinnedArray(4); - Equal(4, array.Length); - array[0] = 10; - var clone = array.Clone(); - Equal(10, clone[0]); - array[0] = 20; - Equal(10, clone[0]); - } - - [Fact] - public static void UnsafeAccess() - { - var array = new PinnedArray(4); - array[0] = 10; - array[1] = 20; - array[2] = 30; - array[3] = 40; - - var ptr = array.Pointer; - Equal(10, ptr.Value); - ptr += 1; - Equal(20, ptr.Value); - ptr += 1; - Equal(30, ptr.Value); - ptr += 1; - Equal(40, ptr.Value); - } - - [Fact] - public static void ListInterop() - { - IList list = new PinnedArray(4); - NotEmpty(list); - Equal(4, list.Count); - list[0] = 10; - list[1] = 20; - list[2] = 30; - list[3] = 40; - True(list.IsReadOnly); - Equal(2, list.IndexOf(30)); - True(list.Contains(30)); - False(list.Contains(99)); - - var output = new int[4]; - list.CopyTo(output, 0); - Equal(output[0], list[0]); - Equal(output[1], list[1]); - Equal(output[2], list[2]); - Equal(output[3], list[3]); - - Throws(() => list.Add(40)); - Throws(() => list.Remove(10)); - Throws(() => list.RemoveAt(0)); - Throws(() => list.Insert(0, 20)); - Throws(list.Clear); - } - - [Fact] - public static void ReadOnlyListInterop() - { - var array = new PinnedArray(4); - array[0] = 10; - array[1] = 20; - array[2] = 30; - array[3] = 40; - - IReadOnlyList list = array; - NotEmpty(list); - Equal(4, list.Count); - Equal(array[0], list[0]); - Equal(array[1], list[1]); - Equal(array[2], list[2]); - Equal(array[3], list[3]); - } - - [Fact] - public static void BitwiseComparison() - { - var array = new PinnedArray(4); - array[0] = 10; - array[1] = 20; - array[2] = 30; - array[3] = 40; - - var other = (int[])array.Array.Clone(); - True(array.BitwiseEquals(other)); - Equal(0, array.BitwiseCompare(other)); - - array[0] = 99; - False(array.BitwiseEquals(other)); - True(array.BitwiseCompare(other) > 0); - } - - [Fact] - public static void StreamInterop() - { - var array = new PinnedArray(4); - array[0] = 10; - array[1] = 20; - array[2] = 30; - array[3] = 40; - - using var ms = array.AsStream(); - Equal(10, ms.Read()); - Equal(20, ms.Read()); - Equal(30, ms.Read()); - Equal(40, ms.Read()); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs index 6f706f301..a5d5be265 100644 --- a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs +++ b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs @@ -8,23 +8,6 @@ namespace DotNext.Runtime.InteropServices; public sealed class PointerTests : Test { - [Fact] - public static unsafe void BitwiseOperations() - { - var array1 = new ushort[] { 1, 2, 3 }; - var array2 = new ushort[] { 1, 2, 3 }; - fixed (ushort* p1 = array1, p2 = array2) - { - var ptr1 = new Pointer(p1); - var ptr2 = new Pointer(p2); - True(ptr1.BitwiseEquals(ptr2, array1.Length)); - Equal(0, ptr1.BitwiseCompare(ptr2, array1.Length)); - array2[1] = 55; - False(ptr1.BitwiseEquals(ptr2, array1.Length)); - NotEqual(0, ptr1.BitwiseCompare(ptr2, array1.Length)); - } - } - [Fact] public static void StreamInterop() { @@ -35,7 +18,7 @@ public static void StreamInterop() ptr.WriteTo(ms, array.Length); Equal(6L, ms.Length); True(ms.TryGetBuffer(out var buffer)); - buffer.Array.ForEach(static (ref byte value, nint _) => + buffer.AsSpan().ForEach(static (ref byte value, int _) => { if (value == 1) value = 20; @@ -55,7 +38,7 @@ public static async Task StreamInteropAsync() await ptr.WriteToAsync(ms, array.Length); Equal(6L, ms.Length); True(ms.TryGetBuffer(out var buffer)); - buffer.Array.ForEach(static (ref byte value, nint _) => + buffer.AsSpan().ForEach(static (ref byte value, int _) => { if (value == 1) value = 20; diff --git a/src/DotNext.Tests/Runtime/IntrinsicsTests.cs b/src/DotNext.Tests/Runtime/IntrinsicsTests.cs index cc491ca8e..9374a6426 100644 --- a/src/DotNext.Tests/Runtime/IntrinsicsTests.cs +++ b/src/DotNext.Tests/Runtime/IntrinsicsTests.cs @@ -18,7 +18,7 @@ public void FieldTypedReferenceValueType() Equal(Guid.Empty, g); g = Guid.NewGuid(); Equal(field, g); - True(Intrinsics.AreSame(in field, in g)); + True(Unsafe.AreSame(in field, in g)); } [Fact] @@ -29,7 +29,7 @@ public void FieldTypedReferenceClass() Null(f); f = "Hello, world!"; Equal(str, f); - True(Intrinsics.AreSame(in str, in f)); + True(Unsafe.AreSame(in str, in f)); } [Fact] @@ -56,7 +56,7 @@ public static unsafe void SwapValuesByPointer() public static void AddressOfLocal() { var i = 20; - True(Intrinsics.AddressOf(i) != IntPtr.Zero); + True(Intrinsics.AddressOf(in i) != IntPtr.Zero); } [Fact] @@ -79,14 +79,6 @@ public static unsafe void BitwiseEqualityForLong() True(Intrinsics.Equals(&value1, &value2, (nuint)sizeof(long))); } - [Fact] - public static unsafe void BitwiseHashCode() - { - var i = 42L; - NotEqual(0, Intrinsics.GetHashCode32(&i, (nuint)sizeof(long))); - NotEqual(0L, Intrinsics.GetHashCode64(&i, (nuint)sizeof(long))); - } - [Fact] public static void CopyBlock() { @@ -106,23 +98,6 @@ public static unsafe void CopyValue() Equal(42, b); } - [Fact] - public static unsafe void ZeroMem() - { - var g = Guid.NewGuid(); - Intrinsics.ClearBits(&g, (nuint)sizeof(Guid)); - Equal(Guid.Empty, g); - } - - [Fact] - [Obsolete] - public static void ReadonlyRef2() - { - var array = new[] { "a", "b", "c" }; - ref readonly var element = ref array.GetReadonlyRef(2); - Equal("c", element.Clone()); - } - [Fact] public static void IsNullable() { @@ -162,26 +137,6 @@ public static void SmallStructDefault() False(Intrinsics.IsDefault((short)42)); } - [Fact] - public static void Bitcast() - { - var point = new Point { X = 40, Y = 100 }; - Intrinsics.Bitcast(point, out decimal dec); - Intrinsics.Bitcast(dec, out point); - Equal(40, point.X); - Equal(100, point.Y); - Intrinsics.Bitcast(2U, out var i); - Equal(2, i); - } - - [Fact] - public static void BitcastToLargerValueType() - { - var point = new Point { X = 40, Y = 100 }; - Intrinsics.Bitcast(point, out Guid g); - False(g == Guid.Empty); - } - [Fact] public static void LightweightTypeOf() { @@ -214,22 +169,6 @@ private enum LongEnum : long Two = 2L, } - [Fact] - public static void HasFlag() - { - static void HasFlag(T value, T validFlag, T invalidFlag) - where T : struct, Enum - { - True(Intrinsics.HasFlag(value, validFlag)); - False(Intrinsics.HasFlag(value, invalidFlag)); - } - - HasFlag(BindingFlags.Public | BindingFlags.Instance, BindingFlags.Public, BindingFlags.Static); - HasFlag(ByteEnum.Two, ByteEnum.Two, ByteEnum.One); - HasFlag(ShortEnum.Two, ShortEnum.Two, ShortEnum.One); - HasFlag(LongEnum.Two, LongEnum.Two, LongEnum.One); - } - [Fact] public static void CastObject() { @@ -253,29 +192,6 @@ public static void ExactTypeCheck() False(Intrinsics.IsExactTypeOf(obj)); } - [Fact] - public static void ThrowObjectAsException() - { - Throws(() => Intrinsics.Throw(new InvalidOperationException())); - var e = Throws(() => Intrinsics.Throw("String")); - Equal("String", e.WrappedException); - Throws(new Action(() => throw Intrinsics.Error(new InvalidOperationException()))); - e = Throws(new Action(() => throw Intrinsics.Error("String"))); - Equal("String", e.WrappedException); - } - - [Fact] - public static void ObjectClone() - { - var obj = new IntrinsicsTests(); - var obj2 = Intrinsics.ShallowCopy(obj); - obj.field = Guid.NewGuid(); - obj2.str = string.Empty; - NotEqual(obj.field, obj2.field); - NotEqual(obj.str, obj2.str); - NotSame(obj, obj2); - } - [Fact] public static void ArrayLength() { @@ -286,24 +202,6 @@ public static void ArrayLength() Equal(new IntPtr(4), (IntPtr)Intrinsics.GetLength(new int[2, 2])); } - [Fact] - public static void ThrowOnNullReference() - { - ref int value = ref Unsafe.NullRef(); - - var thrown = false; - try - { - Intrinsics.ThrowIfNull(in value); - } - catch (NullReferenceException) - { - thrown = true; - } - - True(thrown); - } - [Fact] public static void HasFinalizer() { @@ -312,27 +210,6 @@ public static void HasFinalizer() False(Intrinsics.HasFinalizer(new object())); } - private sealed class ObjectWithFinalizer - { - internal bool IsFinalizerCalled; - - ~ObjectWithFinalizer() => IsFinalizerCalled = true; - } - - [Fact] - public static void InvokeFinalizer() - { - var obj = new ObjectWithFinalizer(); - False(obj.IsFinalizerCalled); - Intrinsics.Finalize(obj); - True(obj.IsFinalizerCalled); - - obj.IsFinalizerCalled = false; - GC.SuppressFinalize(obj); - Intrinsics.Finalize(obj); - True(obj.IsFinalizerCalled); - } - [Fact] public static void TypeAlignment() { diff --git a/src/DotNext.Tests/SpanTests.cs b/src/DotNext.Tests/SpanTests.cs index fd93332ad..3a62b7e32 100644 --- a/src/DotNext.Tests/SpanTests.cs +++ b/src/DotNext.Tests/SpanTests.cs @@ -12,10 +12,10 @@ public static void BitwiseEquality() Span array1 = [Guid.Empty, Guid.NewGuid(), Guid.NewGuid()]; Span array2 = [Guid.Empty, array1[1], array1[2]]; True(array1.SequenceEqual(array2)); - True(array1.BitwiseEquals(array2)); + True(Span.BitwiseEquals(array1, array2)); array2[1] = Guid.Empty; False(array1.SequenceEqual(array2)); - False(array1.BitwiseEquals(array2)); + False(Span.BitwiseEquals(array1, array2)); } [Fact] @@ -23,15 +23,15 @@ public static void BitwiseComparison() { Span array1 = [Guid.Empty, Guid.NewGuid(), Guid.NewGuid()]; Span array2 = [Guid.Empty, array1[1], array1[2]]; - Equal(0, array1.BitwiseCompare(array2)); + Equal(0, Span.BitwiseCompare(array1, array2)); array2[1] = Guid.Empty; - True(array1.BitwiseCompare(array2) > 0); + True(Span.BitwiseCompare(array1, array2) > 0); } [Fact] public static unsafe void SortingUsingPointer() { - Span span = new ulong[] { 3, 2, 6, 4 }; + Span span = [3, 2, 6, 4]; span.Sort(&Sort); Equal(6UL, span[0]); Equal(4UL, span[1]); @@ -177,7 +177,8 @@ public static void Tuple1ToSpan() [Fact] public static void Tuple1ToReadOnlySpan() { - var span = new ValueTuple(42).AsReadOnlySpan(); + var tuple = new ValueTuple(42); + var span = tuple.AsReadOnlySpan(); False(span.IsEmpty); Equal(1, span.Length); Equal(42, span[0]); @@ -202,7 +203,8 @@ public static void Tuple2ToSpan() [Fact] public static void Tuple2ToReadOnlySpan() { - var span = (42, 43).AsReadOnlySpan(); + var tuple = (42, 43); + var span = tuple.AsReadOnlySpan(); False(span.IsEmpty); Equal(2, span.Length); Equal(42, span[0]); @@ -231,7 +233,8 @@ public static void Tuple3ToSpan() [Fact] public static void Tuple3ToReadOnlySpan() { - var span = (42, 43, 44).AsReadOnlySpan(); + var tuple = (42, 43, 44); + var span = tuple.AsReadOnlySpan(); False(span.IsEmpty); Equal(3, span.Length); Equal(42, span[0]); @@ -264,7 +267,8 @@ public static void Tuple4ToSpan() [Fact] public static void Tuple4ToReadOnlySpan() { - var span = (42, 43, 44, 45).AsReadOnlySpan(); + var tuple = (42, 43, 44, 45); + var span = tuple.AsReadOnlySpan(); False(span.IsEmpty); Equal(4, span.Length); Equal(42, span[0]); @@ -301,7 +305,8 @@ public static void Tuple5ToSpan() [Fact] public static void Tuple5ToReadOnlySpan() { - var span = (42, 43, 44, 45, 46).AsReadOnlySpan(); + var tuple = (42, 43, 44, 45, 46); + var span = tuple.AsReadOnlySpan(); False(span.IsEmpty); Equal(5, span.Length); Equal(42, span[0]); @@ -342,7 +347,8 @@ public static void Tuple6ToSpan() [Fact] public static void Tuple6ToReadOnlySpan() { - var span = (42, 43, 44, 45, 46, 47).AsReadOnlySpan(); + var tuple = (42, 43, 44, 45, 46, 47); + var span = tuple.AsReadOnlySpan(); False(span.IsEmpty); Equal(6, span.Length); Equal(42, span[0]); @@ -387,7 +393,8 @@ public static void Tuple7ToSpan() [Fact] public static void Tuple7ToReadOnlySpan() { - var span = (42, 43, 44, 45, 46, 47, 48).AsReadOnlySpan(); + var tuple = (42, 43, 44, 45, 46, 47, 48); + var span = tuple.AsReadOnlySpan(); False(span.IsEmpty); Equal(7, span.Length); Equal(42, span[0]); @@ -416,12 +423,14 @@ public static void ConcatStrings() Empty(buffer.Span.ToString()); } - using (var buffer = Span.Concat(new ValueTuple("Hello, world!").AsReadOnlySpan())) + var tuple1 = new ValueTuple("Hello, world!"); + using (var buffer = Span.Concat(tuple1.AsReadOnlySpan())) { Equal("Hello, world!", buffer.Span.ToString()); } - using (var buffer = Span.Concat(("Hello, ", "world!").AsReadOnlySpan())) + var tuple2 = ("Hello, ", "world!"); + using (var buffer = Span.Concat(tuple2.AsReadOnlySpan())) { Equal("Hello, world!", buffer.Span.ToString()); } diff --git a/src/DotNext.Tests/Threading/AsyncDelegateTests.cs b/src/DotNext.Tests/Threading/AsyncDelegateTests.cs index 163ef3d57..becd84191 100644 --- a/src/DotNext.Tests/Threading/AsyncDelegateTests.cs +++ b/src/DotNext.Tests/Threading/AsyncDelegateTests.cs @@ -58,7 +58,7 @@ static MethodInfo GetMethod(int argCount) Array.Fill(types, typeof(string)); var actionType = Expression.GetActionType(types); var parameters = new ParameterExpression[argCount]; - parameters.ForEach((ref ParameterExpression p, nint _) => p = Expression.Parameter(typeof(string))); + parameters.AsSpan().ForEach(static (ref ParameterExpression p, int _) => p = Expression.Parameter(typeof(string))); //prepare args var args = new object[parameters.LongLength + 2]; Array.Fill(args, string.Empty); diff --git a/src/DotNext.Tests/ValueTypeTests.cs b/src/DotNext.Tests/ValueTypeTests.cs index 83dae5962..cf2420cb3 100644 --- a/src/DotNext.Tests/ValueTypeTests.cs +++ b/src/DotNext.Tests/ValueTypeTests.cs @@ -122,13 +122,4 @@ public static void BitwiseCompare() IComparer comparer = BitwiseComparer.Instance; True(comparer.Compare(0, int.MinValue) < 0); } - - [Fact] - public static void CustomHashCode() - { - var result = BitwiseComparer.GetHashCode(new Guid(), 0, static (data, hash) => hash + 1, false); - Equal(4, result); - result = BitwiseComparer.GetHashCode(new Guid(), 0, static (data, hash) => hash + 1, true); - Equal(5, result); - } } \ No newline at end of file diff --git a/src/DotNext.Unsafe/Buffers/UnmanagedMemoryOwner.cs b/src/DotNext.Unsafe/Buffers/UnmanagedMemoryOwner.cs index bcdb0ff6e..5b126afb9 100644 --- a/src/DotNext.Unsafe/Buffers/UnmanagedMemoryOwner.cs +++ b/src/DotNext.Unsafe/Buffers/UnmanagedMemoryOwner.cs @@ -53,7 +53,7 @@ unsafe object ICloneable.Clone() /// /// The enumerator over all elements in the unmanaged memory. /// The underlying unmanaged memory is released. - public Pointer.Enumerator GetEnumerator() => Pointer.GetEnumerator(Length); + public Pointer.Enumerator GetEnumerator() => Pointer.GetEnumerator((nuint)Length); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); diff --git a/src/DotNext.Unsafe/IO/MemoryMappedFiles/MemoryMappedDirectAccessor.cs b/src/DotNext.Unsafe/IO/MemoryMappedFiles/MemoryMappedDirectAccessor.cs index 81d866fde..f0e2a8be3 100644 --- a/src/DotNext.Unsafe/IO/MemoryMappedFiles/MemoryMappedDirectAccessor.cs +++ b/src/DotNext.Unsafe/IO/MemoryMappedFiles/MemoryMappedDirectAccessor.cs @@ -64,10 +64,9 @@ internal MemoryMappedDirectAccessor(MemoryMappedViewAccessor accessor) /// The underlying unmanaged memory is released. public readonly void Clear() { - if (ptr is null) - throw new ObjectDisposedException(GetType().Name); + ObjectDisposedException.ThrowIf(ptr is null, this); - Pointer.Clear(new IntPtr(Size)); + Pointer.Clear((nuint)Size); } /// diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs b/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs index 0d7686295..1a7caef06 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs @@ -23,15 +23,6 @@ public interface IUnmanagedArray : IUnmanagedMemory, IEnumerable, ICloneab /// Span Span { get; } - /// - /// Gets element of the unmanaged array. - /// - /// The index of the element to get. - /// is out of range. - /// The pointer to the array element. - [Obsolete("Use indexer overload with native-sized integer parameter")] - ref T this[int index] => ref this[(nint)index]; - /// /// Gets element of the unmanaged array. /// @@ -49,26 +40,6 @@ ref T this[nint index] } } - /// - /// Copies elements from the memory location to the managed array. - /// - /// The destination array. - /// The position in the destination array from which copying begins. - /// The number of array elements to be copied. - /// The actual number of copied elements. - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - long WriteTo(T[] destination, long offset, long count) => Pointer.WriteTo(destination, offset, count); - - /// - /// Copies elements from the memory location to the managed array. - /// - /// The destination array. - /// The actual number of copied elements. - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - long WriteTo(T[] destination) => Pointer.WriteTo(destination, 0, Math.Min(destination.LongLength, Length)); - /// /// Copies elements from the unmanaged array into managed heap. /// @@ -78,93 +49,4 @@ ref T this[nint index] /// T[] ISupplier.Invoke() => ToArray(); - - /// - /// Copies elements from the specified array into - /// the memory block identified by this object. - /// - /// The source array. - /// The position in the source array from which copying begins. - /// The number of elements of type to be copied. - /// Actual number of copied elements. - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - long ReadFrom(T[] source, long offset, long count) => Pointer.ReadFrom(source, offset, Math.Min(Length, count)); - - /// - /// Copies elements from the memory location to the managed array. - /// - /// The source array. - /// The actual number of copied elements. - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - long ReadFrom(T[] source) - { - source.AsSpan().CopyTo(Span, out var writtenCount); - return writtenCount; - } - - /// - /// Copies elements from the current memory location to the specified memory location. - /// - /// The target memory location. - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - void WriteTo(Pointer destination) => Pointer.CopyTo(destination, Length); - - /// - /// Copies bytes from the source memory to the memory identified by this object. - /// - /// The pointer to the source unmanaged memory. - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - void ReadFrom(Pointer source) => source.CopyTo(Pointer, Length); - - /// - /// Copies elements from the current memory location to the specified memory location. - /// - /// The target memory location. - /// The actual number of copied elements. - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - long WriteTo(IUnmanagedArray destination) - { - var count = Math.Min(Length, destination.Length); - Pointer.CopyTo(destination.Pointer, count); - return count; - } - - /// - /// Computes bitwise equality between two blocks of memory. - /// - /// The block of memory to be compared. - /// , if both memory blocks have the same bytes; otherwise, . - /// The underlying unmanaged memory has been released. - bool BitwiseEquals(Pointer other) => Pointer.BitwiseEquals(other, Length); - - /// - /// Computes bitwise equality between this array and the specified managed array. - /// - /// The array to be compared. - /// , if both memory blocks have the same bytes; otherwise, . - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - bool BitwiseEquals(T[] other) => Span.SequenceEqual(other); - - /// - /// Bitwise comparison of the memory blocks. - /// - /// The block of memory to be compared. - /// Comparison result which has the semantics as return type of . - /// The underlying unmanaged memory has been released. - int BitwiseCompare(Pointer other) => Pointer.BitwiseCompare(other, Length); - - /// - /// Bitwise comparison of the memory blocks. - /// - /// The array to be compared. - /// Comparison result which has the semantics as return type of . - /// The underlying unmanaged memory has been released. - [Obsolete("Use Span property instead")] - int BitwiseCompare(T[] other) => Span.BitwiseCompare(other); } \ No newline at end of file diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedMemory.cs b/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedMemory.cs index 5a5734030..0406069d0 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedMemory.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedMemory.cs @@ -16,7 +16,7 @@ public interface IUnmanagedMemory : IDisposable, ISupplier /// Sets all bits of allocated memory to zero. /// /// The underlying unmanaged memory has been released. - void Clear() => Pointer.Clear(new IntPtr(Size)); + void Clear() => Pointer.Clear((uint)Size); /// /// Gets a pointer to the allocated unmanaged memory. @@ -86,22 +86,6 @@ public interface IUnmanagedMemory : IDisposable, ISupplier /// The pointer to the source unmanaged memory. /// The underlying unmanaged memory has been released. void ReadFrom(Pointer source) => source.CopyTo(Pointer, new IntPtr(Size)); - - /// - /// Computes bitwise equality between two blocks of memory. - /// - /// The block of memory to be compared. - /// , if both memory blocks have the same bytes; otherwise, . - /// The underlying unmanaged memory has been released. - bool BitwiseEquals(IUnmanagedMemory other) => Size == other.Size && Pointer.BitwiseEquals(other.Pointer, new IntPtr(Size)); - - /// - /// Bitwise comparison of the memory blocks. - /// - /// The block of memory to be compared. - /// Comparison result which has the semantics as return type of . - /// The underlying unmanaged memory has been released. - int BitwiseCompare(IUnmanagedMemory other) => Size == other.Size ? Pointer.BitwiseCompare(other.Pointer, new IntPtr(Size)) : Size.CompareTo(other.Size); } /// diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs b/src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs deleted file mode 100644 index 74768600b..000000000 --- a/src/DotNext.Unsafe/Runtime/InteropServices/PinnedArray.cs +++ /dev/null @@ -1,258 +0,0 @@ -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace DotNext.Runtime.InteropServices; - -/// -/// Represents pinned array that can be passed to unmanaged code -/// without marshalling overhead. -/// -/// The type of the array elements. -[StructLayout(LayoutKind.Auto)] -public readonly struct PinnedArray : IUnmanagedArray, IList, IReadOnlyList, IEquatable> - where T : unmanaged -{ - private readonly T[]? array; - - /// - /// Allocates a new pinned array in Pinned Object Heap. - /// - /// The length of the array. - /// to allocate the array with zeroed content; otherwise, . - public PinnedArray(int length, bool zeroMem = false) - { - if (length is 0) - array = System.Array.Empty(); - else if (zeroMem) - array = GC.AllocateArray(length, true); - else - array = GC.AllocateUninitializedArray(length, true); - } - - private PinnedArray(T[] array) => this.array = array; - - /// - bool ICollection.IsReadOnly => true; - - /// - /// Creates deep copy of this array. - /// - /// The deep copy of this array. - public PinnedArray Clone() - { - if (array.IsNullOrEmpty()) - return default; - - var copy = GC.AllocateUninitializedArray(array.Length, true); - array.CopyTo(copy, 0); - return new PinnedArray(copy); - } - - /// - object ICloneable.Clone() => Clone(); - - /// - /// Gets managed pointer to the array element at the specified index. - /// - /// The index of the element in memory. - /// The managed pointer to the array element. - public ref T this[nint index] => ref Array[index]; - - /// - T IList.this[int index] - { - get => this[index]; - set => this[index] = value; - } - - /// - T IReadOnlyList.this[int index] => this[index]; - - /// - /// Returns the index of a specific item in this array. - /// - /// The object to locate in this array. - /// The index of if found in this array; otherwise, -1. - public int IndexOf(T item) => System.Array.IndexOf(Array, item); - - /// - /// Determines whether this array contains a specific value. - /// - /// The object to locate in this array. - /// if item is found in this array; otherwise, . - public bool Contains(T item) => Array.As>().Contains(item); - - /// - /// Copies the elements of this array to another array, starting at a particular index in - /// the destination array. - /// - /// The array that is the destination of the elements copied from this array. - /// The zero-based index in the destination at which copying begins. - /// is less than 0. - /// The number of elements in this array is greater than the available space from to the end of the destination array. - public void CopyTo(T[] destination, int index) => Array.CopyTo(destination, index); - - /// - void IList.Insert(int index, T item) => throw new NotSupportedException(); - - /// - void IList.RemoveAt(int index) => throw new NotSupportedException(); - - /// - void ICollection.Add(T item) => throw new NotSupportedException(); - - /// - void ICollection.Clear() => throw new NotSupportedException(); - - /// - bool ICollection.Remove(T item) => throw new NotSupportedException(); - - /// - /// Creates a stream over elements in this array. - /// - /// The stream over elements in this array. - public unsafe Stream AsStream() - { - return array.IsNullOrEmpty() - ? Stream.Null - : typeof(T) == typeof(byte) - ? new MemoryStream(Unsafe.As(array), writable: true) - : new UnmanagedMemoryStream((byte*)RawPointer, Size); - } - - /// - /// Gets the span over array elements. - /// - public Span Span => array.AsSpan(); - - /// - /// Represents underlying array as . - /// - public Memory Memory => array.IsNullOrEmpty() ? Memory.Empty : MemoryMarshal.CreateFromPinnedArray(array, 0, array.Length); - - /// - Span IUnmanagedMemory.Bytes => MemoryMarshal.AsBytes(Span); - - private unsafe void* RawPointer => array.IsNullOrEmpty() ? null : Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(array)); - - /// - /// Gets the pointer to the first element of the pinned array. - /// - public unsafe Pointer Pointer => new((T*)RawPointer); - - /// - unsafe Pointer IUnmanagedMemory.Pointer => new((byte*)RawPointer); - - /// - /// Gets length of the pinned array. - /// - public int Length => array?.Length ?? 0; - - /// - int ICollection.Count => Length; - - /// - int IReadOnlyCollection.Count => Length; - - private unsafe long Size => Math.BigMul(Length, sizeof(T)); - - /// - long IUnmanagedMemory.Size => Size; - - /// - /// Gets underlying array. - /// - public T[] Array => array ?? System.Array.Empty(); - - /// - T[] IUnmanagedArray.ToArray() => Array; - - /// - T[] ISupplier.Invoke() => Array; - - /// - /// Computes bitwise equality between this array and the specified managed array. - /// - /// The array to be compared. - /// , if both memory blocks have the same bytes; otherwise, . - public bool BitwiseEquals(T[] other) => OneDimensionalArray.BitwiseEquals(Array, other); - - /// - /// Bitwise comparison of the memory blocks. - /// - /// The array to be compared. - /// Comparison result which has the semantics as return type of . - public int BitwiseCompare(T[] other) => OneDimensionalArray.BitwiseCompare(Array, other); - - /// - /// Gets underlying array. - /// - /// The pinned array. - /// The underlying array. - public static implicit operator T[](PinnedArray array) => array.Array; - - /// - /// Gets enumerator over array elements. - /// - /// The enumerator over array elements. - public IEnumerator GetEnumerator() => Array.AsEnumerable().GetEnumerator(); - - /// - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - void IDisposable.Dispose() - { - } - - /// - /// Returns a string that represents the current array. - /// - /// A string that represents the current array. - public override string ToString() => Pointer.ToString(); - - /// - /// Determines whether the current object references the same array as other object. - /// - /// The array to be compared. - /// if the current object references the same array as ; otherwise, . - public bool Equals(PinnedArray other) => ReferenceEquals(Array, other.Array); - - /// - /// Determines whether the current object references the same array as other object. - /// - /// The array to be compared. - /// if the current object references the same array as ; otherwise, . - public override bool Equals([NotNullWhen(true)] object? other) => other switch - { - PinnedArray pinned => Equals(pinned), - T[] array => ReferenceEquals(Array, array), - _ => false - }; - - /// - /// Gets the identity of the referenced array. - /// - /// The identity of the referenced array. - public override int GetHashCode() => RuntimeHelpers.GetHashCode(Array); - - /// - /// Determines whether the two objects represent the same array. - /// - /// The first array to be compared. - /// The second array to be compared. - /// if both objects represent the same array; otherwise, . - public static bool operator ==(PinnedArray first, PinnedArray second) - => first.Equals(second); - - /// - /// Determines whether the two objects represent different arrays. - /// - /// The first array to be compared. - /// The second array to be compared. - /// if both objects represent different arrays; otherwise, . - public static bool operator !=(PinnedArray first, PinnedArray second) - => !first.Equals(second); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs index 583fd3dd4..dd60ad631 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CancellationToken = System.Threading.CancellationToken; @@ -22,7 +23,28 @@ namespace DotNext.Runtime.InteropServices; /// Null-pointer is the only check performed by methods. /// [StructLayout(LayoutKind.Sequential)] -public readonly struct Pointer : IEquatable>, IComparable>, IStrongBox, ISupplier, ISupplier, IPinnable, ISpanFormattable +public readonly struct Pointer : + IEquatable>, + IComparable>, + IStrongBox, + ISupplier, + ISupplier, + IPinnable, + ISpanFormattable, + IComparisonOperators, Pointer, bool>, + IEqualityOperators, Pointer, bool>, + IAdditionOperators, int, Pointer>, + IAdditionOperators, long, Pointer>, + IAdditionOperators, nint, Pointer>, + IAdditionOperators, nuint, Pointer>, + ISubtractionOperators, int, Pointer>, + ISubtractionOperators, long, Pointer>, + ISubtractionOperators, nint, Pointer>, + ISubtractionOperators, nuint, Pointer>, + IIncrementOperators>, + IDecrementOperators>, + IAdditiveIdentity, nint>, + IAdditiveIdentity, nuint> where T : unmanaged { /// @@ -36,7 +58,7 @@ public unsafe struct Enumerator : IEnumerator private nuint index; /// - object IEnumerator.Current => Current; + readonly object IEnumerator.Current => Current; internal Enumerator(T* ptr, nuint count) { @@ -113,6 +135,12 @@ public unsafe Pointer(nuint ptr) { } + /// + static nint IAdditiveIdentity, nint>.AdditiveIdentity => 0; + + /// + static nuint IAdditiveIdentity, nuint>.AdditiveIdentity => 0U; + [DoesNotReturn] [StackTraceHidden] private static void ThrowNullPointerException() => throw new NullPointerException(); @@ -131,32 +159,6 @@ public unsafe Pointer(nuint ptr) /// public unsafe bool IsAligned => Address % Intrinsics.AlignOf() is 0; - /// - /// Fills the elements of the array with a specified value. - /// - /// The value to assign to each element of the array. - /// The length of the array. - /// This pointer is zero. - /// is less than zero. - [Obsolete("Use Fill overload with native-sized integer parameter")] - public void Fill(T value, long count) - => Fill(value, new IntPtr(count)); - - /// - /// Fills the elements of the array with a specified value. - /// - /// The value to assign to each element of the array. - /// The length of the array. - /// This pointer is zero. - /// is less than zero. - public void Fill(T value, nint count) - { - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); - - Fill(value, (nuint)count); - } - /// /// Fills the elements of the array with a specified value. /// @@ -182,12 +184,12 @@ public unsafe void Fill(T value, nuint count) /// /// The number of elements located in the unmanaged memory identified by this pointer. /// representing elements in the unmanaged memory. - public unsafe Span ToSpan(int length) => IsNull ? Span.Empty : new(value, length); + public unsafe Span ToSpan(int length) => IsNull ? [] : new(value, length); /// /// Converts this pointer into span of bytes. /// - public unsafe Span Bytes => IsNull ? Span.Empty : Span.AsBytes(value); + public unsafe Span Bytes => IsNull ? [] : Span.AsBytes(value); /// /// Gets or sets pointer value at the specified position in the memory. @@ -198,8 +200,8 @@ public unsafe void Fill(T value, nuint count) /// Element index. /// Array element. /// This array is not allocated. - [Obsolete("Use indexer overload with native-sized integer parameter")] - public unsafe ref T this[long index] + [CLSCompliant(false)] + public unsafe ref T this[nuint index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -220,6 +222,7 @@ public unsafe ref T this[long index] /// Element index. /// Array element. /// This array is not allocated. + [CLSCompliant(false)] public unsafe ref T this[nint index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -265,29 +268,6 @@ void IPinnable.Unpin() { } - /// - /// Fill memory with zero bytes. - /// - /// Number of elements in the unmanaged array. - /// This pointer is equal to zero. - /// is less than zero. - [Obsolete("Use Clear overload with native-sized integer parameter")] - public void Clear(long count) => Clear(new IntPtr(count)); - - /// - /// Fill memory with zero bytes. - /// - /// Number of elements in the unmanaged array. - /// This pointer is equal to zero. - /// is less than zero. - public void Clear(nint count) - { - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); - - Clear((nuint)count); - } - /// /// Fill memory with zero bytes. /// @@ -296,11 +276,10 @@ public void Clear(nint count) [CLSCompliant(false)] public unsafe void Clear(nuint count) { - // TODO: Replace with NativeMemory.Clear if (IsNull) ThrowNullPointerException(); - Intrinsics.ClearBits(value, checked((nuint)sizeof(T) * count)); + NativeMemory.Clear(value, count); } /// @@ -736,16 +715,8 @@ public unsafe ref T Value /// The index of the element. /// The value stored in the memory at the specified position. /// The pointer is 0. - [Obsolete("Use Get overload with native-sized integer parameter")] - public T Get(long index) => Get(new IntPtr(index)); - - /// - /// Gets the value stored in the memory at the specified position. - /// - /// The index of the element. - /// The value stored in the memory at the specified position. - /// The pointer is 0. - public T Get(nint index) => this[index]; + [CLSCompliant(false)] + public T Get(nuint index) => this[index]; /// /// Sets the value stored in the memory identified by this pointer. @@ -761,16 +732,8 @@ public unsafe ref T Value /// The value to be stored in the memory. /// The index of the element to modify. /// The pointer is 0. - [Obsolete("Use Set overload with native-sized integer parameter")] - public void Set(T value, long index) => Set(value, new IntPtr(index)); - - /// - /// Sets the value at the specified position in the memory. - /// - /// The value to be stored in the memory. - /// The index of the element to modify. - /// The pointer is 0. - public void Set(T value, nint index) => this[index] = value; + [CLSCompliant(false)] + public void Set(T value, nuint index) => this[index] = value; /// /// Dereferences this pointer, assuming that this pointer is unaligned. @@ -792,7 +755,8 @@ public unsafe T GetUnaligned() /// The index of the element. /// The value stored in the memory at the specified position. /// The pointer is zero. - public unsafe T GetUnaligned(nint index) + [CLSCompliant(false)] + public unsafe T GetUnaligned(nuint index) { if (IsNull) ThrowNullPointerException(); @@ -821,7 +785,8 @@ public unsafe void SetUnaligned(T value) /// The value to be stored in the memory. /// The index of the element to modify. /// The pointer is 0. - public unsafe void SetUnaligned(T value, nint index) + [CLSCompliant(false)] + public unsafe void SetUnaligned(T value, nuint index) { if (IsNull) ThrowNullPointerException(); @@ -829,24 +794,6 @@ public unsafe void SetUnaligned(T value, nint index) Unsafe.WriteUnaligned(this.value + index, value); } - /// - /// Gets enumerator over raw memory. - /// - /// A number of elements to iterate. - /// Iterator object. - /// is less than zero. - [Obsolete("Use GetEnumerator overload with native-sized integer parameter")] - public Enumerator GetEnumerator(long length) - => GetEnumerator(new IntPtr(length)); - - /// - /// Gets enumerator over raw memory. - /// - /// A number of elements to iterate. - /// Iterator object. - public unsafe Enumerator GetEnumerator(nint length) - => length >= 0 ? GetEnumerator((nuint)length) : throw new ArgumentOutOfRangeException(nameof(length)); - /// /// Gets enumerator over raw memory. /// @@ -855,286 +802,6 @@ public unsafe Enumerator GetEnumerator(nint length) [CLSCompliant(false)] public unsafe Enumerator GetEnumerator(nuint length) => IsNull ? default : new Enumerator(value, length); - /// - /// Computes bitwise equality between two blocks of memory. - /// - /// The pointer identifies block of memory to be compared. - /// The number of elements of type referenced by both pointers. - /// , if both memory blocks have the same bytes; otherwise, . - [Obsolete("Use BitwiseEquals overload with native-sized integer parameter")] - public bool BitwiseEquals(Pointer other, long count) => BitwiseEquals(other, new IntPtr(count)); - - /// - /// Computes bitwise equality between two blocks of memory. - /// - /// The pointer identifies block of memory to be compared. - /// The number of elements of type referenced by both pointers. - /// , if both memory blocks have the same bytes; otherwise, . - public bool BitwiseEquals(Pointer other, nint count) - => count >= 0 ? BitwiseEquals(other, (nuint)count) : throw new ArgumentOutOfRangeException(nameof(count)); - - /// - /// Computes bitwise equality between two blocks of memory. - /// - /// The pointer identifies block of memory to be compared. - /// The number of elements of type referenced by both pointers. - /// , if both memory blocks have the same bytes; otherwise, . - [CLSCompliant(false)] - public unsafe bool BitwiseEquals(Pointer other, nuint count) - { - if (value == other.value) - return true; - if (IsNull || other.IsNull) - return false; - - return Intrinsics.Equals(value, other, checked(count * (nuint)sizeof(T))); - } - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [Obsolete("Use BitwiseHashCode overload with native-sized integer parameter")] - public int BitwiseHashCode(long count, bool salted = true) => BitwiseHashCode(new IntPtr(count), salted); - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - public int BitwiseHashCode(nint count, bool salted = true) - => count >= 0 ? BitwiseHashCode((nuint)count, salted) : throw new ArgumentOutOfRangeException(nameof(count)); - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [CLSCompliant(false)] - public unsafe int BitwiseHashCode(nuint count, bool salted = true) - { - if (IsNull) - ThrowNullPointerException(); - - return Intrinsics.GetHashCode32(value, checked(count * (nuint)sizeof(T)), salted); - } - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [Obsolete("Use BitwiseHashCode64 overload with native-sized integer parameter")] - public long BitwiseHashCode64(long count, bool salted = true) - => BitwiseHashCode64(new IntPtr(count), salted); - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - public long BitwiseHashCode64(nint count, bool salted = true) - => count >= 0 ? BitwiseHashCode64((nuint)count, salted) : throw new ArgumentOutOfRangeException(nameof(count)); - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [CLSCompliant(false)] - public unsafe long BitwiseHashCode64(nuint count, bool salted = true) - { - if (IsNull) - ThrowNullPointerException(); - - return Intrinsics.GetHashCode64(value, checked(count * (nuint)sizeof(T)), salted); - } - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// Initial value of the hash to be passed into hashing function. - /// The custom hash function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [Obsolete("Use BitwiseHashCode overload with native-sized integer parameter")] - public int BitwiseHashCode(long count, int hash, Func hashFunction, bool salted = true) - => BitwiseHashCode(new IntPtr(count), hash, hashFunction, salted); - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// Initial value of the hash to be passed into hashing function. - /// The custom hash function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - public int BitwiseHashCode(nint count, int hash, Func hashFunction, bool salted = true) - => count >= 0 ? BitwiseHashCode((nuint)count, hash, hashFunction, salted) : throw new ArgumentOutOfRangeException(nameof(count)); - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// Initial value of the hash to be passed into hashing function. - /// The custom hash function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [CLSCompliant(false)] - public unsafe int BitwiseHashCode(nuint count, int hash, Func hashFunction, bool salted = true) - { - if (IsNull) - ThrowNullPointerException(); - - return Intrinsics.GetHashCode32(value, checked(count * (nuint)sizeof(T)), hash, hashFunction, salted); - } - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The type of the hash algorithm. - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [Obsolete("Use BitwiseHashCode overload with native-sized integer parameter")] - public int BitwiseHashCode(long count, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - => BitwiseHashCode(new IntPtr(count), salted); - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The type of the hash algorithm. - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - public int BitwiseHashCode(nint count, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - => count >= 0 ? BitwiseHashCode((nuint)count, salted) : throw new ArgumentOutOfRangeException(nameof(count)); - - /// - /// Computes 32-bit hash code for the block of memory identified by this pointer. - /// - /// The type of the hash algorithm. - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [CLSCompliant(false)] - public unsafe int BitwiseHashCode(nuint count, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - { - if (IsNull) - ThrowNullPointerException(); - - return Intrinsics.GetHashCode32(value, checked(count * (nuint)sizeof(T)), salted); - } - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The type of the hash algorithm. - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [Obsolete("Use BitwiseHashCode64 overload with native-sized integer parameter")] - public long BitwiseHashCode64(long count, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - => BitwiseHashCode64(new IntPtr(count), salted); - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The type of the hash algorithm. - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - public long BitwiseHashCode64(nint count, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - => count >= 0 ? BitwiseHashCode64((nuint)count, salted) : throw new ArgumentOutOfRangeException(nameof(count)); - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The type of the hash algorithm. - /// The number of elements of type referenced by this pointer. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [CLSCompliant(false)] - public unsafe long BitwiseHashCode64(nuint count, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - { - if (IsNull) - ThrowNullPointerException(); - - return Intrinsics.GetHashCode64(value, checked(count * (nuint)sizeof(T)), salted); - } - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// Initial value of the hash to be passed into hashing function. - /// The custom hash function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [Obsolete("Use BitwiseHashCode64 overload with native-sized integer parameter")] - public long BitwiseHashCode64(long count, long hash, Func hashFunction, bool salted = true) - => BitwiseHashCode64(new IntPtr(count), hash, hashFunction, salted); - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// Initial value of the hash to be passed into hashing function. - /// The custom hash function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - public long BitwiseHashCode64(nint count, long hash, Func hashFunction, bool salted = true) - => count >= 0 ? BitwiseHashCode64((nuint)count, hash, hashFunction, salted) : throw new ArgumentOutOfRangeException(nameof(count)); - - /// - /// Computes 64-bit hash code for the block of memory identified by this pointer. - /// - /// The number of elements of type referenced by this pointer. - /// Initial value of the hash to be passed into hashing function. - /// The custom hash function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Content hash code. - [CLSCompliant(false)] - public unsafe long BitwiseHashCode64(nuint count, long hash, Func hashFunction, bool salted = true) - { - if (IsNull) - ThrowNullPointerException(); - - return Intrinsics.GetHashCode64(value, checked(count * (nuint)sizeof(T)), hash, hashFunction, salted); - } - - /// - /// Bitwise comparison of two memory blocks. - /// - /// The pointer identifies block of memory to be compared. - /// The number of elements of type referenced by both pointers. - /// Comparison result which has the semantics as return type of . - [Obsolete("Use BitwiseCompare overload with native-sized integer parameter")] - public int BitwiseCompare(Pointer other, long count) => BitwiseCompare(other, new IntPtr(count)); - - /// - /// Bitwise comparison of two memory blocks. - /// - /// The pointer identifies block of memory to be compared. - /// The number of elements of type referenced by both pointers. - /// Comparison result which has the semantics as return type of . - public int BitwiseCompare(Pointer other, nint count) - => count >= 0 ? BitwiseCompare(other, (nuint)count) : throw new ArgumentOutOfRangeException(nameof(count)); - /// /// Bitwise comparison of two memory blocks. /// @@ -1167,6 +834,19 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) public static unsafe Pointer operator +(Pointer pointer, int offset) => pointer + (nint)offset; + /// + /// Adds an offset to the value of a pointer. + /// + /// + /// The offset specifies number of elements of type , not bytes. + /// + /// The pointer to add the offset to. + /// The offset to add. + /// A new pointer that reflects the addition of offset to pointer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Pointer operator checked +(Pointer pointer, int offset) + => checked(pointer + (nint)offset); + /// /// Adds an offset to the value of a pointer. /// @@ -1185,6 +865,24 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) return new(pointer.value + offset); } + /// + /// Adds an offset to the value of a pointer. + /// + /// + /// The offset specifies number of elements of type , not bytes. + /// + /// The pointer to add the offset to. + /// The offset to add. + /// A new pointer that reflects the addition of offset to pointer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Pointer operator checked +(Pointer pointer, nint offset) + { + if (pointer.IsNull) + ThrowNullPointerException(); + + return new(checked(pointer.value + offset)); + } + /// /// Subtracts an offset from the value of a pointer. /// @@ -1198,6 +896,19 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) public static unsafe Pointer operator -(Pointer pointer, int offset) => pointer - (nint)offset; + /// + /// Subtracts an offset from the value of a pointer. + /// + /// + /// The offset specifies number of elements of type , not bytes. + /// + /// The pointer to subtract the offset from. + /// The offset to subtract. + /// A new pointer that reflects the subtraction of offset from pointer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Pointer operator checked -(Pointer pointer, int offset) + => checked(pointer - (nint)offset); + /// /// Subtracts an offset from the value of a pointer. /// @@ -1216,6 +927,24 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) return new(pointer.value - offset); } + /// + /// Subtracts an offset from the value of a pointer. + /// + /// + /// The offset specifies number of elements of type , not bytes. + /// + /// The pointer to subtract the offset from. + /// The offset to subtract. + /// A new pointer that reflects the subtraction of offset from pointer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Pointer operator checked -(Pointer pointer, nint offset) + { + if (pointer.IsNull) + ThrowNullPointerException(); + + return new(checked(pointer.value - offset)); + } + /// /// Adds an offset to the value of a pointer. /// @@ -1227,7 +956,20 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) /// A new pointer that reflects the addition of offset to pointer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Pointer operator +(Pointer pointer, long offset) - => pointer + new IntPtr(offset); + => pointer + (nint)offset; + + /// + /// Adds an offset to the value of a pointer. + /// + /// + /// The offset specifies number of elements of type , not bytes. + /// + /// The pointer to add the offset to. + /// The offset to add. + /// A new pointer that reflects the addition of offset to pointer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Pointer operator checked +(Pointer pointer, long offset) + => checked(pointer + (nint)offset); /// /// Subtracts an offset from the value of a pointer. @@ -1240,7 +982,20 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) /// A new pointer that reflects the subtraction of offset from pointer. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe Pointer operator -(Pointer pointer, long offset) - => pointer - new IntPtr(offset); + => pointer - (nint)offset; + + /// + /// Subtracts an offset from the value of a pointer. + /// + /// + /// The offset specifies number of elements of type , not bytes. + /// + /// The pointer to subtract the offset from. + /// The offset to subtract. + /// A new pointer that reflects the subtraction of offset from pointer. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Pointer operator checked -(Pointer pointer, long offset) + => checked(pointer - (nint)offset); /// /// Adds an offset to the value of a pointer. @@ -1280,6 +1035,25 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) return new(pointer.value - offset); } + /// + /// Subtracts an offset from the value of a pointer. + /// + /// + /// The offset specifies number of elements of type , not bytes. + /// + /// The pointer to subtract the offset from. + /// The offset to subtract. + /// A new pointer that reflects the subtraction of offset from pointer. + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe Pointer operator checked -(Pointer pointer, nuint offset) + { + if (pointer.IsNull) + ThrowNullPointerException(); + + return new(checked(pointer.value - offset)); + } + /// /// Increments this pointer by 1 element of type . /// @@ -1287,6 +1061,13 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) /// A new pointer that reflects the addition of offset to pointer. public static Pointer operator ++(Pointer pointer) => pointer + (nuint)1; + /// + /// Increments this pointer by 1 element of type . + /// + /// The pointer to add the offset to. + /// A new pointer that reflects the addition of offset to pointer. + public static Pointer operator checked ++(Pointer pointer) => checked(pointer + (nuint)1); + /// /// Decrements this pointer by 1 element of type . /// @@ -1294,6 +1075,13 @@ public unsafe int BitwiseCompare(Pointer other, nuint count) /// A new pointer that reflects the subtraction of offset from pointer. public static Pointer operator --(Pointer pointer) => pointer - (nuint)1; + /// + /// Decrements this pointer by 1 element of type . + /// + /// The pointer to subtract the offset from. + /// A new pointer that reflects the subtraction of offset from pointer. + public static Pointer operator checked --(Pointer pointer) => checked(pointer - (nuint)1); + /// /// Indicates that the first pointer represents the same memory location as the second pointer. /// diff --git a/src/DotNext/BitwiseComparer.cs b/src/DotNext/BitwiseComparer.cs index 8655899c9..eb21d4d95 100644 --- a/src/DotNext/BitwiseComparer.cs +++ b/src/DotNext/BitwiseComparer.cs @@ -3,13 +3,14 @@ namespace DotNext; using static Runtime.Intrinsics; +using FNV1a32 = IO.Hashing.FNV1a32; /// /// Represents bitwise comparer for the arbitrary value type. /// /// The value type. public sealed class BitwiseComparer : IEqualityComparer, IComparer - where T : struct + where T : unmanaged { private BitwiseComparer() { @@ -36,16 +37,16 @@ private BitwiseComparer() /// The first value to check. /// The second value to check. /// , if both values are equal; otherwise, . - public static bool Equals(in T first, in TOther second) - where TOther : struct - => SizeOf() == SizeOf() && SizeOf() switch + public static unsafe bool Equals(in T first, in TOther second) + where TOther : unmanaged + => sizeof(T) == sizeof(TOther) && sizeof(T) switch { 0 => true, - sizeof(byte) => InToRef(first) == InToRef(second), - sizeof(ushort) => ReadUnaligned(ref InToRef(first)) == ReadUnaligned(ref InToRef(second)), - sizeof(uint) => ReadUnaligned(ref InToRef(first)) == ReadUnaligned(ref InToRef(second)), - sizeof(ulong) => ReadUnaligned(ref InToRef(first)) == ReadUnaligned(ref InToRef(second)), - _ => EqualsUnaligned(ref InToRef(first), ref InToRef(second), (nuint)SizeOf()), + sizeof(byte) => InToRef(in first) == InToRef(in second), + sizeof(ushort) => ReadUnaligned(ref InToRef(in first)) == ReadUnaligned(ref InToRef(in second)), + sizeof(uint) => ReadUnaligned(ref InToRef(in first)) == ReadUnaligned(ref InToRef(in second)), + sizeof(ulong) => ReadUnaligned(ref InToRef(in first)) == ReadUnaligned(ref InToRef(in second)), + _ => EqualsUnaligned(ref InToRef(in first), ref InToRef(in second), (nuint)SizeOf()), }; /// @@ -55,17 +56,27 @@ public static bool Equals(in T first, in TOther second) /// The first value to compare. /// The second value to compare. /// A value that indicates the relative order of the objects being compared. - public static int Compare(in T first, in TOther second) - where TOther : struct - => SizeOf() != SizeOf() ? SizeOf() - SizeOf() : SizeOf() switch + public static unsafe int Compare(in T first, in TOther second) + where TOther : unmanaged + { + var result = sizeof(T); + result = result.CompareTo(sizeof(TOther)); + + if (result is 0) { - 0 => 0, - sizeof(byte) => InToRef(first).CompareTo(InToRef(second)), - sizeof(ushort) => ReadUnaligned(ref InToRef(first)).CompareTo(ReadUnaligned(ref InToRef(second))), - sizeof(uint) => ReadUnaligned(ref InToRef(first)).CompareTo(ReadUnaligned(ref InToRef(second))), - sizeof(ulong) => ReadUnaligned(ref InToRef(first)).CompareTo(ReadUnaligned(ref InToRef(second))), - _ => CompareUnaligned(ref InToRef(first), ref InToRef(second), (nuint)SizeOf()), - }; + result = sizeof(T) switch + { + 0 => 0, + sizeof(byte) => InToRef(in first).CompareTo(InToRef(in second)), + sizeof(ushort) => ReadUnaligned(ref InToRef(in first)).CompareTo(ReadUnaligned(ref InToRef(in second))), + sizeof(uint) => ReadUnaligned(ref InToRef(in first)).CompareTo(ReadUnaligned(ref InToRef(in second))), + sizeof(ulong) => ReadUnaligned(ref InToRef(in first)).CompareTo(ReadUnaligned(ref InToRef(in second))), + _ => CompareUnaligned(ref InToRef(in first), ref InToRef(in second), (nuint)SizeOf()), + }; + } + + return result; + } /// /// Computes hash code for the structure content. @@ -73,27 +84,27 @@ public static int Compare(in T first, in TOther second) /// Value to be hashed. /// to include randomized salt data into hashing; to use data from memory only. /// Content hash code. - public static int GetHashCode(in T value, bool salted = true) + public static unsafe int GetHashCode(in T value, bool salted = true) { int hash; - switch (SizeOf()) + switch (sizeof(T)) { default: - return GetHashCode32Unaligned(ref InToRef(value), (nuint)SizeOf(), salted); + return FNV1a32.Hash(Span.AsReadOnlyBytes(in value), salted); case 0: hash = 0; break; case sizeof(byte): - hash = InToRef(value); + hash = InToRef(in value); break; case sizeof(ushort): - hash = ReadUnaligned(ref InToRef(value)); + hash = ReadUnaligned(ref InToRef(in value)); break; case sizeof(uint): - hash = ReadUnaligned(ref InToRef(value)); + hash = ReadUnaligned(ref InToRef(in value)); break; case sizeof(ulong): - hash = ReadUnaligned(ref InToRef(value)).GetHashCode(); + hash = ReadUnaligned(ref InToRef(in value)).GetHashCode(); break; } @@ -103,70 +114,6 @@ public static int GetHashCode(in T value, bool salted = true) return hash; } - private static void GetHashCodeUnaligned(in T value, ref THashFunction hashFunction, bool salted) - where THashFunction : struct, IConsumer - { - switch (SizeOf()) - { - default: - GetHashCode32Unaligned(ref hashFunction, ref InToRef(value), (nuint)SizeOf()); - break; - case 0: - break; - case sizeof(byte): - hashFunction.Invoke(InToRef(in value)); - break; - case sizeof(ushort): - hashFunction.Invoke(ReadUnaligned(ref InToRef(in value))); - break; - case sizeof(int): - hashFunction.Invoke(ReadUnaligned(ref InToRef(in value))); - break; - } - - if (salted) - hashFunction.Invoke(RandomExtensions.BitwiseHashSalt); - } - - /// - /// Computes bitwise hash code for the specified value. - /// - /// - /// This method doesn't use - /// even if it is overridden by value type. - /// - /// A value to be hashed. - /// Initial value of the hash. - /// Hashing function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Bitwise hash code. - public static int GetHashCode(in T value, int hash, Func hashFunction, bool salted = true) - { - var fn = new Accumulator(hashFunction, hash); - GetHashCodeUnaligned(in value, ref fn, salted); - return fn.Invoke(); - } - - /// - /// Computes bitwise hash code for the specified value. - /// - /// - /// This method doesn't use - /// even if it is overridden by value type. - /// - /// The type of the hash algorithm. - /// A value to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// Bitwise hash code. - [CLSCompliant(false)] - public static int GetHashCode(in T value, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - { - var hash = new THashFunction(); - GetHashCodeUnaligned(in value, ref hash, salted); - return hash.Invoke(); - } - /// bool IEqualityComparer.Equals(T x, T y) => Equals(in x, in y); diff --git a/src/DotNext/Buffers/PooledArrayBufferWriter.cs b/src/DotNext/Buffers/PooledArrayBufferWriter.cs index 375cb6150..7ba83cd6e 100644 --- a/src/DotNext/Buffers/PooledArrayBufferWriter.cs +++ b/src/DotNext/Buffers/PooledArrayBufferWriter.cs @@ -118,17 +118,16 @@ void IList.Insert(int index, T item) public void Insert(int index, ReadOnlySpan items) { ThrowIfDisposed(); - if ((uint)index > (uint)position) - throw new ArgumentOutOfRangeException(nameof(index)); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, (uint)position, nameof(index)); if (items.IsEmpty) goto exit; - if (GetLength(buffer) is 0) + if (buffer.GetLength() is 0U) { buffer = pool.Rent(items.Length); } - else if (position + items.Length <= GetLength(buffer)) + else if ((uint)position + (uint)items.Length <= buffer.GetLength()) { CopyFast(buffer, index, buffer, index + items.Length, position - index); } @@ -160,14 +159,13 @@ public void Insert(int index, ReadOnlySpan items) public void Overwrite(int index, ReadOnlySpan items) { ThrowIfDisposed(); - if ((uint)index > (uint)position) - throw new ArgumentOutOfRangeException(nameof(index)); + ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, (uint)position, nameof(index)); - if (GetLength(buffer) is 0) + if (buffer.GetLength() is 0U) { buffer = pool.Rent(items.Length); } - else if (index + items.Length <= GetLength(buffer)) + else if ((uint)index + (uint)items.Length <= buffer.GetLength()) { if (RuntimeHelpers.IsReferenceOrContainsReferences()) Array.Clear(buffer, index, position - index); @@ -255,14 +253,14 @@ public override void Clear(bool reuseBuffer = false) { ThrowIfDisposed(); - if (GetLength(buffer) is 0) + if (buffer.GetLength() is 0) { // nothing to do } else if (!reuseBuffer) { ReturnBuffer(); - buffer = Array.Empty(); + buffer = []; } else if (RuntimeHelpers.IsReferenceOrContainsReferences()) { @@ -360,17 +358,16 @@ public override void AddAll(ICollection items) public void RemoveLast(int count) { ThrowIfDisposed(); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); + ArgumentOutOfRangeException.ThrowIfNegative(count); - if (GetLength(buffer) is 0) + if (buffer.GetLength() is 0) { // nothing to do } else if (count >= position) { ReturnBuffer(); - buffer = Array.Empty(); + buffer = []; position = 0; } else if (count > 0) @@ -394,17 +391,16 @@ public void RemoveLast(int count) public void RemoveFirst(int count) { ThrowIfDisposed(); - if (count < 0) - throw new ArgumentOutOfRangeException(nameof(count)); + ArgumentOutOfRangeException.ThrowIfNegative(count); - if (GetLength(buffer) is 0) + if (buffer.GetLength() is 0) { // nothing to do } else if (count >= position) { ReturnBuffer(); - buffer = Array.Empty(); + buffer = []; position = 0; } else if (count > 0) @@ -448,7 +444,7 @@ private static void CopyFast(T[] source, int sourceIndex, T[] destination, int d private protected override void Resize(int newSize) { var newBuffer = pool.Rent(newSize); - if (GetLength(buffer) > 0) + if (buffer.GetLength() > 0U) { CopyFast(buffer, newBuffer, position); ReturnBuffer(); @@ -463,7 +459,7 @@ protected override void Dispose(bool disposing) { if (disposing) { - if (GetLength(buffer) > 0) + if (buffer.GetLength() > 0U) { ReturnBuffer(); buffer = []; diff --git a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs index efd7ec4d6..b3c10bb80 100644 --- a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs +++ b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs @@ -88,7 +88,7 @@ private void GetOrderableDynamicPartitions(IEnumerator>[] { unsafe { - partitions.ForEach(&CreatePartition, GetOrderableDynamicPartitions()); + partitions.AsSpan().ForEach(&CreatePartition, GetOrderableDynamicPartitions()); } static void CreatePartition(ref IEnumerator> partition, IEnumerable> partitions) @@ -97,8 +97,7 @@ static void CreatePartition(ref IEnumerator> partition, IE public override IList>> GetOrderablePartitions(int partitionCount) { - if (partitionCount <= 0) - throw new ArgumentOutOfRangeException(nameof(partitionCount)); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(partitionCount); var partitions = new IEnumerator>[partitionCount]; @@ -115,8 +114,7 @@ public override IEnumerable> GetOrderableDynamicPartitions public override IList> GetPartitions(int partitionCount) { - if (partitionCount <= 0) - throw new ArgumentOutOfRangeException(nameof(partitionCount)); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(partitionCount); var partitions = new IEnumerator[partitionCount]; var (quotient, remainder) = Math.DivRem(sequence.Length, partitions.Length); @@ -153,5 +151,5 @@ public static class ReadOnlySequencePartitioner /// /// The partitioner for the sequence. public static OrderablePartitioner CreatePartitioner(this in ReadOnlySequence sequence, bool splitOnSegments = false) - => sequence.IsEmpty ? Partitioner.Create(Array.Empty(), splitOnSegments) : new ReadOnlySequencePartitioner(in sequence, splitOnSegments); + => sequence.IsEmpty ? Partitioner.Create([], splitOnSegments) : new ReadOnlySequencePartitioner(in sequence, splitOnSegments); } \ No newline at end of file diff --git a/src/DotNext/Collections/Generic/Collection.Buffer.cs b/src/DotNext/Collections/Generic/Collection.Buffer.cs index cf19a7317..1ce347a28 100644 --- a/src/DotNext/Collections/Generic/Collection.Buffer.cs +++ b/src/DotNext/Collections/Generic/Collection.Buffer.cs @@ -28,8 +28,8 @@ public static MemoryOwner Copy(this IEnumerable enumerable, int sizeHin return enumerable switch { List typedList => Span.Copy(CollectionsMarshal.AsSpan(typedList), allocator), - T[] array => Span.Copy(array, allocator), - string str => ReinterpretCast, MemoryOwner>(str.AsSpan().Copy(Unsafe.As>(allocator))), + T[] array => Span.Copy(array, allocator), + string str => Unsafe.BitCast, MemoryOwner>(str.AsSpan().Copy(Unsafe.As>(allocator))), ArraySegment segment => Span.Copy(segment.AsSpan(), allocator), ICollection collection => collection.Count == 0 ? default : allocator is null ? CopyCollection(collection) : CopySlow(collection, collection.Count, allocator), IReadOnlyCollection collection => collection.Count == 0 ? default : CopySlow(enumerable, collection.Count, allocator), diff --git a/src/DotNext/Collections/Generic/Collection.cs b/src/DotNext/Collections/Generic/Collection.cs index b40a35944..a98853a40 100644 --- a/src/DotNext/Collections/Generic/Collection.cs +++ b/src/DotNext/Collections/Generic/Collection.cs @@ -175,8 +175,8 @@ public static bool ElementAt(this IEnumerable collection, int index, [Mayb { return collection switch { - List list => Span.ElementAt(CollectionsMarshal.AsSpan(list), index, out element), - T[] array => Span.ElementAt(array, index, out element), + List list => Span.ElementAt(CollectionsMarshal.AsSpan(list), index, out element), + T[] array => Span.ElementAt(array, index, out element), LinkedList list => NodeValueAt(list, index, out element), IList list => ListElementAt(list, index, out element), IReadOnlyList readOnlyList => ReadOnlyListElementAt(readOnlyList, index, out element), diff --git a/src/DotNext/Collections/Specialized/ConcurrentTypeMap.Enumerator.cs b/src/DotNext/Collections/Specialized/ConcurrentTypeMap.Enumerator.cs index f0d816ea7..9fae17d55 100644 --- a/src/DotNext/Collections/Specialized/ConcurrentTypeMap.Enumerator.cs +++ b/src/DotNext/Collections/Specialized/ConcurrentTypeMap.Enumerator.cs @@ -14,13 +14,13 @@ public partial class ConcurrentTypeMap public struct Enumerator { private readonly Entry[] entries; - private nint index; + private nuint index; private TValue? current; internal Enumerator(Entry[] entries) { this.entries = entries; - index = -1; + index = nuint.MaxValue; current = default; } @@ -37,10 +37,10 @@ public bool MoveNext() { if (entries is not null) { - for (nint nextIndex; ;) + for (nuint nextIndex; ;) { - nextIndex = index + 1; - if (nextIndex >= GetLength(entries)) + nextIndex = index + 1U; + if (nextIndex >= entries.GetLength()) break; index = nextIndex; diff --git a/src/DotNext/Collections/Specialized/InvocationList.cs b/src/DotNext/Collections/Specialized/InvocationList.cs index 1fa16fd2d..88a97cfeb 100644 --- a/src/DotNext/Collections/Specialized/InvocationList.cs +++ b/src/DotNext/Collections/Specialized/InvocationList.cs @@ -1,6 +1,7 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; +using DotNext.Collections.Generic; using Unsafe = System.Runtime.CompilerServices.Unsafe; namespace DotNext.Collections.Specialized; @@ -88,7 +89,12 @@ public bool MoveNext() public InvocationList(TDelegate d) => list = d; private InvocationList(TDelegate[] array, TDelegate d) - => list = (TDelegate[])[.. array, d]; + { + var list = new TDelegate[array.Length + 1]; + array.CopyTo(list.AsSpan()); + list[^1] = d; + this.list = list; + } private InvocationList(TDelegate d1, TDelegate d2) => list = new TDelegate[] { d1, d2 }; @@ -133,10 +139,10 @@ public InvocationList Remove(TDelegate? d) else { var array = Unsafe.As(list); - long index = Array.IndexOf(array, d); + var index = Array.IndexOf(array, d); - if (index >= 0L) - array = OneDimensionalArray.RemoveAt(array, index); + if (index >= 0) + array = DotNext.Span.ConcatToArray(array.AsSpan(0, index), array.AsSpan(index + 1)); result = new(array); } diff --git a/src/DotNext/Collections/Specialized/TypeMap.Enumerator.cs b/src/DotNext/Collections/Specialized/TypeMap.Enumerator.cs index 42fded984..53dc33ca3 100644 --- a/src/DotNext/Collections/Specialized/TypeMap.Enumerator.cs +++ b/src/DotNext/Collections/Specialized/TypeMap.Enumerator.cs @@ -15,12 +15,12 @@ public partial class TypeMap public struct Enumerator { private readonly Entry[] entries; - private nint index; + private nuint index; internal Enumerator(Entry[] entries) { this.entries = entries; - index = -1; + index = nuint.MaxValue; } /// @@ -37,10 +37,10 @@ public bool MoveNext() { if (entries is not null) { - for (nint nextIndex; ;) + for (nuint nextIndex; ;) { - nextIndex = index + 1; - if (nextIndex >= GetLength(entries)) + nextIndex = index + 1U; + if (nextIndex >= entries.GetLength()) break; index = nextIndex; diff --git a/src/DotNext/Disposable.cs b/src/DotNext/Disposable.cs index 67dacf5fd..c102d5e28 100644 --- a/src/DotNext/Disposable.cs +++ b/src/DotNext/Disposable.cs @@ -174,7 +174,7 @@ public static async ValueTask DisposeAsync(IEnumerable object /// An array of objects to dispose. public static void Dispose(params IDisposable?[] objects) { - for (nint i = 0; i < GetLength(objects); i++) + for (nuint i = 0; i < objects.GetLength(); i++) objects[i]?.Dispose(); } diff --git a/src/DotNext/EqualityComparerBuilder.cs b/src/DotNext/EqualityComparerBuilder.cs index c810be1de..5c688169f 100644 --- a/src/DotNext/EqualityComparerBuilder.cs +++ b/src/DotNext/EqualityComparerBuilder.cs @@ -9,6 +9,7 @@ namespace DotNext; using Collections.Generic; using Reflection; using Intrinsics = Runtime.Intrinsics; +using FNV1a32 = IO.Hashing.FNV1a32; /// /// Generates hash code and equality check functions for the particular type. @@ -21,6 +22,7 @@ namespace DotNext; public readonly struct EqualityComparerBuilder { private const BindingFlags PublicStaticFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly; + private const BindingFlags NonPublicStaticFlags = BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly; private readonly IReadOnlySet? excludedFields; @@ -134,8 +136,8 @@ private static MethodInfo EqualsMethodForArrayElementType(Type itemType) if (itemType.IsUnmanaged()) { var arrayType = Type.MakeGenericMethodParameter(0).MakeArrayType(); - return typeof(OneDimensionalArray) - .GetMethod(nameof(OneDimensionalArray.BitwiseEquals), 1, PublicStaticFlags, null, new[] { arrayType, arrayType }, null)! + return typeof(Span) + .GetMethod(nameof(Span.BitwiseEquals), 1, NonPublicStaticFlags, null, [arrayType, arrayType], null)! .MakeGenericMethod(itemType); } @@ -163,8 +165,8 @@ private static MethodInfo HashCodeMethodForArrayElementType(Type itemType) if (itemType.IsUnmanaged()) { var arrayType = Type.MakeGenericMethodParameter(0).MakeArrayType(); - return typeof(OneDimensionalArray) - .GetMethod(nameof(OneDimensionalArray.BitwiseHashCode), 1, PublicStaticFlags, null, [arrayType, typeof(bool)], null)! + return typeof(FNV1a32) + .GetMethod(nameof(FNV1a32.Hash), 1, NonPublicStaticFlags, null, [arrayType, typeof(bool)], null)! .MakeGenericMethod(itemType); } diff --git a/src/DotNext/IO/Hashing/FNV1a32.cs b/src/DotNext/IO/Hashing/FNV1a32.cs index 3fefbd34c..5b1f2721d 100644 --- a/src/DotNext/IO/Hashing/FNV1a32.cs +++ b/src/DotNext/IO/Hashing/FNV1a32.cs @@ -5,4 +5,7 @@ namespace DotNext.IO.Hashing; /// public sealed class FNV1a32(bool salted = false) : FNV1a(salted) { + internal static int Hash(T[] array, bool salted) + where T : unmanaged + => Hash(new ReadOnlySpan(array), salted); } \ No newline at end of file diff --git a/src/DotNext/OneDimensionalArray.cs b/src/DotNext/OneDimensionalArray.cs deleted file mode 100644 index abc2c9e5f..000000000 --- a/src/DotNext/OneDimensionalArray.cs +++ /dev/null @@ -1,473 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace DotNext; - -using Intrinsics = Runtime.Intrinsics; - -/// -/// Provides specialized methods to work with one-dimensional array. -/// -public static class OneDimensionalArray -{ - private struct RemovalCounter : IConsumer - { - internal long Count; - - void IConsumer.Invoke(T item) => Count += 1; - } - - /// - /// Concatenates the array with the specified span of elements. - /// - /// The type of elements in the array. - /// The array to concatenate. - /// The tail of concatenation. - /// The starting index in at which should be inserted. - /// The array representing all elements from up to exclusively including elements from . - /// is less than 0 or greater than length of array. - public static T[] Concat(this T[] left, T[] right, long startIndex) - { - if ((ulong)startIndex > (ulong)left.LongLength) - throw new ArgumentOutOfRangeException(nameof(startIndex)); - - var result = new T[startIndex + right.LongLength]; - Array.Copy(left, result, startIndex); - Array.Copy(right, 0L, result, startIndex, right.Length); - return result; - } - - /// - /// Applies specific action to each array element. - /// - /// - /// This method support modification of array elements - /// because each array element is passed by reference into action. - /// - /// Type of array elements. - /// An array to iterate. - /// An action to be applied for each element. - public static void ForEach(this T[] array, RefAction action) - { - for (nint i = 0; i < Intrinsics.GetLength(array); i++) - action(ref array[i], i); - } - - /// - /// Applies specific action to each array element. - /// - /// - /// This method support modification of array elements - /// because each array element is passed by reference into action. - /// - /// Type of array elements. - /// The type of the argument to be passed to the action. - /// An array to iterate. - /// An action to be applied for each element. - /// The argument to be passed to the action. - /// is zero. - [CLSCompliant(false)] - public static unsafe void ForEach(this T[] array, delegate* action, TArg arg) - { - if (action is null) - throw new ArgumentNullException(nameof(action)); - - for (nint i = 0; i < Intrinsics.GetLength(array); i++) - action(ref array[i], arg); - } - - /// - /// Insert a new element into array and return modified array. - /// - /// Type of array elements. - /// Source array. Cannot be . - /// The object to insert. - /// The zero-based index at which item should be inserted. - /// A modified array with inserted element. - public static T[] Insert(this T[] array, T element, long index) - { - var length = Intrinsics.GetLength(array); - if ((ulong)index > (ulong)length) - throw new ArgumentOutOfRangeException(nameof(index)); - - T[] result; - if (length is 0) - { - result = new[] { element }; - } - else - { - result = new T[length + 1]; - Array.Copy(array, 0, result, 0, Math.Min(index + 1, length)); - Array.Copy(array, index, result, index + 1, length - index); - result[index] = element; - } - - return result; - } - - /// - /// Insert a new element into array and return modified array. - /// - /// Type of array elements. - /// Source array. Cannot be . - /// The object to insert. - /// The zero-based index at which item should be inserted. - /// A modified array with inserted element. - public static T[] Insert(this T[] array, T element, Index index) - => Insert(array, element, (long)index.GetOffset(array.Length)); - - /// - /// Removes the element at the specified in the array and returns modified array. - /// - /// Source array. Cannot be . - /// The zero-based index of the element to remove. - /// Type of array elements. - /// A modified array with removed element. - /// is incorrect. - public static T[] RemoveAt(this T[] array, long index) - { - var length = Intrinsics.GetLength(array); - if ((ulong)index >= (ulong)length) - throw new ArgumentOutOfRangeException(nameof(index)); - - if (length is 1) - return Array.Empty(); - - var newStore = new T[length - 1]; - Array.Copy(array, 0L, newStore, 0L, index); - Array.Copy(array, index + 1L, newStore, index, length - index - 1L); - return newStore; - } - - /// - /// Removes the element at the specified in the array and returns modified array. - /// - /// Source array. Cannot be . - /// The zero-based index of the element to remove. - /// Type of array elements. - /// A modified array with removed element. - /// is incorrect. - public static T[] RemoveAt(this T[] array, Index index) - => RemoveAt(array, (long)index.GetOffset(array.Length)); - - private static T[] RemoveAll(T[] array, TPredicate condition, ref TConsumer callback) - where TPredicate : struct, ISupplier - where TConsumer : struct, IConsumer - { - var length = Intrinsics.GetLength(array); - if (length == 0) - return array; - - nint newLength = 0; - var tempArray = new T[length]; - foreach (var item in array) - { - if (condition.Invoke(item)) - callback.Invoke(item); - else - tempArray[newLength++] = item; - } - - if (length - newLength == 0) - return array; - - if (newLength == 0) - return Array.Empty(); - - array = new T[newLength]; - Array.Copy(tempArray, 0L, array, 0L, newLength); - return array; - } - - private static T[] RemoveAll(T[] array, TPredicate condition, out long count) - where TPredicate : struct, ISupplier - { - var counter = new RemovalCounter(); - var result = RemoveAll(array, condition, ref counter); - count = counter.Count; - return result; - } - - /// - /// Removes all the elements that match the conditions defined by the specified predicate. - /// - /// The type of the elements in array. - /// Source array. Cannot be . - /// The predicate that defines the conditions of the elements to remove. - /// The number of elements removed from this list. - /// A modified array with removed elements. - public static T[] RemoveAll(this T[] array, Predicate match, out long count) - => RemoveAll>(array, match, out count); - - /// - /// Removes all the elements that match the conditions defined by the specified predicate. - /// - /// The type of the elements in array. - /// Source array. Cannot be . - /// The predicate that defines the conditions of the elements to remove. - /// The number of elements removed from this list. - /// A modified array with removed elements. - [CLSCompliant(false)] - public static unsafe T[] RemoveAll(this T[] array, delegate* match, out long count) - => RemoveAll>(array, match, out count); - - /// - /// Removes all the elements that match the conditions defined by the specified predicate. - /// - /// The type of the elements in array. - /// Source array. Cannot be . - /// The predicate that defines the conditions of the elements to remove. - /// The delegate that is used to accept removed items. - /// A modified array with removed elements. - public static T[] RemoveAll(this T[] array, Predicate match, Action callback) - { - var action = new DelegatingConsumer(callback); - return RemoveAll(array, new DelegatingPredicate(match), ref action); - } - - /// - /// Removes all the elements that match the conditions defined by the specified predicate. - /// - /// The type of the elements in array. - /// Source array. Cannot be . - /// The predicate that defines the conditions of the elements to remove. - /// The delegate that is used to accept removed items. - /// A modified array with removed elements. - [CLSCompliant(false)] - public static unsafe T[] RemoveAll(this T[] array, delegate* match, Action callback) - { - var action = new DelegatingConsumer(callback); - return RemoveAll(array, new Supplier(match), ref action); - } - - /// - /// Removes the specified number of elements from the beginning of the array. - /// - /// Type of array elements. - /// Source array. - /// A number of elements to be removed. - /// Modified array. - public static T[] RemoveFirst(this T[] input, long count) - { - if (count == 0L) - return input; - - var length = Intrinsics.GetLength(input); - if (count >= length) - return Array.Empty(); - - var result = new T[length - count]; - Array.Copy(input, count, result, 0, result.LongLength); - return result; - } - - /// - /// Returns sub-array. - /// - /// Input array. Cannot be . - /// The index at which to begin this slice. - /// The desired length for the slice. - /// Type of array elements. - /// A new sliced array. - public static T[] Slice(this T[] input, long startIndex, long length) - { - if (startIndex >= Intrinsics.GetLength(input) || length == 0L) - return Array.Empty(); - - if (startIndex == 0 && length == input.Length) - return input; - - length = Math.Min(input.LongLength - startIndex, length); - var result = new T[length]; - Array.Copy(input, startIndex, result, 0, length); - return result; - } - - /// - /// Computes view over the specified array. - /// - /// The type of array elements. - /// The array instance. - /// The range in the array to return. - /// The range in . - public static ArraySegment Slice(this T[] input, Range range) - { - var (start, length) = range.GetOffsetAndLength(input.Length); - return new(input, start, length); - } - - /// - /// Removes the specified number of elements from the end of the array. - /// - /// Type of array elements. - /// Source array. - /// A number of elements to be removed. - /// Modified array. - public static T[] RemoveLast(this T[] input, long count) - { - if (count == 0L) - return input; - - var length = Intrinsics.GetLength(input); - if (count >= length) - return Array.Empty(); - - var result = new T[length - count]; - Array.Copy(input, result, result.LongLength); - return result; - } - - /// - /// Determines whether two arrays contain the same set of bits. - /// - /// - /// This method performs bitwise equality between each pair of elements. - /// - /// Type of array elements. Should be unmanaged value type. - /// First array for equality check. - /// Second array of equality check. - /// , if both arrays are equal; otherwise, . - public static bool BitwiseEquals(this T[]? first, T[]? second) - where T : unmanaged - => Span.BitwiseEquals(new ReadOnlySpan(first), new ReadOnlySpan(second)); - - /// - /// Computes bitwise hash code for the array content. - /// - /// The type of array elements. - /// The array to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the array content. - public static int BitwiseHashCode(this T[] array, bool salted = true) - where T : unmanaged - => Span.BitwiseHashCode(new ReadOnlySpan(array), salted); - - /// - /// Computes bitwise hash code for the array content using custom hash function. - /// - /// The type of array elements. - /// The array to be hashed. - /// Initial value of the hash. - /// Custom hashing algorithm. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the array content. - public static int BitwiseHashCode(this T[] array, int hash, Func hashFunction, bool salted = true) - where T : unmanaged - => Span.BitwiseHashCode(new ReadOnlySpan(array), hash, hashFunction, salted); - - /// - /// Computes bitwise hash code for the array content using custom hash function. - /// - /// The type of array elements. - /// The type of the hash algorithm. - /// The array to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the array content. - [CLSCompliant(false)] - public static int BitwiseHashCode(this T[] array, bool salted = true) - where T : unmanaged - where THashFunction : struct, IConsumer, ISupplier - => Span.BitwiseHashCode(new ReadOnlySpan(array), salted); - - /// - /// Computes bitwise hash code for the array content using custom hash function. - /// - /// The type of array elements. - /// The array to be hashed. - /// Initial value of the hash. - /// Custom hashing algorithm. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the array content. - public static long BitwiseHashCode64(this T[] array, long hash, Func hashFunction, bool salted = true) - where T : unmanaged - => Span.BitwiseHashCode64(new ReadOnlySpan(array), hash, hashFunction, salted); - - /// - /// Computes bitwise hash code for the array content using custom hash function. - /// - /// The type of array elements. - /// The type of the hash algorithm. - /// The array to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the array content. - [CLSCompliant(false)] - public static long BitwiseHashCode64(this T[] array, bool salted = true) - where T : unmanaged - where THashFunction : struct, IConsumer, ISupplier - => Span.BitwiseHashCode64(new ReadOnlySpan(array), salted); - - /// - /// Computes bitwise hash code for the array content. - /// - /// The type of array elements. - /// The array to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the array content. - public static long BitwiseHashCode64(this T[] array, bool salted = true) - where T : unmanaged - => Span.BitwiseHashCode64(new ReadOnlySpan(array), salted); - - private sealed class ArrayEqualityComparer - { - private readonly object?[] first, second; - - internal ArrayEqualityComparer(object?[] first, object?[] second) - { - this.first = first; - this.second = second; - } - - internal void Iteration(long index, ParallelLoopState state) - { - if (!(state.ShouldExitCurrentIteration || Equals(first[index], second[index]))) - state.Break(); - } - } - - /// - /// Determines whether two arrays contain the same set of elements. - /// - /// - /// This method calls for each element type. - /// - /// The first array to compare. - /// The second array to compare. - /// to perform parallel iteration over array elements; to perform sequential iteration. - /// , if both arrays are equal; otherwise, . - public static bool SequenceEqual(this object?[]? first, object?[]? second, bool parallel = false) - { - static bool EqualsSequential(object?[] first, object?[] second) - { - for (nint i = 0; i < Intrinsics.GetLength(first); i++) - { - if (!Equals(first[i], second[i])) - return false; - } - - return true; - } - - static bool EqualsParallel(object?[] first, object?[] second) - => Parallel.For(0L, first.LongLength, new ArrayEqualityComparer(first, second).Iteration).IsCompleted; - - if (ReferenceEquals(first, second)) - return true; - if (first is null) - return second is null; - if (second is null || Intrinsics.GetLength(first) != Intrinsics.GetLength(second)) - return false; - - return parallel ? EqualsParallel(first, second) : EqualsSequential(first, second); - } - - /// - /// Compares content of the two arrays. - /// - /// The type of array elements. - /// The first array to compare. - /// The second array to compare. - /// Comparison result. - public static int BitwiseCompare(this T[]? first, T[]? second) - where T : unmanaged - => Span.BitwiseCompare(new ReadOnlySpan(first), new ReadOnlySpan(second)); -} \ No newline at end of file diff --git a/src/DotNext/Runtime/Intrinsics.cs b/src/DotNext/Runtime/Intrinsics.cs index 2e93b2d82..ebf40e7b0 100644 --- a/src/DotNext/Runtime/Intrinsics.cs +++ b/src/DotNext/Runtime/Intrinsics.cs @@ -17,36 +17,6 @@ namespace DotNext.Runtime; [EditorBrowsable(EditorBrowsableState.Advanced)] public static class Intrinsics { - [StructLayout(LayoutKind.Auto)] - private struct FNV1a32 : IConsumer - { - private const int Offset = unchecked((int)2166136261); - private const int Prime = 16777619; - - private int result; - - public FNV1a32() => result = Offset; - - internal readonly int Result => result; - - public void Invoke(int data) => result = (result ^ data) * Prime; - } - - [StructLayout(LayoutKind.Auto)] - private struct FNV1a64 : IConsumer - { - private const long Offset = unchecked((long)14695981039346656037); - private const long Prime = 1099511628211; - - private long result; - - public FNV1a64() => result = Offset; - - internal readonly long Result => result; - - public void Invoke(long data) => result = (result ^ data) * Prime; - } - /// /// Provides the fast way to check whether the specified type accepts value as valid value. /// @@ -64,68 +34,12 @@ public static bool IsNullable() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ref TTo InToRef(in TFrom source) + internal static ref TTo InToRef(ref readonly TFrom source) { PushInRef(in source); return ref ReturnRef(); } - /// - /// Returns default value of the given type. - /// - /// - /// This method helps to avoid generation of temporary variables - /// necessary for default keyword implementation. - /// - /// The type for which default value should be obtained. - /// The default value of type . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Use default keyword in C# instead")] - public static T? DefaultOf() => default; - - /// - /// Obtain a value of type by - /// reinterpreting the object representation of . - /// - /// - /// Every bit in the value representation of the returned object - /// is equal to the corresponding bit in the object representation of . - /// The values of padding bits in the returned object are unspecified. - /// The method takes into account size of and types - /// and able to provide conversion between types of different size. However, the result may vary between - /// CPU architectures if size of types is different. - /// - /// A value to convert. - /// Conversion result. - /// The value type to be converted. - /// The type of output struct. - public static void Bitcast(in T input, out TResult output) - where T : unmanaged - where TResult : unmanaged - { - // ldobj/stobj pair is used instead of cpobj because this instruction - // has unspecified behavior if src is not assignable to dst, ECMA-335 III.4.4 - const string slowPath = "slow"; - PushOutRef(out output); - Sizeof(); - Sizeof(); - Blt_Un(slowPath); - - // copy from input into output as-is - PushInRef(in input); - Unaligned(1); - Ldobj(); - Stobj(); - Ret(); - - MarkLabel(slowPath); - PushInRef(in input); - Ldobj(); - Unaligned(1); - Stobj(); - Ret(); - } - /// /// Indicates that specified value type is the default value. /// @@ -135,7 +49,7 @@ public static void Bitcast(in T input, out TResult output) public static bool IsDefault(in T value) => Unsafe.SizeOf() switch { 0 => true, - sizeof(byte) => InToRef(value) is 0, + sizeof(byte) => InToRef(in value) is 0, sizeof(ushort) => Unsafe.ReadUnaligned(ref InToRef(value)) is 0, sizeof(uint) => Unsafe.ReadUnaligned(ref InToRef(value)) is 0U, sizeof(ulong) => Unsafe.ReadUnaligned(ref InToRef(value)) is 0UL, @@ -154,24 +68,6 @@ public static RuntimeTypeHandle TypeOf() return Return(); } - /// - /// Determines whether one or more bit fields are set in the given value. - /// - /// The enum type. - /// The value to check. - /// An enumeration value. - /// if the bit field or bit fields that are set in are also set in ; otherwise, . - public static bool HasFlag(T value, T flag) - where T : struct, Enum => Unsafe.SizeOf() switch - { - 0 => true, - sizeof(byte) => (ReinterpretCast(value) & ReinterpretCast(flag)) is not 0, - sizeof(ushort) => (ReinterpretCast(value) & ReinterpretCast(flag)) is not 0, - sizeof(uint) => (ReinterpretCast(value) & ReinterpretCast(flag)) is not 0U, - sizeof(ulong) => (ReinterpretCast(value) & ReinterpretCast(flag)) is not 0UL, - _ => value.HasFlag(flag), - }; - /// /// Provides unified behavior of type cast for reference and value types. /// @@ -218,7 +114,7 @@ public static unsafe int PointerHashCode([In] void* pointer) /// The object whose address is obtained. /// An address of the given object. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint AddressOf(in T value) + public static nint AddressOf(ref readonly T value) { PushInRef(in value); Conv_I(); @@ -253,18 +149,6 @@ internal static int CompareUnaligned(ref byte first, ref byte second, nuint leng return comparison; } - /// - /// Bitwise comparison of two memory blocks. - /// - /// The pointer to the first memory block. - /// The pointer to the second memory block. - /// The length of the first and second memory blocks. - /// Comparison result which has the semantics as return type of . - [CLSCompliant(false)] - [Obsolete("Use Compare overload that accepts the length as unsigned integer")] - public static unsafe int Compare([In] void* first, [In] void* second, nint length) - => length >= 0 ? Compare(first, second, (nuint)length) : throw new ArgumentOutOfRangeException(nameof(length)); - /// /// Bitwise comparison of two memory blocks. /// @@ -288,18 +172,6 @@ internal static bool EqualsUnaligned(ref byte first, ref byte second, nuint leng return true; } - /// - /// Computes equality between two blocks of memory. - /// - /// A pointer to the first memory block. - /// A pointer to the second memory block. - /// Length of first and second memory blocks, in bytes. - /// , if both memory blocks have the same data; otherwise, . - [Obsolete("Use Compare overload that accepts the length as unsigned integer")] - [CLSCompliant(false)] - public static unsafe bool Equals([In] void* first, [In] void* second, nint length) - => length >= 0 ? Equals(first, second, (nuint)length) : throw new ArgumentOutOfRangeException(nameof(length)); - /// /// Computes equality between two blocks of memory. /// @@ -311,42 +183,6 @@ public static unsafe bool Equals([In] void* first, [In] void* second, nint lengt public static unsafe bool Equals([In] void* first, [In] void* second, nuint length) => EqualsUnaligned(ref Unsafe.AsRef(first), ref Unsafe.AsRef(second), length); - /// - /// Allows to reinterpret managed pointer to array element. - /// - /// The type of array elements. - /// The requested type. - /// The array object. - /// The index of the array element. - /// The reference to the array element with restricted mutability. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [Obsolete("Can be replaced with regular indexer applied to the array and ObjectExtensions.As() method")] - public static ref readonly TBase GetReadonlyRef(this T[] array, nint index) - where T : class, TBase - { - Push(array); - Push(index); - Readonly(); - Ldelema(); - return ref ReturnRef(); - } - - /// - /// Throws if given managed pointer is . - /// - /// The managed pointer to check. - /// The type of the managed pointer. - /// pointer is . - public static void ThrowIfNull(in T value) - { - if (Unsafe.IsNullRef(ref Unsafe.AsRef(in value))) - Throw(); - - [StackTraceHidden] - [DoesNotReturn] - static void Throw() => throw new NullReferenceException(); - } - /// /// Copies one value into another. /// @@ -371,7 +207,6 @@ public static void Copy(in T input, out T output) /// The reference to the destination location. [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] - [SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1107", Justification = "Unaligned is a prefix instruction")] public static unsafe void CopyUnaligned([In] T* input, [Out] T* output) where T : unmanaged => Unsafe.WriteUnaligned(output, Unsafe.ReadUnaligned(input)); @@ -397,23 +232,6 @@ private static void Copy([In] ref byte source, [In] ref byte destination, nuint } } - /// - /// Copies the specified number of elements from source address to the destination address. - /// - /// The address of the bytes to copy. - /// The target address. - /// The number of elements to copy. - /// The type of the element. - [Obsolete("Use Copy overload that accepts the length as unsigned integer")] - public static unsafe void Copy(in T source, out T destination, long count) - where T : unmanaged - { - if (count < 0L) - throw new ArgumentOutOfRangeException(nameof(count)); - - Copy(in source, out destination, (nuint)count); - } - /// /// Copies the specified number of elements from source address to the destination address. /// @@ -451,22 +269,6 @@ public static unsafe void Swap(T* first, T* second) where T : unmanaged => Swap(ref first[0], ref second[0]); - /// - /// Indicates that two managed pointers are equal. - /// - /// Type of managed pointer. - /// The first managed pointer. - /// The second managed pointer. - /// , if both managed pointers are equal; otherwise, . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool AreSame(in T first, in T second) // TODO: Replace 'in' with 'ref readonly' in C# 12 - { - PushInRef(in first); - PushInRef(in second); - Ceq(); - return Return(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe ref byte Advance(this ref byte ptr) where T : unmanaged @@ -516,397 +318,6 @@ private static unsafe bool IsZero([In] ref byte address, nuint length) return result; } - /// - /// Sets all bits of allocated memory to zero. - /// - /// The pointer to the memory to be cleared. - /// The length of the memory to be cleared, in bytes. - [CLSCompliant(false)] - [Obsolete("Use ClearBits overload that accepts the length as unsigned integer")] - public static unsafe void ClearBits([In, Out] void* address, nint length) - { - if (length < 0) - throw new ArgumentOutOfRangeException(nameof(length)); - - ClearBits(address, (nuint)length); - } - - /// - /// Sets all bits of allocated memory to zero. - /// - /// The pointer to the memory to be cleared. - /// The length of the memory to be cleared, in bytes. - [CLSCompliant(false)] - public static unsafe void ClearBits([In, Out] void* address, nuint length) - { - // TODO: Replace with NativeMemory.Clear - for (int count; length > 0; length -= (nuint)count, address = Unsafe.Add(address, count)) - { - count = length > int.MaxValue ? int.MaxValue : (int)length; - - Unsafe.InitBlockUnaligned(address, 0, (uint)count); - } - } - - #region Bitwise Hash Code - - internal static unsafe void GetHashCode64Unaligned(ref THashFunction hash, [In] ref byte source, nuint length) - where THashFunction : struct, IConsumer - { - switch (length) - { - default: - for (; length >= sizeof(long); source = ref source.Advance(&length)) - hash.Invoke(Unsafe.ReadUnaligned(ref source)); - for (; length > 0; source = ref source.Advance(&length)) - hash.Invoke(source); - break; - case 0: - break; - case sizeof(byte): - hash.Invoke(source); - break; - case sizeof(ushort): - hash.Invoke(Unsafe.ReadUnaligned(ref source)); - break; - case sizeof(uint): - hash.Invoke(Unsafe.ReadUnaligned(ref source)); - break; - } - } - - internal static unsafe long GetHashCode64Unaligned([In] ref byte source, nuint length, bool salted) - { - var hash = new FNV1a64(); - GetHashCode64Unaligned(ref hash, ref source, length); - - if (salted) - hash.Invoke(RandomExtensions.BitwiseHashSalt); - - return hash.Result; - } - - private static THashFunction GetHashCode(Func getter, int count, T arg) - where THashFunction : IConsumer, new() - { - var hash = new THashFunction(); - - for (var i = 0; i < count; i++) - hash.Invoke(getter(arg, i)); - - return hash; - } - - /// - /// Computes 64-bit hash code for the vector. - /// - /// The pointer to the function responsible for providing data from the vector. - /// The number of elements in the vector. - /// The argument to be passed to the data getter. - /// to include randomized salt data into hashing; to use data from memory only. - /// The type of the argument to be passed to the vector accessor. - /// The computed hash. - public static long GetHashCode64(Func getter, int count, T arg, bool salted = true) - { - ArgumentNullException.ThrowIfNull(getter); - - var hash = GetHashCode(getter, count, arg); - - if (salted) - hash.Invoke(RandomExtensions.BitwiseHashSalt); - - return hash.Result; - } - - /// - /// Computes 32-bit hash code for the vector. - /// - /// The pointer to the function responsible for providing data from the vector. - /// The number of elements in the vector. - /// The argument to be passed to the data getter. - /// to include randomized salt data into hashing; to use data from memory only. - /// The type of the argument to be passed to the vector accessor. - /// The computed hash. - public static int GetHashCode32(Func getter, int count, T arg, bool salted = true) - { - ArgumentNullException.ThrowIfNull(getter); - - var hash = GetHashCode(getter, count, arg); - - if (salted) - hash.Invoke(RandomExtensions.BitwiseHashSalt); - - return hash.Result; - } - - /// - /// Computes 64-bit hash code for the block of memory, 64-bit version. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// Initial value of the hash. - /// Hashing function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - [Obsolete("Use GetHashCode64 overload that accepts the length as unsigned integer")] - public static unsafe long GetHashCode64([In] void* source, nint length, long hash, Func hashFunction, bool salted = true) - => length >= 0 ? GetHashCode64(source, (nuint)length, hash, hashFunction, salted) : throw new ArgumentOutOfRangeException(nameof(length)); - - /// - /// Computes 64-bit hash code for the block of memory, 64-bit version. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// Initial value of the hash. - /// Hashing function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - public static unsafe long GetHashCode64([In] void* source, nuint length, long hash, Func hashFunction, bool salted = true) - { - var fn = new Accumulator(hashFunction, hash); - GetHashCode64Unaligned(ref fn, ref *((byte*)source), length); - - if (salted) - fn.Invoke(RandomExtensions.BitwiseHashSalt); - - return fn.Invoke(); - } - - /// - /// Computes 64-bit hash code for the block of memory, 64-bit version. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// The type providing implementation of the hash function. - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - [Obsolete("Use GetHashCode64 overload that accepts the length as unsigned integer")] - public static unsafe long GetHashCode64([In] void* source, nint length, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - => length >= 0 ? GetHashCode64(source, (nuint)length, salted) : throw new ArgumentOutOfRangeException(nameof(length)); - - /// - /// Computes 64-bit hash code for the block of memory, 64-bit version. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// The type providing implementation of the hash function. - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - public static unsafe long GetHashCode64([In] void* source, nuint length, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - { - var hash = new THashFunction(); - GetHashCode64Unaligned(ref hash, ref *((byte*)source), length); - - if (salted) - hash.Invoke(RandomExtensions.BitwiseHashSalt); - - return hash.Invoke(); - } - - /// - /// Computes 64-bit hash code for the block of memory. - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// - /// This method uses FNV-1a hash algorithm. - /// - /// Content hash code. - /// FNV-1a - [CLSCompliant(false)] - [Obsolete("Use GetHashCode64 overload that accepts the length as unsigned integer")] - public static unsafe long GetHashCode64([In] void* source, nint length, bool salted = true) - => length >= 0 ? GetHashCode64Unaligned(ref *((byte*)source), (nuint)length, salted) : throw new ArgumentOutOfRangeException(nameof(length)); - - /// - /// Computes 64-bit hash code for the block of memory. - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// - /// This method uses FNV-1a hash algorithm. - /// - /// Content hash code. - /// FNV-1a - [CLSCompliant(false)] - public static unsafe long GetHashCode64([In] void* source, nuint length, bool salted = true) - => GetHashCode64Unaligned(ref *((byte*)source), length, salted); - - /// - /// Computes 32-bit hash code for the block of memory. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// Initial value of the hash. - /// Hashing function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - [Obsolete("Use GetHashCode32 overload that accepts the length as unsigned integer")] - public static unsafe int GetHashCode32([In] void* source, nint length, int hash, Func hashFunction, bool salted = true) - => length >= 0 ? GetHashCode32(source, (nuint)length, hash, hashFunction, salted) : throw new ArgumentOutOfRangeException(nameof(length)); - - /// - /// Computes 32-bit hash code for the block of memory. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// Initial value of the hash. - /// Hashing function. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - public static unsafe int GetHashCode32([In] void* source, nuint length, int hash, Func hashFunction, bool salted = true) - { - var fn = new Accumulator(hashFunction, hash); - GetHashCode32Unaligned(ref fn, ref *((byte*)source), length); - - if (salted) - fn.Invoke(RandomExtensions.BitwiseHashSalt); - - return fn.Invoke(); - } - - internal static unsafe void GetHashCode32Unaligned(ref THashFunction hash, [In] ref byte source, nuint length) - where THashFunction : struct, IConsumer - { - switch (length) - { - default: - for (; length >= sizeof(int); source = ref source.Advance(&length)) - hash.Invoke(Unsafe.ReadUnaligned(ref source)); - for (; length > 0; source = ref source.Advance(&length)) - hash.Invoke(source); - break; - case 0: - break; - case sizeof(byte): - hash.Invoke(source); - break; - case sizeof(ushort): - hash.Invoke(Unsafe.ReadUnaligned(ref source)); - break; - } - } - - internal static unsafe int GetHashCode32Unaligned([In] ref byte source, nuint length, bool salted) - { - var hash = new FNV1a32(); - GetHashCode32Unaligned(ref hash, ref source, length); - - if (salted) - hash.Invoke(RandomExtensions.BitwiseHashSalt); - - return hash.Result; - } - - /// - /// Computes 32-bit hash code for the block of memory. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// The type providing implementation of the hash function. - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - [Obsolete("Use GetHashCode32 overload that accepts the length as unsigned integer")] - public static unsafe int GetHashCode32([In] void* source, nint length, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - => length >= 0 ? GetHashCode32(source, (nuint)length, salted) : throw new ArgumentOutOfRangeException(nameof(length)); - - /// - /// Computes 32-bit hash code for the block of memory. - /// - /// - /// This method may give different value each time you run the program for - /// the same data. To disable this behavior, pass false to . - /// - /// The type providing implementation of the hash function. - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// Hash code of the memory block. - [CLSCompliant(false)] - public static unsafe int GetHashCode32([In] void* source, nuint length, bool salted = true) - where THashFunction : struct, IConsumer, ISupplier - { - var hash = new THashFunction(); - GetHashCode32Unaligned(ref hash, ref *((byte*)source), length); - - if (salted) - hash.Invoke(RandomExtensions.BitwiseHashSalt); - - return hash.Invoke(); - } - - /// - /// Computes 32-bit hash code for the block of memory. - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// - /// This method uses FNV-1a hash algorithm. - /// - /// Content hash code. - /// FNV-1a - [CLSCompliant(false)] - [Obsolete("Use GetHashCode32 overload that accepts the length as unsigned integer")] - public static unsafe int GetHashCode32([In] void* source, nint length, bool salted = true) - => length >= 0 ? GetHashCode32(source, (nuint)length, salted) : throw new ArgumentOutOfRangeException(nameof(length)); - - /// - /// Computes 32-bit hash code for the block of memory. - /// - /// A pointer to the block of memory. - /// Length of memory block to be hashed, in bytes. - /// to include randomized salt data into hashing; to use data from memory only. - /// - /// This method uses FNV-1a hash algorithm. - /// - /// Content hash code. - /// FNV-1a - [CLSCompliant(false)] - public static unsafe int GetHashCode32([In] void* source, nuint length, bool salted = true) - => GetHashCode32Unaligned(ref *((byte*)source), length, salted); - #endregion - /// /// Checks whether the specified object is exactly of the specified type. /// @@ -916,58 +327,6 @@ public static unsafe int GetHashCode32([In] void* source, nuint length, bool sal [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsExactTypeOf(object? obj) => obj?.GetType() == typeof(T); - /// - /// Throws arbitrary object as exception. - /// - /// - /// This method never returns successfully. - /// - /// The object to be thrown. - /// The exception containing wrapped . - [DoesNotReturn] - [StackTraceHidden] - public static void Throw(object obj) - { - Push(obj); - Emit.Throw(); - throw Unreachable(); - } - - /// - /// Throws arbitrary object as exception. - /// - /// - /// This method never returns successfully but returned value helpful for constructing terminated statement - /// such as throw Error("Error");. - /// - /// The object to be thrown. - /// The value is never returned from the method. - /// The exception containing wrapped . - /// - [DoesNotReturn] - [StackTraceHidden] - public static Exception Error(object obj) - { - Push(obj); - Emit.Throw(); - throw Unreachable(); - } - - /// - /// Creates shallow copy of the given object. - /// - /// The object to clone. - /// The type of the object to clone. - /// The clone of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ShallowCopy(T obj) - where T : class - { - Push(obj); - Call(Method(Type(), nameof(MemberwiseClone))); - return Return(); - } - /// /// Gets length of the array. /// @@ -977,12 +336,12 @@ public static T ShallowCopy(T obj) /// The array object. /// The length of the array as native unsigned integer. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static nint GetLength(Array array) + [CLSCompliant(false)] + public static nuint GetLength(this Array array) { Push(array); Ldlen(); - Conv_I(); - return Return(); + return Return(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1013,18 +372,6 @@ internal static ReadOnlySpan ReinterpretCast(ReadOnlyS return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(input)), input.Length); } - /// - /// Explicitly invokes object finalizer. - /// - /// The object. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Finalize(object obj) - { - Push(obj); - Callvirt(Method(Type(), nameof(Finalize))); - Ret(); - } - /// /// Determines whether the object overrides method. /// diff --git a/src/DotNext/Runtime/Reference.cs b/src/DotNext/Runtime/Reference.cs deleted file mode 100644 index f5610ce5a..000000000 --- a/src/DotNext/Runtime/Reference.cs +++ /dev/null @@ -1,268 +0,0 @@ -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Debug = System.Diagnostics.Debug; - -namespace DotNext.Runtime; - -/// -/// Provides a reference to the memory location. -/// -/// -/// This type encapsulates the reference to the memory location where the value is stored. -/// The reference can be used in async context and stored in a field or a regular class in -/// contrast to ref-structs. -/// -/// The type of the value stored at a memory location. -[StructLayout(LayoutKind.Auto)] -public readonly unsafe struct Reference -{ - /* - * if owner is null then accessor is of type delegate*, - * if owner is TValue[] then accessor is of type nint; - * if owner is the same as Sentinel.Instance then accessor is of type TValue*; - * otherwise, access is of type delegate*. - */ - private readonly void* accessor; - private readonly object? owner; - - private Reference(object owner, delegate* accessor) - { - Debug.Assert(owner is not null); - Debug.Assert(accessor != null); - - this.owner = owner; - this.accessor = accessor; - } - - internal Reference(delegate* accessor) - { - Debug.Assert(accessor != null); - - owner = null; - this.accessor = accessor; - } - - internal Reference(TValue[] array, nint index) - { - Debug.Assert(array is not null); - - owner = array; - accessor = (void*)index; - } - - internal Reference(void* pointer) - { - Debug.Assert(pointer != null); - - owner = Sentinel.Instance; - accessor = pointer; - } - - internal static Reference Create(TOwner owner, delegate* accessor) - where TOwner : class - => new(owner, (delegate*)accessor); - - /// - /// Gets a value indicating that this reference is valid. - /// - public bool IsValid => accessor != null || owner is TValue[]; - - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private ref TValue RawValue - { - get - { - ref TValue result = ref Unsafe.NullRef(); - - // array index may be zero so check on array type first - if (owner is TValue[] array) - { - result = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)accessor); - } - else if (accessor is null) - { - // leave getter - } - else if (owner is null) - { - result = ref ((delegate*)accessor)(); - } - else if (ReferenceEquals(owner, Sentinel.Instance)) - { - result = ref Unsafe.AsRef(accessor); - } - else - { - result = ref ((delegate*)accessor)(owner); - } - - return ref result; - } - } - - /// - /// Gets a reference to the memory location where the value is stored. - /// - /// This reference is not valid. - public ref TValue Target - { - get - { - ref TValue result = ref RawValue; - Intrinsics.ThrowIfNull(in result); - return ref result; - } - } - - /// - /// Gets a span with the single element representing the underlying value. - /// - /// - /// The returned span is always of size 1 for the valid reference. If this reference is invalid - /// then returned span is empty. - /// - public Span Span - { - get - { - ref TValue result = ref RawValue; - return Unsafe.IsNullRef(ref result) ? Span.Empty : MemoryMarshal.CreateSpan(ref result, 1); - } - } - - /// - /// Converts the underlying value to a string. - /// - /// The string representing underlying value. - public override string? ToString() - { - ref var value = ref RawValue; - return Unsafe.IsNullRef(ref value) ? null : value?.ToString(); - } -} - -/// -/// Provides factory methods for creating references. -/// -public static class Reference -{ - /// - /// Creates a reference to the value stored in a static field. - /// - /// The type of the value. - /// The function providing the location of the value in the memory. - /// The reference to the memory location where the value is stored. - /// is . - [CLSCompliant(false)] - public static unsafe Reference Create(delegate* getter) - { - if (getter is null) - throw new ArgumentNullException(nameof(getter)); - - return new(getter); - } - - /// - /// Creates a reference to the value stored in the object. - /// - /// The type of the object that stores the value. - /// The type of the value. - /// The object that stores the value. - /// The function providing the location of the value in the memory. - /// The reference to the memory location where the value is stored. - /// - /// is ; - /// or is . - /// - /// - /// is a single-dimensional array. - /// - [CLSCompliant(false)] - public static unsafe Reference Create(TOwner owner, delegate* getter) - where TOwner : class - { - ArgumentNullException.ThrowIfNull(owner); - - if (getter is null) - throw new ArgumentNullException(nameof(getter)); - - if (owner is Array { Rank: 1 }) - throw new ArgumentException(ExceptionMessages.ObjectMustNotBeArray, nameof(owner)); - - return Reference.Create(owner, getter); - } - - /// - /// Creates a reference to the value stored in field. - /// - /// The type of the value. - /// The container for the value. - /// The reference to field. - /// is . - public static unsafe Reference Field(StrongBox box) - => Create, TValue>(box, &GetValueRef); - - /// - /// Creates a reference to the array element. - /// - /// The type of the value. - /// One-dimensional array. - /// The index of the element. - /// The reference to the array element. - /// is . - /// is less than zero or greater than or equal to length. - public static Reference ArrayElement(TValue[] array, nint index) - { - ArgumentNullException.ThrowIfNull(array); - - if ((nuint)index >= (nuint)Intrinsics.GetLength(array)) - throw new ArgumentOutOfRangeException(nameof(index)); - - return new(array, index); - } - - /// - /// Allocates the memory location for the value and returns the reference to that location. - /// - /// The type of the value. - /// The initial value to be placed to the memory location. - /// The reference to the allocated memory location. - public static unsafe Reference Allocate(TValue value) - => Reference.Create(new StrongBox(value), &GetValueRef); - - /// - /// Creates a reference to the boxed value. - /// - /// The value type. - /// The boxed representation of the value type. - /// The reference to the boxed value. - /// is not an instance of . - public static unsafe Reference Unbox(object boxed) - where TValue : struct - { - if (boxed is not TValue) - throw new ArgumentException(ExceptionMessages.BoxedValueTypeExpected(), nameof(boxed)); - - return Reference.Create(boxed, &Unsafe.Unbox); - } - - /// - /// Converts the pointer to . - /// - /// The type of the value located at the specified memory location. - /// The pointer representing the address of the value location. - /// The reference to the value. - /// is . - [CLSCompliant(false)] - public static unsafe Reference FromPointer(TValue* ptr) - where TValue : unmanaged - { - if (ptr is null) - throw new ArgumentNullException(nameof(ptr)); - - return new(ptr); - } - - private static ref TValue GetValueRef(StrongBox box) => ref box.Value!; -} \ No newline at end of file diff --git a/src/DotNext/Span.cs b/src/DotNext/Span.cs index e6fdd8857..14e019de2 100644 --- a/src/DotNext/Span.cs +++ b/src/DotNext/Span.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using static System.Runtime.CompilerServices.Unsafe; namespace DotNext; @@ -15,206 +14,6 @@ namespace DotNext; /// public static partial class Span { - /// - /// Computes bitwise hash code for the memory identified by the given span. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the span content. - public static int BitwiseHashCode(this Span span, bool salted = true) - where T : unmanaged - => BitwiseHashCode((ReadOnlySpan)span, salted); - - /// - /// Computes bitwise hash code for the memory identified by the given span. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the span content. - public static unsafe int BitwiseHashCode(this ReadOnlySpan span, bool salted = true) - where T : unmanaged - { - if (span.IsEmpty) - return salted ? RandomExtensions.BitwiseHashSalt : 0; - - return Intrinsics.GetHashCode32Unaligned(ref As(ref MemoryMarshal.GetReference(span)), checked((nuint)span.Length * (nuint)sizeof(T)), salted); - } - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// Initial value of the hash. - /// Custom hashing algorithm. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the array content. - public static int BitwiseHashCode(this Span span, int hash, Func hashFunction, bool salted = true) - where T : unmanaged - => BitwiseHashCode((ReadOnlySpan)span, hash, hashFunction, salted); - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The type of the hash algorithm. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the array content. - [CLSCompliant(false)] - public static int BitwiseHashCode(this Span span, bool salted = true) - where T : unmanaged - where THashFunction : struct, IConsumer, ISupplier - => BitwiseHashCode((ReadOnlySpan)span, salted); - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// Initial value of the hash. - /// Custom hashing algorithm. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the array content. - public static long BitwiseHashCode64(this Span span, long hash, Func hashFunction, bool salted = true) - where T : unmanaged - => BitwiseHashCode64((ReadOnlySpan)span, hash, hashFunction, salted); - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The type of the hash algorithm. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the array content. - [CLSCompliant(false)] - public static long BitwiseHashCode64(this Span span, bool salted = true) - where T : unmanaged - where THashFunction : struct, IConsumer, ISupplier - => BitwiseHashCode64((ReadOnlySpan)span, salted); - - private static unsafe void BitwiseHashCode(ReadOnlySpan span, scoped ref THashFunction hashFunction, bool salted) - where T : unmanaged - where THashFunction : struct, IConsumer - { - if (!span.IsEmpty) - Intrinsics.GetHashCode32Unaligned(ref hashFunction, ref As(ref MemoryMarshal.GetReference(span)), checked((nuint)span.Length * (nuint)sizeof(T))); - - if (salted) - hashFunction.Invoke(RandomExtensions.BitwiseHashSalt); - } - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// Initial value of the hash. - /// Custom hashing algorithm. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the array content. - public static int BitwiseHashCode(this ReadOnlySpan span, int hash, Func hashFunction, bool salted = true) - where T : unmanaged - { - var fn = new Accumulator(hashFunction, hash); - BitwiseHashCode(span, ref fn, salted); - return fn.Invoke(); - } - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The type of the hash algorithm. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 32-bit hash code of the array content. - [CLSCompliant(false)] - public static int BitwiseHashCode(this ReadOnlySpan span, bool salted = true) - where T : unmanaged - where THashFunction : struct, IConsumer, ISupplier - { - var hash = new THashFunction(); - BitwiseHashCode(span, ref hash, salted); - return hash.Invoke(); - } - - private static unsafe void BitwiseHashCode64(ReadOnlySpan span, scoped ref THashFunction hashFunction, bool salted) - where T : unmanaged - where THashFunction : struct, IConsumer - { - if (!span.IsEmpty) - Intrinsics.GetHashCode64Unaligned(ref hashFunction, ref As(ref MemoryMarshal.GetReference(span)), checked((nuint)span.Length * (nuint)sizeof(T))); - - if (salted) - hashFunction.Invoke(RandomExtensions.BitwiseHashSalt); - } - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// Initial value of the hash. - /// Custom hashing algorithm. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the array content. - public static long BitwiseHashCode64(this ReadOnlySpan span, long hash, Func hashFunction, bool salted = true) - where T : unmanaged - { - var fn = new Accumulator(hashFunction, hash); - BitwiseHashCode64(span, ref fn, salted); - return fn.Invoke(); - } - - /// - /// Computes bitwise hash code for the memory identified by the given span using custom hash function. - /// - /// The type of elements in the span. - /// The type of the hash algorithm. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the array content. - [CLSCompliant(false)] - public static long BitwiseHashCode64(this ReadOnlySpan span, bool salted = true) - where T : unmanaged - where THashFunction : struct, IConsumer, ISupplier - { - var hash = new THashFunction(); - BitwiseHashCode64(span, ref hash, salted); - return hash.Invoke(); - } - - /// - /// Computes bitwise hash code for the memory identified by the given span. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the span content. - public static long BitwiseHashCode64(this Span span, bool salted = true) - where T : unmanaged - => BitwiseHashCode64((ReadOnlySpan)span, salted); - - /// - /// Computes bitwise hash code for the memory identified by the given span. - /// - /// The type of elements in the span. - /// The span whose content to be hashed. - /// to include randomized salt data into hashing; to use data from memory only. - /// 64-bit hash code of the span content. - public static unsafe long BitwiseHashCode64(this ReadOnlySpan span, bool salted = true) - where T : unmanaged - { - if (span.IsEmpty) - return salted ? RandomExtensions.BitwiseHashSalt : 0L; - - return Intrinsics.GetHashCode64Unaligned(ref As(ref MemoryMarshal.GetReference(span)), checked((nuint)span.Length * (nuint)sizeof(T)), salted); - } - /// /// Determines whether two memory blocks identified by the given spans contain the same set of elements. /// @@ -222,48 +21,63 @@ public static unsafe long BitwiseHashCode64(this ReadOnlySpan span, bool s /// This method performs bitwise equality between each pair of elements. /// /// The type of elements in the span. - /// The first memory span to compare. - /// The second memory span to compare. + /// The first memory span to compare. + /// The second memory span to compare. /// , if both memory blocks are equal; otherwise, . - public static bool BitwiseEquals(this Span first, Span second) + public static unsafe bool BitwiseEquals(this ReadOnlySpan x, ReadOnlySpan y) where T : unmanaged - => MemoryMarshal.AsBytes(first).SequenceEqual(MemoryMarshal.AsBytes(second)); + { + var result = false; + if (x.Length == y.Length) + { + for (int maxSize = Array.MaxLength / sizeof(T), size; !x.IsEmpty; x = x.Slice(size), y = y.Slice(size)) + { + size = Math.Min(maxSize, x.Length); + var sizeInBytes = size * sizeof(T); + var partX = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(x)), sizeInBytes); + var partY = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(y)), sizeInBytes); + if (MemoryExtensions.SequenceEqual(partX, partY) is false) + break; + } - /// - /// Determines whether two memory blocks identified by the given spans contain the same set of elements. - /// - /// - /// This method performs bitwise equality between each pair of elements. - /// - /// The type of elements in the span. - /// The first memory span to compare. - /// The second memory span to compare. - /// , if both memory blocks are equal; otherwise, . - public static bool BitwiseEquals(this ReadOnlySpan first, ReadOnlySpan second) - where T : unmanaged - => MemoryMarshal.AsBytes(first).SequenceEqual(MemoryMarshal.AsBytes(second)); + result = true; + } - /// - /// Compares content of the two memory blocks identified by the given spans. - /// - /// The type of elements in the span. - /// The first memory span to compare. - /// The second array to compare. - /// Comparison result. - public static int BitwiseCompare(this Span first, Span second) + return result; + } + + internal static bool BitwiseEquals(T[] x, T[] y) where T : unmanaged - => MemoryMarshal.AsBytes(first).SequenceCompareTo(MemoryMarshal.AsBytes(second)); + => BitwiseEquals(new ReadOnlySpan(x), new ReadOnlySpan(y)); /// /// Compares content of the two memory blocks identified by the given spans. /// /// The type of elements in the span. - /// The first memory span to compare. - /// The second array to compare. + /// The first memory span to compare. + /// The second array to compare. /// Comparison result. - public static int BitwiseCompare(this ReadOnlySpan first, ReadOnlySpan second) + public static unsafe int BitwiseCompare(this ReadOnlySpan x, ReadOnlySpan y) where T : unmanaged - => MemoryMarshal.AsBytes(first).SequenceCompareTo(MemoryMarshal.AsBytes(second)); + { + var result = x.Length; + result = result.CompareTo(y.Length); + if (result is 0) + { + for (int maxSize = Array.MaxLength / sizeof(T), size; !x.IsEmpty; x = x.Slice(size), y = y.Slice(size)) + { + size = Math.Min(maxSize, x.Length); + var sizeInBytes = size * sizeof(T); + var partX = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(x)), sizeInBytes); + var partY = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(y)), sizeInBytes); + result = MemoryExtensions.SequenceCompareTo(partX, partY); + if (result is not 0) + break; + } + } + + return result; + } /// /// Sorts the elements. @@ -320,10 +134,14 @@ public static Span TrimLength(this Span span, int maxLength, out Span TrimLengthCore(Span span, int maxLength, out Span rest) @@ -333,7 +151,7 @@ private static Span TrimLengthCore(Span span, int maxLength, out Span(this Span span, RefAction action) [CLSCompliant(false)] public static unsafe void ForEach(this Span span, delegate* action, TArg arg) { - if (action is null) - throw new ArgumentNullException(nameof(action)); + ArgumentNullException.ThrowIfNull(action); foreach (ref var item in span) action(ref item, arg); @@ -441,7 +258,7 @@ public static unsafe void ForEach(this Span span, delegate* AsBytes(ref T value) where T : unmanaged - => MemoryMarshal.CreateSpan(ref As(ref value), sizeof(T)); + => MemoryMarshal.CreateSpan(ref Unsafe.As(ref value), sizeof(T)); /// /// Converts contiguous memory identified by the specified pointer @@ -453,7 +270,7 @@ public static unsafe Span AsBytes(ref T value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsReadOnlyBytes(ref readonly T value) where T : unmanaged - => AsBytes(ref AsRef(in value)); + => AsBytes(ref Unsafe.AsRef(in value)); /// /// Converts contiguous memory identified by the specified pointer @@ -501,6 +318,23 @@ public static MemoryOwner Concat(this ReadOnlySpan first, ReadOnlySpan< return result; } + internal static T[] ConcatToArray(ReadOnlySpan x, ReadOnlySpan y) + { + T[] result; + if (x.IsEmpty && y.IsEmpty) + { + result = []; + } + else + { + result = GC.AllocateUninitializedArray(x.Length + y.Length); + x.CopyTo(result); + y.CopyTo(result.AsSpan(x.Length)); + } + + return result; + } + /// /// Concatenates memory blocks. /// @@ -693,7 +527,7 @@ public static MemoryOwner Concat(ReadOnlySpan values, MemoryAlloc public static ReadOnlySpan Contravariance(this ReadOnlySpan span) where T : class?, TBase where TBase : class? - => MemoryMarshal.CreateReadOnlySpan(ref As(ref MemoryMarshal.GetReference(span)), span.Length); + => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), span.Length); /// /// Swaps contents of the two spans. diff --git a/src/DotNext/Threading/Atomic.cs b/src/DotNext/Threading/Atomic.cs index a9d5dd8a7..4bac66754 100644 --- a/src/DotNext/Threading/Atomic.cs +++ b/src/DotNext/Threading/Atomic.cs @@ -27,7 +27,7 @@ private interface IEqualityComparer [StructLayout(LayoutKind.Auto)] private readonly struct BitwiseEqualityComparer : IEqualityComparer { - bool IEqualityComparer.Equals(in T x, in T y) => BitwiseComparer.Equals(in x, in y); + bool IEqualityComparer.Equals(in T x, in T y) => EqualityComparer.Default.Equals(x, y); } [StructLayout(LayoutKind.Auto)] diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs index b360ccc15..804faadfb 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs @@ -7,9 +7,9 @@ namespace DotNext.Net.Cluster; using Buffers; +using IO.Hashing; using Hex = Buffers.Text.Hex; -using Intrinsics = Runtime.Intrinsics; -using HttpEndPoint = Net.Http.HttpEndPoint; +using HttpEndPoint = Http.HttpEndPoint; /// /// Represents unique identifier of cluster member. @@ -39,8 +39,7 @@ private ClusterMemberId(IPEndPoint endPoint) private ClusterMemberId(DnsEndPoint endPoint) { Span bytes = stackalloc byte[16]; - bytes.Clear(); - WriteInt64LittleEndian(bytes, Span.BitwiseHashCode64(endPoint.Host.AsSpan(), false)); + WriteInt64LittleEndian(bytes, FNV1a64.Hash(endPoint.Host)); address = new(bytes); lengthAndPort = Combine(endPoint.Host.Length, endPoint.Port); @@ -49,11 +48,10 @@ private ClusterMemberId(DnsEndPoint endPoint) private ClusterMemberId(HttpEndPoint endPoint) { - Span bytes = stackalloc byte[16]; - bytes.Clear(); - WriteInt64LittleEndian(bytes, Span.BitwiseHashCode64(endPoint.Host.AsSpan(), false)); - bytes[sizeof(long)] = endPoint.IsSecure.ToByte(); - address = new(bytes); + var writer = new SpanWriter(stackalloc byte[16]); + writer.WriteLittleEndian(FNV1a64.Hash(endPoint.Host)); + writer.Add(endPoint.IsSecure.ToByte()); + address = new(writer.Span); lengthAndPort = Combine(endPoint.Host.Length, endPoint.Port); family = (int)endPoint.AddressFamily; @@ -61,11 +59,11 @@ private ClusterMemberId(HttpEndPoint endPoint) private ClusterMemberId(Uri uri) { - Span bytes = stackalloc byte[16]; - WriteInt32LittleEndian(bytes, Span.BitwiseHashCode(uri.Scheme.AsSpan(), false)); - WriteInt32LittleEndian(bytes.Slice(sizeof(int)), Span.BitwiseHashCode(uri.Host.AsSpan(), false)); - WriteInt64LittleEndian(bytes.Slice(sizeof(long)), Span.BitwiseHashCode64(uri.PathAndQuery.AsSpan(), false)); - address = new(bytes); + var writer = new SpanWriter(stackalloc byte[16]); + writer.WriteLittleEndian(FNV1a32.Hash(uri.Scheme)); + writer.WriteLittleEndian(FNV1a32.Hash(uri.Host)); + writer.WriteLittleEndian(FNV1a64.Hash(uri.PathAndQuery)); + address = new(writer.Span); lengthAndPort = Combine(uri.AbsoluteUri.Length, uri.Port); family = (int)uri.HostNameType; @@ -75,7 +73,7 @@ private ClusterMemberId(SocketAddress address) { Span bytes = stackalloc byte[16]; bytes.Clear(); - WriteInt64LittleEndian(bytes, Intrinsics.GetHashCode64(static (address, index) => address[index], address.Size, address, false)); + WriteInt64LittleEndian(bytes, FNV1a64.Hash(static (address, index) => address[index], address.Size, address)); this.address = new(bytes); lengthAndPort = unchecked((uint)address.Size); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs index db69e7ff0..d78d55b4e 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs @@ -64,7 +64,7 @@ protected CommandInterpreter() if (handlerAttr is not null && method.ReturnType == typeof(ValueTask)) { var parameters = method.GetParameterTypes(); - if (GetLength(parameters) != 2 || !parameters[0].IsValueType || parameters[1] != typeof(CancellationToken)) + if (parameters.GetLength() is not 2 || !parameters[0].IsValueType || parameters[1] != typeof(CancellationToken)) continue; var commandType = parameters[0]; if (!identifiers.TryGetValue(commandType, out var commandId)) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs index 7fcd75d67..dd2276e18 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs @@ -97,7 +97,7 @@ bool ISupplier.Invoke() // boxed ClusterMemberId or null if there is not last vote stored private volatile BoxedClusterMemberId? lastVote; - private volatile long[] log = Array.Empty(); // log of uncommitted entries + private volatile long[] log = []; // log of uncommitted entries /// long IPersistentState.Term => term.VolatileRead(); @@ -107,8 +107,16 @@ bool ISupplier.Invoke() private void Append(long[] terms, long startIndex) { - log = log.Concat(terms, startIndex - commitIndex.VolatileRead() - 1L); - index.VolatileWrite(startIndex + terms.LongLength - 1L); + log = Concat(log, terms, startIndex - Volatile.Read(in commitIndex) - 1L); + Volatile.Write(ref index, startIndex + terms.LongLength - 1L); + + static long[] Concat(long[] left, long[] right, long startIndex) + { + var result = new long[startIndex + right.LongLength]; + Array.Copy(left, result, startIndex); + Array.Copy(right, 0L, result, startIndex, right.Length); + return result; + } } private async ValueTask AppendAsync(ILogEntryProducer entries, long? startIndex, bool skipCommitted, CancellationToken token) @@ -224,7 +232,7 @@ private async ValueTask CommitAsync(long? endIndex, CancellationToken toke lastTerm.VolatileWrite(log[count - 1]); // count indicates how many elements should be removed from log - log = log.RemoveFirst(count); + log = log[(int)count..]; commitEvent.Signal(resumeAll: true); } } @@ -246,11 +254,11 @@ async ValueTask IAuditTrail.DropAsync(long startIndex, CancellationToken t long count; using (await syncRoot.AcquireWriteLockAsync(token).ConfigureAwait(false)) { - if (startIndex <= commitIndex.VolatileRead()) + if (startIndex <= Volatile.Read(in commitIndex)) throw new InvalidOperationException(ExceptionMessages.InvalidAppendIndex); - count = index.VolatileRead() - startIndex + 1L; - index.VolatileWrite(startIndex - 1L); - log = log.RemoveLast(count); + count = Volatile.Read(in index) - startIndex + 1L; + Volatile.Write(ref index, startIndex - 1L); + log = log[0..^(int)count]; } return count; From d354e11c460265ac829691edd1f2ea83ee19561f Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 07:46:16 +0200 Subject: [PATCH 052/155] Simplify pattern matching --- src/DotNext/DelegateHelpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DotNext/DelegateHelpers.cs b/src/DotNext/DelegateHelpers.cs index 4cf31f2fb..ac1b7d0a1 100644 --- a/src/DotNext/DelegateHelpers.cs +++ b/src/DotNext/DelegateHelpers.cs @@ -16,7 +16,7 @@ private static MethodInfo GetMethod(Expression expression) MethodCallExpression expr => expr.Method, MemberExpression { Member: PropertyInfo { CanRead: true } property } => property.GetMethod!, BinaryExpression { Method: not null } expr => expr.Method, - IndexExpression { Indexer: { CanRead: true } } expr => expr.Indexer.GetMethod!, + IndexExpression { Indexer.CanRead: true } expr => expr.Indexer.GetMethod!, UnaryExpression { Method: not null } expr => expr.Method, _ => throw new ArgumentException(ExceptionMessages.InvalidExpressionTree, nameof(expression)) }; From 22bde0af3c3e96e93dc6d981b170af27420054a3 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 08:56:58 +0200 Subject: [PATCH 053/155] Removal of AtomicEnum + rewritten EnumConverter using generic math --- .../GenericEnumBenchmark.cs | 6 +- src/DotNext.Tests/EnumTests.cs | 122 +++-- src/DotNext.Tests/Threading/AtomicTests.cs | 52 -- src/DotNext/EnumConverter.cs | 499 ++---------------- src/DotNext/Runtime/Intrinsics.cs | 8 - src/DotNext/Threading/AtomicEnum.cs | 290 ---------- src/DotNext/UndefinedResultException.cs | 5 +- .../Consensus/Raft/Http/RaftClusterMember.cs | 9 +- .../Consensus/Raft/RaftClusterMember.cs | 8 +- .../Net/Cluster/IClusterMember.cs | 8 +- 10 files changed, 129 insertions(+), 878 deletions(-) delete mode 100644 src/DotNext/Threading/AtomicEnum.cs diff --git a/src/DotNext.Benchmarks/GenericEnumBenchmark.cs b/src/DotNext.Benchmarks/GenericEnumBenchmark.cs index 42b2ef207..4aebc7c2a 100644 --- a/src/DotNext.Benchmarks/GenericEnumBenchmark.cs +++ b/src/DotNext.Benchmarks/GenericEnumBenchmark.cs @@ -24,11 +24,11 @@ private static long ToInt64(T value) public long ToInt64UsingConstrainedCall() => ToInt64(EnvironmentVariableTarget.Machine); [Benchmark] - public int ToInt32UsingGenericConverter() => EnvironmentVariableTarget.Machine.ToInt32(); + public int ToInt32UsingGenericConverter() => EnumConverter.FromEnum(EnvironmentVariableTarget.Machine); [Benchmark] - public long ToInt64UsingGenericConverter() => EnvironmentVariableTarget.Machine.ToInt64(); + public long ToInt64UsingGenericConverter() => EnumConverter.FromEnum(EnvironmentVariableTarget.Machine); [Benchmark] - public EnvironmentVariableTarget ToEnumUsingGenericConverter() => 2.ToEnum(); + public EnvironmentVariableTarget ToEnumUsingGenericConverter() => EnumConverter.ToEnum(2); } \ No newline at end of file diff --git a/src/DotNext.Tests/EnumTests.cs b/src/DotNext.Tests/EnumTests.cs index f6950d615..e726482e1 100644 --- a/src/DotNext.Tests/EnumTests.cs +++ b/src/DotNext.Tests/EnumTests.cs @@ -1,4 +1,6 @@ -namespace DotNext; +using System.Numerics; + +namespace DotNext; public sealed class EnumTests : Test { @@ -61,32 +63,32 @@ private enum UInt64Enum : ulong private static void FromPrimitive(TEnum zero, TEnum one, TEnum two) where TEnum : struct, Enum { - Equal(zero, ((sbyte)0).ToEnum()); - Equal(zero, ((byte)0).ToEnum()); - Equal(zero, ((short)0).ToEnum()); - Equal(zero, ((ushort)0).ToEnum()); - Equal(zero, 0.ToEnum()); - Equal(zero, 0U.ToEnum()); - Equal(zero, 0L.ToEnum()); - Equal(zero, 0UL.ToEnum()); - - Equal(one, ((sbyte)1).ToEnum()); - Equal(one, ((byte)1).ToEnum()); - Equal(one, ((short)1).ToEnum()); - Equal(one, ((ushort)1).ToEnum()); - Equal(one, 1.ToEnum()); - Equal(one, 1U.ToEnum()); - Equal(one, 1L.ToEnum()); - Equal(one, 1UL.ToEnum()); - - Equal(two, ((sbyte)2).ToEnum()); - Equal(two, ((byte)2).ToEnum()); - Equal(two, ((short)2).ToEnum()); - Equal(two, ((ushort)2).ToEnum()); - Equal(two, 2.ToEnum()); - Equal(two, 2U.ToEnum()); - Equal(two, 2L.ToEnum()); - Equal(two, 2UL.ToEnum()); + Equal(zero, EnumConverter.ToEnum(0)); + Equal(zero, EnumConverter.ToEnum(0)); + Equal(zero, EnumConverter.ToEnum(0)); + Equal(zero, EnumConverter.ToEnum(0)); + Equal(zero,EnumConverter.ToEnum(0)); + Equal(zero, EnumConverter.ToEnum(0U)); + Equal(zero, EnumConverter.ToEnum(0L)); + Equal(zero, EnumConverter.ToEnum(0UL)); + + Equal(one, EnumConverter.ToEnum(1)); + Equal(one, EnumConverter.ToEnum(1)); + Equal(one, EnumConverter.ToEnum(1)); + Equal(one, EnumConverter.ToEnum(1)); + Equal(one,EnumConverter.ToEnum(1)); + Equal(one, EnumConverter.ToEnum(1U)); + Equal(one, EnumConverter.ToEnum(1L)); + Equal(one, EnumConverter.ToEnum(1UL)); + + Equal(two, EnumConverter.ToEnum(2)); + Equal(two, EnumConverter.ToEnum(2)); + Equal(two, EnumConverter.ToEnum(2)); + Equal(two, EnumConverter.ToEnum(2)); + Equal(two,EnumConverter.ToEnum(2)); + Equal(two, EnumConverter.ToEnum(2U)); + Equal(two, EnumConverter.ToEnum(2L)); + Equal(two, EnumConverter.ToEnum(2UL)); } [Fact] @@ -105,32 +107,32 @@ public static void ConversionFromPrimitive() private static void ToPrimitive(TEnum zero, TEnum one, TEnum two) where TEnum : struct, Enum { - Equal(0, zero.ToByte()); - Equal(0, zero.ToSByte()); - Equal(0, zero.ToInt16()); - Equal(0, zero.ToUInt16()); - Equal(0, zero.ToInt32()); - Equal(0U, zero.ToUInt32()); - Equal(0L, zero.ToInt64()); - Equal(0UL, zero.ToUInt64()); - - Equal(1, one.ToByte()); - Equal(1, one.ToSByte()); - Equal(1, one.ToInt16()); - Equal(1, one.ToUInt16()); - Equal(1, one.ToInt32()); - Equal(1U, one.ToUInt32()); - Equal(1L, one.ToInt64()); - Equal(1UL, one.ToUInt64()); - - Equal(2, two.ToByte()); - Equal(2, two.ToSByte()); - Equal(2, two.ToInt16()); - Equal(2, two.ToUInt16()); - Equal(2, two.ToInt32()); - Equal(2U, two.ToUInt32()); - Equal(2L, two.ToInt64()); - Equal(2UL, two.ToUInt64()); + Equal(0, EnumConverter.FromEnum(zero)); + Equal(0, EnumConverter.FromEnum(zero)); + Equal(0, EnumConverter.FromEnum(zero)); + Equal(0, EnumConverter.FromEnum(zero)); + Equal(0, EnumConverter.FromEnum(zero)); + Equal(0U, EnumConverter.FromEnum(zero)); + Equal(0L, EnumConverter.FromEnum(zero)); + Equal(0UL, EnumConverter.FromEnum(zero)); + + Equal(1, EnumConverter.FromEnum(one)); + Equal(1, EnumConverter.FromEnum(one)); + Equal(1, EnumConverter.FromEnum(one)); + Equal(1, EnumConverter.FromEnum(one)); + Equal(1, EnumConverter.FromEnum(one)); + Equal(1U, EnumConverter.FromEnum(one)); + Equal(1L, EnumConverter.FromEnum(one)); + Equal(1UL, EnumConverter.FromEnum(one)); + + Equal(2, EnumConverter.FromEnum(two)); + Equal(2, EnumConverter.FromEnum(two)); + Equal(2, EnumConverter.FromEnum(two)); + Equal(2, EnumConverter.FromEnum(two)); + Equal(2, EnumConverter.FromEnum(two)); + Equal(2U, EnumConverter.FromEnum(two)); + Equal(2L, EnumConverter.FromEnum(two)); + Equal(2UL, EnumConverter.FromEnum(two)); } [Fact] @@ -151,16 +153,10 @@ public static void NegativeValueConversion() { var expected = (EnvironmentVariableTarget)(-1); - Equal(-1, expected.ToSByte()); - Throws(() => expected.ToByte()); - - Equal(-1, expected.ToInt16()); - Throws(() => expected.ToUInt16()); - - Equal(-1, expected.ToInt32()); - Throws(() => expected.ToUInt32()); + Equal(-1, EnumConverter.FromEnum(expected)); + Throws(() => EnumConverter.FromEnum(expected)); - Equal(-1L, expected.ToInt64()); - Throws(() => expected.ToUInt64()); + Equal(-1, EnumConverter.FromEnum(expected)); + Throws(() => EnumConverter.FromEnum(expected)); } } \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/AtomicTests.cs b/src/DotNext.Tests/Threading/AtomicTests.cs index ca739d9a5..03242f2d1 100644 --- a/src/DotNext.Tests/Threading/AtomicTests.cs +++ b/src/DotNext.Tests/Threading/AtomicTests.cs @@ -251,44 +251,6 @@ public static void AtomicBooleanTest() True(value.Value); } - [Fact] - public static void AtomicEnumTest() - { - var value = new AtomicEnum(); - True(value.Equals(EnvironmentVariableTarget.Process)); - False(value.Equals(EnvironmentVariableTarget.User)); - Equal(EnvironmentVariableTarget.Process, value.Value); - Equal(EnvironmentVariableTarget.Process, value.GetAndSet(EnvironmentVariableTarget.Machine)); - Equal(EnvironmentVariableTarget.Machine, value.Value); - Equal(EnvironmentVariableTarget.Machine, value.GetAndUpdate(static x => EnvironmentVariableTarget.User)); - Equal(EnvironmentVariableTarget.User, value.Value); - Equal(EnvironmentVariableTarget.User.ToString(), value.ToString()); - value.Value = EnvironmentVariableTarget.Process; - Equal(EnvironmentVariableTarget.Machine, value.SetAndGet(EnvironmentVariableTarget.Machine)); - Equal(EnvironmentVariableTarget.Machine, value.Value); - Equal(EnvironmentVariableTarget.User, value.UpdateAndGet(static x => - { - Equal(EnvironmentVariableTarget.Machine, x); - return EnvironmentVariableTarget.User; - })); - value.Value = EnvironmentVariableTarget.Process; - Equal(EnvironmentVariableTarget.Process, value.GetAndAccumulate(EnvironmentVariableTarget.User, static (current, update) => - { - Equal(EnvironmentVariableTarget.Process, current); - Equal(EnvironmentVariableTarget.User, update); - return (int)current + update; - })); - Equal(EnvironmentVariableTarget.User, value.Value); - value.Value = EnvironmentVariableTarget.Process; - Equal(EnvironmentVariableTarget.User, value.AccumulateAndGet(EnvironmentVariableTarget.User, static (current, update) => - { - Equal(EnvironmentVariableTarget.Process, current); - Equal(EnvironmentVariableTarget.User, update); - return (int)current + update; - })); - Equal(EnvironmentVariableTarget.User, value.Value); - } - [Fact] public static void AtomicIntPtrTest() { @@ -337,18 +299,4 @@ private enum ByteEnum : byte One = 0, Two = 1 } - - [Fact] - public static void EnumVolatileReadWrite() - { - var be = ByteEnum.Two; - Equal(ByteEnum.Two, be.VolatileRead()); - be.VolatileWrite(ByteEnum.One); - Equal(ByteEnum.One, be); - - var le = LongEnum.Two; - Equal(LongEnum.Two, le.VolatileRead()); - le.VolatileWrite(LongEnum.One); - Equal(LongEnum.One, le); - } } \ No newline at end of file diff --git a/src/DotNext/EnumConverter.cs b/src/DotNext/EnumConverter.cs index 5154212e5..b0ab0eb66 100644 --- a/src/DotNext/EnumConverter.cs +++ b/src/DotNext/EnumConverter.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; namespace DotNext; @@ -31,466 +32,68 @@ public static TypeCode GetTypeCode() where TEnum : struct, Enum => EnumTypeCode.Value; /// - /// Converts into enum of type . + /// Converts a value of type to enum of type . /// - /// The target enum type. - /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ToEnum(this long value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(Convert.ToBoolean(value)), - TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), - TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), - TypeCode.Int16 => ReinterpretCast(Convert.ToInt16(value)), - TypeCode.UInt16 => ReinterpretCast(Convert.ToUInt16(value)), - TypeCode.Int32 => ReinterpretCast(Convert.ToInt32(value)), - TypeCode.UInt32 => ReinterpretCast(Convert.ToUInt32(value)), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => ReinterpretCast(Convert.ToUInt64(value)), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts into enum of type . - /// - /// The target enum type. - /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ToEnum(this int value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(value.ToBoolean()), - TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), - TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), - TypeCode.Int16 => ReinterpretCast(Convert.ToInt16(value)), - TypeCode.UInt16 => ReinterpretCast(Convert.ToUInt16(value)), - TypeCode.Int32 => ReinterpretCast(value), - TypeCode.UInt32 => ReinterpretCast(Convert.ToUInt32(value)), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => ReinterpretCast(Convert.ToUInt64(value)), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts into enum of type . - /// - /// The target enum type. - /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ToEnum(this short value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), - TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), - TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), - TypeCode.Int16 => ReinterpretCast(value), - TypeCode.UInt16 => ReinterpretCast(Convert.ToUInt16(value)), - TypeCode.Int32 => ReinterpretCast(value), - TypeCode.UInt32 => ReinterpretCast(Convert.ToUInt32(value)), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => ReinterpretCast(Convert.ToUInt64(value)), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts into enum of type . - /// - /// The target enum type. - /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ToEnum(this byte value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), - TypeCode.Int16 => ReinterpretCast(value), - TypeCode.UInt16 => ReinterpretCast(value), - TypeCode.Int32 => ReinterpretCast(value), - TypeCode.UInt32 => ReinterpretCast(value), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => ReinterpretCast(value), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts into enum of type . - /// - /// The target enum type. - /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static T ToEnum(this sbyte value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), - TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), - TypeCode.SByte => ReinterpretCast(value), - TypeCode.Int16 => ReinterpretCast(value), - TypeCode.UInt16 => ReinterpretCast(Convert.ToUInt16(value)), - TypeCode.Int32 => ReinterpretCast(value), - TypeCode.UInt32 => ReinterpretCast(Convert.ToUInt32(value)), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => ReinterpretCast(Convert.ToUInt64(value)), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts into enum of type . - /// - /// The target enum type. - /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static T ToEnum(this ushort value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(BasicExtensions.ToBoolean(value)), - TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), - TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), - TypeCode.Int16 => ReinterpretCast(Convert.ToInt16(value)), - TypeCode.UInt16 => ReinterpretCast(value), - TypeCode.Int32 => ReinterpretCast(value), - TypeCode.UInt32 => ReinterpretCast(value), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => ReinterpretCast(value), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts into enum of type . - /// - /// The target enum type. - /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static T ToEnum(this uint value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(Convert.ToBoolean(value)), - TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), - TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), - TypeCode.Int16 => ReinterpretCast(Convert.ToInt16(value)), - TypeCode.UInt16 => ReinterpretCast(Convert.ToUInt16(value)), - TypeCode.Int32 => ReinterpretCast(Convert.ToInt32(value)), - TypeCode.UInt32 => ReinterpretCast(value), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => ReinterpretCast(value), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts into enum of type . - /// - /// The target enum type. + /// The type of the enum. + /// The numeric type representing enum value. /// The value to be converted. - /// Enum value equals to the given value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] + /// The enum value that is equivalent to . [CLSCompliant(false)] - public static T ToEnum(this ulong value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => default(T), - TypeCode.Char => ReinterpretCast(Convert.ToChar(value)), - TypeCode.Decimal => ReinterpretCast(value), - TypeCode.Boolean => ReinterpretCast(Convert.ToBoolean(value)), - TypeCode.Byte => ReinterpretCast(Convert.ToByte(value)), - TypeCode.SByte => ReinterpretCast(Convert.ToSByte(value)), - TypeCode.Int16 => ReinterpretCast(Convert.ToInt16(value)), - TypeCode.UInt16 => ReinterpretCast(Convert.ToUInt16(value)), - TypeCode.Int32 => ReinterpretCast(Convert.ToInt32(value)), - TypeCode.UInt32 => ReinterpretCast(Convert.ToUInt32(value)), - TypeCode.Int64 => ReinterpretCast(Convert.ToInt64(value)), - TypeCode.UInt64 => ReinterpretCast(value), - TypeCode.Single => ReinterpretCast(value), - TypeCode.Double => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static T ToEnumUnchecked(this ulong value) - where T : struct, Enum - => Unsafe.SizeOf() switch - { - sizeof(byte) => ReinterpretCast(unchecked((byte)value)), - sizeof(ushort) => ReinterpretCast(unchecked((ushort)value)), - sizeof(uint) => ReinterpretCast(unchecked((uint)value)), - sizeof(ulong) => ReinterpretCast(value), - _ => value.ChangeType(), - }; - - /// - /// Converts enum value into primitive type . - /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long ToInt64(this T value) - where T : struct, Enum => EnumTypeCode.Value switch + public static TEnum ToEnum(TValue value) + where TValue : unmanaged, INumberBase, IConvertible + where TEnum : struct, Enum { - TypeCode.Empty => 0L, - TypeCode.Char => ReinterpretCast(value), - TypeCode.Decimal => Convert.ToInt64(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToInt32(), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => ReinterpretCast(value), - TypeCode.Int16 => ReinterpretCast(value), - TypeCode.UInt16 => ReinterpretCast(value), - TypeCode.Int32 => ReinterpretCast(value), - TypeCode.UInt32 => ReinterpretCast(value), - TypeCode.Int64 => ReinterpretCast(value), - TypeCode.UInt64 => Convert.ToInt64(ReinterpretCast(value)), - TypeCode.Single => Convert.ToInt64(ReinterpretCast(value)), - TypeCode.Double => Convert.ToInt64(ReinterpretCast(value)), - _ => value.ChangeType(), - }; + if (AreCompatible()) + return Unsafe.BitCast(value); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ulong ToUInt64Unchecked(this T value) - where T : struct, Enum - => Unsafe.SizeOf() switch + return GetTypeCode() switch { - sizeof(byte) => ReinterpretCast(value), - sizeof(ushort) => ReinterpretCast(value), - sizeof(uint) => ReinterpretCast(value), - sizeof(ulong) => ReinterpretCast(value), - _ => throw new InvalidCastException(), + TypeCode.Byte => Convert(value), + TypeCode.SByte => Convert(value), + TypeCode.Int16 => Convert(value), + TypeCode.UInt16 => Convert(value), + TypeCode.Int32 => Convert(value), + TypeCode.UInt32 => Convert(value), + TypeCode.Int64 => Convert(value), + TypeCode.UInt64 => Convert(value), + _ => BasicExtensions.ChangeType(value), }; - /// - /// Converts enum value into primitive type . - /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ToInt32(this T value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => 0, - TypeCode.Char => ReinterpretCast(value), - TypeCode.Decimal => Convert.ToInt32(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToInt32(), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => ReinterpretCast(value), - TypeCode.Int16 => ReinterpretCast(value), - TypeCode.UInt16 => ReinterpretCast(value), - TypeCode.Int32 => ReinterpretCast(value), - TypeCode.UInt32 => Convert.ToInt32(ReinterpretCast(value)), - TypeCode.Int64 => Convert.ToInt32(ReinterpretCast(value)), - TypeCode.UInt64 => Convert.ToInt32(ReinterpretCast(value)), - TypeCode.Single => Convert.ToInt32(ReinterpretCast(value)), - TypeCode.Double => Convert.ToInt32(ReinterpretCast(value)), - _ => value.ChangeType(), - }; - - /// - /// Converts enum value into primitive type . - /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static short ToInt16(this T value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => 0, - TypeCode.Char => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.Decimal => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToByte(), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => ReinterpretCast(value), - TypeCode.Int16 => ReinterpretCast(value), - TypeCode.UInt16 => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.Int32 => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.UInt32 => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.Int64 => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.UInt64 => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.Single => Convert.ToInt16(ReinterpretCast(value)), - TypeCode.Double => Convert.ToInt16(ReinterpretCast(value)), - _ => value.ChangeType(), - }; - - /// - /// Converts enum value into primitive type . - /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte ToByte(this T value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => 0, - TypeCode.Char => Convert.ToByte(ReinterpretCast(value)), - TypeCode.Decimal => Convert.ToByte(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToByte(), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => Convert.ToByte(ReinterpretCast(value)), - TypeCode.Int16 => Convert.ToByte(ReinterpretCast(value)), - TypeCode.UInt16 => Convert.ToByte(ReinterpretCast(value)), - TypeCode.Int32 => Convert.ToByte(ReinterpretCast(value)), - TypeCode.UInt32 => Convert.ToByte(ReinterpretCast(value)), - TypeCode.Int64 => Convert.ToByte(ReinterpretCast(value)), - TypeCode.UInt64 => Convert.ToByte(ReinterpretCast(value)), - TypeCode.Single => Convert.ToByte(ReinterpretCast(value)), - TypeCode.Double => Convert.ToByte(ReinterpretCast(value)), - _ => value.ChangeType(), - }; - - /// - /// Converts enum value into primitive type . - /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong ToUInt64(this T value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => 0UL, - TypeCode.Char => ReinterpretCast(value), - TypeCode.Decimal => Convert.ToUInt64(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToByte(), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => Convert.ToUInt64(ReinterpretCast(value)), - TypeCode.Int16 => Convert.ToUInt64(ReinterpretCast(value)), - TypeCode.UInt16 => ReinterpretCast(value), - TypeCode.Int32 => Convert.ToUInt64(ReinterpretCast(value)), - TypeCode.UInt32 => ReinterpretCast(value), - TypeCode.Int64 => Convert.ToUInt64(ReinterpretCast(value)), - TypeCode.UInt64 => ReinterpretCast(value), - TypeCode.Single => Convert.ToUInt64(ReinterpretCast(value)), - TypeCode.Double => Convert.ToUInt64(ReinterpretCast(value)), - _ => value.ChangeType(), - }; + static TEnum Convert(TValue value) + where TOther : unmanaged, INumberBase + => Unsafe.BitCast(TOther.CreateChecked(value)); + } /// - /// Converts enum value into primitive type . + /// Converts enum value to numeric value. /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . + /// The type of the enum. + /// The type of numeric value. + /// The enum value to convert. + /// The numeric equivalent of . [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint ToUInt32(this T value) - where T : struct, Enum => EnumTypeCode.Value switch + public static TValue FromEnum(TEnum value) + where TValue : unmanaged, INumberBase, IConvertible + where TEnum : struct, Enum { - TypeCode.Empty => 0, - TypeCode.Char => ReinterpretCast(value), - TypeCode.Decimal => Convert.ToUInt32(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToByte(), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => Convert.ToUInt32(ReinterpretCast(value)), - TypeCode.Int16 => Convert.ToUInt32(ReinterpretCast(value)), - TypeCode.UInt16 => ReinterpretCast(value), - TypeCode.Int32 => Convert.ToUInt32(ReinterpretCast(value)), - TypeCode.UInt32 => ReinterpretCast(value), - TypeCode.Int64 => Convert.ToUInt32(ReinterpretCast(value)), - TypeCode.UInt64 => Convert.ToUInt32(ReinterpretCast(value)), - TypeCode.Single => Convert.ToUInt32(ReinterpretCast(value)), - TypeCode.Double => Convert.ToUInt32(ReinterpretCast(value)), - _ => value.ChangeType(), - }; + if (AreCompatible()) + return Unsafe.BitCast(value); - /// - /// Converts enum value into primitive type . - /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort ToUInt16(this T value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => 0, - TypeCode.Char => ReinterpretCast(value), - TypeCode.Decimal => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToByte(), - TypeCode.Byte => ReinterpretCast(value), - TypeCode.SByte => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.Int16 => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.UInt16 => ReinterpretCast(value), - TypeCode.Int32 => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.UInt32 => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.Int64 => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.UInt64 => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.Single => Convert.ToUInt16(ReinterpretCast(value)), - TypeCode.Double => Convert.ToUInt16(ReinterpretCast(value)), - _ => value.ChangeType(), - }; + return GetTypeCode() switch + { + TypeCode.Byte => Convert(value), + TypeCode.SByte => Convert(value), + TypeCode.Int16 => Convert(value), + TypeCode.UInt16 => Convert(value), + TypeCode.Int32 => Convert(value), + TypeCode.UInt32 => Convert(value), + TypeCode.Int64 => Convert(value), + TypeCode.UInt64 => Convert(value), + _ => BasicExtensions.ChangeType(value), + }; - /// - /// Converts enum value into primitive type . - /// - /// Type of the enum value to be converted. - /// Enum value to be converted. - /// Enum value represented as . - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static sbyte ToSByte(this T value) - where T : struct, Enum => EnumTypeCode.Value switch - { - TypeCode.Empty => 0, - TypeCode.Char => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.Decimal => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.Boolean => ReinterpretCast(value).ToSByte(), - TypeCode.Byte => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.SByte => ReinterpretCast(value), - TypeCode.Int16 => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.UInt16 => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.Int32 => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.UInt32 => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.Int64 => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.UInt64 => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.Single => Convert.ToSByte(ReinterpretCast(value)), - TypeCode.Double => Convert.ToSByte(ReinterpretCast(value)), - _ => value.ChangeType(), - }; + static TValue Convert(TEnum value) + where TOther : unmanaged, INumberBase + => TValue.CreateChecked(Unsafe.BitCast(value)); + } } \ No newline at end of file diff --git a/src/DotNext/Runtime/Intrinsics.cs b/src/DotNext/Runtime/Intrinsics.cs index ebf40e7b0..4276f4d6c 100644 --- a/src/DotNext/Runtime/Intrinsics.cs +++ b/src/DotNext/Runtime/Intrinsics.cs @@ -344,14 +344,6 @@ public static nuint GetLength(this Array array) return Return(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static TOutput ReinterpretCast(TInput input) - { - Debug.Assert(Unsafe.SizeOf() == Unsafe.SizeOf()); - - return Unsafe.As(ref input); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Span ReinterpretCast(Span input) where TInput : unmanaged diff --git a/src/DotNext/Threading/AtomicEnum.cs b/src/DotNext/Threading/AtomicEnum.cs deleted file mode 100644 index bc3b5b99f..000000000 --- a/src/DotNext/Threading/AtomicEnum.cs +++ /dev/null @@ -1,290 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using static Runtime.Intrinsics; - -/// -/// Provides basic atomic operations for arbitrary enum type. -/// -public static class AtomicEnum -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The enum type. - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TEnum VolatileRead(this ref TEnum value) - where TEnum : struct, Enum - => Unsafe.SizeOf() switch - { - sizeof(byte) => ReinterpretCast(Volatile.Read(ref Unsafe.As(ref value))), - sizeof(ushort) => ReinterpretCast(Volatile.Read(ref Unsafe.As(ref value))), - sizeof(uint) => ReinterpretCast(Volatile.Read(ref Unsafe.As(ref value))), - sizeof(ulong) => ReinterpretCast(Volatile.Read(ref Unsafe.As(ref value))), - _ => value, - }; - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The enum type. - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this ref TEnum value, TEnum newValue) - where TEnum : struct, Enum - { - switch (Unsafe.SizeOf()) - { - case sizeof(byte): - Volatile.Write(ref Unsafe.As(ref value), ReinterpretCast(newValue)); - break; - case sizeof(ushort): - Volatile.Write(ref Unsafe.As(ref value), ReinterpretCast(newValue)); - break; - case sizeof(uint): - Volatile.Write(ref Unsafe.As(ref value), ReinterpretCast(newValue)); - break; - case sizeof(ulong): - Volatile.Write(ref Unsafe.As(ref value), ReinterpretCast(newValue)); - break; - } - } -} - -/// -/// Represents atomic enum value. -/// -/// The enum type. -[SuppressMessage("Usage", "CA2231")] -public struct AtomicEnum : IEquatable - where TEnum : struct, Enum -{ - // TODO: Rewrite when https://github.com/dotnet/runtime/issues/64658 becomes available - private ulong value; - - /// - /// Initializes a new atomic boolean container with initial value. - /// - /// Initial value of the atomic boolean. - public AtomicEnum(TEnum value) => this.value = value.ToUInt64Unchecked(); - - /// - /// Gets or sets enum value in volatile manner. - /// - public TEnum Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - readonly get => value.VolatileRead().ToEnumUnchecked(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set => this.value.VolatileWrite(value.ToUInt64Unchecked()); - } - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// The new value. - /// The expected value. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TEnum CompareExchange(TEnum update, TEnum expected) - => Interlocked.CompareExchange(ref value, update.ToUInt64Unchecked(), expected.ToUInt64Unchecked()).ToEnumUnchecked(); - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CompareAndSet(TEnum expected, TEnum update) => EqualityComparer.Default.Equals(CompareExchange(update, expected), expected); - - /// - /// Modifies the current value atomically. - /// - /// A new value to be stored into this container. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TEnum GetAndSet(TEnum update) => value.GetAndSet(update.ToUInt64Unchecked()).ToEnumUnchecked(); - - /// - /// Modifies the current value atomically. - /// - /// A new value to be stored into this container. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TEnum SetAndGet(TEnum update) - { - Value = update; - return update; - } - - private (TEnum OldValue, TEnum NewValue) Update(TUpdater updater) - where TUpdater : struct, ISupplier - { - TEnum oldValue, newValue, tmp = Value; - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while (!EqualityComparer.Default.Equals(tmp = CompareExchange(newValue, oldValue), oldValue)); - - return (oldValue, newValue); - } - - private (TEnum OldValue, TEnum NewValue) Accumulate(TEnum x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - TEnum oldValue, newValue, tmp = Value; - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while (!EqualityComparer.Default.Equals(tmp = CompareExchange(newValue, oldValue), oldValue)); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - public TEnum AccumulateAndGet(TEnum x, Func accumulator) - => Accumulate>(x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [CLSCompliant(false)] - public unsafe TEnum AccumulateAndGet(TEnum x, delegate* accumulator) - => Accumulate>(x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - public TEnum GetAndAccumulate(TEnum x, Func accumulator) - => Accumulate>(x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [CLSCompliant(false)] - public unsafe TEnum GetAndAccumulate(TEnum x, delegate* accumulator) - => Accumulate>(x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// A side-effect-free function. - /// The updated value. - public TEnum UpdateAndGet(Func updater) - => Update>(updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// A side-effect-free function. - /// The updated value. - [CLSCompliant(false)] - public unsafe TEnum UpdateAndGet(delegate* updater) - => Update>(updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// A side-effect-free function. - /// The original value. - public TEnum GetAndUpdate(Func updater) - => Update>(updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// A side-effect-free function. - /// The original value. - [CLSCompliant(false)] - public unsafe TEnum GetAndUpdate(delegate* updater) - => Update>(updater).OldValue; - - /// - /// Determines whether stored value is equal to - /// value passed as argument. - /// - /// Other value to compare. - /// , if stored value is equal to other value; otherwise, . - public readonly bool Equals(TEnum other) => EqualityComparer.Default.Equals(Value, other); - - /// - /// Determines whether stored value is equal to - /// value as the passed argument. - /// - /// Other value to compare. - /// , if stored value is equal to other value; otherwise, . - public override readonly bool Equals([NotNullWhen(true)] object? other) => other switch - { - TEnum b => Equals(b), - AtomicEnum b => Equals(b.Value), - _ => false, - }; - - /// - /// Computes hash code for the stored value. - /// - /// The hash code of the stored boolean value. - public override readonly int GetHashCode() => Value.GetHashCode(); - - /// - /// Converts the value in this container to its textual representation. - /// - /// The value in this container converted to string. - public override readonly string ToString() => Value.ToString(); -} \ No newline at end of file diff --git a/src/DotNext/UndefinedResultException.cs b/src/DotNext/UndefinedResultException.cs index f49042c4b..edba2a6ea 100644 --- a/src/DotNext/UndefinedResultException.cs +++ b/src/DotNext/UndefinedResultException.cs @@ -9,10 +9,7 @@ public sealed class UndefinedResultException : Exception { internal UndefinedResultException(TError errorCode) : base(ExceptionMessages.NoResult(errorCode)) - { - ErrorCode = errorCode; - HResult = errorCode.ToInt32(); - } + => ErrorCode = errorCode; /// /// Gets the error code associated with the exception. diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs index abd4d7b6d..dade85ed3 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs @@ -10,7 +10,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.Http; using Messaging; using Net.Http; using Runtime.Serialization; -using Threading; using IClientMetricsCollector = Metrics.IClientMetricsCollector; using Timestamp = Diagnostics.Timestamp; @@ -26,7 +25,7 @@ internal sealed class RaftClusterMember : HttpPeerClient, IRaftClusterMember, IS internal readonly ClusterMemberId Id; internal readonly UriEndPoint EndPoint; private readonly KeyValuePair cachedRemoteAddressAttribute; - private AtomicEnum status; + private volatile ClusterMemberStatus status; private volatile MemberMetadata? metadata; private InvocationList>> memberStatusChanged; private IRaftClusterMember.ReplicationState state; @@ -44,7 +43,6 @@ internal RaftClusterMember(IHostingContext context, UriEndPoint remoteMember) : base(remoteMember.Uri, context.CreateHttpHandler(), true) { this.context = context; - status = new(ClusterMemberStatus.Unknown); EndPoint = remoteMember; cachedRemoteAddressAttribute = new(IRaftClusterMember.RemoteAddressMeterAttributeName, remoteMember.ToString()); Id = ClusterMemberId.FromEndPoint(remoteMember); @@ -282,8 +280,11 @@ async ValueTask> IClusterMember.GetMetadataA public ClusterMemberStatus Status { - get => IsRemote ? status.Value : ClusterMemberStatus.Available; + get => IsRemote ? status : ClusterMemberStatus.Available; + +#pragma warning disable CS0420 private set => IClusterMember.OnMemberStatusChanged(this, ref status, value, memberStatusChanged); +#pragma warning restore CS0420 } internal Task SendMessageAsync(IMessage message, MessageReader responseReader, bool respectLeadership, CancellationToken token) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs index 69e0d680a..267984621 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs @@ -25,7 +25,7 @@ public abstract class RaftClusterMember : Disposable, IRaftClusterMember internal readonly ClusterMemberId Id; private protected readonly KeyValuePair cachedRemoteAddressAttribute; private volatile IReadOnlyDictionary? metadataCache; - private AtomicEnum status; + private volatile ClusterMemberStatus status; private InvocationList>> statusChangedHandlers; private IRaftClusterMember.ReplicationState state; @@ -36,7 +36,6 @@ private protected RaftClusterMember(ILocalMember localMember, EndPoint endPoint) this.localMember = localMember; EndPoint = endPoint; - status = new AtomicEnum(ClusterMemberStatus.Unknown); Id = ClusterMemberId.FromEndPoint(endPoint); requestTimeout = TimeSpan.FromSeconds(30); cachedRemoteAddressAttribute = new(IRaftClusterMember.RemoteAddressMeterAttributeName, endPoint.ToString()); @@ -81,8 +80,11 @@ internal TimeSpan RequestTimeout /// public ClusterMemberStatus Status { - get => IsRemote ? status.Value : ClusterMemberStatus.Available; + get => IsRemote ? status : ClusterMemberStatus.Available; + +#pragma warning disable CS0420 private protected set => IClusterMember.OnMemberStatusChanged(this, ref status, value, statusChangedHandlers); +#pragma warning restore CS0420 } /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/IClusterMember.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/IClusterMember.cs index c9d655c67..1a3925694 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/IClusterMember.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/IClusterMember.cs @@ -1,4 +1,6 @@ -namespace DotNext.Net.Cluster; +using System.Runtime.CompilerServices; + +namespace DotNext.Net.Cluster; using Collections.Specialized; using Threading; @@ -61,10 +63,10 @@ public interface IClusterMember : IPeer /// The member status holder. /// A new state of the member. /// A collection of event handlers. - protected static void OnMemberStatusChanged(TMember member, ref AtomicEnum status, ClusterMemberStatus newState, InvocationList>> memberStatusChanged) + protected static void OnMemberStatusChanged(TMember member, ref ClusterMemberStatus status, ClusterMemberStatus newState, InvocationList>> memberStatusChanged) where TMember : class, IClusterMember { - var previousState = status.GetAndSet(newState); + var previousState = (ClusterMemberStatus)Interlocked.Exchange(ref Unsafe.As(ref status), (int)newState); if (previousState != newState && !memberStatusChanged.IsEmpty) memberStatusChanged.Invoke(new ClusterMemberStatusChangedEventArgs(member, previousState, newState)); } From ab7e025087d3e195e948f25a32a3c02468469625 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 09:52:18 +0200 Subject: [PATCH 054/155] Migration to primary ctor --- src/DotNext/Threading/AtomicBoolean.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/DotNext/Threading/AtomicBoolean.cs b/src/DotNext/Threading/AtomicBoolean.cs index 9fea4e280..c326c9049 100644 --- a/src/DotNext/Threading/AtomicBoolean.cs +++ b/src/DotNext/Threading/AtomicBoolean.cs @@ -12,8 +12,12 @@ namespace DotNext.Threading; /// /// Represents atomic boolean. /// +/// +/// Initializes a new atomic boolean container with initial value. +/// +/// Initial value of the atomic boolean. [SuppressMessage("Usage", "CA2231")] -public struct AtomicBoolean : IEquatable +public struct AtomicBoolean(bool value) : IEquatable { [StructLayout(LayoutKind.Auto)] private readonly struct Negation : ISupplier @@ -21,13 +25,7 @@ public struct AtomicBoolean : IEquatable bool ISupplier.Invoke(bool value) => !value; } - private int value; - - /// - /// Initializes a new atomic boolean container with initial value. - /// - /// Initial value of the atomic boolean. - public AtomicBoolean(bool value) => this.value = value.ToInt32(); + private int value = value.ToInt32(); /// /// Gets or sets boolean value in volatile manner. From 05ad72c39adaf4ceddb8dddadcc4103e37006310 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 17:19:24 +0200 Subject: [PATCH 055/155] Merge multiple classes with atomic operations --- src/DotNext.IO/IO/Log/SingleEntryProducer.cs | 4 +- ...ueTypeTests.cs => BitwiseComparerTests.cs} | 24 +- .../Runtime/InteropServices/PointerTests.cs | 218 ----------- src/DotNext.Tests/Runtime/IntrinsicsTests.cs | 22 -- .../Threading/AsyncDelegateTests.cs | 6 +- src/DotNext.Tests/Threading/AtomicTests.cs | 289 ++++----------- .../Tasks/ValueTaskSynchronizerTests.cs | 2 +- .../Threading/AsyncBarrier.cs | 12 +- .../Threading/AsyncCountdownEvent.cs | 4 +- .../Threading/AsyncCounter.cs | 37 +- .../Threading/AsyncReaderWriterLock.cs | 8 +- .../Threading/AsyncSharedLock.cs | 12 +- .../Channels/PersistentChannelReader.cs | 2 +- .../Threading/Tasks/TaskCompletionPipe.cs | 2 +- .../Threading/AtomicPointer.Boolean.cs | 32 -- .../Threading/AtomicPointer.Double.cs | 195 ---------- .../Threading/AtomicPointer.Int16.cs | 32 -- .../Threading/AtomicPointer.Int32.cs | 255 ------------- .../Threading/AtomicPointer.Int64.cs | 255 ------------- .../Threading/AtomicPointer.Int8.cs | 34 -- .../Threading/AtomicPointer.IntPtr.cs | 255 ------------- .../Threading/AtomicPointer.Single.cs | 195 ---------- .../Threading/AtomicPointer.UInt16.cs | 34 -- .../Threading/AtomicPointer.UInt32.cs | 264 ------------- .../Threading/AtomicPointer.UInt64.cs | 264 ------------- .../Threading/AtomicPointer.UInt8.cs | 32 -- .../Threading/AtomicPointer.UIntPtr.cs | 34 -- src/DotNext.Unsafe/Threading/AtomicPointer.cs | 8 - src/DotNext/BasicExtensions.cs | 41 --- .../Specialized/ConcurrentTypeMap.cs | 14 +- .../Collections/Specialized/SingletonList.cs | 2 +- src/DotNext/Diagnostics/Timestamp.cs | 9 +- .../Caching/ConcurrentCache.Dictionary.cs | 12 +- src/DotNext/Runtime/Intrinsics.cs | 11 - src/DotNext/Threading/Atomic.Boolean.cs | 282 ++++++++++++++ src/DotNext/Threading/Atomic.Double.cs | 118 ++++++ src/DotNext/Threading/Atomic.Int.cs | 118 ++++++ src/DotNext/Threading/Atomic.Int32.cs | 118 ++++++ src/DotNext/Threading/Atomic.Int64.cs | 118 ++++++ src/DotNext/Threading/Atomic.Single.cs | 118 ++++++ src/DotNext/Threading/Atomic.UInt.cs | 126 +++++++ src/DotNext/Threading/Atomic.UInt32.cs | 126 +++++++ src/DotNext/Threading/Atomic.UInt64.cs | 126 +++++++ src/DotNext/Threading/Atomic.cs | 135 ++++++- src/DotNext/Threading/AtomicBoolean.cs | 302 --------------- src/DotNext/Threading/AtomicDouble.cs | 256 ------------- src/DotNext/Threading/AtomicInt32.cs | 318 ---------------- src/DotNext/Threading/AtomicInt64.cs | 318 ---------------- src/DotNext/Threading/AtomicIntPtr.cs | 348 ------------------ src/DotNext/Threading/AtomicReference.cs | 180 --------- src/DotNext/Threading/AtomicSingle.cs | 256 ------------- src/DotNext/Threading/AtomicUInt32.cs | 315 ---------------- src/DotNext/Threading/AtomicUInt64.cs | 315 ---------------- src/DotNext/Threading/BitwiseAnd.cs | 9 - src/DotNext/Threading/BitwiseOr.cs | 9 - src/DotNext/Threading/BitwiseXor.cs | 17 - src/DotNext/Threading/Decrement.cs | 13 - .../Threading/IInterlockedOperations.cs | 41 +++ src/DotNext/Threading/Increment.cs | 13 - src/DotNext/Threading/ReaderWriterSpinLock.cs | 6 +- .../Http/HttpPeerController.Disconnect.cs | 5 +- .../Http/HttpPeerController.Neighbor.cs | 5 +- .../Net/Cluster/ClusterMemberId.cs | 3 +- .../Consensus/Raft/ConsensusOnlyState.cs | 69 ++-- .../Raft/DiskBasedStateMachine.Snapshot.cs | 4 +- .../Consensus/Raft/DiskBasedStateMachine.cs | 5 +- .../Consensus/Raft/MemoryBasedStateMachine.cs | 9 +- .../Raft/PersistentState.Internal.cs | 8 +- .../Raft/PersistentState.NodeState.cs | 20 +- .../Raft/PersistentState.SessionManagement.cs | 2 +- .../Consensus/Raft/RaftCluster.Membership.cs | 2 +- .../Client.AppendEntries.cs | 3 +- .../ProtocolStreamExtensions.cs | 6 +- .../Server.LogEntryProducer.cs | 2 +- .../Datagram/ResignExchange.cs | 3 +- .../Datagram/ServerExchange.Resign.cs | 4 +- .../Datagram/SynchronizeExchange.cs | 5 +- .../Raft/TransportServices/Result.cs | 6 +- .../Cluster/Consensus/Raft/Udp/UdpClient.cs | 3 +- .../HyParView/PeerController.Command.cs | 9 +- .../Net/EndPointFormatter.cs | 5 +- .../RaftNode/SimplePersistentState.cs | 7 +- 82 files changed, 1672 insertions(+), 5234 deletions(-) rename src/DotNext.Tests/{ValueTypeTests.cs => BitwiseComparerTests.cs} (87%) delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.Boolean.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.Double.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.Int16.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.Int32.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.Int64.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.Int8.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.IntPtr.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.Single.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.UInt16.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.UInt32.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.UInt64.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.UInt8.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.UIntPtr.cs delete mode 100644 src/DotNext.Unsafe/Threading/AtomicPointer.cs create mode 100644 src/DotNext/Threading/Atomic.Boolean.cs create mode 100644 src/DotNext/Threading/Atomic.Double.cs create mode 100644 src/DotNext/Threading/Atomic.Int.cs create mode 100644 src/DotNext/Threading/Atomic.Int32.cs create mode 100644 src/DotNext/Threading/Atomic.Int64.cs create mode 100644 src/DotNext/Threading/Atomic.Single.cs create mode 100644 src/DotNext/Threading/Atomic.UInt.cs create mode 100644 src/DotNext/Threading/Atomic.UInt32.cs create mode 100644 src/DotNext/Threading/Atomic.UInt64.cs delete mode 100644 src/DotNext/Threading/AtomicBoolean.cs delete mode 100644 src/DotNext/Threading/AtomicDouble.cs delete mode 100644 src/DotNext/Threading/AtomicInt32.cs delete mode 100644 src/DotNext/Threading/AtomicInt64.cs delete mode 100644 src/DotNext/Threading/AtomicIntPtr.cs delete mode 100644 src/DotNext/Threading/AtomicReference.cs delete mode 100644 src/DotNext/Threading/AtomicSingle.cs delete mode 100644 src/DotNext/Threading/AtomicUInt32.cs delete mode 100644 src/DotNext/Threading/AtomicUInt64.cs delete mode 100644 src/DotNext/Threading/BitwiseAnd.cs delete mode 100644 src/DotNext/Threading/BitwiseOr.cs delete mode 100644 src/DotNext/Threading/BitwiseXor.cs delete mode 100644 src/DotNext/Threading/Decrement.cs create mode 100644 src/DotNext/Threading/IInterlockedOperations.cs delete mode 100644 src/DotNext/Threading/Increment.cs diff --git a/src/DotNext.IO/IO/Log/SingleEntryProducer.cs b/src/DotNext.IO/IO/Log/SingleEntryProducer.cs index 4477f66ae..3c52c7311 100644 --- a/src/DotNext.IO/IO/Log/SingleEntryProducer.cs +++ b/src/DotNext.IO/IO/Log/SingleEntryProducer.cs @@ -1,3 +1,5 @@ +using System.Runtime.CompilerServices; + namespace DotNext.IO.Log; internal sealed class SingleEntryProducer : ILogEntryProducer @@ -11,7 +13,7 @@ internal SingleEntryProducer(TEntry entry) available = true; } - long ILogEntryProducer.RemainingCount => available.ToInt32(); + long ILogEntryProducer.RemainingCount => Unsafe.BitCast(available); public TEntry Current { get; } diff --git a/src/DotNext.Tests/ValueTypeTests.cs b/src/DotNext.Tests/BitwiseComparerTests.cs similarity index 87% rename from src/DotNext.Tests/ValueTypeTests.cs rename to src/DotNext.Tests/BitwiseComparerTests.cs index cf2420cb3..5999450ad 100644 --- a/src/DotNext.Tests/ValueTypeTests.cs +++ b/src/DotNext.Tests/BitwiseComparerTests.cs @@ -2,30 +2,8 @@ namespace DotNext; -public sealed class ValueTypeTests : Test +public sealed class BitwiseComparerTests : Test { - [Fact] - public static void BoolToIntConversion() - { - Equal(1, true.ToInt32()); - Equal(0, false.ToInt32()); - } - - [Fact] - public static void BoolToByteConversion() - { - Equal(1, true.ToByte()); - Equal(0, false.ToByte()); - } - - [Fact] - public static void IntToBoolConversion() - { - True(1.ToBoolean()); - True(42.ToBoolean()); - False(0.ToBoolean()); - } - [Fact] public static void BitwiseEqualityCheck() { diff --git a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs index a5d5be265..a08f3bcaa 100644 --- a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs +++ b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs @@ -98,224 +98,6 @@ public static unsafe void Swap() Equal(1, array[1]); } - [Fact] - public static unsafe void VolatileReadWriteUInt64() - { - Pointer ptr = stackalloc ulong[3]; - ptr.VolatileWrite(1); - Equal(1UL, ptr.Value); - Equal(1UL, ptr.Get()); - ptr.AddAndGetValue(10); - Equal(11UL, ptr.Value); - Equal(11UL, ptr.Get()); - Equal(11UL, ptr.VolatileRead()); - ptr.DecrementValue(); - Equal(10UL, ptr.Value); - Equal(10UL, ptr.VolatileRead()); - True(ptr.CompareAndSetValue(10, 12)); - Equal(12UL, ptr.Value); - False(ptr.CompareAndSetValue(10, 20)); - Equal(12UL, ptr.Value); - static ulong Sum(ulong x, ulong y) => x + y; - Equal(32UL, ptr.AccumulateAndGetValue(20L, Sum)); - Equal(32UL, ptr.Value); - Equal(32UL, ptr.GetAndAccumulateValue(8L, &Sum)); - Equal(40UL, ptr.Value); - } - - [Fact] - public static unsafe void VolatileReadWriteUInt32() - { - Pointer ptr = stackalloc uint[3]; - ptr.VolatileWrite(1); - Equal(1U, ptr.Value); - ptr.AddAndGetValue(10); - Equal(11U, ptr.Value); - Equal(11U, ptr.VolatileRead()); - ptr.DecrementValue(); - Equal(10U, ptr.Value); - Equal(10U, ptr.VolatileRead()); - True(ptr.CompareAndSetValue(10, 12)); - Equal(12U, ptr.Value); - False(ptr.CompareAndSetValue(10, 20)); - Equal(12U, ptr.Value); - static uint Sum(uint x, uint y) => x + y; - Equal(32U, ptr.AccumulateAndGetValue(20, Sum)); - Equal(32U, ptr.Value); - Equal(32U, ptr.GetAndAccumulateValue(8, &Sum)); - Equal(40U, ptr.Value); - } - - [Fact] - public static unsafe void VolatileReadWriteInt64() - { - Pointer ptr = stackalloc long[3]; - ptr.VolatileWrite(1); - Equal(1, ptr.Value); - Equal(1, ptr.Get()); - ptr.AddAndGetValue(10); - Equal(11, ptr.Value); - Equal(11, ptr.Get()); - Equal(11, ptr.VolatileRead()); - ptr.DecrementValue(); - Equal(10, ptr.Value); - Equal(10, ptr.VolatileRead()); - True(ptr.CompareAndSetValue(10, 12)); - Equal(12, ptr.Value); - False(ptr.CompareAndSetValue(10, 20)); - Equal(12, ptr.Value); - static long Sum(long x, long y) => x + y; - Equal(32L, ptr.AccumulateAndGetValue(20L, Sum)); - Equal(32L, ptr.Value); - Equal(32L, ptr.GetAndAccumulateValue(8L, &Sum)); - Equal(40L, ptr.Value); - } - - [Fact] - public static unsafe void VolatileReadWriteInt32() - { - Pointer ptr = stackalloc int[3]; - ptr.VolatileWrite(1); - Equal(1, ptr.Value); - ptr.AddAndGetValue(10); - Equal(11, ptr.Value); - Equal(11, ptr.VolatileRead()); - ptr.DecrementValue(); - Equal(10, ptr.Value); - Equal(10, ptr.VolatileRead()); - True(ptr.CompareAndSetValue(10, 12)); - Equal(12, ptr.Value); - False(ptr.CompareAndSetValue(10, 20)); - Equal(12, ptr.Value); - static int Sum(int x, int y) => x + y; - Equal(32, ptr.AccumulateAndGetValue(20, Sum)); - Equal(32, ptr.Value); - Equal(32, ptr.GetAndAccumulateValue(8, &Sum)); - Equal(40, ptr.Value); - } - - [Fact] - public static unsafe void VolatileReadWriteIntPtr() - { - Pointer ptr = stackalloc IntPtr[3]; - ptr.VolatileWrite(new IntPtr(1)); - Equal(new IntPtr(1), ptr.Value); - ptr.AddAndGetValue(new IntPtr(10)); - Equal(new IntPtr(11), ptr.Value); - Equal(new IntPtr(11), ptr.VolatileRead()); - ptr.DecrementValue(); - Equal(new IntPtr(10), ptr.Value); - Equal(new IntPtr(10), ptr.VolatileRead()); - True(ptr.CompareAndSetValue(new IntPtr(10), new IntPtr(12))); - Equal(new IntPtr(12), ptr.Value); - False(ptr.CompareAndSetValue(new IntPtr(10), new IntPtr(20))); - Equal(new IntPtr(12), ptr.Value); - static nint Sum(nint x, nint y) => x + y; - Equal(new IntPtr(32), ptr.AccumulateAndGetValue(new IntPtr(20), Sum)); - Equal(new IntPtr(32), ptr.Value); - Equal(new IntPtr(32), ptr.GetAndAccumulateValue(new IntPtr(8), &Sum)); - Equal(new IntPtr(40), ptr.Value); - } - - [Fact] - public static unsafe void VolatileReadWriteFloat32() - { - Pointer ptr = stackalloc float[3]; - ptr.VolatileWrite(1F); - Equal(1F, ptr.Value); - ptr.AddAndGetValue(10F); - Equal(11F, ptr.Value); - Equal(11F, ptr.VolatileRead()); - ptr.DecrementValue(); - Equal(10F, ptr.Value); - Equal(10F, ptr.VolatileRead()); - True(ptr.CompareAndSetValue(10F, 12F)); - Equal(12F, ptr.Value); - False(ptr.CompareAndSetValue(10F, 20F)); - Equal(12F, ptr.Value); - } - - [Fact] - public static unsafe void VolatileReadWriteFloat64() - { - Pointer ptr = stackalloc double[3]; - ptr.VolatileWrite(1D); - Equal(1D, ptr.Value); - ptr.AddAndGetValue(10F); - Equal(11D, ptr.Value); - Equal(11D, ptr.VolatileRead()); - ptr.DecrementValue(); - Equal(10D, ptr.Value); - Equal(10D, ptr.VolatileRead()); - True(ptr.CompareAndSetValue(10D, 12D)); - Equal(12D, ptr.Value); - False(ptr.CompareAndSetValue(10D, 20D)); - Equal(12D, ptr.Value); - } - - [Fact] - public static unsafe void VolatileReadWriteUIntPtr() - { - Pointer ptr = stackalloc UIntPtr[3]; - ptr.VolatileWrite(new UIntPtr(1)); - Equal(new UIntPtr(1), (UIntPtr)ptr.Value); - ptr.Value = ptr.Value + 10; - Equal(new UIntPtr(11), (UIntPtr)ptr.Value); - Equal(new UIntPtr(11), ptr.VolatileRead()); - } - - [Fact] - public static unsafe void VolatileReadWriteInt16() - { - Pointer ptr = stackalloc short[3]; - ptr.VolatileWrite(1); - Equal(1, ptr.Value); - Equal(1, ptr.Get()); - ptr.Value += 10; - Equal(11, ptr.Value); - Equal(11, ptr.Get()); - Equal(11, ptr.VolatileRead()); - } - - [Fact] - public static unsafe void VolatileReadWriteUInt16() - { - Pointer ptr = stackalloc ushort[3]; - ptr.VolatileWrite(1); - Equal(1, ptr.Value); - Equal(1, ptr.Get()); - ptr.Value += 10; - Equal(11, ptr.Value); - Equal(11, ptr.Get()); - Equal(11, ptr.VolatileRead()); - } - - [Fact] - public static unsafe void VolatileReadWriteUInt8() - { - Pointer ptr = stackalloc byte[3]; - ptr.VolatileWrite(1); - Equal(1, ptr.Value); - Equal(1, ptr.Get()); - ptr.Value += 10; - Equal(11, ptr.Value); - Equal(11, ptr.Get()); - Equal(11, ptr.VolatileRead()); - } - - [Fact] - public static unsafe void VolatileReadWriteInt8() - { - Pointer ptr = stackalloc sbyte[3]; - ptr.VolatileWrite(1); - Equal(1, ptr.Value); - Equal(1, ptr.Get()); - ptr.Value += 10; - Equal(11, ptr.Value); - Equal(11, ptr.Get()); - Equal(11, ptr.VolatileRead()); - } - [Fact] public static unsafe void ReadWrite() { diff --git a/src/DotNext.Tests/Runtime/IntrinsicsTests.cs b/src/DotNext.Tests/Runtime/IntrinsicsTests.cs index 9374a6426..876e8c875 100644 --- a/src/DotNext.Tests/Runtime/IntrinsicsTests.cs +++ b/src/DotNext.Tests/Runtime/IntrinsicsTests.cs @@ -218,26 +218,4 @@ public static void TypeAlignment() Equal(8, Intrinsics.AlignOf>()); Equal(IntPtr.Size, Intrinsics.AlignOf()); } - - [Fact] - public static void IsAtomicWrite() - { - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic()); - True(Intrinsics.IsAtomic>()); - - False(Intrinsics.IsAtomic>()); - False(Intrinsics.IsAtomic>()); - False(Intrinsics.IsAtomic()); - } } \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/AsyncDelegateTests.cs b/src/DotNext.Tests/Threading/AsyncDelegateTests.cs index becd84191..3e293af99 100644 --- a/src/DotNext.Tests/Threading/AsyncDelegateTests.cs +++ b/src/DotNext.Tests/Threading/AsyncDelegateTests.cs @@ -11,11 +11,11 @@ private sealed class Accumulator internal int Counter => counter; - internal void IncBy1() => counter.AddAndGet(1); + internal void IncBy1() => Interlocked.Increment(ref counter); - internal void IncBy3() => counter.AddAndGet(3); + internal void IncBy3() => Interlocked.Add(ref counter, 3); - internal void IncBy5() => counter.AddAndGet(5); + internal void IncBy5() => Interlocked.Add(ref counter, 5); internal void Throw() => throw new Exception(); } diff --git a/src/DotNext.Tests/Threading/AtomicTests.cs b/src/DotNext.Tests/Threading/AtomicTests.cs index 03242f2d1..197eb3f64 100644 --- a/src/DotNext.Tests/Threading/AtomicTests.cs +++ b/src/DotNext.Tests/Threading/AtomicTests.cs @@ -5,221 +5,91 @@ public sealed class AtomicTests : Test [Fact] public static void AtomicFloatTest() { - float i = 10F; - Equal(11F, i.IncrementAndGet()); - Equal(10F, i.DecrementAndGet()); - i = 20F; - True(i.CompareAndSet(20F, 30F)); - Equal(30F, i); - False(i.CompareAndSet(20F, 50F)); - Equal(30F, i); - Equal(80F, i.AddAndGet(50F)); - Equal(80F, i); - Equal(80F, i.GetAndAccumulate(10F, static (x, y) => x + y)); - Equal(90F, i); - Equal(10F, i.AccumulateAndGet(80F, static (x, y) => x - y)); - Equal(10F, i); - Equal(10F, i.GetAndSet(25F)); - Equal(25F, i); - Equal(42F, i.UpdateAndGet(static current => 42F)); - Equal(42F, i.GetAndUpdate(static current => 52F)); - Equal(52F, i); + float i = 10; + Equal(80, Atomic.GetAndAccumulate(ref i, 10, static (x, y) => x + y)); + Equal(90, i); + Equal(10, Atomic.AccumulateAndGet(ref i, 80, static (x, y) => x - y)); + Equal(10, i); + + Equal(42, Atomic.UpdateAndGet(ref i, static current => 42)); + Equal(42, Atomic.GetAndUpdate(ref i, static current => 52)); + Equal(52, i); } [Fact] public static void AtomicDoubleTest() { - double i = 10D; - Equal(11D, i.IncrementAndGet()); - Equal(10D, i.DecrementAndGet()); - i = 20D; - True(i.CompareAndSet(20D, 30D)); - Equal(30D, i); - False(i.CompareAndSet(20D, 50D)); - Equal(30D, i); - Equal(80D, i.AddAndGet(50D)); - Equal(80D, i); - Equal(80D, i.GetAndAccumulate(10D, static (x, y) => x + y)); - Equal(90D, i); - Equal(10D, i.AccumulateAndGet(80D, static (x, y) => x - y)); - Equal(10D, i); - Equal(10D, i.GetAndSet(25D)); - Equal(25D, i); - Equal(42D, i.UpdateAndGet(static current => 42D)); - Equal(42D, i.GetAndUpdate(static current => 52D)); - Equal(52D, i); + double i = 10; + Equal(80, Atomic.GetAndAccumulate(ref i, 10, static (x, y) => x + y)); + Equal(90, i); + Equal(10, Atomic.AccumulateAndGet(ref i, 80, static (x, y) => x - y)); + Equal(10, i); + + Equal(42, Atomic.UpdateAndGet(ref i, static current => 42)); + Equal(42, Atomic.GetAndUpdate(ref i, static current => 52)); + Equal(52, i); } [Fact] public static void AtomicIntTest() { var i = 10; - Equal(11, i.IncrementAndGet()); - Equal(10, i.DecrementAndGet()); - i = 20; - True(i.CompareAndSet(20, 30)); - Equal(30, i); - False(i.CompareAndSet(20, 50)); - Equal(30, i); - Equal(80, i.AddAndGet(50)); - Equal(80, i); - Equal(80, i.GetAndAccumulate(10, static (x, y) => x + y)); + Equal(80, Atomic.GetAndAccumulate(ref i, 10, static (x, y) => x + y)); Equal(90, i); - Equal(10, i.AccumulateAndGet(80, static (x, y) => x - y)); + Equal(10, Atomic.AccumulateAndGet(ref i, 80, static (x, y) => x - y)); Equal(10, i); - Equal(10, i.GetAndSet(25)); - Equal(25, i); - Equal(42, i.UpdateAndGet(static current => 42)); - Equal(42, i.GetAndUpdate(static current => 52)); - Equal(52, i); - - Equal(52, i.GetAndBitwiseOr(1)); - Equal(53, i); - i = 52; - Equal(53, i.BitwiseOrAndGet(1)); - Equal(53, i.GetAndBitwiseAnd(1 << 2)); - Equal(4, i); - i = 53; - Equal(4, i.BitwiseAndAndGet(1 << 2)); - - Equal(4, i.GetAndBitwiseXor(3)); - Equal(7, i); - i = 4; - Equal(7, i.BitwiseXorAndGet(3)); + Equal(42, Atomic.UpdateAndGet(ref i, static current => 42)); + Equal(42, Atomic.GetAndUpdate(ref i, static current => 52)); + Equal(52, i); } [Fact] public static void AtomicUIntTest() { uint i = 10U; - Equal(11U, i.IncrementAndGet()); - Equal(10U, i.DecrementAndGet()); - i = 20U; - True(i.CompareAndSet(20U, 30U)); - Equal(30U, i); - False(i.CompareAndSet(20U, 50U)); - Equal(30U, i); - Equal(80U, i.AddAndGet(50U)); - Equal(80U, i); - Equal(80U, i.GetAndAccumulate(10, static (x, y) => x + y)); + Equal(80U, Atomic.GetAndAccumulate(ref i, 10U, static (x, y) => x + y)); Equal(90U, i); - Equal(10U, i.AccumulateAndGet(80, static (x, y) => x - y)); + Equal(10U, Atomic.AccumulateAndGet(ref i, 80U, static (x, y) => x - y)); Equal(10U, i); - Equal(10U, i.GetAndSet(25U)); - Equal(25U, i); - Equal(42U, i.UpdateAndGet(static current => 42U)); - Equal(42U, i.GetAndUpdate(static current => 52U)); - Equal(52U, i); - - Equal(52U, i.GetAndBitwiseOr(1U)); - Equal(53U, i); - i = 52U; - Equal(53U, i.BitwiseOrAndGet(1U)); - - Equal(53U, i.GetAndBitwiseAnd(1U << 2)); - Equal(4U, i); - i = 53U; - Equal(4U, i.BitwiseAndAndGet(1U << 2)); - Equal(4U, i.GetAndBitwiseXor(3U)); - Equal(7U, i); - i = 4U; - Equal(7U, i.BitwiseXorAndGet(3U)); + Equal(42U, Atomic.UpdateAndGet(ref i, static current => 42U)); + Equal(42U, Atomic.GetAndUpdate(ref i, static current => 52U)); + Equal(52U, i); } [Fact] public static void AtomicULongTest() { - ulong i = 10UL; - Equal(11UL, i.IncrementAndGet()); - Equal(10UL, i.DecrementAndGet()); - i = 20UL; - True(i.CompareAndSet(20UL, 30UL)); - Equal(30UL, i); - False(i.CompareAndSet(20UL, 50UL)); - Equal(30UL, i); - Equal(80UL, i.AddAndGet(50UL)); - Equal(80UL, i); - Equal(80UL, i.GetAndAccumulate(10UL, static (x, y) => x + y)); + var i = 10UL; + Equal(80UL, Atomic.GetAndAccumulate(ref i, 10UL, static (x, y) => x + y)); Equal(90UL, i); - Equal(10UL, i.AccumulateAndGet(80UL, static (x, y) => x - y)); + Equal(10UL, Atomic.AccumulateAndGet(ref i, 80UL, static (x, y) => x - y)); Equal(10UL, i); - Equal(10UL, i.GetAndSet(25UL)); - Equal(25UL, i); - Equal(42UL, i.UpdateAndGet(static current => 42UL)); - Equal(42UL, i.GetAndUpdate(static current => 52UL)); - Equal(52UL, i); - - Equal(52UL, i.GetAndBitwiseOr(1UL)); - Equal(53UL, i); - i = 52UL; - Equal(53UL, i.BitwiseOrAndGet(1UL)); - Equal(53UL, i.GetAndBitwiseAnd(1UL << 2)); - Equal(4UL, i); - i = 53; - Equal(4UL, i.BitwiseAndAndGet(1UL << 2)); - - Equal(4UL, i.GetAndBitwiseXor(3UL)); - Equal(7UL, i); - i = 4UL; - Equal(7UL, i.BitwiseXorAndGet(3UL)); + Equal(42UL, Atomic.UpdateAndGet(ref i, static current => 42UL)); + Equal(42UL, Atomic.GetAndUpdate(ref i, static current => 52UL)); + Equal(52UL, i); } [Fact] public static void AtomicLongTest() { var i = 10L; - Equal(11L, i.IncrementAndGet()); - Equal(10L, i.DecrementAndGet()); - i = 20L; - True(i.CompareAndSet(20L, 30L)); - Equal(30L, i); - False(i.CompareAndSet(20L, 50L)); - Equal(30L, i); - Equal(80L, i.AddAndGet(50L)); - Equal(80L, i); - Equal(80L, i.GetAndAccumulate(10L, static (x, y) => x + y)); - Equal(90L, i); - Equal(10L, i.AccumulateAndGet(80L, static (x, y) => x - y)); - Equal(10L, i); - Equal(10L, i.GetAndSet(25L)); - Equal(25L, i); - Equal(42L, i.UpdateAndGet(static current => 42L)); - Equal(42L, i.GetAndUpdate(static current => 52L)); - Equal(52L, i); - - Equal(52L, i.GetAndBitwiseOr(1L)); - Equal(53L, i); - i = 52L; - Equal(53L, i.BitwiseOrAndGet(1L)); - - Equal(53L, i.GetAndBitwiseAnd(1L << 2)); - Equal(4L, i); - i = 53L; - Equal(4L, i.BitwiseAndAndGet(1L << 2)); - - Equal(4L, i.GetAndBitwiseXor(3L)); - Equal(7L, i); - i = 4L; - Equal(7L, i.BitwiseXorAndGet(3L)); - } + Equal(80, Atomic.GetAndAccumulate(ref i, 10, static (x, y) => x + y)); + Equal(90, i); + Equal(10, Atomic.AccumulateAndGet(ref i, 80, static (x, y) => x - y)); + Equal(10, i); - [Fact] - public static void AtomicReferenceTest() - { - var stref = "Hello"; - Equal("Hello, world!", AtomicReference.AccumulateAndGet(ref stref, ", world!", static (x, y) => x + y)); - Equal("Hello, world!", stref); - Equal("Hello, world!", AtomicReference.GetAndUpdate(ref stref, static current => "")); - Empty(stref); + Equal(42, Atomic.UpdateAndGet(ref i, static current => 42)); + Equal(42, Atomic.GetAndUpdate(ref i, static current => 52)); + Equal(52, i); } [Fact] public static void AtomicBooleanTest() { - var value = new AtomicBoolean(false); + var value = new Atomic.Boolean(false); False(value.Equals(true)); True(value.Equals(false)); True(value.FalseToTrue()); @@ -254,49 +124,50 @@ public static void AtomicBooleanTest() [Fact] public static void AtomicIntPtrTest() { - var i = new IntPtr(10); - Equal(new IntPtr(11), i.IncrementAndGet()); - Equal(new IntPtr(10), i.DecrementAndGet()); - i = new IntPtr(20); - True(i.CompareAndSet(new IntPtr(20), new IntPtr(30))); - Equal(new IntPtr(30), i); - False(i.CompareAndSet(new IntPtr(20), new IntPtr(50))); - Equal(new IntPtr(30), i); - Equal(new IntPtr(30), i.GetAndAccumulate(new IntPtr(60), static (x, y) => x + y.ToInt32())); - Equal(new IntPtr(90), i); - Equal(new IntPtr(10), i.AccumulateAndGet(new IntPtr(80), static (x, y) => x - y.ToInt32())); - Equal(new IntPtr(10), i); - Equal(new IntPtr(10), i.GetAndSet(new IntPtr(25))); - Equal(new IntPtr(25), i); - Equal(new IntPtr(42), i.UpdateAndGet(static current => new IntPtr(42))); - Equal(new IntPtr(42), i.GetAndUpdate(static current => new IntPtr(52))); - Equal(new IntPtr(52), i); - - Equal((IntPtr)52, i.GetAndBitwiseOr((IntPtr)1)); - Equal((IntPtr)53, i); - i = (IntPtr)52; - Equal((IntPtr)53, i.BitwiseOrAndGet((IntPtr)1)); - - Equal((IntPtr)53, i.GetAndBitwiseAnd((nint)1 << 2)); - Equal((IntPtr)4, i); - i = (IntPtr)53; - Equal((IntPtr)4, i.BitwiseAndAndGet((nint)1 << 2)); + nint i = 10; + Equal(80, Atomic.GetAndAccumulate(ref i, 10, static (x, y) => x + y)); + Equal(90, i); + Equal(10, Atomic.AccumulateAndGet(ref i, 80, static (x, y) => x - y)); + Equal(10, i); - Equal((IntPtr)4, i.GetAndBitwiseXor((IntPtr)3)); - Equal((IntPtr)7, i); - i = (IntPtr)4; - Equal((IntPtr)7, i.BitwiseXorAndGet((IntPtr)3)); + Equal(42, Atomic.UpdateAndGet(ref i, static current => 42)); + Equal(42, Atomic.GetAndUpdate(ref i, static current => 52)); + Equal(52, i); } - private enum LongEnum : long + [Fact] + public static void AtomicUIntPtrTest() { - One = 0L, - Two = 1L + nuint i = 10U; + Equal(80U, Atomic.GetAndAccumulate(ref i, 10U, static (x, y) => x + y)); + Equal(90U, i); + Equal(10U, Atomic.AccumulateAndGet(ref i, 80U, static (x, y) => x - y)); + Equal(10U, i); + + Equal(42U, Atomic.UpdateAndGet(ref i, static current => 42U)); + Equal(42U, Atomic.GetAndUpdate(ref i, static current => 52U)); + Equal(52U, i); } - private enum ByteEnum : byte + [Fact] + public static void IsAtomicWrite() { - One = 0, - Two = 1 + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic()); + True(Atomic.IsAtomic>()); + + False(Atomic.IsAtomic>()); + False(Atomic.IsAtomic>()); + False(Atomic.IsAtomic()); } } \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/Tasks/ValueTaskSynchronizerTests.cs b/src/DotNext.Tests/Threading/Tasks/ValueTaskSynchronizerTests.cs index 385e09e8d..20ce088ad 100644 --- a/src/DotNext.Tests/Threading/Tasks/ValueTaskSynchronizerTests.cs +++ b/src/DotNext.Tests/Threading/Tasks/ValueTaskSynchronizerTests.cs @@ -6,7 +6,7 @@ private sealed class SharedCounter { private long value = 0; - internal void Inc() => value.IncrementAndGet(); + internal void Inc() => Interlocked.Increment(ref value); internal long Value => value; } diff --git a/src/DotNext.Threading/Threading/AsyncBarrier.cs b/src/DotNext.Threading/Threading/AsyncBarrier.cs index 2cf3c7925..5fd8d91d0 100644 --- a/src/DotNext.Threading/Threading/AsyncBarrier.cs +++ b/src/DotNext.Threading/Threading/AsyncBarrier.cs @@ -42,12 +42,12 @@ public AsyncBarrier(long participantCount) /// /// Gets the number of the barrier's current phase. /// - public long CurrentPhaseNumber => currentPhase.VolatileRead(); + public long CurrentPhaseNumber => Volatile.Read(in currentPhase); /// /// Gets the total number of participants in the barrier. /// - public long ParticipantCount => participants.VolatileRead(); + public long ParticipantCount => Volatile.Read(in participants); /// /// Gets the number of participants in the barrier that haven't yet signaled in the current phase. @@ -80,7 +80,7 @@ public long AddParticipants(long participantCount) return CurrentPhaseNumber; default: countdown.AddCountAndReset(participantCount); - participants.AddAndGet(participantCount); + Interlocked.Add(ref participants, participantCount); goto case 0L; } } @@ -108,7 +108,7 @@ public void RemoveParticipants(long participantCount) throw new ArgumentOutOfRangeException(nameof(participantCount)); countdown.Signal(participantCount); - participants.AddAndGet(-participantCount); + Interlocked.Add(ref participants, -participantCount); } /// @@ -142,7 +142,7 @@ public async ValueTask SignalAndWaitAsync(TimeSpan timeout, CancellationTo { try { - await PostPhase(currentPhase.AddAndGet(1L)).ConfigureAwait(false); + await PostPhase(Interlocked.Increment(ref currentPhase)).ConfigureAwait(false); } catch (Exception e) { @@ -173,7 +173,7 @@ public async ValueTask SignalAndWaitAsync(CancellationToken token = default) { try { - await PostPhase(currentPhase.AddAndGet(1L)).ConfigureAwait(false); + await PostPhase(Interlocked.Increment(ref currentPhase)).ConfigureAwait(false); } catch (Exception e) { diff --git a/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs b/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs index 59dd0c99b..a4c53a03f 100644 --- a/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs +++ b/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs @@ -95,12 +95,12 @@ private void OnCompleted(DefaultWaitNode node) /// /// Gets the numbers of signals initially required to set the event. /// - public long InitialCount => manager.Initial.VolatileRead(); + public long InitialCount => Volatile.Read(in manager.Initial); /// /// Gets the number of remaining signals required to set the event. /// - public long CurrentCount => manager.Current.VolatileRead(); + public long CurrentCount => Volatile.Read(in manager.Current); /// /// Indicates whether this event is set. diff --git a/src/DotNext.Threading/Threading/AsyncCounter.cs b/src/DotNext.Threading/Threading/AsyncCounter.cs index 1c71a1481..111a78507 100644 --- a/src/DotNext.Threading/Threading/AsyncCounter.cs +++ b/src/DotNext.Threading/Threading/AsyncCounter.cs @@ -21,27 +21,20 @@ public class AsyncCounter : QueuedSynchronizer, IAsyncEvent [StructLayout(LayoutKind.Auto)] private struct StateManager : ILockManager { - private long state; + required internal long Value; - internal StateManager(long initialValue) - => state = initialValue; + internal void Increment(long delta) => Value = checked(Value + delta); - internal readonly long Value => state; - - internal readonly long VolatileRead() => state.VolatileRead(); - - internal void Increment(long delta) => state = checked(state + delta); - - internal void Decrement() => state--; + internal void Decrement() => Value--; internal bool TryReset() { - var result = state > 0L; - state = 0L; + var result = Value > 0L; + Value = 0L; return result; } - readonly bool ILockManager.IsLockAllowed => state > 0L; + readonly bool ILockManager.IsLockAllowed => Value > 0L; void ILockManager.AcquireLock() => Decrement(); @@ -62,13 +55,10 @@ readonly void ILockManager.InitializeNode(DefaultWaitNode node) /// is less than or equal to zero. public AsyncCounter(long initialValue, int concurrencyLevel) { - if (initialValue < 0L) - throw new ArgumentOutOfRangeException(nameof(initialValue)); - - if (concurrencyLevel < 1) - throw new ArgumentOutOfRangeException(nameof(concurrencyLevel)); + ArgumentOutOfRangeException.ThrowIfLessThan(initialValue, 0L); + ArgumentOutOfRangeException.ThrowIfLessThan(concurrencyLevel, 1); - manager = new(initialValue); + manager = new() { Value = initialValue }; pool = new(OnCompleted, concurrencyLevel); } @@ -79,10 +69,9 @@ public AsyncCounter(long initialValue, int concurrencyLevel) /// is less than zero. public AsyncCounter(long initialValue = 0L) { - if (initialValue < 0L) - throw new ArgumentOutOfRangeException(nameof(initialValue)); + ArgumentOutOfRangeException.ThrowIfLessThan(initialValue, 0L); - manager = new(initialValue); + manager = new() { Value = initialValue }; pool = new(OnCompleted); } @@ -98,7 +87,7 @@ private void OnCompleted(DefaultWaitNode node) } /// - bool IAsyncEvent.IsSet => manager.VolatileRead() > 0L; + bool IAsyncEvent.IsSet => Value > 0L; /// /// Gets the counter value. @@ -108,7 +97,7 @@ private void OnCompleted(DefaultWaitNode node) /// using without /// blocking. /// - public long Value => manager.VolatileRead(); + public long Value => Volatile.Read(in manager.Value); /// bool IAsyncEvent.Reset() diff --git a/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs b/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs index d74d47332..ea54ef9cb 100644 --- a/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs +++ b/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs @@ -64,9 +64,9 @@ internal void ExitLock() } } - internal readonly long ReadLocks => readLocks.VolatileRead(); + internal readonly long ReadLocks => Volatile.Read(in readLocks); - internal readonly ulong Version => version.VolatileRead(); + internal readonly ulong Version => Volatile.Read(in version); internal readonly bool IsWriteLockAllowed => writeLock is false && readLocks is 0L; @@ -88,10 +88,10 @@ internal void AcquireWriteLock() private readonly struct ReadLockManager : ILockManager { bool ILockManager.IsLockAllowed - => Unsafe.As(ref Unsafe.AsRef(this)).IsReadLockAllowed; + => Unsafe.As(ref Unsafe.AsRef(in this)).IsReadLockAllowed; void ILockManager.AcquireLock() - => Unsafe.As(ref Unsafe.AsRef(this)).AcquireReadLock(); + => Unsafe.As(ref Unsafe.AsRef(in this)).AcquireReadLock(); void ILockManager.InitializeNode(WaitNode node) => node.Type = LockType.Read; diff --git a/src/DotNext.Threading/Threading/AsyncSharedLock.cs b/src/DotNext.Threading/Threading/AsyncSharedLock.cs index 922ab37fa..b4feea2b7 100644 --- a/src/DotNext.Threading/Threading/AsyncSharedLock.cs +++ b/src/DotNext.Threading/Threading/AsyncSharedLock.cs @@ -38,11 +38,11 @@ private struct State internal State(long concurrencyLevel) => ConcurrencyLevel = remainingLocks = concurrencyLevel; - internal readonly long RemainingLocks => remainingLocks.VolatileRead(); + internal readonly long RemainingLocks => Volatile.Read(in remainingLocks); internal readonly bool IsWeakLockAllowed => remainingLocks > 0L; - internal void AcquireWeakLock() => remainingLocks.DecrementAndGet(); + internal void AcquireWeakLock() => Interlocked.Decrement(ref remainingLocks); internal void ExitLock() { @@ -64,10 +64,10 @@ internal void ExitLock() private readonly struct WeakLockManager : ILockManager { bool ILockManager.IsLockAllowed - => Unsafe.As(ref Unsafe.AsRef(this)).IsWeakLockAllowed; + => Unsafe.As(ref Unsafe.AsRef(in this)).IsWeakLockAllowed; void ILockManager.AcquireLock() - => Unsafe.As(ref Unsafe.AsRef(this)).AcquireWeakLock(); + => Unsafe.As(ref Unsafe.AsRef(in this)).AcquireWeakLock(); void ILockManager.InitializeNode(WaitNode node) => node.IsStrongLock = false; @@ -77,10 +77,10 @@ void ILockManager.InitializeNode(WaitNode node) private readonly struct StrongLockManager : ILockManager { bool ILockManager.IsLockAllowed - => Unsafe.As(ref Unsafe.AsRef(this)).IsStrongLockAllowed; + => Unsafe.As(ref Unsafe.AsRef(in this)).IsStrongLockAllowed; void ILockManager.AcquireLock() - => Unsafe.As(ref Unsafe.AsRef(this)).AcquireStrongLock(); + => Unsafe.As(ref Unsafe.AsRef(in this)).AcquireStrongLock(); void ILockManager.InitializeNode(WaitNode node) => node.IsStrongLock = true; diff --git a/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs b/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs index 4df0ebe95..452c4f824 100644 --- a/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs +++ b/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs @@ -25,7 +25,7 @@ private interface IReadBuffer private sealed class SingleReaderBuffer : IReadBuffer { - private AtomicBoolean readyToRead; + private Atomic.Boolean readyToRead; [AllowNull] private T value; diff --git a/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs b/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs index 8f48a77bf..0922f79fa 100644 --- a/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs +++ b/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.cs @@ -278,5 +278,5 @@ private async IAsyncEnumerator GetAsyncEnumerator(uint expectedVersion, Cance public IAsyncEnumerator GetAsyncEnumerator(CancellationToken token) => GetAsyncEnumerator(Version, token); - internal uint Version => version.VolatileRead(); + internal uint Version => Volatile.Read(in version); } \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.Boolean.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.Boolean.cs deleted file mode 100644 index 98400d39e..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.Boolean.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, bool value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.Double.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.Double.cs deleted file mode 100644 index 344fb06a7..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.Double.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, double value) => AtomicDouble.VolatileWrite(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double VolatileRead(this Pointer pointer) => AtomicDouble.VolatileRead(in pointer.Value); - - /// - /// Increments a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be incremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IncrementValue(this Pointer pointer) => AtomicDouble.IncrementAndGet(ref pointer.Value); - - /// - /// Decrements a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be decremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double DecrementValue(this Pointer pointer) => AtomicDouble.DecrementAndGet(ref pointer.Value); - - /// - /// Sets a value located in the memory at the address specified by pointer to a specified value as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to which the memory is set. - /// The original value in the memory. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndSetValue(this Pointer pointer, double update) => AtomicDouble.GetAndSet(ref pointer.Value, update); - - /// - /// Adds two numbers and replaces the first number with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the number located in the memory at the address specified by pointer. - /// The new value stored at memory address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double AddAndGetValue(this Pointer pointer, double value) => AtomicDouble.AddAndGet(ref pointer.Value, value); - - /// - /// Adds two numbers and replaces the first number with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the number located in the memory at the address specified by pointer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndAddValue(this Pointer pointer, double value) => AtomicDouble.GetAndAdd(ref pointer.Value, value); - - /// - /// Compares two 64-bit floating-point numbers for equality and, if they are equal, replaces the first value. - /// - /// A pointer to a value to be modified. - /// The value that replaces the destination value if the comparison results in equality. - /// The value that is compared to the value at the memory address. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double CompareExchangeValue(this Pointer pointer, double value, double comparand) => Interlocked.CompareExchange(ref pointer.Value, value, comparand); - - /// - /// Atomically sets a value located at the specified address in the memory to the given updated value if the current value == the expected value. - /// - /// A pointer to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSetValue(this Pointer pointer, double expected, double update) => AtomicDouble.CompareAndSet(ref pointer.Value, expected, update); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double AccumulateAndGetValue(this Pointer pointer, double x, Func accumulator) => AtomicDouble.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double AccumulateAndGetValue(this Pointer pointer, double x, delegate* accumulator) => AtomicDouble.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndAccumulateValue(this Pointer pointer, double x, Func accumulator) => AtomicDouble.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double GetAndAccumulateValue(this Pointer pointer, double x, delegate* accumulator) => AtomicDouble.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double UpdateAndGetValue(this Pointer pointer, Func updater) => AtomicDouble.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double UpdateAndGetValue(this Pointer pointer, delegate* updater) => AtomicDouble.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndUpdateValue(this Pointer pointer, Func updater) => AtomicDouble.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double GetAndUpdateValue(this Pointer pointer, delegate* updater) => AtomicDouble.GetAndUpdate(ref pointer.Value, updater); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.Int16.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.Int16.cs deleted file mode 100644 index 0a18a3506..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.Int16.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, short value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static short VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.Int32.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.Int32.cs deleted file mode 100644 index 74add9369..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.Int32.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, int value) => AtomicInt32.VolatileWrite(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int VolatileRead(this Pointer pointer) => AtomicInt32.VolatileRead(in pointer.Value); - - /// - /// Increments a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be incremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IncrementValue(this Pointer pointer) => AtomicInt32.IncrementAndGet(ref pointer.Value); - - /// - /// Decrements a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be decremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int DecrementValue(this Pointer pointer) => AtomicInt32.DecrementAndGet(ref pointer.Value); - - /// - /// Sets a value located in the memory at the address specified by pointer to a specified value as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to which the memory is set. - /// The original value in the memory. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndSetValue(this Pointer pointer, int update) => AtomicInt32.GetAndSet(ref pointer.Value, update); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The new value stored at memory address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int AddAndGetValue(this Pointer pointer, int value) => AtomicInt32.AddAndGet(ref pointer.Value, value); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndAddValue(this Pointer pointer, int value) => AtomicInt32.GetAndAdd(ref pointer.Value, value); - - /// - /// Compares two 32-bit signed integers for equality and, if they are equal, replaces the first value. - /// - /// A pointer to a value to be modified. - /// The value that replaces the destination value if the comparison results in equality. - /// The value that is compared to the value at the memory address. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CompareExchangeValue(this Pointer pointer, int value, int comparand) => Interlocked.CompareExchange(ref pointer.Value, value, comparand); - - /// - /// Atomically sets a value located at the specified address in the memory to the given updated value if the current value == the expected value. - /// - /// A pointer to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSetValue(this Pointer pointer, int expected, int update) => AtomicInt32.CompareAndSet(ref pointer.Value, expected, update); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int AccumulateAndGetValue(this Pointer pointer, int x, Func accumulator) => AtomicInt32.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int AccumulateAndGetValue(this Pointer pointer, int x, delegate* accumulator) => AtomicInt32.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndAccumulateValue(this Pointer pointer, int x, Func accumulator) => AtomicInt32.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int GetAndAccumulateValue(this Pointer pointer, int x, delegate* accumulator) => AtomicInt32.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int UpdateAndGetValue(this Pointer pointer, Func updater) => AtomicInt32.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int UpdateAndGetValue(this Pointer pointer, delegate* updater) => AtomicInt32.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndUpdateValue(this Pointer pointer, Func updater) => AtomicInt32.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int GetAndUpdateValue(this Pointer pointer, delegate* updater) => AtomicInt32.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndBitwiseAndGetValue(this Pointer pointer, int operand) => AtomicInt32.GetAndBitwiseAnd(ref pointer.Value, operand); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int BitwiseAndAndGetValue(this Pointer pointer, int operand) => AtomicInt32.BitwiseAndAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndBitwiseOrValue(this Pointer pointer, int operand) => AtomicInt32.GetAndBitwiseOr(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int BitwiseOrAndGetValue(this Pointer pointer, int operand) => AtomicInt32.BitwiseOrAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndBitwiseXorValue(this Pointer pointer, int operand) => AtomicInt32.GetAndBitwiseXor(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int BitwiseXorAndGetValue(this Pointer pointer, int operand) => AtomicInt32.BitwiseXorAndGet(ref pointer.Value, operand); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.Int64.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.Int64.cs deleted file mode 100644 index afa82b5b1..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.Int64.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, long value) => AtomicInt64.VolatileWrite(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long VolatileRead(this Pointer pointer) => AtomicInt64.VolatileRead(in pointer.Value); - - /// - /// Increments a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be incremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IncrementValue(this Pointer pointer) => AtomicInt64.IncrementAndGet(ref pointer.Value); - - /// - /// Decrements a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be decremented. - /// The decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long DecrementValue(this Pointer pointer) => AtomicInt64.DecrementAndGet(ref pointer.Value); - - /// - /// Sets a value located in the memory at the address specified by pointer to a specified value as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to which the memory is set. - /// The original value in the memory. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndSetValue(this Pointer pointer, long update) => AtomicInt64.GetAndSet(ref pointer.Value, update); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The new value stored at memory address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long AddAndGetValue(this Pointer pointer, long value) => AtomicInt64.AddAndGet(ref pointer.Value, value); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndAddValue(this Pointer pointer, long value) => AtomicInt64.GetAndAdd(ref pointer.Value, value); - - /// - /// Compares two 64-bit signed integers for equality and, if they are equal, replaces the first value. - /// - /// A pointer to a value to be modified. - /// The value that replaces the destination value if the comparison results in equality. - /// The value that is compared to the value at the memory address. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long CompareExchangeValue(this Pointer pointer, long value, long comparand) => Interlocked.CompareExchange(ref pointer.Value, value, comparand); - - /// - /// Atomically sets a value located at the specified address in the memory to the given updated value if the current value == the expected value. - /// - /// A pointer to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSetValue(this Pointer pointer, long expected, long update) => AtomicInt64.CompareAndSet(ref pointer.Value, expected, update); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long AccumulateAndGetValue(this Pointer pointer, long x, Func accumulator) => AtomicInt64.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long AccumulateAndGetValue(this Pointer pointer, long x, delegate* accumulator) => AtomicInt64.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndAccumulateValue(this Pointer pointer, long x, Func accumulator) => AtomicInt64.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long GetAndAccumulateValue(this Pointer pointer, long x, delegate* accumulator) => AtomicInt64.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long UpdateAndGetValue(this Pointer pointer, Func updater) => AtomicInt64.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long UpdateAndGetValue(this Pointer pointer, delegate* updater) => AtomicInt64.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndUpdateValue(this Pointer pointer, Func updater) => AtomicInt64.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long GetAndUpdateValue(this Pointer pointer, delegate* updater) => AtomicInt64.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndBitwiseAndGetValue(this Pointer pointer, int operand) => AtomicInt64.GetAndBitwiseAnd(ref pointer.Value, operand); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long BitwiseAndAndGetValue(this Pointer pointer, int operand) => AtomicInt64.BitwiseAndAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndBitwiseOrValue(this Pointer pointer, long operand) => AtomicInt64.GetAndBitwiseOr(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long BitwiseOrAndGetValue(this Pointer pointer, long operand) => AtomicInt64.BitwiseOrAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndBitwiseXorValue(this Pointer pointer, long operand) => AtomicInt64.GetAndBitwiseXor(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long BitwiseXorAndGetValue(this Pointer pointer, long operand) => AtomicInt64.BitwiseXorAndGet(ref pointer.Value, operand); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.Int8.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.Int8.cs deleted file mode 100644 index 592adfc95..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.Int8.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void VolatileWrite(this Pointer pointer, sbyte value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static sbyte VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.IntPtr.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.IntPtr.cs deleted file mode 100644 index dfcef0b08..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.IntPtr.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, IntPtr value) => AtomicIntPtr.VolatileWrite(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr VolatileRead(this Pointer pointer) => AtomicIntPtr.VolatileRead(in pointer.Value); - - /// - /// Increments a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be incremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr IncrementValue(this Pointer pointer) => AtomicIntPtr.IncrementAndGet(ref pointer.Value); - - /// - /// Decrements a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be decremented. - /// The decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr DecrementValue(this Pointer pointer) => AtomicIntPtr.DecrementAndGet(ref pointer.Value); - - /// - /// Sets a value located in the memory at the address specified by pointer to a specified value as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to which the memory is set. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndSetValue(this Pointer pointer, IntPtr update) => AtomicIntPtr.GetAndSet(ref pointer.Value, update); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The new value stored at memory address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr AddAndGetValue(this Pointer pointer, IntPtr value) => AtomicIntPtr.AddAndGet(ref pointer.Value, value); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndAddValue(this Pointer pointer, IntPtr value) => AtomicIntPtr.GetAndAdd(ref pointer.Value, value); - - /// - /// Compares two native-sized signed integers for equality and, if they are equal, replaces the first value. - /// - /// A pointer to a value to be modified. - /// The value that replaces the destination value if the comparison results in equality. - /// The value that is compared to the value at the memory address. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr CompareExchangeValue(this Pointer pointer, IntPtr value, IntPtr comparand) => Interlocked.CompareExchange(ref pointer.Value, value, comparand); - - /// - /// Atomically sets a value located at the specified address in the memory to the given updated value if the current value == the expected value. - /// - /// A pointer to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSetValue(this Pointer pointer, IntPtr expected, IntPtr update) => AtomicIntPtr.CompareAndSet(ref pointer.Value, expected, update); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr AccumulateAndGetValue(this Pointer pointer, IntPtr x, Func accumulator) => AtomicIntPtr.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr AccumulateAndGetValue(this Pointer pointer, IntPtr x, delegate* accumulator) => AtomicIntPtr.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndAccumulateValue(this Pointer pointer, IntPtr x, Func accumulator) => AtomicIntPtr.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr GetAndAccumulateValue(this Pointer pointer, IntPtr x, delegate* accumulator) => AtomicIntPtr.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr UpdateAndGetValue(this Pointer pointer, Func updater) => AtomicIntPtr.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr UpdateAndGetValue(this Pointer pointer, delegate* updater) => AtomicIntPtr.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndUpdateValue(this Pointer pointer, Func updater) => AtomicIntPtr.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr GetAndUpdateValue(this Pointer pointer, delegate* updater) => AtomicIntPtr.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndBitwiseAndGetValue(this Pointer pointer, IntPtr operand) => AtomicIntPtr.GetAndBitwiseAnd(ref pointer.Value, operand); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr BitwiseAndAndGetValue(this Pointer pointer, IntPtr operand) => AtomicIntPtr.BitwiseAndAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndBitwiseOrValue(this Pointer pointer, IntPtr operand) => AtomicIntPtr.GetAndBitwiseOr(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr BitwiseOrAndGetValue(this Pointer pointer, IntPtr operand) => AtomicIntPtr.BitwiseOrAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndBitwiseXorValue(this Pointer pointer, IntPtr operand) => AtomicIntPtr.GetAndBitwiseXor(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr BitwiseXorAndGetValue(this Pointer pointer, IntPtr operand) => AtomicIntPtr.BitwiseXorAndGet(ref pointer.Value, operand); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.Single.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.Single.cs deleted file mode 100644 index 5c527cf20..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.Single.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, float value) => AtomicSingle.VolatileWrite(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float VolatileRead(this Pointer pointer) => AtomicSingle.VolatileRead(in pointer.Value); - - /// - /// Increments a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be incremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float IncrementValue(this Pointer pointer) => AtomicSingle.IncrementAndGet(ref pointer.Value); - - /// - /// Decrements a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be decremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DecrementValue(this Pointer pointer) => AtomicSingle.DecrementAndGet(ref pointer.Value); - - /// - /// Sets a value located in the memory at the address specified by pointer to a specified value as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to which the memory is set. - /// The original value in the memory. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndSetValue(this Pointer pointer, float update) => AtomicSingle.GetAndSet(ref pointer.Value, update); - - /// - /// Adds two numbers and replaces the first number with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the number located in the memory at the address specified by pointer. - /// The new value stored at memory address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float AddAndGetValue(this Pointer pointer, float value) => AtomicSingle.AddAndGet(ref pointer.Value, value); - - /// - /// Adds two numbers and replaces the first number with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the number located in the memory at the address specified by pointer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndAddValue(this Pointer pointer, float value) => AtomicSingle.GetAndAdd(ref pointer.Value, value); - - /// - /// Compares two 32-bit floating-point numbers for equality and, if they are equal, replaces the first value. - /// - /// A pointer to a value to be modified. - /// The value that replaces the destination value if the comparison results in equality. - /// The value that is compared to the value at the memory address. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float CompareExchangeValue(this Pointer pointer, float value, float comparand) => Interlocked.CompareExchange(ref pointer.Value, value, comparand); - - /// - /// Atomically sets a value located at the specified address in the memory to the given updated value if the current value == the expected value. - /// - /// A pointer to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSetValue(this Pointer pointer, float expected, float update) => AtomicSingle.CompareAndSet(ref pointer.Value, expected, update); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float AccumulateAndGetValue(this Pointer pointer, float x, Func accumulator) => AtomicSingle.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float AccumulateAndGetValue(this Pointer pointer, float x, delegate* accumulator) => AtomicSingle.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndAccumulateValue(this Pointer pointer, float x, Func accumulator) => AtomicSingle.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float GetAndAccumulateValue(this Pointer pointer, float x, delegate* accumulator) => AtomicSingle.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float UpdateAndGetValue(this Pointer pointer, Func updater) => AtomicSingle.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float UpdateAndGetValue(this Pointer pointer, delegate* updater) => AtomicSingle.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndUpdateValue(this Pointer pointer, Func updater) => AtomicSingle.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float GetAndUpdateValue(this Pointer pointer, delegate* updater) => AtomicSingle.GetAndUpdate(ref pointer.Value, updater); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt16.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.UInt16.cs deleted file mode 100644 index 396d3cd8b..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt16.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void VolatileWrite(this Pointer pointer, ushort value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ushort VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt32.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.UInt32.cs deleted file mode 100644 index fcbbe6057..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt32.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void VolatileWrite(this Pointer pointer, uint value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); - - /// - /// Increments a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be incremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint IncrementValue(this Pointer pointer) => AtomicUInt32.IncrementAndGet(ref pointer.Value); - - /// - /// Decrements a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be decremented. - /// The decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint DecrementValue(this Pointer pointer) => AtomicUInt32.DecrementAndGet(ref pointer.Value); - - /// - /// Sets a value located in the memory at the address specified by pointer to a specified value as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to which the memory is set. - /// The original value in the memory. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint GetAndSetValue(this Pointer pointer, uint update) => AtomicUInt32.GetAndSet(ref pointer.Value, update); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The new value stored at memory address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint AddAndGetValue(this Pointer pointer, uint value) => AtomicUInt32.AddAndGet(ref pointer.Value, value); - - /// - /// Compares two 64-bit signed integers for equality and, if they are equal, replaces the first value. - /// - /// A pointer to a value to be modified. - /// The value that replaces the destination value if the comparison results in equality. - /// The value that is compared to the value at the memory address. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint CompareExchangeValue(this Pointer pointer, uint value, uint comparand) => Interlocked.CompareExchange(ref pointer.Value, value, comparand); - - /// - /// Atomically sets a value located at the specified address in the memory to the given updated value if the current value == the expected value. - /// - /// A pointer to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static bool CompareAndSetValue(this Pointer pointer, uint expected, uint update) => AtomicUInt32.CompareAndSet(ref pointer.Value, expected, update); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint AccumulateAndGetValue(this Pointer pointer, uint x, Func accumulator) => AtomicUInt32.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe uint AccumulateAndGetValue(this Pointer pointer, uint x, delegate* accumulator) => AtomicUInt32.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint GetAndAccumulateValue(this Pointer pointer, uint x, Func accumulator) => AtomicUInt32.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe uint GetAndAccumulateValue(this Pointer pointer, uint x, delegate* accumulator) => AtomicUInt32.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint UpdateAndGetValue(this Pointer pointer, Func updater) => AtomicUInt32.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe uint UpdateAndGetValue(this Pointer pointer, delegate* updater) => AtomicUInt32.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint GetAndUpdateValue(this Pointer pointer, Func updater) => AtomicUInt32.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe uint GetAndUpdateValue(this Pointer pointer, delegate* updater) => AtomicUInt32.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint GetAndBitwiseAndGetValue(this Pointer pointer, uint operand) => AtomicUInt32.GetAndBitwiseAnd(ref pointer.Value, operand); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint BitwiseAndAndGetValue(this Pointer pointer, uint operand) => AtomicUInt32.BitwiseAndAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint GetAndBitwiseOrValue(this Pointer pointer, uint operand) => AtomicUInt32.GetAndBitwiseOr(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint BitwiseOrAndGetValue(this Pointer pointer, uint operand) => AtomicUInt32.BitwiseOrAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint GetAndBitwiseXorValue(this Pointer pointer, uint operand) => AtomicUInt32.GetAndBitwiseXor(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static uint BitwiseXorAndGetValue(this Pointer pointer, uint operand) => AtomicUInt32.BitwiseXorAndGet(ref pointer.Value, operand); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt64.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.UInt64.cs deleted file mode 100644 index 963a21f07..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt64.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void VolatileWrite(this Pointer pointer, ulong value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); - - /// - /// Increments a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be incremented. - /// The incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong IncrementValue(this Pointer pointer) => AtomicUInt64.IncrementAndGet(ref pointer.Value); - - /// - /// Decrements a value located in the memory at the address specified by pointer and stores the result, as an atomic operation. - /// - /// A pointer to a value to be decremented. - /// The decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong DecrementValue(this Pointer pointer) => AtomicUInt64.DecrementAndGet(ref pointer.Value); - - /// - /// Sets a value located in the memory at the address specified by pointer to a specified value as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to which the memory is set. - /// The original value in the memory. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong GetAndSetValue(this Pointer pointer, ulong update) => AtomicUInt64.GetAndSet(ref pointer.Value, update); - - /// - /// Adds two integers and replaces the first integer with the sum, as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be added to the integer located in the memory at the address specified by pointer. - /// The new value stored at memory address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong AddAndGetValue(this Pointer pointer, ulong value) => AtomicUInt64.AddAndGet(ref pointer.Value, value); - - /// - /// Compares two 64-bit signed integers for equality and, if they are equal, replaces the first value. - /// - /// A pointer to a value to be modified. - /// The value that replaces the destination value if the comparison results in equality. - /// The value that is compared to the value at the memory address. - /// The original value that was in the memory before. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong CompareExchangeValue(this Pointer pointer, ulong value, ulong comparand) => Interlocked.CompareExchange(ref pointer.Value, value, comparand); - - /// - /// Atomically sets a value located at the specified address in the memory to the given updated value if the current value == the expected value. - /// - /// A pointer to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static bool CompareAndSetValue(this Pointer pointer, ulong expected, ulong update) => AtomicUInt64.CompareAndSet(ref pointer.Value, expected, update); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong AccumulateAndGetValue(this Pointer pointer, ulong x, Func accumulator) => AtomicUInt64.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe ulong AccumulateAndGetValue(this Pointer pointer, ulong x, delegate* accumulator) => AtomicUInt64.AccumulateAndGet(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong GetAndAccumulateValue(this Pointer pointer, ulong x, Func accumulator) => AtomicUInt64.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the current value referenced by pointer with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// A pointer to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe ulong GetAndAccumulateValue(this Pointer pointer, ulong x, delegate* accumulator) => AtomicUInt64.GetAndAccumulate(ref pointer.Value, x, accumulator); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong UpdateAndGetValue(this Pointer pointer, Func updater) => AtomicUInt64.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the updated value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe ulong UpdateAndGetValue(this Pointer pointer, delegate* updater) => AtomicUInt64.UpdateAndGet(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong GetAndUpdateValue(this Pointer pointer, Func updater) => AtomicUInt64.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Atomically updates the value referenced by pointer with the results - /// of applying the given function, returning the original value. - /// - /// A pointer to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe ulong GetAndUpdateValue(this Pointer pointer, delegate* updater) => AtomicUInt64.GetAndUpdate(ref pointer.Value, updater); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong GetAndBitwiseAndGetValue(this Pointer pointer, ulong operand) => AtomicUInt64.GetAndBitwiseAnd(ref pointer.Value, operand); - - /// - /// Bitwise "ands" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong BitwiseAndAndGetValue(this Pointer pointer, ulong operand) => AtomicUInt64.BitwiseAndAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong GetAndBitwiseOrValue(this Pointer pointer, ulong operand) => AtomicUInt64.GetAndBitwiseOr(ref pointer.Value, operand); - - /// - /// Bitwise "ors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong BitwiseOrAndGetValue(this Pointer pointer, ulong operand) => AtomicUInt64.BitwiseOrAndGet(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong GetAndBitwiseXorValue(this Pointer pointer, ulong operand) => AtomicUInt64.GetAndBitwiseXor(ref pointer.Value, operand); - - /// - /// Bitwise "xors" two integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// A pointer to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static ulong BitwiseXorAndGetValue(this Pointer pointer, ulong operand) => AtomicUInt64.BitwiseXorAndGet(ref pointer.Value, operand); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt8.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.UInt8.cs deleted file mode 100644 index d5e293a4c..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.UInt8.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(this Pointer pointer, byte value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.UIntPtr.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.UIntPtr.cs deleted file mode 100644 index af9cf5e02..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.UIntPtr.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -using Runtime.InteropServices; - -public static partial class AtomicPointer -{ - /// - /// Writes a value to the memory location identified by the pointer . - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears before this method in the code, the processor cannot move it after this method. - /// - /// The pointer to write. - /// The value to write. The value is written immediately so that it is visible to all processors in the computer. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static void VolatileWrite(this Pointer pointer, UIntPtr value) => Volatile.Write(ref pointer.Value, value); - - /// - /// Reads the value from the memory location identified by the pointer. - /// - /// - /// On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: - /// If a read or write appears after this method in the code, the processor cannot move it before this method. - /// - /// The pointer to read. - /// The value that was read. This value is the latest written by any processor in the computer, regardless of the number of processors or the state of processor cache. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static UIntPtr VolatileRead(this Pointer pointer) => Volatile.Read(ref pointer.Value); -} \ No newline at end of file diff --git a/src/DotNext.Unsafe/Threading/AtomicPointer.cs b/src/DotNext.Unsafe/Threading/AtomicPointer.cs deleted file mode 100644 index bafb0c661..000000000 --- a/src/DotNext.Unsafe/Threading/AtomicPointer.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace DotNext.Threading; - -/// -/// Represents atomic operations that can be applied to the value referenced by pointer. -/// -public static partial class AtomicPointer -{ -} \ No newline at end of file diff --git a/src/DotNext/BasicExtensions.cs b/src/DotNext/BasicExtensions.cs index 03d1f412b..b79dee21f 100644 --- a/src/DotNext/BasicExtensions.cs +++ b/src/DotNext/BasicExtensions.cs @@ -106,47 +106,6 @@ public static bool TryGetValue(this T? nullable, out T value) return nullable.HasValue; } - /// - /// Converts into . - /// - /// The value to convert. - /// representation of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int ToInt32(this bool value) - { - Push(value); - return Return(); - } - - /// - /// Converts into . - /// - /// The value to convert. - /// representation of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte ToByte(this bool value) - { - Push(value); - Conv_U1(); - return Return(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static sbyte ToSByte(this bool value) - { - Push(value); - Conv_I1(); - return Return(); - } - - /// - /// Converts into . - /// - /// The value to convert. - /// if value != 0; otherwise, . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool ToBoolean(this int value) => value is not 0; - /// /// Normalizes value in the specified range. /// diff --git a/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs b/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs index e016c4243..78628a2b3 100644 --- a/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs +++ b/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs @@ -18,7 +18,7 @@ public partial class ConcurrentTypeMap : ITypeMap internal sealed class Entry { - private int state; // volatile + private volatile int state; internal TValue? Value; internal int AcquireLock() @@ -26,14 +26,14 @@ internal int AcquireLock() int currentState; for (var spinner = new SpinWait(); ; spinner.SpinOnce()) { - currentState = state.VolatileRead(); + currentState = state; - if (currentState is not LockedState && state.CompareAndSet(currentState, LockedState)) + if (currentState is not LockedState && Interlocked.CompareExchange(ref state, LockedState, currentState) == currentState) return currentState; } } - internal void ReleaseLock(int newState) => state.VolatileWrite(newState); + internal void ReleaseLock(int newState) => state = newState; internal bool HasValue { @@ -43,7 +43,7 @@ internal bool HasValue for (var spinner = new SpinWait(); ; spinner.SpinOnce()) { - currentState = state.VolatileRead(); + currentState = state; if (currentState is LockedState) continue; @@ -59,7 +59,7 @@ internal bool TryAcquireLock(int expectedState) for (var spinner = new SpinWait(); ; spinner.SpinOnce()) { - currentState = state.VolatileRead(); + currentState = state; if (currentState is LockedState) continue; @@ -67,7 +67,7 @@ internal bool TryAcquireLock(int expectedState) if (currentState != expectedState) return false; - if (state.CompareAndSet(currentState, LockedState)) + if (Interlocked.CompareExchange(ref state, LockedState, currentState) == currentState) return true; } } diff --git a/src/DotNext/Collections/Specialized/SingletonList.cs b/src/DotNext/Collections/Specialized/SingletonList.cs index b380e1c44..fe8d25c3e 100644 --- a/src/DotNext/Collections/Specialized/SingletonList.cs +++ b/src/DotNext/Collections/Specialized/SingletonList.cs @@ -127,7 +127,7 @@ readonly void ICollection.CopyTo(T[] array, int arrayIndex) readonly void ICollection.Add(T item) => throw new NotSupportedException(); /// - readonly int IList.IndexOf(T item) => EqualityComparer.Default.Equals(Item, item).ToInt32() - 1; + readonly int IList.IndexOf(T item) => Unsafe.BitCast(EqualityComparer.Default.Equals(Item, item)) - 1; /// readonly void IList.Insert(int index, T item) => throw new NotSupportedException(); diff --git a/src/DotNext/Diagnostics/Timestamp.cs b/src/DotNext/Diagnostics/Timestamp.cs index b55d6272d..4a168c867 100644 --- a/src/DotNext/Diagnostics/Timestamp.cs +++ b/src/DotNext/Diagnostics/Timestamp.cs @@ -4,6 +4,8 @@ namespace DotNext.Diagnostics; +using Threading; + /// /// Represents timestamp. /// @@ -15,7 +17,8 @@ namespace DotNext.Diagnostics; IComparable, IComparisonOperators, IAdditionOperators, - ISubtractionOperators + ISubtractionOperators, + IInterlockedOperations { private static readonly double TickFrequency = (double)TimeSpan.TicksPerSecond / Frequency; private readonly long ticks; @@ -228,4 +231,8 @@ public static void VolatileWrite(ref Timestamp location, Timestamp newValue) /// The location of the timestampt to update. public static void Refresh(ref Timestamp location) => Volatile.Write(ref Unsafe.AsRef(in location.ticks), Math.Max(1L, GetTimestamp())); + + /// + public static Timestamp CompareExchange(ref Timestamp location, Timestamp value, Timestamp comparand) + => new(Interlocked.CompareExchange(ref Unsafe.AsRef(in location.ticks), value.ticks, comparand.ticks)); } \ No newline at end of file diff --git a/src/DotNext/Runtime/Caching/ConcurrentCache.Dictionary.cs b/src/DotNext/Runtime/Caching/ConcurrentCache.Dictionary.cs index 9f1d6175c..a13772037 100644 --- a/src/DotNext/Runtime/Caching/ConcurrentCache.Dictionary.cs +++ b/src/DotNext/Runtime/Caching/ConcurrentCache.Dictionary.cs @@ -5,6 +5,8 @@ namespace DotNext.Runtime.Caching; +using Atomic = Threading.Atomic; + public partial class ConcurrentCache { private class KeyValuePair @@ -91,18 +93,18 @@ internal TValue Value [MethodImpl(MethodImplOptions.AggressiveInlining)] private static TValue GetValue(KeyValuePair pair) { - Debug.Assert(Intrinsics.IsAtomic() ? pair is KeyValuePairAtomicAccess : pair is KeyValuePairNonAtomicAccess); + Debug.Assert(Atomic.IsAtomic() ? pair is KeyValuePairAtomicAccess : pair is KeyValuePairNonAtomicAccess); - return Intrinsics.IsAtomic() ? Unsafe.As(pair).Value : Unsafe.As(pair).Value; + return Atomic.IsAtomic() ? Unsafe.As(pair).Value : Unsafe.As(pair).Value; } // devirtualize Value setter manually (JIT will replace this method with one of the actual branches) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void SetValue(KeyValuePair pair, TValue value) { - Debug.Assert(Intrinsics.IsAtomic() ? pair is KeyValuePairAtomicAccess : pair is KeyValuePairNonAtomicAccess); + Debug.Assert(Atomic.IsAtomic() ? pair is KeyValuePairAtomicAccess : pair is KeyValuePairNonAtomicAccess); - if (Intrinsics.IsAtomic()) + if (Atomic.IsAtomic()) Unsafe.As(pair).Value = value; else Unsafe.As(pair).Value = value; @@ -201,7 +203,7 @@ private unsafe bool TryAdd(TKey key, IEqualityComparer? keyComparer, int h } previous = default; - pair = Intrinsics.IsAtomic() + pair = Atomic.IsAtomic() ? new KeyValuePairAtomicAccess(key, value, hashCode) : new KeyValuePairNonAtomicAccess(key, value, hashCode); pair.Next = bucket; diff --git a/src/DotNext/Runtime/Intrinsics.cs b/src/DotNext/Runtime/Intrinsics.cs index 4276f4d6c..05f3c14ed 100644 --- a/src/DotNext/Runtime/Intrinsics.cs +++ b/src/DotNext/Runtime/Intrinsics.cs @@ -398,17 +398,6 @@ private readonly struct AlignmentHelperType public static int AlignOf() => Unsafe.SizeOf>() - Unsafe.SizeOf(); - /// - /// Determines that the write to the location in the memory of - /// type is atomic. - /// - /// The type of the value to be written. - /// if write is atomic; otherwise, . - /// Section I.12.6.6. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsAtomic() - => AlignOf() == Unsafe.SizeOf() && Unsafe.SizeOf() <= UIntPtr.Size; - /// /// Determines whether the two types are binary compatible, i.e. both types have the same /// size and memory alignment. diff --git a/src/DotNext/Threading/Atomic.Boolean.cs b/src/DotNext/Threading/Atomic.Boolean.cs new file mode 100644 index 000000000..46ad4ab09 --- /dev/null +++ b/src/DotNext/Threading/Atomic.Boolean.cs @@ -0,0 +1,282 @@ +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Represents atomic boolean. + /// + /// + /// Initializes a new atomic boolean container with initial value. + /// + /// Initial value of the atomic boolean. + [SuppressMessage("Usage", "CA2231")] + public struct Boolean(bool value) : IEquatable + { + [StructLayout(LayoutKind.Auto)] + private readonly struct Negation : ISupplier + { + bool ISupplier.Invoke(bool value) => !value; + } + + private int value = Unsafe.BitCast(value); + + /// + /// Gets or sets boolean value in volatile manner. + /// + public bool Value + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + readonly get => Unsafe.BitCast((byte)Volatile.Read(in value)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set => Volatile.Write(ref this.value, Unsafe.BitCast(value)); + } + + /// + /// Atomically sets referenced value to the given updated value if the current value == the expected value. + /// + /// The new value. + /// The expected value. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CompareExchange(bool update, bool expected) + => Unsafe.BitCast((byte)Interlocked.CompareExchange(ref value, Unsafe.BitCast(update), Unsafe.BitCast(expected))); + + /// + /// Atomically sets referenced value to the given updated value if the current value == the expected value. + /// + /// The expected value. + /// The new value. + /// if successful. return indicates that the actual value was not equal to the expected value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CompareAndSet(bool expected, bool update) + => CompareExchange(update, expected) == expected; + + /// + /// Atomically sets value if the + /// current value is . + /// + /// if current value is modified successfully; otherwise, . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool FalseToTrue() => CompareAndSet(false, true); + + /// + /// Atomically sets value if the + /// current value is . + /// + /// if current value is modified successfully; otherwise, . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TrueToFalse() => CompareAndSet(true, false); + + /// + /// Negates currently stored value atomically. + /// + /// Negation result. + public unsafe bool NegateAndGet() => Update(new Negation()).NewValue; + + /// + /// Negates currently stored value atomically. + /// + /// The original value before negation. + public unsafe bool GetAndNegate() => Update(new Negation()).OldValue; + + /// + /// Modifies the current value atomically. + /// + /// A new value to be stored into this container. + /// Original value before modification. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool GetAndSet(bool update) + => Unsafe.BitCast((byte)Interlocked.Exchange(ref value, Unsafe.BitCast(update))); + + /// + /// Modifies the current value atomically. + /// + /// A new value to be stored into this container. + /// A new value passed as argument. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SetAndGet(bool update) + { + Value = update; + return update; + } + + private (bool OldValue, bool NewValue) Update(TUpdater updater) + where TUpdater : notnull, ISupplier + { + bool oldValue, newValue, tmp = Value; + do + { + newValue = updater.Invoke(oldValue = tmp); + } + while ((tmp = CompareExchange(newValue, oldValue)) != oldValue); + + return (oldValue, newValue); + } + + private (bool OldValue, bool NewValue) Accumulate(bool x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + { + bool oldValue, newValue, tmp = Value; + do + { + newValue = accumulator.Invoke(oldValue = tmp, x); + } + while ((tmp = CompareExchange(newValue, oldValue)) != oldValue); + + return (oldValue, newValue); + } + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + public bool AccumulateAndGet(bool x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + public bool AccumulateAndGet(bool x, Func accumulator) + => AccumulateAndGet>(x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + public bool GetAndAccumulate(bool x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + public bool GetAndAccumulate(bool x, Func accumulator) + => GetAndAccumulate>(x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// A side-effect-free function. + /// The updated value. + public bool UpdateAndGet(TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// A side-effect-free function. + /// The updated value. + public bool UpdateAndGet(Func updater) + => UpdateAndGet>(updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// A side-effect-free function. + /// The original value. + public bool GetAndUpdate(TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// A side-effect-free function. + /// The original value. + public bool GetAndUpdate(Func updater) + => GetAndUpdate>(updater); + + internal void Acquire() + { + ref var lockState = ref value; + if (Interlocked.Exchange(ref lockState, 1) is 1) + Contention(ref lockState); + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Contention(ref int value) + { + var spinner = new SpinWait(); + do + { + spinner.SpinOnce(); + } + while (Interlocked.Exchange(ref value, 1) is 1); + } + } + + internal void Release() => Volatile.Write(ref value, 0); + + /// + /// Determines whether stored value is equal to value passed as argument. + /// + /// Other value to compare. + /// , if stored value is equal to other value; otherwise, . + public readonly bool Equals(bool other) => Volatile.Read(in value) == Unsafe.BitCast(other); + + /// + /// Computes hash code for the stored value. + /// + /// The hash code of the stored boolean value. + public override readonly int GetHashCode() => Volatile.Read(in value); + + /// + /// Determines whether stored value is equal to + /// value as the passed argument. + /// + /// Other value to compare. + /// , if stored value is equal to other value; otherwise, . + public override readonly bool Equals([NotNullWhen(true)] object? other) => other switch + { + bool b => Equals(b), + Boolean b => Value == b.Value, + _ => false, + }; + + /// + /// Returns stored boolean value in the form of . + /// + /// Textual representation of stored boolean value. + public override readonly string ToString() => Value ? bool.TrueString : bool.FalseString; + } +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.Double.cs b/src/DotNext/Threading/Atomic.Double.cs new file mode 100644 index 000000000..6835ff59e --- /dev/null +++ b/src/DotNext/Threading/Atomic.Double.cs @@ -0,0 +1,118 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double AccumulateAndGet(ref double value, double x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double AccumulateAndGet(ref double value, double x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetAndAccumulate(ref double value, double x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetAndAccumulate(ref double value, double x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double UpdateAndGet(ref double value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double UpdateAndGet(ref double value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetAndUpdate(ref double value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetAndUpdate(ref double value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.Int.cs b/src/DotNext/Threading/Atomic.Int.cs new file mode 100644 index 000000000..067e052ac --- /dev/null +++ b/src/DotNext/Threading/Atomic.Int.cs @@ -0,0 +1,118 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint AccumulateAndGet(ref nint value, nint x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint AccumulateAndGet(ref nint value, nint x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint GetAndAccumulate(ref nint value, nint x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint GetAndAccumulate(ref nint value, nint x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint UpdateAndGet(ref nint value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint UpdateAndGet(ref nint value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint GetAndUpdate(ref nint value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nint GetAndUpdate(ref nint value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.Int32.cs b/src/DotNext/Threading/Atomic.Int32.cs new file mode 100644 index 000000000..46a8742a6 --- /dev/null +++ b/src/DotNext/Threading/Atomic.Int32.cs @@ -0,0 +1,118 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int AccumulateAndGet(ref int value, int x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int AccumulateAndGet(ref int value, int x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetAndAccumulate(ref int value, int x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetAndAccumulate(ref int value, int x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int UpdateAndGet(ref int value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int UpdateAndGet(ref int value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetAndUpdate(ref int value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetAndUpdate(ref int value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.Int64.cs b/src/DotNext/Threading/Atomic.Int64.cs new file mode 100644 index 000000000..f4b3a13ff --- /dev/null +++ b/src/DotNext/Threading/Atomic.Int64.cs @@ -0,0 +1,118 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long AccumulateAndGet(ref long value, long x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long AccumulateAndGet(ref long value, long x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long GetAndAccumulate(ref long value, long x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long GetAndAccumulate(ref long value, long x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long UpdateAndGet(ref long value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long UpdateAndGet(ref long value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long GetAndUpdate(ref long value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long GetAndUpdate(ref long value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.Single.cs b/src/DotNext/Threading/Atomic.Single.cs new file mode 100644 index 000000000..bb11d2d9d --- /dev/null +++ b/src/DotNext/Threading/Atomic.Single.cs @@ -0,0 +1,118 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float AccumulateAndGet(ref float value, float x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float AccumulateAndGet(ref float value, float x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float GetAndAccumulate(ref float value, float x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float GetAndAccumulate(ref float value, float x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float UpdateAndGet(ref float value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float UpdateAndGet(ref float value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float GetAndUpdate(ref float value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float GetAndUpdate(ref float value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.UInt.cs b/src/DotNext/Threading/Atomic.UInt.cs new file mode 100644 index 000000000..6f35789ff --- /dev/null +++ b/src/DotNext/Threading/Atomic.UInt.cs @@ -0,0 +1,126 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint AccumulateAndGet(ref nuint value, nuint x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint AccumulateAndGet(ref nuint value, nuint x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint GetAndAccumulate(ref nuint value, nuint x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint GetAndAccumulate(ref nuint value, nuint x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint UpdateAndGet(ref nuint value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint UpdateAndGet(ref nuint value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint GetAndUpdate(ref nuint value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static nuint GetAndUpdate(ref nuint value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.UInt32.cs b/src/DotNext/Threading/Atomic.UInt32.cs new file mode 100644 index 000000000..f0fce5c31 --- /dev/null +++ b/src/DotNext/Threading/Atomic.UInt32.cs @@ -0,0 +1,126 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint AccumulateAndGet(ref uint value, uint x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint AccumulateAndGet(ref uint value, uint x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint GetAndAccumulate(ref uint value, uint x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint GetAndAccumulate(ref uint value, uint x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint UpdateAndGet(ref uint value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint UpdateAndGet(ref uint value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint GetAndUpdate(ref uint value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static uint GetAndUpdate(ref uint value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.UInt64.cs b/src/DotNext/Threading/Atomic.UInt64.cs new file mode 100644 index 000000000..856a51870 --- /dev/null +++ b/src/DotNext/Threading/Atomic.UInt64.cs @@ -0,0 +1,126 @@ +using System.Runtime.CompilerServices; + +namespace DotNext.Threading; + +public static partial class Atomic +{ + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong AccumulateAndGet(ref ulong value, ulong x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).NewValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the updated value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong AccumulateAndGet(ref ulong value, ulong x, Func accumulator) + => AccumulateAndGet>(ref value, x, accumulator); + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// The type implementing accumulator. + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong GetAndAccumulate(ref ulong value, ulong x, TAccumulator accumulator) + where TAccumulator : notnull, ISupplier + => Accumulate(ref value, x, accumulator).OldValue; + + /// + /// Atomically updates the current value with the results of applying the given function + /// to the current and given values, returning the original value. + /// + /// + /// The function is applied with the current value as its first argument, and the given update as the second argument. + /// + /// Reference to a value to be modified. + /// Accumulator operand. + /// A side-effect-free function of two arguments. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong GetAndAccumulate(ref ulong value, ulong x, Func accumulator) + => GetAndAccumulate>(ref value, x, accumulator); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong UpdateAndGet(ref ulong value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).NewValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the updated value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The updated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong UpdateAndGet(ref ulong value, Func updater) + => UpdateAndGet>(ref value, updater); + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// The type implementing updater. + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong GetAndUpdate(ref ulong value, TUpdater updater) + where TUpdater : notnull, ISupplier + => Update(ref value, updater).OldValue; + + /// + /// Atomically updates the stored value with the results + /// of applying the given function, returning the original value. + /// + /// Reference to a value to be modified. + /// A side-effect-free function. + /// The original value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [CLSCompliant(false)] + public static ulong GetAndUpdate(ref ulong value, Func updater) + => GetAndUpdate>(ref value, updater); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Atomic.cs b/src/DotNext/Threading/Atomic.cs index 4bac66754..c46e6401c 100644 --- a/src/DotNext/Threading/Atomic.cs +++ b/src/DotNext/Threading/Atomic.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DotNext.Threading; @@ -69,7 +70,7 @@ internal EqualityComparer(delegate* ptr) private T value; - private AtomicBoolean lockState; + private Atomic.Boolean lockState; /// /// Clones this container atomically. @@ -386,4 +387,134 @@ public T Value Read(out var result); return result.ToString(); } +} + +/// +/// Exposes atomic operations for thread-safe scenarios. +/// +public static partial class Atomic +{ + private static (TValue OldValue, TValue NewValue) Update(ref TValue value, TUpdater updater) + where TValue : IEqualityOperators + where TUpdater : notnull, ISupplier + where TOperations : IInterlockedOperations + { + TValue oldValue, newValue, tmp = TOperations.VolatileRead(in value); + do + { + newValue = updater.Invoke(oldValue = tmp); + } + while ((tmp = TOperations.CompareExchange(ref value, newValue, oldValue)) != oldValue); + + return (oldValue, newValue); + } + + private static (TValue OldValue, TValue NewValue) Accumulate(ref TValue value, TValue x, TAccumulator accumulator) + where TValue : IEqualityOperators + where TAccumulator : notnull, ISupplier + where TOperations : IInterlockedOperations + { + TValue oldValue, newValue, tmp = TOperations.VolatileRead(in value); + do + { + newValue = accumulator.Invoke(oldValue = tmp, x); + } + while ((tmp = TOperations.CompareExchange(ref value, newValue, oldValue)) != oldValue); + + return (oldValue, newValue); + } + + /// + /// Determines that the write to the location in the memory of + /// type is atomic. + /// + /// The type of the value to be written. + /// if write is atomic; otherwise, . + /// Section I.12.6.6. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsAtomic() + => AlignOf() == Unsafe.SizeOf() && Unsafe.SizeOf() <= nuint.Size; + + private struct InterlockedOperations : + IInterlockedOperations, + IInterlockedOperations, + IInterlockedOperations, + IInterlockedOperations, + IInterlockedOperations, + IInterlockedOperations, + IInterlockedOperations, + IInterlockedOperations + { + static int IInterlockedOperations.VolatileRead(ref readonly int location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref int location, int value) + => Volatile.Write(ref location, value); + + static int IInterlockedOperations.CompareExchange(ref int location, int value, int comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + + static uint IInterlockedOperations.VolatileRead(ref readonly uint location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref uint location, uint value) + => Volatile.Write(ref location, value); + + static uint IInterlockedOperations.CompareExchange(ref uint location, uint value, uint comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + + static long IInterlockedOperations.VolatileRead(ref readonly long location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref long location, long value) + => Volatile.Write(ref location, value); + + static long IInterlockedOperations.CompareExchange(ref long location, long value, long comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + + static ulong IInterlockedOperations.VolatileRead(ref readonly ulong location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref ulong location, ulong value) + => Volatile.Write(ref location, value); + + static ulong IInterlockedOperations.CompareExchange(ref ulong location, ulong value, ulong comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + + static nint IInterlockedOperations.VolatileRead(ref readonly nint location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref nint location, nint value) + => Volatile.Write(ref location, value); + + static nint IInterlockedOperations.CompareExchange(ref nint location, nint value, nint comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + + static nuint IInterlockedOperations.VolatileRead(ref readonly nuint location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref nuint location, nuint value) + => Volatile.Write(ref location, value); + + static nuint IInterlockedOperations.CompareExchange(ref nuint location, nuint value, nuint comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + + static float IInterlockedOperations.VolatileRead(ref readonly float location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref float location, float value) + => Volatile.Write(ref location, value); + + static float IInterlockedOperations.CompareExchange(ref float location, float value, float comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + + static double IInterlockedOperations.VolatileRead(ref readonly double location) + => Volatile.Read(in location); + + static void IInterlockedOperations.VolatileWrite(ref double location, double value) + => Volatile.Write(ref location, value); + + static double IInterlockedOperations.CompareExchange(ref double location, double value, double comparand) + => Interlocked.CompareExchange(ref location, value, comparand); + } } \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicBoolean.cs b/src/DotNext/Threading/AtomicBoolean.cs deleted file mode 100644 index c326c9049..000000000 --- a/src/DotNext/Threading/AtomicBoolean.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using static InlineIL.FieldRef; -using static InlineIL.IL; -using static InlineIL.IL.Emit; -using static InlineIL.MethodRef; -using static InlineIL.TypeRef; - -namespace DotNext.Threading; - -/// -/// Represents atomic boolean. -/// -/// -/// Initializes a new atomic boolean container with initial value. -/// -/// Initial value of the atomic boolean. -[SuppressMessage("Usage", "CA2231")] -public struct AtomicBoolean(bool value) : IEquatable -{ - [StructLayout(LayoutKind.Auto)] - private readonly struct Negation : ISupplier - { - bool ISupplier.Invoke(bool value) => !value; - } - - private int value = value.ToInt32(); - - /// - /// Gets or sets boolean value in volatile manner. - /// - public bool Value - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - readonly get - { - Ldarg_0(); - Volatile(); - Ldfld(Field(Type(), nameof(value))); - return Return(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - set - { - Ldarg_0(); - Push(value); - Volatile(); - Stfld(Field(Type(), nameof(this.value))); - Ret(); - } - } - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// The new value. - /// The expected value. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CompareExchange(bool update, bool expected) - { - // push this.value - Ldarg_0(); - Ldflda(Field(Type(), nameof(value))); - - Push(update); - Push(expected); - - Call(Method(Type(typeof(Interlocked)), nameof(Interlocked.CompareExchange), Type().MakeByRefType(), Type(), Type())); - return Return(); - } - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CompareAndSet(bool expected, bool update) - => CompareExchange(update, expected) == expected; - - /// - /// Atomically sets value if the - /// current value is . - /// - /// if current value is modified successfully; otherwise, . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool FalseToTrue() => CompareAndSet(false, true); - - /// - /// Atomically sets value if the - /// current value is . - /// - /// if current value is modified successfully; otherwise, . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TrueToFalse() => CompareAndSet(true, false); - - /// - /// Negates currently stored value atomically. - /// - /// Negation result. - public unsafe bool NegateAndGet() => Update(new Negation()).NewValue; - - /// - /// Negates currently stored value atomically. - /// - /// The original value before negation. - public unsafe bool GetAndNegate() => Update(new Negation()).OldValue; - - /// - /// Modifies the current value atomically. - /// - /// A new value to be stored into this container. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool GetAndSet(bool update) => value.GetAndSet(update.ToInt32()).ToBoolean(); - - /// - /// Modifies the current value atomically. - /// - /// A new value to be stored into this container. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool SetAndGet(bool update) - { - Value = update; - return update; - } - - private (bool OldValue, bool NewValue) Update(TUpdater updater) - where TUpdater : struct, ISupplier - { - bool oldValue, newValue, tmp = Value; - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while ((tmp = CompareExchange(newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - private (bool OldValue, bool NewValue) Accumulate(bool x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - bool oldValue, newValue, tmp = Value; - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while ((tmp = CompareExchange(newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - public bool AccumulateAndGet(bool x, Func accumulator) - => Accumulate>(x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [CLSCompliant(false)] - public unsafe bool AccumulateAndGet(bool x, delegate* accumulator) - => Accumulate>(x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - public bool GetAndAccumulate(bool x, Func accumulator) - => Accumulate>(x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [CLSCompliant(false)] - public unsafe bool GetAndAccumulate(bool x, delegate* accumulator) - => Accumulate>(x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// A side-effect-free function. - /// The updated value. - public bool UpdateAndGet(Func updater) - => Update>(updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// A side-effect-free function. - /// The updated value. - [CLSCompliant(false)] - public unsafe bool UpdateAndGet(delegate* updater) - => Update>(updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// A side-effect-free function. - /// The original value. - public bool GetAndUpdate(Func updater) - => Update>(updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// A side-effect-free function. - /// The original value. - [CLSCompliant(false)] - public unsafe bool GetAndUpdate(delegate* updater) - => Update>(updater).OldValue; - - internal void Acquire() - { - ref var lockState = ref value; - if (Interlocked.Exchange(ref lockState, 1) is 1) - Contention(ref lockState); - - [MethodImpl(MethodImplOptions.NoInlining)] - static void Contention(ref int value) - { - var spinner = new SpinWait(); - do - { - spinner.SpinOnce(); - } - while (Interlocked.Exchange(ref value, 1) is 1); - } - } - - internal void Release() => System.Threading.Volatile.Write(ref value, 0); - - /// - /// Determines whether stored value is equal to value passed as argument. - /// - /// Other value to compare. - /// , if stored value is equal to other value; otherwise, . - public readonly bool Equals(bool other) => Unsafe.AsRef(in value).VolatileRead() == other.ToInt32(); - - /// - /// Computes hash code for the stored value. - /// - /// The hash code of the stored boolean value. - public override readonly int GetHashCode() => Unsafe.AsRef(in value).VolatileRead(); - - /// - /// Determines whether stored value is equal to - /// value as the passed argument. - /// - /// Other value to compare. - /// , if stored value is equal to other value; otherwise, . - public override readonly bool Equals([NotNullWhen(true)] object? other) => other switch - { - bool b => Equals(b), - AtomicBoolean b => Value == b.Value, - _ => false, - }; - - /// - /// Returns stored boolean value in the form of . - /// - /// Textual representation of stored boolean value. - public override readonly string ToString() => value.ToBoolean() ? bool.TrueString : bool.FalseString; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicDouble.cs b/src/DotNext/Threading/AtomicDouble.cs deleted file mode 100644 index 066f14de8..000000000 --- a/src/DotNext/Threading/AtomicDouble.cs +++ /dev/null @@ -1,256 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Various atomic operations for data type -/// accessible as extension methods. -/// -/// -/// Methods exposed by this class provide volatile read/write -/// of the field even if it is not declared as volatile field. -/// -/// -public static class AtomicDouble -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double VolatileRead(in this double value) => Volatile.Read(ref Unsafe.AsRef(in value)); - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref this double value, double newValue) => Volatile.Write(ref value, newValue); - - /// - /// Atomically increments by one referenced value. - /// - /// Reference to a value to be modified. - /// Incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IncrementAndGet(ref this double value) - => Update(ref value, new Increment()).NewValue; - - /// - /// Atomically decrements by one the current value. - /// - /// Reference to a value to be modified. - /// Decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double DecrementAndGet(ref this double value) - => Update(ref value, new Decrement()).NewValue; - - /// - /// Adds two 64-bit floating-point numbers and replaces referenced storage with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// Result of sum operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double AddAndGet(ref this double value, double operand) - => Accumulate(ref value, operand, new Adder()).NewValue; - - /// - /// Adds two 64-bit floating-point numbers and replaces referenced storage with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndAdd(ref this double value, double operand) - => Accumulate(ref value, operand, new Adder()).OldValue; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool Equals(double x, double y) - => x == y || double.IsNaN(x) && double.IsNaN(y); - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// Reference to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref this double value, double expected, double update) - => Equals(Interlocked.CompareExchange(ref value, update, expected), expected); - - /// - /// Modifies referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndSet(ref this double value, double update) - => Interlocked.Exchange(ref value, update); - - /// - /// Modifies value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double SetAndGet(ref this double value, double update) - { - VolatileWrite(ref value, update); - return update; - } - - private static (double OldValue, double NewValue) Update(ref double value, TUpdater updater) - where TUpdater : struct, ISupplier - { - double oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - private static (double OldValue, double NewValue) Accumulate(ref double value, double x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - double oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double AccumulateAndGet(ref this double value, double x, Func accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double AccumulateAndGet(ref this double value, double x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndAccumulate(ref this double value, double x, Func accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double GetAndAccumulate(ref this double value, double x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double UpdateAndGet(ref this double value, Func updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double UpdateAndGet(ref this double value, delegate* updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double GetAndUpdate(ref this double value, Func updater) - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe double GetAndUpdate(ref this double value, delegate* updater) - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicInt32.cs b/src/DotNext/Threading/AtomicInt32.cs deleted file mode 100644 index 403d2c39c..000000000 --- a/src/DotNext/Threading/AtomicInt32.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Various atomic operations for data type -/// accessible as extension methods. -/// -/// -/// Methods exposed by this class provide volatile read/write -/// of the field even if it is not declared as volatile field. -/// -/// -public static class AtomicInt32 -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int VolatileRead(in this int value) => Volatile.Read(ref Unsafe.AsRef(in value)); - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref this int value, int newValue) => Volatile.Write(ref value, newValue); - - /// - /// Atomically increments the referenced value by one. - /// - /// Reference to a value to be modified. - /// Incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IncrementAndGet(ref this int value) - => Interlocked.Increment(ref value); - - /// - /// Atomically decrements the referenced value by one. - /// - /// Reference to a value to be modified. - /// Decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int DecrementAndGet(ref this int value) - => Interlocked.Decrement(ref value); - - /// - /// Atomically sets the referenced value to the given updated value if the current value == the expected value. - /// - /// Reference to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref this int value, int expected, int update) - => Interlocked.CompareExchange(ref value, update, expected) == expected; - - /// - /// Adds two 32-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// Result of sum operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int AddAndGet(ref this int value, int operand) - => Interlocked.Add(ref value, operand); - - /// - /// Adds two 32-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndAdd(ref this int value, int operand) - => Accumulate(ref value, operand, new Adder()).OldValue; - - /// - /// Bitwise "ands" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndBitwiseAnd(ref this int value, int operand) - => Interlocked.And(ref value, operand); - - /// - /// Bitwise "ands" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int BitwiseAndAndGet(ref this int value, int operand) - => Interlocked.And(ref value, operand) & operand; - - /// - /// Bitwise "ors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndBitwiseOr(ref this int value, int operand) - => Interlocked.Or(ref value, operand); - - /// - /// Bitwise "ors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int BitwiseOrAndGet(ref this int value, int operand) - => Interlocked.Or(ref value, operand) | operand; - - /// - /// Bitwise "xors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndBitwiseXor(ref this int value, int operand) - => Accumulate(ref value, operand, new BitwiseXor()).OldValue; - - /// - /// Bitwise "xors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int BitwiseXorAndGet(ref this int value, int operand) - => Accumulate(ref value, operand, new BitwiseXor()).NewValue; - - /// - /// Modifies the referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndSet(ref this int value, int update) - => Interlocked.Exchange(ref value, update); - - /// - /// Modifies the referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SetAndGet(ref this int value, int update) - { - VolatileWrite(ref value, update); - return update; - } - - private static (int OldValue, int NewValue) Update(ref int value, TUpdater updater) - where TUpdater : struct, ISupplier - { - int oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - private static (int OldValue, int NewValue) Accumulate(ref int value, int x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - int oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int AccumulateAndGet(ref this int value, int x, Func accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int AccumulateAndGet(ref this int value, int x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndAccumulate(ref this int value, int x, Func accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int GetAndAccumulate(ref this int value, int x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int UpdateAndGet(ref this int value, Func updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int UpdateAndGet(ref this int value, delegate* updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetAndUpdate(ref this int value, Func updater) - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe int GetAndUpdate(ref this int value, delegate* updater) - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicInt64.cs b/src/DotNext/Threading/AtomicInt64.cs deleted file mode 100644 index 72ac77fbd..000000000 --- a/src/DotNext/Threading/AtomicInt64.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Various atomic operations for data type -/// accessible as extension methods. -/// -/// -/// Methods exposed by this class provide volatile read/write -/// of the field even if it is not declared as volatile field. -/// -/// -public static class AtomicInt64 -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long VolatileRead(in this long value) => Volatile.Read(ref Unsafe.AsRef(in value)); - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref this long value, long newValue) => Volatile.Write(ref value, newValue); - - /// - /// Atomically increments by one referenced value. - /// - /// Reference to a value to be modified. - /// Incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IncrementAndGet(ref this long value) - => Interlocked.Increment(ref value); - - /// - /// Atomically decrements by one the current value. - /// - /// Reference to a value to be modified. - /// Decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long DecrementAndGet(ref this long value) - => Interlocked.Decrement(ref value); - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// Reference to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref this long value, long expected, long update) - => Interlocked.CompareExchange(ref value, update, expected) == expected; - - /// - /// Adds two 64-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// Result of sum operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long AddAndGet(ref this long value, long operand) - => Interlocked.Add(ref value, operand); - - /// - /// Adds two 64-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndAdd(ref this long value, long operand) - => Accumulate(ref value, operand, new Adder()).OldValue; - - /// - /// Bitwise "ands" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndBitwiseAnd(ref this long value, long operand) - => Interlocked.And(ref value, operand); - - /// - /// Bitwise "ands" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long BitwiseAndAndGet(ref this long value, long operand) - => Interlocked.And(ref value, operand) & operand; - - /// - /// Bitwise "ors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndBitwiseOr(ref this long value, long operand) - => Interlocked.Or(ref value, operand); - - /// - /// Bitwise "ors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long BitwiseOrAndGet(ref this long value, long operand) - => Interlocked.Or(ref value, operand) | operand; - - /// - /// Bitwise "xors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndBitwiseXor(ref this long value, long operand) - => Accumulate(ref value, operand, new BitwiseXor()).OldValue; - - /// - /// Bitwise "xors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long BitwiseXorAndGet(ref this long value, long operand) - => Accumulate(ref value, operand, new BitwiseXor()).NewValue; - - /// - /// Modifies referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndSet(ref this long value, long update) - => Interlocked.Exchange(ref value, update); - - /// - /// Modifies value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long SetAndGet(ref this long value, long update) - { - VolatileWrite(ref value, update); - return update; - } - - private static (long OldValue, long NewValue) Update(ref long value, TUpdater updater) - where TUpdater : struct, ISupplier - { - long oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - private static (long OldValue, long NewValue) Accumulate(ref long value, long x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - long oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long AccumulateAndGet(ref this long value, long x, Func accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long AccumulateAndGet(ref this long value, long x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndAccumulate(ref this long value, long x, Func accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long GetAndAccumulate(ref this long value, long x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long UpdateAndGet(ref this long value, Func updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long UpdateAndGet(ref this long value, delegate* updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long GetAndUpdate(ref this long value, Func updater) - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe long GetAndUpdate(ref this long value, delegate* updater) - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicIntPtr.cs b/src/DotNext/Threading/AtomicIntPtr.cs deleted file mode 100644 index 2e737d493..000000000 --- a/src/DotNext/Threading/AtomicIntPtr.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Various atomic operations for data type -/// accessible as extension methods. -/// -/// -/// Methods exposed by this class provide volatile read/write -/// of the field even if it is not declared as volatile field. -/// -/// -public static class AtomicIntPtr -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr VolatileRead(in this IntPtr value) - => Volatile.Read(ref Unsafe.AsRef(in value)); - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref this IntPtr value, IntPtr newValue) - => Volatile.Write(ref value, newValue); - - /// - /// Atomically increments the referenced value by one. - /// - /// Reference to a value to be modified. - /// Incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr IncrementAndGet(ref this IntPtr value) => IntPtr.Size switch - { - sizeof(int) => (IntPtr)Interlocked.Increment(ref Unsafe.As(ref value)), - sizeof(long) => (IntPtr)Interlocked.Increment(ref Unsafe.As(ref value)), - _ => Update(ref value, new Increment()).NewValue, - }; - - /// - /// Atomically decrements the referenced value by one. - /// - /// Reference to a value to be modified. - /// Decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr DecrementAndGet(ref this IntPtr value) => IntPtr.Size switch - { - sizeof(int) => (IntPtr)Interlocked.Decrement(ref Unsafe.As(ref value)), - sizeof(long) => (IntPtr)Interlocked.Decrement(ref Unsafe.As(ref value)), - _ => Update(ref value, new Decrement()).NewValue, - }; - - /// - /// Adds two native integers and replaces referenced storage with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// Result of sum operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr AddAndGet(ref this IntPtr value, IntPtr operand) => IntPtr.Size switch - { - sizeof(int) => (IntPtr)Interlocked.Add(ref Unsafe.As(ref value), (int)operand), - sizeof(long) => (IntPtr)Interlocked.Add(ref Unsafe.As(ref value), (long)operand), - _ => Accumulate(ref value, operand, new Adder()).NewValue, - }; - - /// - /// Adds two native integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndAdd(ref this IntPtr value, IntPtr operand) - => Accumulate(ref value, operand, new Adder()).OldValue; - - /// - /// Bitwise "ands" two native integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndBitwiseAnd(ref this IntPtr value, IntPtr operand) => IntPtr.Size switch - { - sizeof(int) => (IntPtr)Interlocked.And(ref Unsafe.As(ref value), (int)operand), - sizeof(long) => (IntPtr)Interlocked.And(ref Unsafe.As(ref value), (long)operand), - _ => Accumulate(ref value, operand, new BitwiseAnd()).OldValue, - }; - - /// - /// Bitwise "ands" two native integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr BitwiseAndAndGet(ref this IntPtr value, IntPtr operand) => IntPtr.Size switch - { - sizeof(int) => (nint)Interlocked.And(ref Unsafe.As(ref value), (int)operand) & operand, - sizeof(long) => (nint)Interlocked.And(ref Unsafe.As(ref value), (long)operand) & operand, - _ => Accumulate(ref value, operand, new BitwiseAnd()).NewValue, - }; - - /// - /// Bitwise "ors" two native integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndBitwiseOr(ref this IntPtr value, IntPtr operand) => IntPtr.Size switch - { - sizeof(int) => (IntPtr)Interlocked.Or(ref Unsafe.As(ref value), (int)operand), - sizeof(long) => (IntPtr)Interlocked.Or(ref Unsafe.As(ref value), (long)operand), - _ => Accumulate(ref value, operand, new BitwiseAnd()).OldValue, - }; - - /// - /// Bitwise "ors" two native integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr BitwiseOrAndGet(ref this IntPtr value, IntPtr operand) => IntPtr.Size switch - { - sizeof(int) => (nint)Interlocked.Or(ref Unsafe.As(ref value), (int)operand) | operand, - sizeof(long) => (nint)Interlocked.Or(ref Unsafe.As(ref value), (long)operand) | operand, - _ => Accumulate(ref value, operand, new BitwiseOr()).NewValue, - }; - - /// - /// Bitwise "xors" two native integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndBitwiseXor(ref this IntPtr value, IntPtr operand) - => Accumulate(ref value, operand, new BitwiseXor()).OldValue; - - /// - /// Bitwise "xors" two native integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr BitwiseXorAndGet(ref this IntPtr value, IntPtr operand) - => Accumulate(ref value, operand, new BitwiseXor()).NewValue; - - /// - /// Atomically sets the referenced value to the given updated value if the current value == the expected value. - /// - /// Reference to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref this IntPtr value, IntPtr expected, IntPtr update) - => Interlocked.CompareExchange(ref value, update, expected) == expected; - - /// - /// Modifies the referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndSet(ref this IntPtr value, IntPtr update) - => Interlocked.Exchange(ref value, update); - - /// - /// Modifies the referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr SetAndGet(ref this IntPtr value, IntPtr update) - { - VolatileWrite(ref value, update); - return update; - } - - private static (IntPtr OldValue, IntPtr NewValue) Update(ref IntPtr value, TUpdater updater) - where TUpdater : struct, ISupplier - { - IntPtr oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - private static (IntPtr OldValue, IntPtr NewValue) Accumulate(ref IntPtr value, IntPtr x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - IntPtr oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr AccumulateAndGet(ref this IntPtr value, IntPtr x, Func accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr AccumulateAndGet(ref this IntPtr value, IntPtr x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndAccumulate(ref this IntPtr value, IntPtr x, Func accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr GetAndAccumulate(ref this IntPtr value, IntPtr x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr UpdateAndGet(ref this IntPtr value, Func updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr UpdateAndGet(ref this IntPtr value, delegate* updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static IntPtr GetAndUpdate(ref this IntPtr value, Func updater) - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe IntPtr GetAndUpdate(ref this IntPtr value, delegate* updater) - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicReference.cs b/src/DotNext/Threading/AtomicReference.cs deleted file mode 100644 index 19a67ac81..000000000 --- a/src/DotNext/Threading/AtomicReference.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Provides atomic operations for the reference type. -/// -public static class AtomicReference -{ - /// - /// Compares two values for equality and, if they are equal, - /// replaces the stored value. - /// - /// Type of value in the memory storage. - /// The value to update. - /// The expected value. - /// The new value. - /// true if successful. False return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref T value, T expected, T update) - where T : class? - => ReferenceEquals(Interlocked.CompareExchange(ref value, update, expected), expected); - - private static (T OldValue, T NewValue) Update(ref T value, TUpdater updater) - where T : class? - where TUpdater : struct, ISupplier - { - T oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while (!ReferenceEquals(tmp = Interlocked.CompareExchange(ref value, newValue, oldValue), oldValue)); - - return (oldValue, newValue); - } - - private static (T OldValue, T NewValue) Accumulate(ref T value, T x, TAccumulator accumulator) - where T : class? - where TAccumulator : struct, ISupplier - { - T oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while (!ReferenceEquals(tmp = Interlocked.CompareExchange(ref value, newValue, oldValue), oldValue)); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Type of value in the memory storage. - /// The value to update. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T AccumulateAndGet(ref T value, T x, Func accumulator) - where T : class? - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Type of value in the memory storage. - /// The value to update. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe T AccumulateAndGet(ref T value, T x, delegate* accumulator) - where T : class? - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Type of value in the memory storage. - /// The value to update. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNullIfNotNull(nameof(value))] - public static T GetAndAccumulate(ref T value, T x, Func accumulator) - where T : class? - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Type of value in the memory storage. - /// The value to update. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNullIfNotNull(nameof(value))] - [CLSCompliant(false)] - public static unsafe T GetAndAccumulate(ref T value, T x, delegate* accumulator) - where T : class? - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Type of value in the memory storage. - /// The value to update. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T UpdateAndGet(ref T value, Func updater) - where T : class? - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Type of value in the memory storage. - /// The value to update. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe T UpdateAndGet(ref T value, delegate* updater) - where T : class? - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Type of value in the memory storage. - /// The value to update. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNullIfNotNull(nameof(value))] - public static T GetAndUpdate(ref T value, Func updater) - where T : class? - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Type of value in the memory storage. - /// The value to update. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNullIfNotNull(nameof(value))] - [CLSCompliant(false)] - public static unsafe T GetAndUpdate(ref T value, delegate* updater) - where T : class? - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicSingle.cs b/src/DotNext/Threading/AtomicSingle.cs deleted file mode 100644 index bc21fee99..000000000 --- a/src/DotNext/Threading/AtomicSingle.cs +++ /dev/null @@ -1,256 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Various atomic operations for data type -/// accessible as extension methods. -/// -/// -/// Methods exposed by this class provide volatile read/write -/// of the field even if it is not declared as volatile field. -/// -/// -public static class AtomicSingle -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float VolatileRead(in this float value) => Volatile.Read(ref Unsafe.AsRef(in value)); - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref this float value, float newValue) => Volatile.Write(ref value, newValue); - - /// - /// Atomically increments by one referenced value. - /// - /// Reference to a value to be modified. - /// Incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float IncrementAndGet(ref this float value) - => Update(ref value, new Increment()).NewValue; - - /// - /// Atomically decrements by one the current value. - /// - /// Reference to a value to be modified. - /// Decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float DecrementAndGet(ref this float value) - => Update(ref value, new Decrement()).NewValue; - - /// - /// Adds two 64-bit floating-point numbers and replaces referenced storage with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// Result of sum operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float AddAndGet(ref this float value, float operand) - => Accumulate(ref value, operand, new Adder()).NewValue; - - /// - /// Adds two 64-bit floating-point numbers and replaces referenced storage with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// The initial value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndAdd(ref this float value, float operand) - => Accumulate(ref value, operand, new Adder()).OldValue; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool Equals(float x, float y) - => BitConverter.SingleToInt32Bits(x) == BitConverter.SingleToInt32Bits(y); - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// Reference to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref this float value, float expected, float update) - => Equals(Interlocked.CompareExchange(ref value, update, expected), expected); - - /// - /// Modifies referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndSet(ref this float value, float update) - => Interlocked.Exchange(ref value, update); - - /// - /// Modifies value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float SetAndGet(ref this float value, float update) - { - VolatileWrite(ref value, update); - return update; - } - - private static (float OldValue, float NewValue) Update(ref float value, TUpdater updater) - where TUpdater : struct, ISupplier - { - float oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while (!Equals(tmp = Interlocked.CompareExchange(ref value, newValue, oldValue), oldValue)); - - return (oldValue, newValue); - } - - private static (float OldValue, float NewValue) Accumulate(ref float value, float x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - float oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while (!Equals(tmp = Interlocked.CompareExchange(ref value, newValue, oldValue), oldValue)); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float AccumulateAndGet(ref this float value, float x, Func accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float AccumulateAndGet(ref this float value, float x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndAccumulate(ref this float value, float x, Func accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float GetAndAccumulate(ref this float value, float x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float UpdateAndGet(ref this float value, Func updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float UpdateAndGet(ref this float value, delegate* updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float GetAndUpdate(ref this float value, Func updater) - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [CLSCompliant(false)] - public static unsafe float GetAndUpdate(ref this float value, delegate* updater) - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicUInt32.cs b/src/DotNext/Threading/AtomicUInt32.cs deleted file mode 100644 index bfdbc4af0..000000000 --- a/src/DotNext/Threading/AtomicUInt32.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Various atomic operations for data type -/// accessible as extension methods. -/// -/// -/// Methods exposed by this class provide volatile read/write -/// of the field even if it is not declared as volatile field. -/// -/// -[CLSCompliant(false)] -public static class AtomicUInt32 -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint VolatileRead(in this uint value) => Volatile.Read(ref Unsafe.AsRef(in value)); - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref this uint value, uint newValue) => Volatile.Write(ref value, newValue); - - /// - /// Atomically increments the referenced value by one. - /// - /// Reference to a value to be modified. - /// Incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IncrementAndGet(ref this uint value) - => Interlocked.Increment(ref value); - - /// - /// Atomically decrements the referenced value by one. - /// - /// Reference to a value to be modified. - /// Decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint DecrementAndGet(ref this uint value) - => Interlocked.Decrement(ref value); - - /// - /// Atomically sets the referenced value to the given updated value if the current value == the expected value. - /// - /// Reference to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref this uint value, uint expected, uint update) - => Interlocked.CompareExchange(ref value, update, expected) == expected; - - /// - /// Adds two 32-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// Result of sum operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint AddAndGet(ref this uint value, uint operand) - => Interlocked.Add(ref value, operand); - - /// - /// Adds two 32-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint GetAndAdd(ref this uint value, uint operand) - => Accumulate(ref value, operand, new Adder()).OldValue; - - /// - /// Bitwise "ands" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint GetAndBitwiseAnd(ref this uint value, uint operand) - => Interlocked.And(ref value, operand); - - /// - /// Bitwise "ands" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint BitwiseAndAndGet(ref this uint value, uint operand) - => Interlocked.And(ref value, operand) & operand; - - /// - /// Bitwise "ors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint GetAndBitwiseOr(ref this uint value, uint operand) - => Interlocked.Or(ref value, operand); - - /// - /// Bitwise "ors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint BitwiseOrAndGet(ref this uint value, uint operand) - => Interlocked.Or(ref value, operand) | operand; - - /// - /// Bitwise "xors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint GetAndBitwiseXor(ref this uint value, uint operand) - => Accumulate(ref value, operand, new BitwiseXor()).OldValue; - - /// - /// Bitwise "xors" two 32-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint BitwiseXorAndGet(ref this uint value, uint operand) - => Accumulate(ref value, operand, new BitwiseXor()).NewValue; - - /// - /// Modifies the referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint GetAndSet(ref this uint value, uint update) - => Interlocked.Exchange(ref value, update); - - /// - /// Modifies the referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint SetAndGet(ref this uint value, uint update) - { - VolatileWrite(ref value, update); - return update; - } - - private static (uint OldValue, uint NewValue) Update(ref uint value, TUpdater updater) - where TUpdater : struct, ISupplier - { - uint oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - private static (uint OldValue, uint NewValue) Accumulate(ref uint value, uint x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - uint oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint AccumulateAndGet(ref this uint value, uint x, Func accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe uint AccumulateAndGet(ref this uint value, uint x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint GetAndAccumulate(ref this uint value, uint x, Func accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe uint GetAndAccumulate(ref this uint value, uint x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint UpdateAndGet(ref this uint value, Func updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe uint UpdateAndGet(ref this uint value, delegate* updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint GetAndUpdate(ref this uint value, Func updater) - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe uint GetAndUpdate(ref this uint value, delegate* updater) - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AtomicUInt64.cs b/src/DotNext/Threading/AtomicUInt64.cs deleted file mode 100644 index f683f27e1..000000000 --- a/src/DotNext/Threading/AtomicUInt64.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace DotNext.Threading; - -/// -/// Various atomic operations for data type -/// accessible as extension methods. -/// -/// -/// Methods exposed by this class provide volatile read/write -/// of the field even if it is not declared as volatile field. -/// -/// -[CLSCompliant(false)] -public static class AtomicUInt64 -{ - /// - /// Reads the value of the specified field. On systems that require it, inserts a - /// memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears after this method in the code, the processor - /// cannot move it before this method. - /// - /// The field to read. - /// - /// The value that was read. This value is the latest written by any processor in - /// the computer, regardless of the number of processors or the state of processor - /// cache. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong VolatileRead(in this ulong value) => Volatile.Read(ref Unsafe.AsRef(in value)); - - /// - /// Writes the specified value to the specified field. On systems that require it, - /// inserts a memory barrier that prevents the processor from reordering memory operations - /// as follows: If a read or write appears before this method in the code, the processor - /// cannot move it after this method. - /// - /// The field where the value is written. - /// - /// The value to write. The value is written immediately so that it is visible to - /// all processors in the computer. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void VolatileWrite(ref this ulong value, ulong newValue) => Volatile.Write(ref value, newValue); - - /// - /// Atomically increments by one referenced value. - /// - /// Reference to a value to be modified. - /// Incremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong IncrementAndGet(ref this ulong value) - => Interlocked.Increment(ref value); - - /// - /// Atomically decrements by one the current value. - /// - /// Reference to a value to be modified. - /// Decremented value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong DecrementAndGet(ref this ulong value) - => Interlocked.Decrement(ref value); - - /// - /// Atomically sets referenced value to the given updated value if the current value == the expected value. - /// - /// Reference to a value to be modified. - /// The expected value. - /// The new value. - /// if successful. return indicates that the actual value was not equal to the expected value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool CompareAndSet(ref this ulong value, ulong expected, ulong update) - => Interlocked.CompareExchange(ref value, update, expected) == expected; - - /// - /// Adds two 64-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// Result of sum operation. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong AddAndGet(ref this ulong value, ulong operand) - => Interlocked.Add(ref value, operand); - - /// - /// Adds two 64-bit integers and replaces referenced integer with the sum, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be added to the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong GetAndAdd(ref this ulong value, ulong operand) - => Accumulate(ref value, operand, new Adder()).OldValue; - - /// - /// Bitwise "ands" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong GetAndBitwiseAnd(ref this ulong value, ulong operand) - => Interlocked.And(ref value, operand); - - /// - /// Bitwise "ands" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong BitwiseAndAndGet(ref this ulong value, ulong operand) - => Interlocked.And(ref value, operand) & operand; - - /// - /// Bitwise "ors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong GetAndBitwiseOr(ref this ulong value, ulong operand) - => Interlocked.Or(ref value, operand); - - /// - /// Bitwise "ors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong BitwiseOrAndGet(ref this ulong value, ulong operand) - => Interlocked.Or(ref value, operand) | operand; - - /// - /// Bitwise "xors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong GetAndBitwiseXor(ref this ulong value, ulong operand) - => Accumulate(ref value, operand, new BitwiseXor()).OldValue; - - /// - /// Bitwise "xors" two 64-bit integers and replaces referenced integer with the result, - /// as an atomic operation. - /// - /// Reference to a value to be modified. - /// The value to be combined with the currently stored integer. - /// The modified value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong BitwiseXorAndGet(ref this ulong value, ulong operand) - => Accumulate(ref value, operand, new BitwiseXor()).NewValue; - - /// - /// Modifies referenced value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// Original value before modification. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong GetAndSet(ref this ulong value, ulong update) - => Interlocked.Exchange(ref value, update); - - /// - /// Modifies value atomically. - /// - /// Reference to a value to be modified. - /// A new value to be stored into managed pointer. - /// A new value passed as argument. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong SetAndGet(ref this ulong value, ulong update) - { - VolatileWrite(ref value, update); - return update; - } - - private static (ulong OldValue, ulong NewValue) Update(ref ulong value, TUpdater updater) - where TUpdater : struct, ISupplier - { - ulong oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = updater.Invoke(oldValue = tmp); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - private static (ulong OldValue, ulong NewValue) Accumulate(ref ulong value, ulong x, TAccumulator accumulator) - where TAccumulator : struct, ISupplier - { - ulong oldValue, newValue, tmp = Volatile.Read(ref value); - do - { - newValue = accumulator.Invoke(oldValue = tmp, x); - } - while ((tmp = Interlocked.CompareExchange(ref value, newValue, oldValue)) != oldValue); - - return (oldValue, newValue); - } - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong AccumulateAndGet(ref this ulong value, ulong x, Func accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the updated value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ulong AccumulateAndGet(ref this ulong value, ulong x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).NewValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong GetAndAccumulate(ref this ulong value, ulong x, Func accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the current value with the results of applying the given function - /// to the current and given values, returning the original value. - /// - /// - /// The function is applied with the current value as its first argument, and the given update as the second argument. - /// - /// Reference to a value to be modified. - /// Accumulator operand. - /// A side-effect-free function of two arguments. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ulong GetAndAccumulate(ref this ulong value, ulong x, delegate* accumulator) - => Accumulate>(ref value, x, accumulator).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong UpdateAndGet(ref this ulong value, Func updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the updated value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The updated value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ulong UpdateAndGet(ref this ulong value, delegate* updater) - => Update>(ref value, updater).NewValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong GetAndUpdate(ref this ulong value, Func updater) - => Update>(ref value, updater).OldValue; - - /// - /// Atomically updates the stored value with the results - /// of applying the given function, returning the original value. - /// - /// Reference to a value to be modified. - /// A side-effect-free function. - /// The original value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ulong GetAndUpdate(ref this ulong value, delegate* updater) - => Update>(ref value, updater).OldValue; -} \ No newline at end of file diff --git a/src/DotNext/Threading/BitwiseAnd.cs b/src/DotNext/Threading/BitwiseAnd.cs deleted file mode 100644 index ea6fe8b4b..000000000 --- a/src/DotNext/Threading/BitwiseAnd.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Threading; - -[StructLayout(LayoutKind.Auto)] -internal readonly struct BitwiseAnd : ISupplier -{ - nint ISupplier.Invoke(nint x, nint y) => x & y; -} \ No newline at end of file diff --git a/src/DotNext/Threading/BitwiseOr.cs b/src/DotNext/Threading/BitwiseOr.cs deleted file mode 100644 index c80ce86bf..000000000 --- a/src/DotNext/Threading/BitwiseOr.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Threading; - -[StructLayout(LayoutKind.Auto)] -internal readonly struct BitwiseOr : ISupplier -{ - nint ISupplier.Invoke(nint x, nint y) => x | y; -} \ No newline at end of file diff --git a/src/DotNext/Threading/BitwiseXor.cs b/src/DotNext/Threading/BitwiseXor.cs deleted file mode 100644 index be6a66456..000000000 --- a/src/DotNext/Threading/BitwiseXor.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Threading; - -[StructLayout(LayoutKind.Auto)] -internal readonly struct BitwiseXor : ISupplier, ISupplier, ISupplier, ISupplier, ISupplier -{ - int ISupplier.Invoke(int x, int y) => x ^ y; - - long ISupplier.Invoke(long x, long y) => x ^ y; - - nint ISupplier.Invoke(nint x, nint y) => x ^ y; - - uint ISupplier.Invoke(uint x, uint y) => x ^ y; - - ulong ISupplier.Invoke(ulong x, ulong y) => x ^ y; -} \ No newline at end of file diff --git a/src/DotNext/Threading/Decrement.cs b/src/DotNext/Threading/Decrement.cs deleted file mode 100644 index 8c854132e..000000000 --- a/src/DotNext/Threading/Decrement.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Threading; - -[StructLayout(LayoutKind.Auto)] -internal readonly struct Decrement : ISupplier, ISupplier, ISupplier -{ - double ISupplier.Invoke(double value) => value - 1D; - - float ISupplier.Invoke(float value) => value - 1F; - - nint ISupplier.Invoke(nint value) => value - 1; -} \ No newline at end of file diff --git a/src/DotNext/Threading/IInterlockedOperations.cs b/src/DotNext/Threading/IInterlockedOperations.cs new file mode 100644 index 000000000..a24ce2c87 --- /dev/null +++ b/src/DotNext/Threading/IInterlockedOperations.cs @@ -0,0 +1,41 @@ +using System.Numerics; + +namespace DotNext.Threading; + +/// +/// Represents interlocked operations. +/// +/// The type that supports interlocked operations. +public interface IInterlockedOperations + where T : IEqualityOperators +{ + /// + /// Reads the value of the specified location. On systems that require it, inserts a + /// memory barrier that prevents the processor from reordering memory operations + /// as follows: If a read or write appears after this method in the code, the processor + /// cannot move it before this method. + /// + /// The location of the value. + /// The value that was read. + static abstract T VolatileRead(ref readonly T location); + + /// + /// Writes the specified value to the specified location. On systems that require it, + /// inserts a memory barrier that prevents the processor from reordering memory operations + /// as follows: If a read or write appears before this method in the code, the processor + /// cannot move it after this method. + /// + /// The location of the value. + /// The value to write. + static abstract void VolatileWrite(ref T location, T value); + + /// + /// Compares two values for equality and, if they + /// are equal, replaces the first value. + /// + /// The destination, whose value is compared with comparand and possibly replaced. + /// The value that replaces the destination value if the comparison results in equality. + /// The value that is compared to the value at . + /// The original value in . + static abstract T CompareExchange(ref T location, T value, T comparand); +} \ No newline at end of file diff --git a/src/DotNext/Threading/Increment.cs b/src/DotNext/Threading/Increment.cs deleted file mode 100644 index 6f5c54601..000000000 --- a/src/DotNext/Threading/Increment.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Threading; - -[StructLayout(LayoutKind.Auto)] -internal readonly struct Increment : ISupplier, ISupplier, ISupplier -{ - double ISupplier.Invoke(double value) => value + 1D; - - float ISupplier.Invoke(float value) => value + 1F; - - nint ISupplier.Invoke(nint value) => value + 1; -} \ No newline at end of file diff --git a/src/DotNext/Threading/ReaderWriterSpinLock.cs b/src/DotNext/Threading/ReaderWriterSpinLock.cs index d1c194efd..2e0273791 100644 --- a/src/DotNext/Threading/ReaderWriterSpinLock.cs +++ b/src/DotNext/Threading/ReaderWriterSpinLock.cs @@ -21,13 +21,13 @@ public struct ReaderWriterSpinLock private readonly uint version; private readonly bool valid; - internal LockStamp(in uint version) + internal LockStamp(ref readonly uint version) { - this.version = version.VolatileRead(); + this.version = Volatile.Read(in version); valid = true; } - internal bool IsValid(in uint version) => valid && this.version == version.VolatileRead(); + internal bool IsValid(in uint version) => valid && this.version == Volatile.Read(in version); private bool Equals(in LockStamp other) => version == other.version && valid == other.valid; diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs index e4c4dd992..a699860d2 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Net; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; using Debug = System.Diagnostics.Debug; @@ -35,7 +36,7 @@ private async Task ProcessDisconnectAsync(HttpRequest request, HttpResponse resp } private static (EndPoint, bool) DeserializeDisconnectRequest(ref SequenceReader reader) - => (reader.ReadEndPoint(), BasicExtensions.ToBoolean(reader.Read())); + => (reader.ReadEndPoint(), reader.Read()); private static (EndPoint, bool) DeserializeDisconnectRequest(ReadOnlyMemory buffer) { @@ -67,7 +68,7 @@ private MemoryOwner SerializeDisconnectRequest(bool isAlive) try { writer.WriteEndPoint(localNode); - writer.Add(isAlive.ToByte()); + writer.Add(Unsafe.BitCast(isAlive)); if (!writer.TryDetachBuffer(out result)) result = writer.WrittenSpan.Copy(allocator); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs index 1d1400cf0..6cde1dd38 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.Net; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; using Debug = System.Diagnostics.Debug; @@ -37,7 +38,7 @@ private async Task ProcessNeighborAsync(HttpRequest request, HttpResponse respon } private static (EndPoint, bool) DeserializeNeighborRequest(ref SequenceReader reader) - => (reader.ReadEndPoint(), BasicExtensions.ToBoolean(reader.Read())); + => (reader.ReadEndPoint(), reader.Read()); private static (EndPoint, bool) DeserializeNeighborRequest(ReadOnlyMemory buffer) { @@ -69,7 +70,7 @@ private MemoryOwner SerializeNeighborRequest(bool highPriority) try { writer.WriteEndPoint(localNode); - writer.Add(highPriority.ToByte()); + writer.Add(Unsafe.BitCast(highPriority)); if (!writer.TryDetachBuffer(out result)) result = writer.WrittenSpan.Copy(allocator); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs index 804faadfb..54712d17e 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/ClusterMemberId.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Net; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.AspNetCore.Connections; using static System.Buffers.Binary.BinaryPrimitives; @@ -50,7 +51,7 @@ private ClusterMemberId(HttpEndPoint endPoint) { var writer = new SpanWriter(stackalloc byte[16]); writer.WriteLittleEndian(FNV1a64.Hash(endPoint.Host)); - writer.Add(endPoint.IsSecure.ToByte()); + writer.Add(Unsafe.BitCast(endPoint.IsSecure)); address = new(writer.Span); lengthAndPort = Combine(endPoint.Host.Length, endPoint.Port); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs index dd2276e18..5c8da99cf 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs @@ -88,7 +88,7 @@ internal CommitChecker(ConsensusOnlyState state, long index) } bool ISupplier.Invoke() - => index <= state.commitIndex.VolatileRead(); + => index <= Volatile.Read(in state.commitIndex); } private readonly AsyncReaderWriterLock syncRoot = new(); @@ -99,16 +99,20 @@ bool ISupplier.Invoke() private volatile BoxedClusterMemberId? lastVote; private volatile long[] log = []; // log of uncommitted entries - /// - long IPersistentState.Term => term.VolatileRead(); + /// + public long Term + { + get => Volatile.Read(in term); + private set => Volatile.Write(ref term, value); + } /// bool IAuditTrail.IsLogEntryLengthAlwaysPresented => true; private void Append(long[] terms, long startIndex) { - log = Concat(log, terms, startIndex - Volatile.Read(in commitIndex) - 1L); - Volatile.Write(ref index, startIndex + terms.LongLength - 1L); + log = Concat(log, terms, startIndex - LastCommittedEntryIndex - 1L); + LastEntryIndex = startIndex + terms.LongLength - 1L; static long[] Concat(long[] left, long[] right, long startIndex) { @@ -130,10 +134,10 @@ private async ValueTask AppendAsync(ILogEntryProducer.AppendAsync(TEntry entry, lon { using (await syncRoot.AcquireWriteLockAsync(token).ConfigureAwait(false)) { - if (startIndex <= commitIndex.VolatileRead()) + if (startIndex <= LastCommittedEntryIndex) { throw new InvalidOperationException(ExceptionMessages.InvalidAppendIndex); } else if (entry.IsSnapshot) { - lastTerm.VolatileWrite(entry.Term); - commitIndex.VolatileWrite(startIndex); - index.VolatileWrite(startIndex); - log = Array.Empty(); + Volatile.Write(ref lastTerm, entry.Term); + LastCommittedEntryIndex = LastEntryIndex = startIndex; + log = []; commitEvent.Signal(resumeAll: true); } - else if (startIndex > index.VolatileRead() + 1L) + else if (startIndex > LastEntryIndex + 1L) { throw new ArgumentOutOfRangeException(nameof(startIndex)); } @@ -224,12 +227,12 @@ private async ValueTask CommitAsync(long? endIndex, CancellationToken toke long count; using (await syncRoot.AcquireWriteLockAsync(token).ConfigureAwait(false)) { - var startIndex = commitIndex.VolatileRead() + 1L; - count = (endIndex ?? index.VolatileRead()) - startIndex + 1L; + var startIndex = LastCommittedEntryIndex + 1L; + count = (endIndex ?? LastEntryIndex) - startIndex + 1L; if (count > 0) { - commitIndex.VolatileWrite(startIndex + count - 1); - lastTerm.VolatileWrite(log[count - 1]); + LastCommittedEntryIndex = startIndex + count - 1; + Volatile.Write(ref lastTerm, log[count - 1]); // count indicates how many elements should be removed from log log = log[(int)count..]; @@ -254,10 +257,10 @@ async ValueTask IAuditTrail.DropAsync(long startIndex, CancellationToken t long count; using (await syncRoot.AcquireWriteLockAsync(token).ConfigureAwait(false)) { - if (startIndex <= Volatile.Read(in commitIndex)) + if (startIndex <= LastCommittedEntryIndex) throw new InvalidOperationException(ExceptionMessages.InvalidAppendIndex); - count = Volatile.Read(in index) - startIndex + 1L; - Volatile.Write(ref index, startIndex - 1L); + count = LastEntryIndex - startIndex + 1L; + LastEntryIndex = startIndex - 1L; log = log[0..^(int)count]; } @@ -267,18 +270,26 @@ async ValueTask IAuditTrail.DropAsync(long startIndex, CancellationToken t /// /// Gets the index of the last committed log entry. /// - public long LastCommittedEntryIndex => commitIndex.VolatileRead(); + public long LastCommittedEntryIndex + { + get => Volatile.Read(in commitIndex); + private set => Volatile.Write(ref commitIndex, value); + } /// /// Gets the index of the last uncommitted log entry. /// - public long LastEntryIndex => index.VolatileRead(); + public long LastEntryIndex + { + get => Volatile.Read(in index); + private set => Volatile.Write(ref index, value); + } /// ValueTask IPersistentState.IncrementTermAsync(ClusterMemberId member) { lastVote = BoxedClusterMemberId.Box(member); - return new(term.IncrementAndGet()); + return new(Interlocked.Increment(ref term)); } /// @@ -290,12 +301,12 @@ Task IAuditTrail.InitializeAsync(CancellationToken token) private ValueTask ReadCoreAsync(ILogEntryConsumer reader, long startIndex, long endIndex, CancellationToken token) { - if (endIndex > index.VolatileRead()) + if (endIndex > LastEntryIndex) return ValueTask.FromException(new ArgumentOutOfRangeException(nameof(endIndex))); - var commitIndex = this.commitIndex.VolatileRead(); + var commitIndex = LastCommittedEntryIndex; var offset = startIndex - commitIndex - 1L; - return reader.ReadAsync(new EntryList(log, endIndex - startIndex + 1, offset, lastTerm.VolatileRead()), offset >= 0 ? null : new long?(commitIndex), token); + return reader.ReadAsync(new EntryList(log, endIndex - startIndex + 1, offset, Volatile.Read(in lastTerm)), offset >= 0 ? null : new long?(commitIndex), token); } /// @@ -332,13 +343,13 @@ public async ValueTask ReadAsync(ILogEntryConsumer ValueTask IPersistentState.UpdateTermAsync(long value, bool resetLastVote) { - term.VolatileWrite(value); + Term = value; if (resetLastVote) lastVote = null; @@ -364,7 +375,7 @@ ValueTask IAuditTrail.WaitForCommitAsync(long index, CancellationToken token) [Obsolete("Use IRaftCluster.ApplyReadBarrierAsync instead.")] async ValueTask IPersistentState.EnsureConsistencyAsync(CancellationToken token) { - while (term.VolatileRead() != lastTerm.VolatileRead()) + while (Term != Volatile.Read(in lastTerm)) await commitEvent.WaitAsync(token).ConfigureAwait(false); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.Snapshot.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.Snapshot.cs index ed309454c..a07019a7b 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.Snapshot.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.Snapshot.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using static System.Globalization.CultureInfo; using Debug = System.Diagnostics.Debug; @@ -6,7 +5,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; using Buffers; -using static Threading.AtomicInt64; using IAsyncBinaryReader = IO.IAsyncBinaryReader; public partial class DiskBasedStateMachine @@ -48,7 +46,7 @@ private protected sealed override async ValueTask InstallSnapshotAsync /// Represents disk-based state machine. @@ -54,7 +53,7 @@ protected DiskBasedStateMachine(string path, int recordsPerPartition, Options? c private ValueTask ApplyCoreAsync(LogEntry entry) => entry.IsEmpty ? new(default(long?)) : ApplyAsync(entry); - private protected sealed override long LastTerm => lastTerm.VolatileRead(); + private protected sealed override long LastTerm => Volatile.Read(in lastTerm); [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] private async ValueTask ApplyAsync(int sessionId, long startIndex, CancellationToken token) @@ -68,7 +67,7 @@ protected DiskBasedStateMachine(string path, int recordsPerPartition, Options? c { var entry = partition.Read(sessionId, startIndex, out var persisted); var snapshotLength = await ApplyCoreAsync(entry).ConfigureAwait(false); - lastTerm.VolatileWrite(entry.Term); + Volatile.Write(ref lastTerm, entry.Term); // Remove log entry from the cache according to eviction policy if (!persisted) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs index 290fe660e..bdca1e946 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs @@ -8,7 +8,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; using IO.Log; using Runtime.CompilerServices; -using static Threading.AtomicInt64; /// /// Represents memory-based state machine with snapshotting support. @@ -85,7 +84,7 @@ protected MemoryBasedStateMachine(string path, int recordsPerPartition, Options? { } - private protected sealed override long LastTerm => lastTerm.VolatileRead(); + private protected sealed override long LastTerm => Volatile.Read(in lastTerm); /// /// Gets a value indicating that log compaction should @@ -243,7 +242,7 @@ private protected sealed override async ValueTask InstallSnapshotAsync RandomAccess.GetLength(Handle); - internal void Invalidate() => version.IncrementAndGet(); + internal void Invalidate() => Interlocked.Increment(ref version); internal ValueTask SetWritePositionAsync(long position, CancellationToken token = default) { @@ -325,13 +324,14 @@ private protected FileReader GetSessionReader(int sessionId) ref var result = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(readers), sessionId); + var version = Volatile.Read(in this.version); if (result is null) { - result = new(Handle, fileOffset, writer.MaxBufferSize, allocator, version.VolatileRead()); + result = new(Handle, fileOffset, writer.MaxBufferSize, allocator, version); } else { - result.VerifyVersion(version.VolatileRead()); + result.VerifyVersion(version); } return result; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs index 7e6c454d2..3d310220a 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs @@ -88,7 +88,7 @@ private NodeState(string fileName, MemoryAllocator allocator, bool integri lastIndex = ReadInt64LittleEndian(bufferSpan.Slice(LastIndexOffset)); lastApplied = ReadInt64LittleEndian(bufferSpan.Slice(LastAppliedOffset)); snapshot = new(bufferSpan.Slice(SnapshotMetadataOffset)); - if (BasicExtensions.ToBoolean(bufferSpan[LastVotePresenceOffset])) + if (Unsafe.BitCast(bufferSpan[LastVotePresenceOffset])) votedFor = BoxedClusterMemberId.Box(new ClusterMemberId(bufferSpan.Slice(LastVoteOffset))); this.integrityCheck = integrityCheck; } @@ -170,37 +170,37 @@ internal ValueTask ClearAsync(CancellationToken token = default) internal long CommitIndex { - get => commitIndex.VolatileRead(); + get => Volatile.Read(in commitIndex); set { WriteInt64LittleEndian(buffer.Span.Slice(CommitIndexOffset), value); - commitIndex.VolatileWrite(value); + Volatile.Write(ref commitIndex, value); } } internal long LastApplied { - get => lastApplied.VolatileRead(); + get => Volatile.Read(in lastApplied); set { WriteInt64LittleEndian(buffer.Span.Slice(LastAppliedOffset), value); - lastApplied.VolatileWrite(value); + Volatile.Write(ref lastApplied, value); } } internal long LastIndex { - get => lastIndex.VolatileRead(); + get => Volatile.Read(in lastIndex); set { WriteInt64LittleEndian(buffer.Span.Slice(LastIndexOffset), value); - lastIndex.VolatileWrite(value); + Volatile.Write(ref lastIndex, value); } } internal long TailIndex => LastIndex + 1L; - internal long Term => term.VolatileRead(); + internal long Term => Volatile.Read(in term); internal void UpdateTerm(long value, bool resetLastVote) { @@ -211,12 +211,12 @@ internal void UpdateTerm(long value, bool resetLastVote) buffer[LastVotePresenceOffset] = False; } - term.VolatileWrite(value); + Volatile.Write(ref term, value); } internal long IncrementTerm(ClusterMemberId id) { - var result = term.IncrementAndGet(); + var result = Interlocked.Increment(ref term); WriteInt64LittleEndian(buffer.Span.Slice(TermOffset), result); UpdateVotedFor(id); return result; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs index 0e9e7ccdf..8af635741 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.SessionManagement.cs @@ -4,7 +4,7 @@ namespace DotNext.Net.Cluster.Consensus.Raft; -using AtomicBoolean = Threading.AtomicBoolean; +using AtomicBoolean = Threading.Atomic.Boolean; public partial class PersistentState { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Membership.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Membership.cs index 732eb2955..d3caf821b 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Membership.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Membership.cs @@ -87,7 +87,7 @@ private ConcurrentMembershipModificationException(SerializationInfo info, Stream private IMemberList members; private InvocationList, RaftClusterMemberEventArgs>> memberAddedHandlers, memberRemovedHandlers; - private AtomicBoolean membershipState; + private Atomic.Boolean membershipState; /// /// Gets the member by its identifier. diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs index 5aacf6e0b..3cf827936 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Debug = System.Diagnostics.Debug; @@ -67,7 +68,7 @@ protected int WriteHeaders(ProtocolStream protocol, in ClusterMemberId sender, i var writer = protocol.BeginRequestMessage(MessageType.AppendEntries); AppendEntriesMessage.Write(ref writer, in sender, term, prevLogIndex, prevLogTerm, commitIndex, entriesCount); - writer.Add(applyConfig.ToByte()); + writer.Add(Unsafe.BitCast(applyConfig)); writer.WriteLittleEndian(config.Fingerprint); writer.WriteLittleEndian(config.Length); return writer.WrittenCount; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs index 35012fbda..ea8526435 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs @@ -57,7 +57,7 @@ internal static async ValueTask> ReadPreVoteResultAsync(th internal static ValueTask WriteBoolAsync(this ProtocolStream protocol, bool value, CancellationToken token) { protocol.Reset(); - protocol.RemainingBufferSpan[0] = value.ToByte(); + protocol.RemainingBufferSpan[0] = Unsafe.BitCast(value); protocol.AdvanceWriteCursor(1); return protocol.WriteToTransportAsync(token); } @@ -65,14 +65,14 @@ internal static ValueTask WriteBoolAsync(this ProtocolStream protocol, bool valu internal static async ValueTask ReadBoolAsync(this ProtocolStream protocol, CancellationToken token) { await protocol.ReadAsync(sizeof(byte), token).ConfigureAwait(false); - return BasicExtensions.ToBoolean(protocol.WrittenBufferSpan[0]); + return Unsafe.BitCast(protocol.WrittenBufferSpan[0]); } internal static ValueTask WriteNullableInt64Async(this ProtocolStream protocol, in long? value, CancellationToken token) { protocol.Reset(); var writer = new SpanWriter(protocol.RemainingBufferSpan); - writer.Add(value.HasValue.ToByte()); + writer.Add(Unsafe.BitCast(value.HasValue)); writer.WriteLittleEndian(value.GetValueOrDefault()); protocol.AdvanceWriteCursor(writer.WrittenCount); return protocol.WriteToTransportAsync(token); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs index c9e295402..981e561ea 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs @@ -30,7 +30,7 @@ internal ReceivedLogEntries(ProtocolStream stream, MemoryAllocator allocat var reader = new SpanReader(stream.WrittenBufferSpan); (Id, Term, PrevLogIndex, PrevLogTerm, CommitIndex, entriesCount) = AppendEntriesMessage.Read(ref reader); - ApplyConfig = BasicExtensions.ToBoolean(reader.Read()); + ApplyConfig = Unsafe.BitCast(reader.Read()); var fingerprint = reader.ReadLittleEndian(isUnsigned: false); var configLength = reader.ReadLittleEndian(isUnsigned: false); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs index 40ead4d31..1a5e9d3ba 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ResignExchange.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using Debug = System.Diagnostics.Debug; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.Datagram; @@ -14,7 +15,7 @@ internal ResignExchange() public override ValueTask ProcessInboundMessageAsync(PacketHeaders headers, ReadOnlyMemory payload, CancellationToken token) { Debug.Assert(headers.Control == FlowControl.Ack); - TrySetResult(BasicExtensions.ToBoolean(payload.Span[0])); + TrySetResult(Unsafe.BitCast(payload.Span[0])); return new(false); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Resign.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Resign.cs index d1576ab23..f251cff71 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Resign.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Resign.cs @@ -1,3 +1,5 @@ +using System.Runtime.CompilerServices; + namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.Datagram; using static Runtime.Intrinsics; @@ -11,7 +13,7 @@ private void BeginResign(CancellationToken token) { var result = await Cast>(Interlocked.Exchange(ref task, null)).ConfigureAwait(false); task = null; - payload.Span[0] = result.ToByte(); + payload.Span[0] = Unsafe.BitCast(result); return (new PacketHeaders(MessageType.Resign, FlowControl.Ack), 1, false); } } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs index e508296fc..ade0657bf 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/SynchronizeExchange.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using BinaryPrimitives = System.Buffers.Binary.BinaryPrimitives; using Debug = System.Diagnostics.Debug; @@ -18,7 +19,7 @@ public override ValueTask ProcessInboundMessageAsync(PacketHeaders headers Debug.Assert(headers.Control == FlowControl.Ack); var reader = new SpanReader(payload.Span); - var hasValue = BasicExtensions.ToBoolean(reader.Read()); + var hasValue = Unsafe.BitCast(reader.Read()); var commitIndex = reader.ReadLittleEndian(isUnsigned: false); TrySetResult(hasValue ? commitIndex : null); return new(false); @@ -27,7 +28,7 @@ public override ValueTask ProcessInboundMessageAsync(PacketHeaders headers internal static int WriteResponse(Span output, long? commitIndex) { var writer = new SpanWriter(output); - writer.Add(commitIndex.HasValue.ToByte()); + writer.Add(Unsafe.BitCast(commitIndex.HasValue)); writer.WriteLittleEndian(commitIndex.GetValueOrDefault()); return writer.WrittenCount; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs index 899be8b9f..267bf61a3 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Result.cs @@ -1,3 +1,5 @@ +using System.Runtime.CompilerServices; + namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices; using Buffers; @@ -9,7 +11,7 @@ internal static class Result internal static void Write(ref SpanWriter writer, in Result result) { writer.WriteLittleEndian(result.Term); - writer.Add(result.Value.ToByte()); + writer.Add(Unsafe.BitCast(result.Value)); } internal static int Write(Span output, in Result result) @@ -48,7 +50,7 @@ internal static int WriteHeartbeatResult(Span output, in Result Read(ref SpanReader reader) => new() { Term = reader.ReadLittleEndian(isUnsigned: false), - Value = BasicExtensions.ToBoolean(reader.Read()), + Value = Unsafe.BitCast(reader.Read()), }; internal static Result Read(ReadOnlySpan input) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Udp/UdpClient.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Udp/UdpClient.cs index 7d528ca00..655489bd6 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Udp/UdpClient.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Udp/UdpClient.cs @@ -7,7 +7,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.Udp; using Buffers; using TransportServices.Datagram; -using static Threading.AtomicInt64; internal sealed class UdpClient : UdpSocket, IClient { @@ -106,7 +105,7 @@ public async void Enqueue(IExchange exchange, CancellationToken token) if (!IsBound && !Start(exchange)) return; - var id = new CorrelationId(applicationId, streamNumber.IncrementAndGet()); + var id = new CorrelationId(applicationId, Interlocked.Increment(ref streamNumber)); var channel = new Channel(exchange, cancellationHandler, id, token, LifecycleToken); if (channels.TryAdd(id, channel)) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs index b7880f771..14f3d1bdd 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using System.Net; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Debug = System.Diagnostics.Debug; @@ -72,7 +73,7 @@ internal void ForwardJoin(out EndPoint sender, out EndPoint joinedPeer, out int } internal static Command Neighbor(EndPoint sender, bool highPriority) - => new() { Type = CommandType.Neighbor, Address1 = sender, Int32Param = highPriority.ToInt32() }; + => new() { Type = CommandType.Neighbor, Address1 = sender, Int32Param = Unsafe.BitCast(highPriority) }; internal void Neighbor(out EndPoint sender, out bool highPriority) { @@ -80,11 +81,11 @@ internal void Neighbor(out EndPoint sender, out bool highPriority) Debug.Assert(Address1 is not null); sender = Address1; - highPriority = Int32Param.ToBoolean(); + highPriority = Unsafe.BitCast((byte)Int32Param); } internal static Command Disconnect(EndPoint sender, bool isAlive) - => new() { Type = CommandType.Disconnect, Address1 = sender, Int32Param = isAlive.ToInt32() }; + => new() { Type = CommandType.Disconnect, Address1 = sender, Int32Param = Unsafe.BitCast(isAlive) }; internal void Disconnect(out EndPoint sender, out bool isAlive) { @@ -92,7 +93,7 @@ internal void Disconnect(out EndPoint sender, out bool isAlive) Debug.Assert(Address1 is not null); sender = Address1; - isAlive = Int32Param.ToBoolean(); + isAlive = Unsafe.BitCast((byte)Int32Param); } internal static Command Shuffle(EndPoint sender, EndPoint origin, IReadOnlyCollection peers, int ttl) diff --git a/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs b/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs index b16a8379c..1272f33a1 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/EndPointFormatter.cs @@ -1,6 +1,7 @@ using System.ComponentModel; using System.Net; using System.Net.Sockets; +using System.Runtime.CompilerServices; using System.Text; namespace DotNext.Net; @@ -91,7 +92,7 @@ public static void WriteEndPoint(this ref BufferWriterSlim writer, EndPoin // host name length, N = 4 bytes // host name = N bytes writer.Add(HttpEndPointPrefix); - writer.Add(http.IsSecure.ToByte()); + writer.Add(Unsafe.BitCast(http.IsSecure)); writer.WriteLittleEndian(http.Port); writer.WriteLittleEndian((int)http.AddressFamily); Serialize(http.Host, ref writer); @@ -213,7 +214,7 @@ private static DnsEndPoint DeserializeHost(ref SequenceReader reader) private static HttpEndPoint DeserializeHttp(ref SequenceReader reader) { - var secure = BasicExtensions.ToBoolean(reader.Read()); + var secure = reader.Read(); DeserializeHost(ref reader, out var hostName, out var port, out var family); return new(hostName, port, secure, family); } diff --git a/src/examples/RaftNode/SimplePersistentState.cs b/src/examples/RaftNode/SimplePersistentState.cs index c8842fc36..70f2d6b8b 100644 --- a/src/examples/RaftNode/SimplePersistentState.cs +++ b/src/examples/RaftNode/SimplePersistentState.cs @@ -1,7 +1,6 @@ using DotNext; using DotNext.IO; using DotNext.Net.Cluster.Consensus.Raft; -using static DotNext.Threading.AtomicInt64; namespace RaftNode; @@ -33,16 +32,16 @@ public SimplePersistentState(string path) } public SimplePersistentState(IConfiguration configuration) - : this(configuration[LogLocation]) + : this(configuration[LogLocation] ?? string.Empty) { } - long ISupplier.Invoke() => content.VolatileRead(); + long ISupplier.Invoke() => Volatile.Read(in content); private async ValueTask UpdateValue(LogEntry entry) { var value = await entry.ToTypeAsync().ConfigureAwait(false); - content.VolatileWrite(value); + Volatile.Write(ref content, value); Console.WriteLine($"Accepting value {value}"); } From 16fbbf13f2ff61fa3baa0bcb03632ee0118b2a40 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 17:32:21 +0200 Subject: [PATCH 056/155] Removed AsyncDelegate in favor of TaskToAsyncResult from .NET --- src/DotNext.IO/IO/FileBufferingWriter.cs | 40 +- .../Threading/AsyncDelegateTests.cs | 85 --- src/DotNext/IO/ReadOnlyStream.cs | 12 +- src/DotNext/IO/WriterStream.cs | 40 +- src/DotNext/Threading/Adder.cs | 21 - src/DotNext/Threading/AsyncDelegate.cs | 500 ------------------ 6 files changed, 6 insertions(+), 692 deletions(-) delete mode 100644 src/DotNext.Tests/Threading/AsyncDelegateTests.cs delete mode 100644 src/DotNext/Threading/Adder.cs delete mode 100644 src/DotNext/Threading/AsyncDelegate.cs diff --git a/src/DotNext.IO/IO/FileBufferingWriter.cs b/src/DotNext.IO/IO/FileBufferingWriter.cs index e6131842a..fc09272d7 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.cs @@ -9,7 +9,6 @@ namespace DotNext.IO; using Buffers; -using static Threading.AsyncDelegate; /// /// Represents builder of contiguous block of memory that may @@ -479,45 +478,10 @@ public override void WriteByte(byte value) /// public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - Task task; - - if (fileProvider.IsAsynchronous) - { - task = WriteAsync(buffer, offset, count, CancellationToken.None); - - // attach state only if it's necessary - if (state is not null) - task = task.ContinueWith(static (task, state) => task.ConfigureAwait(false).GetAwaiter().GetResult(), state, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Default); - - if (callback is not null) - { - if (task.IsCompleted) - callback(task); - else - task.ConfigureAwait(false).GetAwaiter().OnCompleted(() => callback(task)); - } - } - else - { - // start synchronous write as separated task - task = new Action(_ => Write(buffer, offset, count)).BeginInvoke(state, callback); - } - - return task; - } - - private static void EndWrite(Task task) - { - using (task) - { - task.Wait(); - } - } + => TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count), callback, state); /// - public override void EndWrite(IAsyncResult ar) - => EndWrite((Task)ar); + public override void EndWrite(IAsyncResult ar) => TaskToAsyncResult.End(ar); /// public override void Flush() => Flush(flushToDisk: false); diff --git a/src/DotNext.Tests/Threading/AsyncDelegateTests.cs b/src/DotNext.Tests/Threading/AsyncDelegateTests.cs deleted file mode 100644 index 3e293af99..000000000 --- a/src/DotNext.Tests/Threading/AsyncDelegateTests.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Linq.Expressions; -using System.Reflection; - -namespace DotNext.Threading; - -public sealed class AsyncDelegateTests : Test -{ - private sealed class Accumulator - { - private int counter; - - internal int Counter => counter; - - internal void IncBy1() => Interlocked.Increment(ref counter); - - internal void IncBy3() => Interlocked.Add(ref counter, 3); - - internal void IncBy5() => Interlocked.Add(ref counter, 5); - - internal void Throw() => throw new Exception(); - } - - [Fact] - public static async Task InvokeActionAsync() - { - var acc = new Accumulator(); - Action action = acc.IncBy1; - action += acc.IncBy3; - action += acc.IncBy5; - await action.InvokeAsync(); - Equal(9, acc.Counter); - } - - [Fact] - public static async Task InvokeActionAsyncFailure() - { - var acc = new Accumulator(); - Action action = acc.IncBy1; - action += acc.Throw; - action += acc.IncBy3; - await ThrowsAsync(async () => await action.InvokeAsync()); - } - - [Fact] - public static async Task InvokeActionsAsync() - { - static MethodInfo GetMethod(int argCount) - { - const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly; - return Single(typeof(AsyncDelegate).GetMethods(flags), candidate => candidate.Name == nameof(AsyncDelegate.InvokeAsync) && candidate.GetParameters().Length == argCount + 2 && candidate.GetParameters()[0].ParameterType.Name.Contains("Action")); - } - - var successValue = Expression.Empty(); - var failedValue = Expression.Throw(Expression.New(typeof(ArithmeticException))); - for (var argCount = 0; argCount <= 10; argCount++) - { - var types = new Type[argCount]; - Array.Fill(types, typeof(string)); - var actionType = Expression.GetActionType(types); - var parameters = new ParameterExpression[argCount]; - parameters.AsSpan().ForEach(static (ref ParameterExpression p, int _) => p = Expression.Parameter(typeof(string))); - //prepare args - var args = new object[parameters.LongLength + 2]; - Array.Fill(args, string.Empty); - args[args.LongLength - 1L] = new CancellationToken(false); - //find method to test - var method = GetMethod(argCount); - if (parameters.LongLength > 0L) - method = method.MakeGenericMethod(types); - //check success scenario - args[0] = Expression.Lambda(actionType, successValue, parameters).Compile(); - var result = (Task)method.Invoke(null, args); - await result; - //check cancellation - args[args.LongLength - 1L] = new CancellationToken(true); - result = (Task)method.Invoke(null, args); - await ThrowsAsync(Func.Constant(result)); - //check failure - args[args.LongLength - 1L] = new CancellationToken(false); - args[0] = Expression.Lambda(actionType, failedValue, parameters).Compile(); - result = (Task)method.Invoke(null, args); - await ThrowsAsync(Func.Constant(result)); - } - } -} \ No newline at end of file diff --git a/src/DotNext/IO/ReadOnlyStream.cs b/src/DotNext/IO/ReadOnlyStream.cs index d9180229b..b5b2a31db 100644 --- a/src/DotNext/IO/ReadOnlyStream.cs +++ b/src/DotNext/IO/ReadOnlyStream.cs @@ -2,8 +2,6 @@ namespace DotNext.IO; -using static Threading.AsyncDelegate; - internal abstract class ReadOnlyStream : Stream, IFlushable { public sealed override bool CanRead => true; @@ -60,15 +58,9 @@ public sealed override Task ReadAsync(byte[] buffer, int offset, int count, => ReadAsync(buffer.AsMemory(offset, count), token).AsTask(); public sealed override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - => new Func(_ => Read(buffer, offset, count)).BeginInvoke(state, callback); - - private static int EndRead(Task task) - { - using (task) - return task.Result; - } + => TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count), callback, state); - public sealed override int EndRead(IAsyncResult ar) => EndRead((Task)ar); + public sealed override int EndRead(IAsyncResult ar) => TaskToAsyncResult.End(ar); public sealed override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); diff --git a/src/DotNext/IO/WriterStream.cs b/src/DotNext/IO/WriterStream.cs index 1cae98daa..c2fe255bf 100644 --- a/src/DotNext/IO/WriterStream.cs +++ b/src/DotNext/IO/WriterStream.cs @@ -2,9 +2,6 @@ namespace DotNext.IO; -using static Threading.AsyncDelegate; -using static Threading.Tasks.Continuation; - internal abstract class WriterStream : Stream, IFlushable where TOutput : notnull, IFlushable { @@ -50,43 +47,10 @@ public sealed override void Write(byte[] buffer, int offset, int count) public sealed override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token) => WriteAsync(buffer.AsMemory(offset, count), token).AsTask(); - private async Task WriteWithTimeoutAsync(ReadOnlyMemory buffer) - { - using var source = new CancellationTokenSource(WriteTimeout); - await WriteAsync(buffer, source.Token).ConfigureAwait(false); - } - public sealed override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) - { - Task task; - if (CanTimeout) - { - task = WriteWithTimeoutAsync(buffer.AsMemory(offset, count)); - - // attach state only if it's necessary - if (state is not null) - task = task.AttachState(state); - - if (callback is not null) - task.OnCompleted(callback); - } - else - { - task = new Action(_ => Write(buffer, offset, count)).BeginInvoke(state, callback); - } - - return task; - } - - private static void EndWrite(Task task) - { - using (task) - { - task.Wait(); - } - } + => TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count), callback, state); - public override void EndWrite(IAsyncResult ar) => EndWrite((Task)ar); + public override void EndWrite(IAsyncResult ar) => TaskToAsyncResult.End(ar); public sealed override void CopyTo(Stream destination, int bufferSize) => throw new NotSupportedException(); diff --git a/src/DotNext/Threading/Adder.cs b/src/DotNext/Threading/Adder.cs deleted file mode 100644 index 8015293e2..000000000 --- a/src/DotNext/Threading/Adder.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Threading; - -[StructLayout(LayoutKind.Auto)] -internal readonly struct Adder : ISupplier, ISupplier, ISupplier, ISupplier, ISupplier, ISupplier, ISupplier -{ - double ISupplier.Invoke(double x, double y) => x + y; - - float ISupplier.Invoke(float x, float y) => x + y; - - nint ISupplier.Invoke(nint x, nint y) => x + y; - - int ISupplier.Invoke(int x, int y) => x + y; - - long ISupplier.Invoke(long x, long y) => x + y; - - uint ISupplier.Invoke(uint x, uint y) => x + y; - - ulong ISupplier.Invoke(ulong x, ulong y) => x + y; -} \ No newline at end of file diff --git a/src/DotNext/Threading/AsyncDelegate.cs b/src/DotNext/Threading/AsyncDelegate.cs deleted file mode 100644 index bf7500338..000000000 --- a/src/DotNext/Threading/AsyncDelegate.cs +++ /dev/null @@ -1,500 +0,0 @@ -using Debug = System.Diagnostics.Debug; -using Missing = System.Reflection.Missing; - -namespace DotNext.Threading; - -using static Tasks.Continuation; -using ExceptionAggregator = Runtime.ExceptionServices.ExceptionAggregator; - -/// -/// Provides set of methods for asynchronous invocation of various delegates. -/// -/// -/// BeginInvoke and EndInvoke methods of delegate type are supported only in .NET Framework. -/// This class provides alternative approach which allows to invoke delegate asynchronously -/// with full support of async/await feature. -/// -/// BeginInvoke throws NotSupportedException -public static partial class AsyncDelegate -{ - private static unsafe Task InvokeAsync(TDelegate @delegate, TContext context, delegate* invoker, CancellationToken token) - where TDelegate : MulticastDelegate - { - Debug.Assert(@delegate is not null); - Debug.Assert(invoker is not null); - - return Task.Factory.StartNew( - InvokeCore, - token, - TaskCreationOptions.DenyChildAttach, - TaskScheduler.Current); - - void InvokeCore() - { - var errors = new ExceptionAggregator(); - foreach (TDelegate target in @delegate.GetInvocationList()) - { - if (token.IsCancellationRequested) - { - errors.Add(new OperationCanceledException(token)); - break; - } - - try - { - invoker(target, in context); - } - catch (Exception e) - { - errors.Add(e); - } - } - - // aggregate all exceptions - errors.ThrowIfNeeded(); - } - } - - /// - /// Invokes a delegate of arbitrary type asynchronously. - /// - /// A delegate to be invoked asynchronously. - /// Synchronous invoker of the delegate from invocation list. - /// Cancellation token. - /// Type of delegate to invoke. - /// A task allows to control asynchronous invocation of methods attached to the multicast delegate. - /// is . - public static unsafe Task InvokeAsync(this TDelegate @delegate, Action invoker, CancellationToken token = default) - where TDelegate : MulticastDelegate - { - ArgumentNullException.ThrowIfNull(@delegate); - - return InvokeAsync(@delegate, invoker, &Invoke, token); - - static void Invoke(TDelegate target, in Action invoker) => invoker(target); - } - - /// - /// Invokes event handlers asynchronously. - /// - /// Type of event object. - /// A set event handlers combined as single delegate. - /// Event sender. - /// Event arguments. - /// Optional cancellation token. - /// An object representing state of the asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this EventHandler handler, object sender, TEventArgs args, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(handler); - - return InvokeAsync(handler, (sender, args), &Invoke, token); - - static void Invoke(EventHandler handler, in (object, TEventArgs) args) - => handler(args.Item1, args.Item2); - } - - /// - /// Invokes event handlers asynchronously. - /// - /// A set event handlers combined as single delegate. - /// Event sender. - /// Event arguments. - /// Optional cancellation token. - /// An object representing state of the asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this EventHandler handler, object sender, EventArgs args, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(handler); - - return InvokeAsync(handler, (sender, args), &Invoke, token); - - static void Invoke(EventHandler handler, in (object, EventArgs) args) - => handler(args.Item1, args.Item2); - } - - /// - /// Invokes action asynchronously. - /// - /// The action to invoke asynchronously. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, Missing.Value, &Invoke, token); - - static void Invoke(Action handler, in Missing args) - => handler(); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the action argument. - /// The action to invoke asynchronously. - /// The action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T arg, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, arg, &Invoke, token); - - static void Invoke(Action handler, in T arg) - => handler(arg); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2), &Invoke, token); - - static void Invoke(Action action, in (T1, T2) args) - => action(args.Item1, args.Item2); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3) args) - => action(args.Item1, args.Item2, args.Item3); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// Type of the fourth action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// The fourth action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3, arg4), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3, T4) args) - => action(args.Item1, args.Item2, args.Item3, args.Item4); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// Type of the fourth action argument. - /// Type of the fifth action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// The fourth action argument. - /// The fifth action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3, arg4, arg5), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3, T4, T5) args) - => action(args.Item1, args.Item2, args.Item3, args.Item4, args.Item5); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// Type of the fourth action argument. - /// Type of the fifth action argument. - /// Type of the sixth action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// The fourth action argument. - /// The fifth action argument. - /// The sixth action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3, arg4, arg5, arg6), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3, T4, T5, T6) args) - => action(args.Item1, args.Item2, args.Item3, args.Item4, args.Item5, args.Item6); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// Type of the fourth action argument. - /// Type of the fifth action argument. - /// Type of the sixth action argument. - /// Type of the seventh action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// The fourth action argument. - /// The fifth action argument. - /// The sixth action argument. - /// The seventh action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3, arg4, arg5, arg6, arg7), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3, T4, T5, T6, T7) args) - => action(args.Item1, args.Item2, args.Item3, args.Item4, args.Item5, args.Item6, args.Item7); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// Type of the fourth action argument. - /// Type of the fifth action argument. - /// Type of the sixth action argument. - /// Type of the seventh action argument. - /// Type of the eighth action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// The fourth action argument. - /// The fifth action argument. - /// The sixth action argument. - /// The seventh action argument. - /// The eighth action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3, T4, T5, T6, T7, T8) args) - => action(args.Item1, args.Item2, args.Item3, args.Item4, args.Item5, args.Item6, args.Item7, args.Item8); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// Type of the fourth action argument. - /// Type of the fifth action argument. - /// Type of the sixth action argument. - /// Type of the seventh action argument. - /// Type of the eighth action argument. - /// Type of the ninth action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// The fourth action argument. - /// The fifth action argument. - /// The sixth action argument. - /// The seventh action argument. - /// The eighth action argument. - /// THe ninth action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3, T4, T5, T6, T7, T8, T9) args) - => action(args.Item1, args.Item2, args.Item3, args.Item4, args.Item5, args.Item6, args.Item7, args.Item8, args.Item9); - } - - /// - /// Invokes action asynchronously. - /// - /// Type of the first action argument. - /// Type of the second action argument. - /// Type of the third action argument. - /// Type of the fourth action argument. - /// Type of the fifth action argument. - /// Type of the sixth action argument. - /// Type of the seventh action argument. - /// Type of the eighth action argument. - /// Type of the ninth action argument. - /// Type of the tenth action argument. - /// The action to invoke asynchronously. - /// The first action argument. - /// The second action argument. - /// The third action argument. - /// The fourth action argument. - /// The fifth action argument. - /// The sixth action argument. - /// The seventh action argument. - /// The eighth action argument. - /// The ninth action argument. - /// The tenth action argument. - /// Invocation cancellation token. - /// The task representing state of asynchronous invocation. - /// is . - public static unsafe Task InvokeAsync(this Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - return InvokeAsync(action, (arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10), &Invoke, token); - - static void Invoke(Action action, in (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) args) - => action(args.Item1, args.Item2, args.Item3, args.Item4, args.Item5, args.Item6, args.Item7, args.Item8, args.Item9, args.Item10); - } - - /// - /// Invokes synchronous delegate asynchronously. - /// - /// The action to invoke asynchronously. - /// The state object to be passed to the action. - /// The callback to be invoked on completion. - /// The task scheduling options. - /// The task scheduler. - /// The task representing asynchronous execution of the action. - /// is . - public static Task BeginInvoke(this Action action, object? state, AsyncCallback? callback, TaskCreationOptions options = TaskCreationOptions.None, TaskScheduler? scheduler = null) - { - ArgumentNullException.ThrowIfNull(action); - - var task = Task.Factory.StartNew(action, state, CancellationToken.None, options, scheduler ?? TaskScheduler.Default); - if (callback is not null) - task.OnCompleted(callback); - - return task; - } - - /// - /// Invokes synchronous delegate asynchronously. - /// - /// The action to invoke asynchronously. - /// The state object to be passed to the action. - /// The callback to be invoked on completion. - /// The task scheduling options. - /// The task scheduler. - /// The token that can be used to cancel the operation. - /// The task representing asynchronous execution of the action. - /// is . - public static Task BeginInvoke(this Action action, object? state, AsyncCallback? callback, TaskCreationOptions options = TaskCreationOptions.None, TaskScheduler? scheduler = null, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(action); - - var task = Task.Factory.StartNew(s => action(s, token), state, token, options, scheduler ?? TaskScheduler.Default); - if (callback is not null) - task.OnCompleted(callback); - - return task; - } - - /// - /// Invokes synchronous delegate asynchronously. - /// - /// The type of result of asynchronous operation. - /// The function to invoke asynchronously. - /// The state object to be passed to the action. - /// The callback to be invoked on completion. - /// The task scheduling options. - /// The task scheduler. - /// The task representing asynchronous execution of the action. - /// is . - public static Task BeginInvoke(this Func function, object? state, AsyncCallback? callback, TaskCreationOptions options = TaskCreationOptions.None, TaskScheduler? scheduler = null) - { - ArgumentNullException.ThrowIfNull(function); - - var task = Task.Factory.StartNew(function, state, CancellationToken.None, options, scheduler ?? TaskScheduler.Default); - if (callback is not null) - task.OnCompleted(callback); - - return task; - } - - /// - /// Invokes synchronous delegate asynchronously. - /// - /// The type of result of asynchronous operation. - /// The function to invoke asynchronously. - /// The state object to be passed to the action. - /// The callback to be invoked on completion. - /// The task scheduling options. - /// The task scheduler. - /// The token that can be used to cancel the operation. - /// The task representing asynchronous execution of the action. - /// is . - public static Task BeginInvoke(this Func function, object? state, AsyncCallback? callback, TaskCreationOptions options = TaskCreationOptions.None, TaskScheduler? scheduler = null, CancellationToken token = default) - { - ArgumentNullException.ThrowIfNull(function); - - var task = Task.Factory.StartNew(s => function(s, token), state, token, options, scheduler ?? TaskScheduler.Default); - if (callback is not null) - task.OnCompleted(callback); - - return task; - } -} \ No newline at end of file From deefbc70ace376f282b407f35c1a427e58d266c5 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 17:51:12 +0200 Subject: [PATCH 057/155] Removed generic constants --- .../Generic/GenericConstTests.cs | 49 ------ .../Threading/Tasks/CompletedTaskTests.cs | 17 -- .../Threading/Tasks/ContinuationTest.cs | 94 ----------- .../Tasks/ValueTaskCompletionSource.cs | 8 +- src/DotNext/Generic/BooleanConst.cs | 40 ----- src/DotNext/Generic/Constant.cs | 59 ------- src/DotNext/Generic/DefaultConst.cs | 16 -- src/DotNext/Generic/Int32Const.cs | 73 --------- src/DotNext/Generic/Int64Const.cs | 73 --------- src/DotNext/Generic/StringConst.cs | 54 ------- src/DotNext/ISupplier0.cs | 10 ++ src/DotNext/Threading/Tasks/CompletedTask.cs | 36 ----- src/DotNext/Threading/Tasks/Continuation.cs | 153 ------------------ 13 files changed, 12 insertions(+), 670 deletions(-) delete mode 100644 src/DotNext.Tests/Generic/GenericConstTests.cs delete mode 100644 src/DotNext.Tests/Threading/Tasks/CompletedTaskTests.cs delete mode 100644 src/DotNext.Tests/Threading/Tasks/ContinuationTest.cs delete mode 100644 src/DotNext/Generic/BooleanConst.cs delete mode 100644 src/DotNext/Generic/Constant.cs delete mode 100644 src/DotNext/Generic/DefaultConst.cs delete mode 100644 src/DotNext/Generic/Int32Const.cs delete mode 100644 src/DotNext/Generic/Int64Const.cs delete mode 100644 src/DotNext/Generic/StringConst.cs delete mode 100644 src/DotNext/Threading/Tasks/CompletedTask.cs delete mode 100644 src/DotNext/Threading/Tasks/Continuation.cs diff --git a/src/DotNext.Tests/Generic/GenericConstTests.cs b/src/DotNext.Tests/Generic/GenericConstTests.cs deleted file mode 100644 index 0e33b2a22..000000000 --- a/src/DotNext.Tests/Generic/GenericConstTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace DotNext.Generic; - -using Threading.Tasks; - -public sealed class GenericConstTests : Test -{ - [Fact] - public static void BooleanGenericConst() - { - False(CompletedTask.Task.Result); - True(CompletedTask.Task.Result); - var value = new BooleanConst.False(); - True(value.Equals(false)); - False(value.Equals(null)); - Equal(bool.FalseString, value.ToString()); - Equal(false.GetHashCode(), value.GetHashCode()); - } - - [Fact] - public static void StringGenericConst() - { - Empty(CompletedTask.Task.Result); - Null(CompletedTask.Task.Result); - } - - [Fact] - public static void DefaultGenericConst() - { - Equal(0L, CompletedTask>.Task.Result); - Null(CompletedTask>.Task.Result); - Null(CompletedTask>.Task.Result); - } - - [Fact] - public static void IntGenericConst() - { - Equal(0, CompletedTask.Task.Result); - Equal(int.MaxValue, CompletedTask.Task.Result); - Equal(int.MinValue, CompletedTask.Task.Result); - } - - [Fact] - public static void LongGenericConst() - { - Equal(0L, CompletedTask.Task.Result); - Equal(long.MaxValue, CompletedTask.Task.Result); - Equal(long.MinValue, CompletedTask.Task.Result); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/Tasks/CompletedTaskTests.cs b/src/DotNext.Tests/Threading/Tasks/CompletedTaskTests.cs deleted file mode 100644 index b33a57ac5..000000000 --- a/src/DotNext.Tests/Threading/Tasks/CompletedTaskTests.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace DotNext.Threading.Tasks; - -using Generic; - -public sealed class CompletedTaskTests : Test -{ - [Fact] - public static async Task CompletionTest() - { - var result = await CompletedTask.Task; - True(result); - result = await CompletedTask.Task; - True(result); - result = await CompletedTask.Task; - False(result); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/Tasks/ContinuationTest.cs b/src/DotNext.Tests/Threading/Tasks/ContinuationTest.cs deleted file mode 100644 index c81e2ffe7..000000000 --- a/src/DotNext.Tests/Threading/Tasks/ContinuationTest.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace DotNext.Threading.Tasks; - -using Generic; - -public sealed class ContinuationTest : Test -{ - [Fact] - public static async Task OnCompletedContinuation() - { - var t = Task.Delay(50); - var t2 = await t.OnCompleted(); - Equal(t, t2); - True(t.IsCompletedSuccessfully); - } - - [Fact] - public static async Task OnCompletedContinuation2() - { - var t = Task.Factory.StartNew(() => - { - Thread.Sleep(50); - return 42; - }); - var t2 = await t.OnCompleted(); - Equal(t, t2); - True(t.IsCompletedSuccessfully); - Equal(42, t2.Result); - } - - [Fact] - public static async Task OnFaulted() - { - var task = Task.FromException(new Exception()); - Equal(int.MinValue, await task.OnFaulted()); - task = Task.FromResult(10); - Equal(10, await task.OnFaulted()); - } - - [Fact] - public static async Task OnFaultedOrCanceled() - { - var task = Task.FromException(new Exception()); - Equal(int.MinValue, await task.OnFaultedOrCanceled()); - task = Task.FromResult(10); - Equal(10, await task.OnFaultedOrCanceled()); - task = Task.FromCanceled(new CancellationToken(true)); - Equal(int.MinValue, await task.OnFaultedOrCanceled()); - } - - [Fact] - public static async Task OnCanceled() - { - var task = Task.FromException(new ArithmeticException()); - await ThrowsAsync(() => task.OnCanceled()); - var source = new TaskCompletionSource(); - task = source.Task.OnCanceled(); - source.SetException(new ArithmeticException()); - await ThrowsAsync(() => task); - } - - [Fact] - public static async Task OnFaultedConditional() - { - var task = Task.FromException(new ArithmeticException()); - static bool Filter(AggregateException e) => e.InnerException is ArithmeticException; - task = task.OnFaulted(Filter); - Equal(int.MinValue, await task); - var source = new TaskCompletionSource(); - task = source.Task.OnFaulted(Filter); - source.SetException(new ArithmeticException()); - Equal(int.MinValue, await task); - source = new TaskCompletionSource(); - task = source.Task.OnFaulted(Filter); - source.SetException(new IndexOutOfRangeException()); - await ThrowsAsync(() => task); - } - - [Fact] - public static async Task OnFaultedOrCanceledConditional() - { - var task = Task.FromCanceled(new CancellationToken(true)); - static bool Filter(AggregateException e) => e.InnerException is ArithmeticException; - task = task.OnFaultedOrCanceled(Filter); - Equal(int.MinValue, await task); - var source = new TaskCompletionSource(); - task = source.Task.OnFaulted(Filter); - source.SetException(new ArithmeticException()); - Equal(int.MinValue, await task); - source = new TaskCompletionSource(); - task = source.Task.OnFaulted(Filter); - source.SetException(new IndexOutOfRangeException()); - await ThrowsAsync(() => task); - } -} \ No newline at end of file diff --git a/src/DotNext.Threading/Threading/Tasks/ValueTaskCompletionSource.cs b/src/DotNext.Threading/Threading/Tasks/ValueTaskCompletionSource.cs index 0222be41d..fae25f184 100644 --- a/src/DotNext.Threading/Threading/Tasks/ValueTaskCompletionSource.cs +++ b/src/DotNext.Threading/Threading/Tasks/ValueTaskCompletionSource.cs @@ -6,8 +6,6 @@ namespace DotNext.Threading.Tasks; -using NullExceptionConstant = Generic.DefaultConst; - /// /// Represents the producer side of . /// @@ -18,8 +16,6 @@ namespace DotNext.Threading.Tasks; /// public class ValueTaskCompletionSource : ManualResetCompletionSource, IValueTaskSource, ISupplier { - private static readonly NullExceptionConstant NullSupplier = new(); - // null - success, not null - error private ExceptionDispatchInfo? result; @@ -153,7 +149,7 @@ public bool TrySetResult() /// The data to be saved in property that can be accessed from within method. /// if the result is completed successfully; if the task has been canceled or timed out. public bool TrySetResult(object? completionData) - => SetResult(completionData, completionToken: null, NullSupplier); + => SetResult(completionData, completionToken: null, ISupplier.NullOrDefault); /// /// Attempts to complete the task sucessfully. @@ -170,7 +166,7 @@ public bool TrySetResult(short completionToken) /// The completion token previously obtained from method. /// if the result is completed successfully; if the task has been canceled or timed out. public bool TrySetResult(object? completionData, short completionToken) - => SetResult(completionData, completionToken, NullSupplier); + => SetResult(completionData, completionToken, ISupplier.NullOrDefault); /// /// Creates a fresh task linked with this source. diff --git a/src/DotNext/Generic/BooleanConst.cs b/src/DotNext/Generic/BooleanConst.cs deleted file mode 100644 index ad00f8e54..000000000 --- a/src/DotNext/Generic/BooleanConst.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace DotNext.Generic; - -/// -/// Represents boolean constant as generic parameter. -/// -public abstract class BooleanConst : Constant -{ - private BooleanConst(bool value) - : base(value) - { - } - - /// - /// Represents constant value as generic parameter. - /// - public sealed class True : BooleanConst - { - /// - /// Initializes a new constant value. - /// - public True() - : base(true) - { - } - } - - /// - /// Represents constant value as generic parameter. - /// - public sealed class False : BooleanConst - { - /// - /// Initializes a new constant value. - /// - public False() - : base(false) - { - } - } -} \ No newline at end of file diff --git a/src/DotNext/Generic/Constant.cs b/src/DotNext/Generic/Constant.cs deleted file mode 100644 index a6a3d4bd0..000000000 --- a/src/DotNext/Generic/Constant.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace DotNext.Generic; - -/// -/// Allows to use constant values as generic parameters. -/// -/// -/// Derived class must be sealed or abstract. If class is sealed -/// then it should have at least one constructor without parameters. -/// -/// Type of constant to be passed as generic parameter. -public abstract class Constant : ISupplier -{ - /// - /// Initializes a new generic-level constant. - /// - /// Constant value. - protected Constant(T constVal) => Value = constVal; - - /// - /// Gets value of the constant. - /// - public T Value { get; } - - /// - T ISupplier.Invoke() => Value; - - /// - /// Returns textual representation of the constant value. - /// - /// The textual representation of the constant value. - public sealed override string ToString() => Value?.ToString() ?? "NULL"; - - /// - /// Computes hash code for the constant. - /// - /// The hash code of the constant. - public sealed override int GetHashCode() => Value?.GetHashCode() ?? 0; - - /// - /// Determines whether two constant values are equal. - /// - /// Other constant value to compare. - /// , this object represents the same constant value as other; otherwise, . - public sealed override bool Equals([NotNullWhen(true)] object? other) => other switch - { - T obj => Equals(obj, Value), - Constant @const => Equals(Value, @const.Value), - _ => false, - }; - - /// - /// Extracts constant value. - /// - /// The constant value holder. - [return: NotNullIfNotNull(nameof(@const))] - public static implicit operator T?(Constant? @const) => @const is null ? default : @const.Value; -} \ No newline at end of file diff --git a/src/DotNext/Generic/DefaultConst.cs b/src/DotNext/Generic/DefaultConst.cs deleted file mode 100644 index 928f7ecd1..000000000 --- a/src/DotNext/Generic/DefaultConst.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace DotNext.Generic; - -/// -/// Represents default value of type as constant. -/// -/// The type of the constant value. -public sealed class DefaultConst : Constant -{ - /// - /// Initializes a new constant equal to default value of type . - /// - public DefaultConst() - : base(default) - { - } -} \ No newline at end of file diff --git a/src/DotNext/Generic/Int32Const.cs b/src/DotNext/Generic/Int32Const.cs deleted file mode 100644 index 492aa5e86..000000000 --- a/src/DotNext/Generic/Int32Const.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace DotNext.Generic; - -/// -/// Represents constant as type. -/// -public abstract class Int32Const : Constant -{ - /// - /// Associated value with this type. - /// - /// A value to be associated with this type. - protected Int32Const(int value) - : base(value) - { - } - - /// - /// Represents zero value as type. - /// - public sealed class Zero : Int32Const - { - /// - /// Represents constant value. - /// - public new const int Value = 0; - - /// - /// Initializes a new constant value. - /// - public Zero() - : base(Value) - { - } - } - - /// - /// Represents max integer value as type. - /// - public sealed class Max : Int32Const - { - /// - /// Represents constant value. - /// - public new const int Value = int.MaxValue; - - /// - /// Initializes a new constant value. - /// - public Max() - : base(Value) - { - } - } - - /// - /// Represents min integer value as type. - /// - public sealed class Min : Int32Const - { - /// - /// Represents constant value. - /// - public new const int Value = int.MinValue; - - /// - /// Initializes a new constant value. - /// - public Min() - : base(Value) - { - } - } -} \ No newline at end of file diff --git a/src/DotNext/Generic/Int64Const.cs b/src/DotNext/Generic/Int64Const.cs deleted file mode 100644 index b1d532462..000000000 --- a/src/DotNext/Generic/Int64Const.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace DotNext.Generic; - -/// -/// Represents constant as type. -/// -public abstract class Int64Const : Constant -{ - /// - /// Associated value with this type. - /// - /// A value to be associated with this type. - protected Int64Const(long value) - : base(value) - { - } - - /// - /// Represents zero value as type. - /// - public sealed class Zero : Int64Const - { - /// - /// Represents constant value. - /// - public new const long Value = 0; - - /// - /// Initializes a new constant value. - /// - public Zero() - : base(Value) - { - } - } - - /// - /// Represents max long value as type. - /// - public sealed class Max : Int64Const - { - /// - /// Represents constant value. - /// - public new const long Value = long.MaxValue; - - /// - /// Initializes a new constant value. - /// - public Max() - : base(Value) - { - } - } - - /// - /// Represents min long value as type. - /// - public sealed class Min : Int64Const - { - /// - /// Represents constant value. - /// - public new const long Value = long.MinValue; - - /// - /// Initializes a new constant value. - /// - public Min() - : base(Value) - { - } - } -} \ No newline at end of file diff --git a/src/DotNext/Generic/StringConst.cs b/src/DotNext/Generic/StringConst.cs deleted file mode 100644 index 8ad4ea202..000000000 --- a/src/DotNext/Generic/StringConst.cs +++ /dev/null @@ -1,54 +0,0 @@ -namespace DotNext.Generic; - -/// -/// Represents string constant as generic parameter. -/// -public abstract class StringConst : Constant -{ - /// - /// Initializes string constant. - /// - /// Constant value. - protected StringConst(string value) - : base(value) - { - } - - /// - /// Represents as string constant. - /// - public sealed class Null : StringConst - { - /// - /// Represents constant value. - /// - public new const string Value = null; - - /// - /// Initializes a new string constant. - /// - public Null() - : base(Value) - { - } - } - - /// - /// Represents empty string constant. - /// - public sealed class Empty : StringConst - { - /// - /// Represents constant value. - /// - public new const string Value = ""; - - /// - /// Creates empty string constant. - /// - public Empty() - : base(Value) - { - } - } -} \ No newline at end of file diff --git a/src/DotNext/ISupplier0.cs b/src/DotNext/ISupplier0.cs index 6770d0120..49ba4ec47 100644 --- a/src/DotNext/ISupplier0.cs +++ b/src/DotNext/ISupplier0.cs @@ -25,6 +25,11 @@ public interface ISupplier : IFunctional> /// Func IFunctional>.ToDelegate() => Invoke; + + /// + /// Gets a supplier of default value for type . + /// + public static ISupplier NullOrDefault { get; } = new DefaultSupplier(); } /// @@ -201,4 +206,9 @@ public DelegatingSupplier(Func func) /// The supplier represented by the delegate. /// is . public static implicit operator DelegatingSupplier(Func func) => new(func); +} + +file sealed class DefaultSupplier : ISupplier +{ + T? ISupplier.Invoke() => default; } \ No newline at end of file diff --git a/src/DotNext/Threading/Tasks/CompletedTask.cs b/src/DotNext/Threading/Tasks/CompletedTask.cs deleted file mode 100644 index 284cebe1a..000000000 --- a/src/DotNext/Threading/Tasks/CompletedTask.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Runtime.CompilerServices; -using Debug = System.Diagnostics.Debug; - -namespace DotNext.Threading.Tasks; - -using Generic; - -/// -/// Represents cache of completed tasks. -/// -/// Type of the task result. -/// The constant value to be assigned to the completed task. -public static class CompletedTask - where TConstant : Constant, new() -{ - private static readonly T Value = new TConstant(); - - /// - /// Represents the completed task containing a value passed as constant through generic parameter. - /// - public static readonly Task Task = System.Threading.Tasks.Task.FromResult(Value); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsFaulted(Task task, object? predicate) - { - Debug.Assert(predicate is Predicate); - return task.IsFaulted && Unsafe.As>(predicate).Invoke(task.Exception!); - } - - internal static T WhenFaulted(Task task, object? predicate) - => IsFaulted(task, predicate) ? Value : task.GetAwaiter().GetResult(); - - internal static T WhenCanceled(Task task) => task.IsCanceled ? Value : task.GetAwaiter().GetResult(); - - internal static T WhenFaultedOrCanceled(Task task, object? predicate) => IsFaulted(task, predicate) || task.IsCanceled ? Value : task.GetAwaiter().GetResult(); -} \ No newline at end of file diff --git a/src/DotNext/Threading/Tasks/Continuation.cs b/src/DotNext/Threading/Tasks/Continuation.cs deleted file mode 100644 index bd78b9839..000000000 --- a/src/DotNext/Threading/Tasks/Continuation.cs +++ /dev/null @@ -1,153 +0,0 @@ -namespace DotNext.Threading.Tasks; - -using Generic; - -/// -/// Represents various continuations. -/// -public static class Continuation -{ - /// - /// Allows to obtain original in its final state after await without - /// throwing exception produced by this task. - /// - /// The task to await. - /// in final state. - public static Task OnCompleted(this Task task) - => task.ContinueWith(Func.Identity(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Current); - - /// - /// Allows to obtain original in its final state after await without - /// throwing exception produced by this task. - /// - /// The type of the task result. - /// The task to await. - /// in final state. - public static Task> OnCompleted(this Task task) - => task.ContinueWith(Func.Identity>(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Current); - - /// - /// Returns constant value if underlying task is failed. - /// - /// - /// This continuation doesn't produce memory pressure. The delegate representing - /// continuation is cached for future reuse as well as constant value. - /// - /// The task to check. - /// Optional scheduler used to schedule continuation. - /// The type of task result. - /// The type describing constant value. - /// The task representing continuation. - public static Task OnFaulted(this Task task, TaskScheduler? scheduler = null) - where TConstant : Constant, new() - => OnFaulted(task, Predicate.Constant(value: true), scheduler); - - /// - /// Returns constant value if underlying task is failed with the exception that matches - /// to the filter. - /// - /// - /// This continuation doesn't produce memory pressure. The delegate representing - /// continuation is cached for future reuse as well as constant value. - /// - /// The task to check. - /// The exception filter. - /// Optional scheduler used to schedule continuation. - /// The type of task result. - /// The type describing constant value. - /// The task representing continuation. - public static Task OnFaulted(this Task task, Predicate filter, TaskScheduler? scheduler = null) - where TConstant : Constant, new() - => task.Status switch - { - TaskStatus.Faulted when filter(task.Exception!) => CompletedTask.Task, - TaskStatus.RanToCompletion or TaskStatus.Canceled or TaskStatus.Faulted => task, - _ => task.ContinueWith(CompletedTask.WhenFaulted, filter, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler ?? TaskScheduler.Current), - }; - - /// - /// Returns constant value if underlying task is failed or canceled. - /// - /// - /// This continuation doesn't produce memory pressure. The delegate representing - /// continuation is cached for future reuse as well as constant value. - /// - /// The task to check. - /// Optional scheduler used to schedule continuation. - /// The type of task result. - /// The type describing constant value. - /// The task representing continuation. - public static Task OnFaultedOrCanceled(this Task task, TaskScheduler? scheduler = null) - where TConstant : Constant, new() - => OnFaultedOrCanceled(task, Predicate.Constant(value: true), scheduler); - - /// - /// Returns constant value if underlying task is canceled or failed with the exception that matches - /// to the filter. - /// - /// - /// This continuation doesn't produce memory pressure. The delegate representing - /// continuation is cached for future reuse as well as constant value. - /// - /// The task to check. - /// The exception filter. - /// Optional scheduler used to schedule continuation. - /// The type of task result. - /// The type describing constant value. - /// The task representing continuation. - public static Task OnFaultedOrCanceled(this Task task, Predicate filter, TaskScheduler? scheduler = null) - where TConstant : Constant, new() - { - switch (task.Status) - { - case TaskStatus.Faulted when filter(task.Exception!): - case TaskStatus.Canceled: - task = CompletedTask.Task; - break; - case TaskStatus.RanToCompletion or TaskStatus.Faulted: - break; - default: - task = task.ContinueWith(CompletedTask.WhenFaultedOrCanceled, filter, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler ?? TaskScheduler.Current); - break; - } - - return task; - } - - /// - /// Returns constant value if underlying task is canceled. - /// - /// - /// This continuation doesn't produce memory pressure. The delegate representing - /// continuation is cached for future reuse as well as constant value. - /// - /// The task to check. - /// Optional scheduler used to schedule continuation. - /// The type of task result. - /// The type describing constant value. - /// The task representing continuation. - public static Task OnCanceled(this Task task, TaskScheduler? scheduler = null) - where TConstant : Constant, new() - => task.Status switch - { - TaskStatus.Canceled => CompletedTask.Task, - TaskStatus.RanToCompletion or TaskStatus.Faulted => task, - _ => task.ContinueWith(CompletedTask.WhenCanceled, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, scheduler ?? TaskScheduler.Current), - }; - - internal static void OnCompleted(this Task task, AsyncCallback callback) - { - if (task.IsCompleted) - callback(task); - else - task.ConfigureAwait(false).GetAwaiter().OnCompleted(() => callback(task)); - } - - internal static Task AttachState(this Task task, object? state, CancellationToken token = default) - { - return task.ContinueWith(WhenFaultedOrCanceled, state, token, TaskContinuationOptions.None, TaskScheduler.Default); - - static void WhenFaultedOrCanceled(Task task, object? state) - => task.ConfigureAwait(false).GetAwaiter().GetResult(); - } -} \ No newline at end of file From 886c69f64f4dfdfeba036ad91da6df1c098f1151 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 18:06:57 +0200 Subject: [PATCH 058/155] Added support of UTF-8 formattable objects --- .../Buffers/BufferHelpers.SpanWriter.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs b/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs index 473ff4e4d..4cac9cec6 100644 --- a/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs +++ b/src/DotNext/Buffers/BufferHelpers.SpanWriter.cs @@ -137,4 +137,41 @@ public static bool TryWrite(this ref SpanWriter writer, T value, scoped return result; } + + /// + /// Converts the value to a set of characters and writes them to the buffer. + /// + /// The formattable type. + /// The buffer writer. + /// The value to be converted to a set of characters. + /// The format of the value. + /// The format provider. + /// is not large enough to place the characters. + public static void Write(this ref SpanWriter writer, T value, scoped ReadOnlySpan format, IFormatProvider? provider = null) + where T : notnull, IUtf8SpanFormattable + { + if (!value.TryFormat(writer.RemainingSpan, out var writtenCount, format, provider)) + throw new ArgumentOutOfRangeException(nameof(writer)); + + writer.Advance(writtenCount); + } + + /// + /// Converts the value to a set of characters and writes them to the buffer. + /// + /// The formattable type. + /// The buffer writer. + /// The value to be converted to a set of characters. + /// The format of the value. + /// The format provider. + /// if has enough space to place the value; otherwise, . + public static bool TryWrite(this ref SpanWriter writer, T value, scoped ReadOnlySpan format, IFormatProvider? provider = null) + where T : notnull, IUtf8SpanFormattable + { + bool result; + if (result = value.TryFormat(writer.RemainingSpan, out var writtenCount, format, provider)) + writer.Advance(writtenCount); + + return result; + } } \ No newline at end of file From 19cc4b8998b30aa2fe142daa412265b3fd3eb198 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 20:02:38 +0200 Subject: [PATCH 059/155] Replace MemoryTemplate with CompositeFormat --- .../StringTemplateRenderingBenchmark.cs | 33 -- .../Buffers/MemoryTemplateTests.cs | 115 ------- src/DotNext.Tests/StringExtensionsTests.cs | 13 +- src/DotNext/Buffers/MemoryTemplate.cs | 299 ------------------ src/DotNext/StringExtensions.cs | 30 -- 5 files changed, 1 insertion(+), 489 deletions(-) delete mode 100644 src/DotNext.Benchmarks/Buffers/StringTemplateRenderingBenchmark.cs delete mode 100644 src/DotNext.Tests/Buffers/MemoryTemplateTests.cs delete mode 100644 src/DotNext/Buffers/MemoryTemplate.cs diff --git a/src/DotNext.Benchmarks/Buffers/StringTemplateRenderingBenchmark.cs b/src/DotNext.Benchmarks/Buffers/StringTemplateRenderingBenchmark.cs deleted file mode 100644 index 6ccb1650d..000000000 --- a/src/DotNext.Benchmarks/Buffers/StringTemplateRenderingBenchmark.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Engines; -using BenchmarkDotNet.Order; - -namespace DotNext.Buffers; - -[SimpleJob(runStrategy: RunStrategy.Throughput, launchCount: 1)] -[Orderer(SummaryOrderPolicy.FastestToSlowest)] -[MemoryDiagnoser] -public class StringTemplateRenderingBenchmark -{ - private const string Template1 = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit, {0} - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, - quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor - in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. {1} - Excepteur sint occaecat cupidatat non proident, {2} sunt in culpa qui officia deserunt mollit anim id est - laborum. {3}"; - - private const string Template2 = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit, $$$ - sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, - quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor - in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. $$$ - Excepteur sint occaecat cupidatat non proident, $$$ sunt in culpa qui officia deserunt mollit anim id est - laborum. $$$"; - - private readonly MemoryTemplate precompiledTemplate = Template2.AsTemplate("$$$"); - - [Benchmark] - public string FormatUsingString() => string.Format(Template1, "1", "2", "3", "4"); - - [Benchmark] - public string FormatUsingTemplate() => precompiledTemplate.Render("1", "2", "3", "4"); -} \ No newline at end of file diff --git a/src/DotNext.Tests/Buffers/MemoryTemplateTests.cs b/src/DotNext.Tests/Buffers/MemoryTemplateTests.cs deleted file mode 100644 index 8da0260d0..000000000 --- a/src/DotNext.Tests/Buffers/MemoryTemplateTests.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System.Buffers; -using System.Text; - -namespace DotNext.Buffers; - -public sealed class MemoryTemplateTests : Test -{ - private static void Rewrite(int index, ArrayBufferWriter writer) - { - switch (index) - { - case 0: - writer.Write("world"); - break; - default: - writer.Write('!'); - break; - } - } - - [Fact] - public static void RenderToBuffer() - { - const string placeholder = "%s"; - var template = "Hello, %s!%s".AsTemplate(placeholder); - var writer = new ArrayBufferWriter(); - template.Render(writer, Rewrite); - Equal("Hello, world!!", writer.BuildString()); - - writer.Clear(); - template = "%s%s".AsTemplate(placeholder); - template.Render(writer, Rewrite); - Equal("world!", writer.BuildString()); - - writer.Clear(); - template = "%s!!%s".AsTemplate(placeholder); - template.Render(writer, Rewrite); - Equal("world!!!", writer.BuildString()); - } - - [Fact] - public static void RenderToStringBuilder() - { - const string placeholder = "%s"; - var template = "Hello, %s!%s".AsTemplate(placeholder); - var writer = new StringBuilder(); - string[] replacement = { "world", "!" }; - template.Render(writer, replacement); - Equal("Hello, world!!", writer.ToString()); - - writer.Clear(); - template = "%s%s".AsTemplate(placeholder); - template.Render(writer, replacement); - Equal("world!", writer.ToString()); - - writer.Clear(); - template = "%s!!%s".AsTemplate(placeholder); - template.Render(writer, replacement); - Equal("world!!!", writer.ToString()); - } - - [Fact] - public static void RenderToStringWriter() - { - const string placeholder = "%s"; - var template = "Hello, %s!%s".AsTemplate(placeholder); - var writer = new StringWriter(); - string[] replacement = { "world", "!" }; - template.Render(writer, replacement); - Equal("Hello, world!!", writer.ToString()); - - writer.GetStringBuilder().Clear(); - template = "%s%s".AsTemplate(placeholder); - template.Render(writer, replacement); - Equal("world!", writer.ToString()); - - writer.GetStringBuilder().Clear(); - template = "%s!!%s".AsTemplate(placeholder); - template.Render(writer, replacement); - Equal("world!!!", writer.ToString()); - } - - [Fact] - public static void RenderToString() - { - const char placeholder = '%'; - var template = "Hello, %!%".AsTemplate(placeholder); - string[] replacement = { "world", "!" }; - Equal("Hello, world!!", template.Render(replacement)); - - template = "%%".AsTemplate(placeholder); - Equal("world!", template.Render(replacement)); - - template = "%!!%".AsTemplate(placeholder); - Equal("world!!!", template.Render(replacement)); - } - - [Fact] - public static void EmptyPlaceholder() - { - var template = new MemoryTemplate("Hello, world!".AsMemory(), default); - var writer = new ArrayBufferWriter(); - template.Render(writer, Rewrite); - Equal("Hello, world!", writer.BuildString()); - } - - [Fact] - public static void LargePlaceholder() - { - var template = "Hello, world!".AsTemplate("{Very large placeholder}"); - var writer = new ArrayBufferWriter(); - template.Render(writer, Rewrite); - Equal("Hello, world!", writer.BuildString()); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/StringExtensionsTests.cs b/src/DotNext.Tests/StringExtensionsTests.cs index 04fc6a7ea..7e44d92c3 100644 --- a/src/DotNext.Tests/StringExtensionsTests.cs +++ b/src/DotNext.Tests/StringExtensionsTests.cs @@ -1,6 +1,4 @@ -using System.Text; - -namespace DotNext; +namespace DotNext; public sealed class StringExtensionsTests : Test { @@ -35,13 +33,4 @@ public static void Substring() { Equal("abcd"[1..2], "abcd".Substring(1..2)); } - - [Fact] - [Obsolete] - public static void IsNullOrEmptyStringBuilder() - { - True(default(StringBuilder).IsNullOrEmpty()); - True(new StringBuilder().IsNullOrEmpty()); - False(new StringBuilder("abc").IsNullOrEmpty()); - } } \ No newline at end of file diff --git a/src/DotNext/Buffers/MemoryTemplate.cs b/src/DotNext/Buffers/MemoryTemplate.cs deleted file mode 100644 index bb0074e40..000000000 --- a/src/DotNext/Buffers/MemoryTemplate.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System.Buffers; -using System.Runtime.InteropServices; -using System.Text; - -namespace DotNext.Buffers; - -/// -/// Represents generic template for buffer rendering. -/// -/// -/// This type is aimed to fast replacement of the sequence of elements -/// called placeholder in the original sequence of elements. -/// In other words, it is an implementation of find-and-replace algorithm. -/// Pre-compiled template allows to reuse it when rendering with different -/// arguments is required. The rendering process is much faster -/// than especially for -/// large templates. However, the rendering process doesn't offer -/// formatting procedures. -/// -/// The type of the elements in the memory. -[StructLayout(LayoutKind.Auto)] -public readonly struct MemoryTemplate - where T : IEquatable -{ - private sealed class Placeholder - { - internal readonly int Offset; - internal Placeholder? Next; - - internal Placeholder(int offset) => Offset = offset; - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct BufferConsumer : IReadOnlySpanConsumer, IConsumer - where TWriter : class, IBufferWriter - { - private readonly TWriter buffer; - private readonly Action rewriter; - - internal BufferConsumer(TWriter buffer, Action rewriter) - { - this.buffer = buffer; - this.rewriter = rewriter; - } - - void IReadOnlySpanConsumer.Invoke(ReadOnlySpan input) => buffer.Write(input); - - void IConsumer.Invoke(int index) => rewriter(index, buffer); - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct DelegatingConsumer : IReadOnlySpanConsumer, IConsumer - { - private readonly ReadOnlySpanAction output; - private readonly Action rewriter; - private readonly TArg state; - - internal DelegatingConsumer(ReadOnlySpanAction output, Action rewriter, TArg state) - { - this.output = output; - this.rewriter = rewriter; - this.state = state; - } - - void IReadOnlySpanConsumer.Invoke(ReadOnlySpan input) => output(input, state); - - void IConsumer.Invoke(int index) => rewriter(index, state); - } - - private readonly ReadOnlyMemory template; - private readonly Placeholder? firstOccurence; - private readonly int placeholderLength; - - /// - /// Initializes a new pre-compiled template. - /// - /// The template containing placeholders for replacement. - /// The span of elements representing template placeholder. - public MemoryTemplate(ReadOnlyMemory template, scoped ReadOnlySpan placeholder) - { - this.template = template; - if (placeholder.IsEmpty || placeholder.Length > template.Length) - { - placeholderLength = 0; - firstOccurence = null; - } - else - { - placeholderLength = placeholder.Length; - firstOccurence = BuildPlaceholdersChain(template.Span, placeholder); - } - } - - /// - /// Gets original template passed to this object during construction. - /// - public ReadOnlyMemory Value => template; - - private static Placeholder? BuildPlaceholdersChain(scoped ReadOnlySpan source, scoped ReadOnlySpan placeholder) - { - Placeholder? head = null, tail = null; - - for (var offset = 0; offset < source.Length - placeholder.Length + 1;) - { - if (source.Slice(offset, placeholder.Length).SequenceEqual(placeholder)) - { - CreateNode(ref head, ref tail, offset); - offset += placeholder.Length; - } - else - { - offset += 1; - } - } - - return head; - } - - private static void CreateNode(ref Placeholder? head, ref Placeholder? tail, int offset) - { - if (head is null || tail is null) - { - head = tail = new Placeholder(offset); - } - else - { - var previous = tail; - tail = previous.Next = new Placeholder(offset); - } - } - - /// - /// Replaces all placeholders in the template with custom content. - /// - /// The consumer of the rendered content. - /// The type of the consumer. - public void Render(TConsumer consumer) - where TConsumer : notnull, IReadOnlySpanConsumer, IConsumer - { - ReadOnlySpan source = template.Span; - var placeholder = firstOccurence; - for (int cursor = 0, offset = 0, index = 0; MoveNext(ref cursor, ref placeholder, out var isPlaceholder); offset = cursor) - { - if (isPlaceholder) - { - consumer.Invoke(index++); - } - else - { - consumer.Invoke(source.Slice(offset, cursor - offset)); - } - } - } - - /// - /// Replaces all placeholders in the template with custom content. - /// - /// The buffer writer used to build rendered content. - /// - /// The action responsible for replacing placeholder with custom content. - /// The first argument of the action indicates placeholder index. - /// - /// The type of the buffer writer. - public void Render(TWriter output, Action rewriter) - where TWriter : class, IBufferWriter - => Render(new BufferConsumer(output ?? throw new ArgumentNullException(nameof(output)), rewriter ?? throw new ArgumentNullException(nameof(rewriter)))); - - /// - /// Replaces all placeholders in the template with custom content. - /// - /// The argument to be passed to the write actions. - /// - /// The action responsible for replacing placeholder with custom content. - /// The first argument of the action indicates placeholder index. - /// - /// The action responsible for writing unmodified segments from the original template. - /// The type of the argument to be passed to and . - public void Render(TArg arg, Action rewriter, ReadOnlySpanAction output) - => Render(new DelegatingConsumer(output, rewriter, arg)); - - private bool MoveNext(ref int offset, ref Placeholder? placeholder, out bool isPlaceholder) - { - if (offset >= template.Length) - { - isPlaceholder = false; - return false; - } - - if (placeholder is null) - { - isPlaceholder = false; - offset = template.Length; - } - else if (placeholder.Offset == offset) - { - isPlaceholder = true; - offset += placeholderLength; - placeholder = placeholder.Next; - } - else - { - offset = placeholder.Offset; - isPlaceholder = false; - } - - return true; - } -} - -/// -/// Represents various extensions for type. -/// -public static class MemoryTemplate -{ - [StructLayout(LayoutKind.Auto)] - private readonly struct StringBuilderRenderer : IReadOnlySpanConsumer, IConsumer - { - private readonly IReadOnlyList replacement; - private readonly StringBuilder output; - - internal StringBuilderRenderer(StringBuilder output, IReadOnlyList replacement) - { - this.output = output; - this.replacement = replacement; - } - - void IReadOnlySpanConsumer.Invoke(ReadOnlySpan input) => output.Append(input); - - void IConsumer.Invoke(int index) => output.Append(replacement[index]); - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct CharBufferRenderer : IReadOnlySpanConsumer, IConsumer - { - private readonly IReadOnlyList replacement; - private readonly IBufferWriter output; - - internal CharBufferRenderer(IBufferWriter output, IReadOnlyList replacement) - { - this.output = output; - this.replacement = replacement; - } - - void IReadOnlySpanConsumer.Invoke(ReadOnlySpan input) => output.Write(input); - - void IConsumer.Invoke(int index) => output.Write(replacement[index]); - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct TextRenderer : IReadOnlySpanConsumer, IConsumer - { - private readonly IReadOnlyList replacement; - private readonly TextWriter output; - - internal TextRenderer(TextWriter output, IReadOnlyList replacement) - { - this.output = output; - this.replacement = replacement; - } - - void IReadOnlySpanConsumer.Invoke(ReadOnlySpan input) => output.Write(input); - - void IConsumer.Invoke(int index) => output.Write(replacement[index]); - } - - /// - /// Replaces all occurences of placeholders in the template with - /// actual values from the given array. - /// - /// The string template. - /// The string builder used to write rendered template. - /// An array of actual values used to replace placeholders. - public static void Render(this in MemoryTemplate template, StringBuilder output, params string[] replacement) - => template.Render(new StringBuilderRenderer(output, replacement)); - - /// - /// Replaces all occurences of placeholders in the template with - /// actual values from the given array. - /// - /// The string template. - /// An array of actual values used to replace placeholders. - /// The rendered template. - public static string Render(this in MemoryTemplate template, params string[] replacement) - { - using var writer = new PooledArrayBufferWriter { Capacity = template.Value.Length }; - template.Render(new CharBufferRenderer(writer, replacement)); - return new string(writer.WrittenArray); - } - - /// - /// Replaces all occurences of placeholders in the template with - /// actual values from the given array. - /// - /// The string template. - /// The text writer used to write rendered template. - /// An array of actual values used to replace placeholders. - public static void Render(this in MemoryTemplate template, TextWriter output, params string[] replacement) - => template.Render(new TextRenderer(output, replacement)); -} \ No newline at end of file diff --git a/src/DotNext/StringExtensions.cs b/src/DotNext/StringExtensions.cs index 377782475..04f8e3c51 100644 --- a/src/DotNext/StringExtensions.cs +++ b/src/DotNext/StringExtensions.cs @@ -1,12 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using System.Text; using static System.Runtime.InteropServices.MemoryMarshal; using Unsafe = System.Runtime.CompilerServices.Unsafe; namespace DotNext; -using StringTemplate = Buffers.MemoryTemplate; - /// /// Represents various extension methods for type . /// @@ -90,31 +87,4 @@ public static string Substring(this string str, Range range) var (start, length) = range.GetOffsetAndLength(str.Length); return str.Substring(start, length); } - - /// - /// Compiles string template. - /// - /// The string representing template with placeholders. - /// The placeholder in the template. - /// The compiled template that can be used to replace all placeholders with their original values. - public static StringTemplate AsTemplate(this string template, string placeholder) - => new(template.AsMemory(), placeholder); - - /// - /// Compiles string template. - /// - /// The string representing template with placeholders. - /// The placeholder in the template. - /// The compiled template that can be used to replace all placeholders with their original values. - public static StringTemplate AsTemplate(this string template, char placeholder) - => new(template.AsMemory(), CreateReadOnlySpan(ref placeholder, 1)); - - /// - /// Checks whether the growable string is or empty. - /// - /// The builder to check. - /// , if builder is or empty. - [Obsolete("This method is easily replaceable with pattern matching: sb is not { Length: > 0 };")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this StringBuilder? builder) - => builder is not { Length: > 0 }; } \ No newline at end of file From 8382ac81c1e9c8d04c3cd8f4f52c665d29ecbdcb Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 20:41:00 +0200 Subject: [PATCH 060/155] Removed deprecated members --- .../IO/FileBufferingWriter.Options.cs | 10 - src/DotNext.IO/IO/FileBufferingWriter.cs | 3 - .../Linq/Expressions/ExpressionBuilder.cs | 50 -- src/DotNext.Tests/Buffers/Text/HexTests.cs | 2 - .../CompilerServices/SharedContainerTests.cs | 24 - .../Runtime/InteropServices/PointerTests.cs | 37 -- src/DotNext.Tests/StringExtensionsTests.cs | 9 - src/DotNext.Tests/Text/Base64Tests.cs | 534 ------------------ src/DotNext.Tests/Threading/AsyncLockTests.cs | 36 -- .../Threading/AsyncTriggerTests.cs | 32 -- src/DotNext.Threading/Threading/AsyncLazy.cs | 42 -- src/DotNext.Threading/Threading/AsyncLock.cs | 9 - .../Threading/AsyncLockAcquisition.cs | 78 --- .../Threading/AsyncTrigger.cs | 270 +-------- .../Threading/Channels/PersistentChannel.cs | 8 +- .../Channels/PersistentChannelOptions.cs | 20 - .../Channels/PersistentChannelReader.cs | 8 +- .../Threading/QueuedSynchronizer.cs | 24 +- .../InteropServices/IUnmanagedArray.cs | 2 +- .../Runtime/InteropServices/Pointer.cs | 107 +--- .../Collections/Generic/AsyncEnumerable.cs | 26 - .../Specialized/ConcurrentTypeMap.cs | 10 - .../Collections/Specialized/ITypeMap.cs | 10 - .../Collections/Specialized/TypeMap.cs | 10 - src/DotNext/Diagnostics/Timestamp.cs | 6 - src/DotNext/Predicate.cs | 22 - .../Runtime/CompilerServices/Shared.cs | 41 -- src/DotNext/StringExtensions.cs | 17 - src/DotNext/Text/Base64Decoder.Unicode.cs | 85 --- src/DotNext/Text/Base64Decoder.Utf8.cs | 93 --- src/DotNext/Text/Base64Decoder.cs | 33 -- src/DotNext/Text/Base64Encoder.Unicode.cs | 124 ---- src/DotNext/Text/Base64Encoder.Utf8.cs | 111 ---- src/DotNext/Text/Base64Encoder.cs | 67 --- src/DotNext/UserDataSlot.cs | 9 +- .../Raft/ClusterMemberConfiguration.cs | 10 - .../Consensus/Raft/Http/HttpMessage.cs | 2 +- .../Raft/Http/HttpMetricsCollector.cs | 38 -- .../Consensus/Raft/Http/IHttpMessage.cs | 3 - .../Raft/Http/RaftClusterHttpHost.cs | 3 - .../Consensus/Raft/Http/RaftClusterMember.cs | 9 - .../Consensus/Raft/Http/RaftHttpCluster.cs | 21 +- .../Consensus/Raft/ConsensusOnlyState.cs | 8 - .../Cluster/Consensus/Raft/FollowerState.cs | 3 - .../Raft/IClusterMemberConfiguration.cs | 6 - .../Consensus/Raft/IPersistentState.cs | 10 - .../Cluster/Consensus/Raft/IRaftCluster.cs | 6 - .../Net/Cluster/Consensus/Raft/LeaderState.cs | 7 - .../Raft/MemoryBasedStateMachine.Options.cs | 10 - ...MemoryBasedStateMachine.SnapshotBuilder.cs | 7 - .../Consensus/Raft/MemoryBasedStateMachine.cs | 15 +- .../Raft/Metrics/IClientMetricsCollector.cs | 16 - .../Consensus/Raft/MetricsCollector.cs | 116 ---- .../Consensus/Raft/PersistentState.Options.cs | 63 --- .../Cluster/Consensus/Raft/PersistentState.cs | 45 -- .../Raft/RaftCluster.Configuration.cs | 39 +- .../Consensus/Raft/RaftCluster.DefaultImpl.cs | 12 +- .../Net/Cluster/Consensus/Raft/RaftCluster.cs | 62 +- .../Consensus/Raft/RaftClusterMember.cs | 11 - .../ConnectionOriented/Client.cs | 6 +- .../Datagram/ExchangePeer.cs | 6 +- .../Net/Cluster/Messaging/BinaryMessage.cs | 8 - .../Net/Security/SslOptions.cs | 13 - 63 files changed, 30 insertions(+), 2494 deletions(-) delete mode 100644 src/DotNext.Tests/Runtime/CompilerServices/SharedContainerTests.cs delete mode 100644 src/DotNext.Tests/Text/Base64Tests.cs delete mode 100644 src/DotNext/Runtime/CompilerServices/Shared.cs delete mode 100644 src/DotNext/Text/Base64Decoder.Unicode.cs delete mode 100644 src/DotNext/Text/Base64Decoder.Utf8.cs delete mode 100644 src/DotNext/Text/Base64Decoder.cs delete mode 100644 src/DotNext/Text/Base64Encoder.Unicode.cs delete mode 100644 src/DotNext/Text/Base64Encoder.Utf8.cs delete mode 100644 src/DotNext/Text/Base64Encoder.cs delete mode 100644 src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMetricsCollector.cs delete mode 100644 src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Metrics/IClientMetricsCollector.cs delete mode 100644 src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MetricsCollector.cs diff --git a/src/DotNext.IO/IO/FileBufferingWriter.Options.cs b/src/DotNext.IO/IO/FileBufferingWriter.Options.cs index e1e0cd4ae..3457d9e40 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.Options.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.Options.cs @@ -200,16 +200,6 @@ internal string Path } } - /// - /// Gets or sets the counter used to report allocation of the internal buffer. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public EventCounter? AllocationCounter - { - get; - init; - } - /// /// Sets a list of tags to be associated with each measurement. /// diff --git a/src/DotNext.IO/IO/FileBufferingWriter.cs b/src/DotNext.IO/IO/FileBufferingWriter.cs index fc09272d7..71dbe53d3 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.cs @@ -214,9 +214,6 @@ public FileBufferingWriter(in Options options) this.memoryThreshold = memoryThreshold; fileProvider = new BackingFileProvider(in options); -#pragma warning disable CS0618 - allocationCounter = options.AllocationCounter; -#pragma warning restore CS0618 measurementTags = options.MeasurementTags; } diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs index 19e552909..172c2fcb5 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ExpressionBuilder.cs @@ -763,21 +763,6 @@ public static MethodCallExpression CallStatic(this Type type, string methodName, : throw new MissingMethodException(type.FullName, methodName); } - /// - /// Constructs instance property or indexer access expression. - /// - /// - /// The equivalent code is a.b or a.b[i]. - /// - /// this argument. - /// Property metadata. - /// Indexer indicies. - /// Property access expression. - [Obsolete("Use alternative overloads.", error: true)] - [ExcludeFromCodeCoverage] - public static Expression Property(Expression instance, PropertyInfo property, Expression[] indicies) - => indicies.LongLength == 0L ? instance.Property(property) : instance.Property(property, indicies[0], indicies[1..]); - /// /// Constructs instance property access expression. /// @@ -804,26 +789,6 @@ public static MemberExpression Property(this Expression instance, PropertyInfo p public static IndexExpression Property(this Expression instance, PropertyInfo property, Expression index0, params Expression[] indicies) => Expression.Property(instance, property, indicies.Prepend(index0)); - /// - /// Constructs instance property or indexer access expression declared in the given interface or base type. - /// - /// - /// The equivalent code is a.b or a.b[i]. - /// - /// this argument. - /// The interface or base class declaring property. - /// The name of the instance property or indexer. - /// Indexer indicies. - /// Property access expression. - [Obsolete("Use alternative overloads.", error: true)] - [ExcludeFromCodeCoverage] - public static Expression Property(Expression instance, Type interfaceType, string propertyName, Expression[] indicies) - { - return interfaceType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance) is { } property - ? Property(instance, property, indicies) - : throw new MissingMemberException(interfaceType.FullName, propertyName); - } - /// /// Constructs instance property access expression declared in the given interface or base type. /// @@ -860,21 +825,6 @@ public static IndexExpression Property(this Expression instance, Type interfaceT : throw new MissingMemberException(interfaceType.FullName, propertyName); } - /// - /// Constructs instance property or indexer access expression. - /// - /// - /// The equivalent code is a.b or a.b[i]. - /// - /// this argument. - /// The name of the instance property or indexer. - /// Indexer indicies. - /// Property access expression. - [Obsolete("Use alternative overloads.", error: true)] - [ExcludeFromCodeCoverage] - public static Expression Property(Expression instance, string propertyName, Expression[] indicies) - => Expression.Property(instance, propertyName, indicies); - /// /// Constructs instance property or indexer access expression. /// diff --git a/src/DotNext.Tests/Buffers/Text/HexTests.cs b/src/DotNext.Tests/Buffers/Text/HexTests.cs index e2c010d23..b11710737 100644 --- a/src/DotNext.Tests/Buffers/Text/HexTests.cs +++ b/src/DotNext.Tests/Buffers/Text/HexTests.cs @@ -64,7 +64,6 @@ public static void FromUtf16ConversionVarLength() [InlineData(0, false)] [InlineData(128, false)] [InlineData(2048, false)] - [Obsolete] public static void FromUtf16(int arraySize, bool lowercased) { var expected = RandomBytes(arraySize); @@ -103,7 +102,6 @@ public static void ToUtf8(int arraySize, bool lowercased) [InlineData(0, false)] [InlineData(128, false)] [InlineData(2048, false)] - [Obsolete] public static void FromUtf8(int arraySize, bool lowercased) { var expected = RandomBytes(arraySize); diff --git a/src/DotNext.Tests/Runtime/CompilerServices/SharedContainerTests.cs b/src/DotNext.Tests/Runtime/CompilerServices/SharedContainerTests.cs deleted file mode 100644 index 1501e1763..000000000 --- a/src/DotNext.Tests/Runtime/CompilerServices/SharedContainerTests.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace DotNext.Runtime.CompilerServices; - -[Obsolete] -public sealed class SharedContainerTests : Test -{ - [Fact] - public static void BoxValue() - { - Shared box = 10; - Equal(10, box.Value); - } - - [Fact] - public static void BoxNullableValue() - { - int? i = null; - Shared box = i; - Null(box); - - i = 10; - box = i; - Equal(10, box.Value); - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs index a08f3bcaa..4bf442fc2 100644 --- a/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs +++ b/src/DotNext.Tests/Runtime/InteropServices/PointerTests.cs @@ -48,43 +48,6 @@ public static async Task StreamInteropAsync() Equal(20, ptr[0]); } - [Fact] - [Obsolete] - public static unsafe void ArrayInterop() - { - var array = new ushort[] { 1, 2, 3 }; - fixed (ushort* p = array) - { - var ptr = new Pointer(p); - var dest = new ushort[array.LongLength]; - Equal(3L, ptr.WriteTo(dest, 0, array.LongLength)); - Equal(array, dest); - dest[0] = 50; - Equal(3L, ptr.ReadFrom(dest, 0, dest.LongLength)); - Equal(50, ptr.Value); - Equal(50, array[0]); - } - } - - [Fact] - [Obsolete] - public static unsafe void ArrayInteropWithOffset() - { - var array = new ushort[] { 1, 2, 3 }; - fixed (ushort* p = array) - { - var ptr = new Pointer(p); - var dest = new ushort[array.LongLength]; - Equal(1L, ptr.WriteTo(dest, 2L, 1L)); - NotEqual(array, dest); - Equal(new ushort[] { 0, 0, 1 }, dest); - dest[2] = 50; - Equal(1L, ptr.ReadFrom(dest, 2L, 1L)); - Equal(50, ptr.Value); - Equal(50, array[0]); - } - } - [Fact] public static unsafe void Swap() { diff --git a/src/DotNext.Tests/StringExtensionsTests.cs b/src/DotNext.Tests/StringExtensionsTests.cs index 7e44d92c3..35999d6bd 100644 --- a/src/DotNext.Tests/StringExtensionsTests.cs +++ b/src/DotNext.Tests/StringExtensionsTests.cs @@ -2,15 +2,6 @@ public sealed class StringExtensionsTests : Test { - [Fact] - [Obsolete] - public static void IfNullOrEmptyTest() - { - Equal("a", "".IfNullOrEmpty("a")); - Equal("a", default(string).IfNullOrEmpty("a")); - Equal("b", "b".IfNullOrEmpty("a")); - } - [Fact] public static void ReverseTest() { diff --git a/src/DotNext.Tests/Text/Base64Tests.cs b/src/DotNext.Tests/Text/Base64Tests.cs deleted file mode 100644 index e756ac69e..000000000 --- a/src/DotNext.Tests/Text/Base64Tests.cs +++ /dev/null @@ -1,534 +0,0 @@ -using System.Buffers; -using System.Text; - -namespace DotNext.Text; - -using Buffers; -using IO; - -[Obsolete] -public sealed class Base64Tests : Test -{ - [Theory] - [InlineData(17, 256)] - [InlineData(12, 256)] - [InlineData(32, 256)] - [InlineData(512, 1024)] - public static void DecodeBase64BytesToBufferWriter(int chunkSize, int size) - { - var expected = RandomBytes(size); - var base64 = ToReadOnlySequence(Encoding.UTF8.GetBytes(Convert.ToBase64String(expected)), chunkSize); - var actual = new ArrayBufferWriter(); - var decoder = new Base64Decoder(); - decoder.Decode(base64, actual); - False(decoder.NeedMoreData); - - Equal(expected, actual.WrittenSpan.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - public static void DecodeBase64BytesToMemoryBlock(int size) - { - var expected = RandomBytes(size); - ReadOnlySpan base64 = Encoding.UTF8.GetBytes(Convert.ToBase64String(expected)); - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(base64); - False(decoder.NeedMoreData); - - Equal(expected, actual.Span.ToArray()); - } - - [Theory] - [InlineData(17, 256)] - [InlineData(12, 256)] - [InlineData(32, 256)] - [InlineData(512, 1024)] - public static void DecodeBase64BytesToCallback(int chunkSize, int size) - { - var expected = RandomBytes(size); - var base64 = ToReadOnlySequence(Encoding.UTF8.GetBytes(Convert.ToBase64String(expected)), chunkSize); - var actual = new ArrayBufferWriter(); - var decoder = new Base64Decoder(); - foreach (var segment in base64) - decoder.Decode(segment.Span, static (input, output) => output.Write(input), actual); - False(decoder.NeedMoreData); - - Equal(expected, actual.WrittenSpan.ToArray()); - } - - [Theory] - [InlineData(17, 256)] - [InlineData(12, 256)] - [InlineData(32, 256)] - [InlineData(512, 1024)] - public static unsafe void DecodeBase64BytesToFunctionPointer(int chunkSize, int size) - { - var expected = RandomBytes(size); - var base64 = ToReadOnlySequence(Encoding.UTF8.GetBytes(Convert.ToBase64String(expected)), chunkSize); - var actual = new ArrayBufferWriter(); - var decoder = new Base64Decoder(); - foreach (var segment in base64) - decoder.Decode(segment.Span, &Callback, actual); - False(decoder.NeedMoreData); - - Equal(expected, actual.WrittenSpan.ToArray()); - - static void Callback(ReadOnlySpan input, ArrayBufferWriter output) - => output.Write(input); - } - - [Fact] - public static void DecodeInvalidBlock() - { - var base64 = Encoding.UTF8.GetBytes("AB"); - var decoder = new Base64Decoder(); - using var writer = new PooledArrayBufferWriter(); - decoder.Decode(base64, writer); - True(decoder.NeedMoreData); - Equal(0, writer.WrittenCount); - } - - [Theory] - [InlineData(17, 256)] - [InlineData(12, 256)] - [InlineData(32, 256)] - [InlineData(512, 1024)] - public static void DecodeBase64CharsToBufferWriter(int chunkSize, int size) - { - var expected = RandomBytes(size); - var base64 = ToReadOnlySequence(Convert.ToBase64String(expected).AsMemory(), chunkSize); - var actual = new ArrayBufferWriter(); - var decoder = new Base64Decoder(); - decoder.Decode(base64, actual); - False(decoder.NeedMoreData); - - Equal(expected, actual.WrittenSpan.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - public static void DecodeBase64CharsToMemoryBlock(int size) - { - var expected = RandomBytes(size); - ReadOnlySpan base64 = Convert.ToBase64String(expected); - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(base64); - False(decoder.NeedMoreData); - - Equal(expected, actual.Span.ToArray()); - } - - [Theory] - [InlineData(25, 25)] - [InlineData(17, 256)] - [InlineData(12, 256)] - [InlineData(32, 256)] - [InlineData(512, 1024)] - public static void DecodeBase64CharsToCallback(int chunkSize, int size) - { - var expected = RandomBytes(size); - var base64 = ToReadOnlySequence(Convert.ToBase64String(expected).AsMemory(), chunkSize); - var actual = new ArrayBufferWriter(); - var decoder = new Base64Decoder(); - foreach (var segment in base64) - decoder.Decode(segment.Span, static (input, output) => output.Write(input), actual); - False(decoder.NeedMoreData); - - Equal(expected, actual.WrittenSpan.ToArray()); - } - - [Theory] - [InlineData(25, 25)] - [InlineData(17, 256)] - [InlineData(12, 256)] - [InlineData(32, 256)] - [InlineData(512, 1024)] - public static unsafe void DecodeBase64CharsToFunctionPointer(int chunkSize, int size) - { - var expected = RandomBytes(size); - var base64 = ToReadOnlySequence(Convert.ToBase64String(expected).AsMemory(), chunkSize); - var actual = new ArrayBufferWriter(); - var decoder = new Base64Decoder(); - foreach (var segment in base64) - decoder.Decode(segment.Span, &Callback, actual); - False(decoder.NeedMoreData); - - Equal(expected, actual.WrittenSpan.ToArray()); - - static void Callback(ReadOnlySpan input, ArrayBufferWriter output) - => output.Write(input); - } - - [Theory] - [InlineData(25, 25)] - [InlineData(17, 256)] - [InlineData(12, 256)] - [InlineData(32, 256)] - [InlineData(512, 1024)] - public static void DecodeBase64BytesToStream(int chunkSize, int size) - { - var expected = RandomBytes(size); - var base64 = ToReadOnlySequence(Encoding.UTF8.GetBytes(Convert.ToBase64String(expected)), chunkSize); - var actual = new ArrayBufferWriter(); - var decoder = new Base64Decoder(); - - using var ms = new MemoryStream(size); - foreach (var segment in base64) - decoder.Decode(segment.Span, ms); - - ms.Flush(); - Equal(expected, ms.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToBufferWriterUtf8(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - False(encoder.HasBufferedData); - var writer = new ArrayBufferWriter(); - - encoder.EncodeToUtf8(expected.AsSpan(0, size / 2), writer, flush: false); - encoder.EncodeToUtf8(expected.AsSpan().Slice(size / 2), writer, flush: true); - Equal(0, encoder.BufferedDataSize); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.WrittenSpan); - - Equal(expected, actual.Span.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToBufferWriterChars(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - var writer = new ArrayBufferWriter(); - - encoder.EncodeToChars(expected.AsSpan(0, size / 2), writer, flush: false); - encoder.EncodeToChars(expected.AsSpan().Slice(size / 2), writer, flush: true); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.WrittenSpan); - - Equal(expected, actual.Span.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToBytes(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - using var base64 = encoder.EncodeToUtf8(expected, flush: true); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(base64.Span); - - Equal(expected, actual.Span.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToChars(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - using var base64 = encoder.EncodeToChars(expected, flush: true); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(base64.Span); - - Equal(expected, actual.Span.ToArray()); - } - - [Fact] - public static void FlushToBytes() - { - var encoder = new Base64Encoder(); - var writer = new ArrayBufferWriter(); - byte[] expected = { 1, 2 }; - - encoder.EncodeToUtf8(expected, writer, flush: false); - True(encoder.HasBufferedData); - - Span base64 = stackalloc byte[4]; - Equal(2, encoder.GetBufferedData(base64)); - Equal(expected, base64.Slice(0, 2).ToArray()); - - Equal(4, encoder.Flush(base64)); - } - - [Fact] - public static void FlushToChars() - { - var encoder = new Base64Encoder(); - var writer = new ArrayBufferWriter(); - byte[] expected = { 1, 2 }; - - encoder.EncodeToChars(expected, writer, flush: false); - True(encoder.HasBufferedData); - - Span base64 = stackalloc char[4]; - Equal(4, encoder.Flush(base64)); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToStringBuilder(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - var writer = new StringBuilder(); - - encoder.EncodeToChars(expected.AsSpan(0, size / 2), writer, flush: false); - encoder.EncodeToChars(expected.AsSpan().Slice(size / 2), writer, flush: true); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.ToString()); - - Equal(expected, actual.Span.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToTextWriter(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - using var writer = new StringWriter(); - - encoder.EncodeToChars(expected.AsSpan(0, size / 2), writer, flush: false); - encoder.EncodeToChars(expected.AsSpan().Slice(size / 2), writer, flush: true); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.ToString()); - - Equal(expected, actual.Span.ToArray()); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToCallbackUtf8(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - False(encoder.HasBufferedData); - var writer = new ArrayBufferWriter(); - - encoder.EncodeToUtf8(expected.AsSpan(0, size / 2), Write, writer, flush: false); - encoder.EncodeToUtf8(expected.AsSpan().Slice(size / 2), Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.WrittenSpan); - - Equal(expected, actual.Span.ToArray()); - - static void Write(ReadOnlySpan input, ArrayBufferWriter output) - => output.Write(input); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static void EncodeToCallbackChars(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - False(encoder.HasBufferedData); - var writer = new ArrayBufferWriter(); - - encoder.EncodeToChars(expected.AsSpan(0, size / 2), Write, writer, flush: false); - encoder.EncodeToChars(expected.AsSpan().Slice(size / 2), Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.WrittenSpan); - - Equal(expected, actual.Span.ToArray()); - - static void Write(ReadOnlySpan input, ArrayBufferWriter output) - => output.Write(input); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static unsafe void EncodeToFunctionPointerUtf8(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - False(encoder.HasBufferedData); - var writer = new ArrayBufferWriter(); - - encoder.EncodeToUtf8(expected.AsSpan(0, size / 2), &Write, writer, flush: false); - encoder.EncodeToUtf8(expected.AsSpan().Slice(size / 2), &Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.WrittenSpan); - - Equal(expected, actual.Span.ToArray()); - - static void Write(ReadOnlySpan input, ArrayBufferWriter output) - => output.Write(input); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static unsafe void EncodeToFunctionPointerChars(int size) - { - var expected = RandomBytes(size); - - var encoder = new Base64Encoder(); - False(encoder.HasBufferedData); - var writer = new ArrayBufferWriter(); - - encoder.EncodeToChars(expected.AsSpan(0, size / 2), &Write, writer, flush: false); - encoder.EncodeToChars(expected.AsSpan().Slice(size / 2), &Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); - - var decoder = new Base64Decoder(); - using var actual = decoder.Decode(writer.WrittenSpan); - - Equal(expected, actual.Span.ToArray()); - - static void Write(ReadOnlySpan input, ArrayBufferWriter output) - => output.Write(input); - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static async Task EncodeToCharsAsync(int size) - { - var expected = RandomBytes(size); - string base64; - - // encode - using (var source = new MemoryStream(expected)) - { - using var destination = new StringWriter(); - - await foreach (var chunk in Base64Encoder.EncodeToCharsAsync(source.ReadAllAsync(16))) - { - await destination.WriteAsync(chunk); - } - - await destination.FlushAsync(); - base64 = destination.ToString(); - } - - // decode - using (var source = new StringReader(base64)) - { - using var destination = new MemoryStream(expected.Length); - - await foreach (var chunk in Base64Decoder.DecodeAsync(source.ReadAllAsync(16))) - { - await destination.WriteAsync(chunk); - } - - await destination.FlushAsync(); - Equal(expected, destination.ToArray()); - } - } - - [Theory] - [InlineData(17)] - [InlineData(32)] - [InlineData(63)] - [InlineData(512)] - [InlineData(1024)] - public static async Task EncodeToUtf8Async(int size) - { - var expected = RandomBytes(size); - byte[] base64; - - // encode - using (var source = new MemoryStream(expected)) - { - using var destination = new MemoryStream(); - - await foreach (var chunk in Base64Encoder.EncodeToUtf8Async(source.ReadAllAsync(16))) - { - await destination.WriteAsync(chunk); - } - - await destination.FlushAsync(); - base64 = destination.ToArray(); - } - - // decode - using (var source = new MemoryStream(base64)) - { - using var destination = new MemoryStream(expected.Length); - - await foreach (var chunk in Base64Decoder.DecodeAsync(source.ReadAllAsync(16))) - { - await destination.WriteAsync(chunk); - } - - await destination.FlushAsync(); - Equal(expected, destination.ToArray()); - } - } -} \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/AsyncLockTests.cs b/src/DotNext.Tests/Threading/AsyncLockTests.cs index 157f76291..8d62516a2 100644 --- a/src/DotNext.Tests/Threading/AsyncLockTests.cs +++ b/src/DotNext.Tests/Threading/AsyncLockTests.cs @@ -48,40 +48,4 @@ public static async Task SemaphoreLock() holder.Dispose(); Equal(3, sem.CurrentCount); } - - [Fact] - [Obsolete] - public static void DisposedState() - { - var l = AsyncLock.Exclusive(); - l.Dispose(); - var result = l.AcquireAsync(CancellationToken.None).SuppressDisposedState(); - True(result.IsCompletedSuccessfully); - False(result.Result); - } - - [Fact] - [Obsolete] - public static void CanceledState() - { - var t = ValueTask.FromCanceled(new CancellationToken(true)); - True(t.IsCanceled); - t = t.SuppressCancellation(); - False(t.IsCanceled); - False(t.Result); - } - - [Fact] - [Obsolete] - public static void DisposedOrCanceledState() - { - var t = ValueTask.FromCanceled(new CancellationToken(true)); - t = t.SuppressDisposedStateOrCancellation(); - False(t.IsCanceled); - False(t.Result); - t = ValueTask.FromException(new ObjectDisposedException("obj")); - t = t.SuppressDisposedStateOrCancellation(); - False(t.IsFaulted); - False(t.Result); - } } \ No newline at end of file diff --git a/src/DotNext.Tests/Threading/AsyncTriggerTests.cs b/src/DotNext.Tests/Threading/AsyncTriggerTests.cs index 86cb1ef73..993e7f1d9 100644 --- a/src/DotNext.Tests/Threading/AsyncTriggerTests.cs +++ b/src/DotNext.Tests/Threading/AsyncTriggerTests.cs @@ -65,38 +65,6 @@ public static async Task SignalEmptyQueue() await ThrowsAsync(trigger.SignalAndWaitAsync(true, true).AsTask); } - [Obsolete] - private sealed class TestTransition : AsyncTrigger>.ITransition - { - bool AsyncTrigger>.ITransition.Test(StrongBox state) - => state.Value == 42; - - void AsyncTrigger>.ITransition.Transit(StrongBox state) - => state.Value = 56; - } - - [Fact] - [Obsolete] - public static async Task Transitions() - { - using var trigger = new AsyncTrigger>(new()); - - trigger.State.Value = 64; - var task1 = trigger.WaitAsync(new TestTransition()); - False(task1.IsCompleted); - - trigger.Signal(static state => state.Value = 42); - - var task2 = trigger.WaitAsync(new TestTransition()); - False(task2.IsCompleted); - - await task1; - - trigger.CancelSuspendedCallers(new(true)); - - await ThrowsAsync(task2.AsTask); - } - private sealed class Condition : StrongBox, ISupplier { bool ISupplier.Invoke() => Value; diff --git a/src/DotNext.Threading/Threading/AsyncLazy.cs b/src/DotNext.Threading/Threading/AsyncLazy.cs index 82d9fff2f..889e69450 100644 --- a/src/DotNext.Threading/Threading/AsyncLazy.cs +++ b/src/DotNext.Threading/Threading/AsyncLazy.cs @@ -30,18 +30,6 @@ public AsyncLazy(T value) syncRoot = new(); } - /// - /// Initializes a new instance of lazy value. - /// - /// The function used to compute actual value. - /// if previously computed value can be removed and computation executed again when it will be requested; if value can be computed exactly once. - /// is . - [Obsolete("Use another constructor that accepts a factory with CancellationToken support.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - [ExcludeFromCodeCoverage] - public AsyncLazy(Func> valueFactory, bool resettable = false) - => throw new NotImplementedException(); - /// /// Initializes a new instance of lazy value. /// @@ -136,16 +124,6 @@ static Func> CreateAsyncFunc(Func> cancelable public Task WithCancellation(CancellationToken token) => Volatile.Read(ref task) is { IsCanceled: false } t ? t.WaitAsync(token) : GetOrStartAsync(token); - /// - /// Gets task representing asynchronous computation of lazy value. - /// - /// - [DebuggerBrowsable(DebuggerBrowsableState.Never)] - [EditorBrowsable(EditorBrowsableState.Never)] - [Obsolete("Use WithCancellation(CancellationToken) method instead.", error: true)] - [ExcludeFromCodeCoverage] - public Task Task => WithCancellation(CancellationToken.None); - /// /// Removes already computed value from the current object. /// @@ -165,26 +143,6 @@ public bool Reset() return result; } - /// - /// Gets awaiter for the asynchronous operation responsible for computing value. - /// - /// The task awaiter. - [Obsolete("Use WithCancellation(CancellationToken) method instead.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - [ExcludeFromCodeCoverage] - public TaskAwaiter GetAwaiter() => Task.GetAwaiter(); - - /// - /// Configures an awaiter used to await asynchronous lazy initialization. - /// - /// to attempt to marshal the continuation back to the original context captured; otherwise, . - /// An object used to await asynchronous lazy initialization. - [Obsolete("Use WithCancellation(CancellationToken) method instead.", error: true)] - [EditorBrowsable(EditorBrowsableState.Never)] - [ExcludeFromCodeCoverage] - public ConfiguredTaskAwaitable ConfigureAwait(bool continueOnCapturedContext) - => Task.ConfigureAwait(continueOnCapturedContext); - /// /// Returns textual representation of this object. /// diff --git a/src/DotNext.Threading/Threading/AsyncLock.cs b/src/DotNext.Threading/Threading/AsyncLock.cs index 62ffd5770..5ba4285fe 100644 --- a/src/DotNext.Threading/Threading/AsyncLock.cs +++ b/src/DotNext.Threading/Threading/AsyncLock.cs @@ -303,15 +303,6 @@ private readonly ValueTask TryAcquireCoreAsync(TimeSpan timeout, Cancellat public readonly async ValueTask TryAcquireAsync(TimeSpan timeout, CancellationToken token = default) => await TryAcquireCoreAsync(timeout, token).ConfigureAwait(false) ? CreateHolder() : default; - /// - /// Tries to acquire lock asynchronously. - /// - /// The token that can be used to abort acquisition operation. - /// The task returning the acquired lock holder; or empty lock holder if operation was canceled. - [Obsolete("Use AcquireAsync(CancellationToken) instead.")] - public readonly ValueTask TryAcquireAsync(CancellationToken token) - => TryAcquireAsync(InfiniteTimeSpan, token); - /// /// Destroy this lock and dispose underlying lock object if it is owned by the given lock. /// diff --git a/src/DotNext.Threading/Threading/AsyncLockAcquisition.cs b/src/DotNext.Threading/Threading/AsyncLockAcquisition.cs index a09485f93..55c8b2db0 100644 --- a/src/DotNext.Threading/Threading/AsyncLockAcquisition.cs +++ b/src/DotNext.Threading/Threading/AsyncLockAcquisition.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; namespace DotNext.Threading; @@ -8,10 +7,8 @@ namespace DotNext.Threading; /// public static class AsyncLockAcquisition { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot ReaderWriterLock = new(); - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot ExclusiveLock = new(); [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -126,79 +123,4 @@ private static AsyncLock GetExclusiveLock(this T obj) /// The acquired lock holder. public static ValueTask AcquireWriteLockAsync(this T obj, CancellationToken token) where T : class => AsyncLock.WriteLock(obj.GetReaderWriterLock()).AcquireAsync(token); - - /// - /// Suspends if the target lock - /// has been disposed. - /// - /// - /// This method is usually combined with or - /// calls - /// to avoid if the lock is already disposed - /// at the time of the call. If the lock is disposed then this method returns empty . - /// - /// The result of the lock acquisition. - /// The task representing the lock acquisition. - [Obsolete("Catch exception manually instead.")] - public static async ValueTask SuppressDisposedState(this ValueTask result) - { - try - { - return await result.ConfigureAwait(false); - } - catch (ObjectDisposedException) - { - return default; - } - } - - /// - /// Suspends cancellation of lock acquisition and converts the canceled operation result - /// into unsuccessfully acquired lock. - /// - /// - /// This method is usually combined with or - /// calls - /// to avoid if the lock acquisition is already canceled - /// at the time of the call. If the acqusition is canceled then this method returns empty . - /// - /// The result of the lock acquisition. - /// The task representing the lock acquisition. - [Obsolete("Catch exception manually instead.")] - public static async ValueTask SuppressCancellation(this ValueTask result) - { - try - { - return await result.ConfigureAwait(false); - } - catch (OperationCanceledException) - { - return default; - } - } - - /// - /// Suspends cancellation of lock acquisition or if the target lock - /// has been disposed. - /// - /// - /// This method is usually combined with or - /// calls - /// to replace or - /// with empty . - /// - /// The result of the lock acquisition. - /// The task representing the lock acquisition. - [Obsolete("Catch exception manually instead.")] - public static async ValueTask SuppressDisposedStateOrCancellation(this ValueTask result) - { - try - { - return await result.ConfigureAwait(false); - } - catch (Exception e) when (e is OperationCanceledException or ObjectDisposedException) - { - return default; - } - } } \ No newline at end of file diff --git a/src/DotNext.Threading/Threading/AsyncTrigger.cs b/src/DotNext.Threading/Threading/AsyncTrigger.cs index b2e453277..5d16cef13 100644 --- a/src/DotNext.Threading/Threading/AsyncTrigger.cs +++ b/src/DotNext.Threading/Threading/AsyncTrigger.cs @@ -333,272 +333,4 @@ ValueTask ISupplier.Invoke(TimeSpan time ValueTask ISupplier>.Invoke(TimeSpan timeout, CancellationToken tpken) => ValueTask.FromException(new InvalidOperationException(ExceptionMessages.EmptyWaitQueue)); } -} - -/// -/// Represents asynchronous trigger that allows to resume and suspend -/// concurrent flows. -/// -/// The type of the state used for coordination. -[Obsolete("Use QueuedSynchronizer instead.")] -public class AsyncTrigger : QueuedSynchronizer - where TState : class -{ - /// - /// Represents state transition. - /// - public interface ITransition - { - /// - /// Tests whether the state can be changed. - /// - /// The state to check. - /// if transition is allowed; otherwise, . - bool Test(TState state); - - /// - /// Do transition. - /// - /// The state to modify. - void Transit(TState state); - } - - private new sealed class WaitNode : QueuedSynchronizer.WaitNode, IPooledManualResetCompletionSource> - { - internal ITransition? Transition; - - protected override void AfterConsumed() => AfterConsumed(this); - - protected override void Cleanup() - { - Transition = null; - base.Cleanup(); - } - - Action? IPooledManualResetCompletionSource>.OnConsumed { get; set; } - } - - [StructLayout(LayoutKind.Auto)] - private readonly struct LockManager : ILockManager - { - private readonly ITransition transition; - private readonly TState state; - - internal LockManager(TState state, ITransition transition) - { - this.transition = transition; - this.state = state; - } - - bool ILockManager.IsLockAllowed => transition.Test(state); - - void ILockManager.AcquireLock() => transition.Transit(state); - - void ILockManager.InitializeNode(WaitNode node) - => node.Transition = transition; - } - - private ValueTaskPool> pool; - - /// - /// Initializes a new trigger. - /// - /// The coordination state. - public AsyncTrigger(TState state) - { - State = state ?? throw new ArgumentNullException(nameof(state)); - pool = new(OnCompleted); - } - - /// - /// Initializes a new trigger. - /// - /// The coordination state. - /// The expected number of concurrent flows. - /// is less than or equal to zero. - public AsyncTrigger(TState state, int concurrencyLevel) - { - if (concurrencyLevel < 1) - throw new ArgumentOutOfRangeException(nameof(concurrencyLevel)); - - State = state ?? throw new ArgumentNullException(nameof(state)); - pool = new(OnCompleted, concurrencyLevel); - } - - private void OnCompleted(WaitNode node) - { - LinkedValueTaskCompletionSource? suspendedCallers; - lock (SyncRoot) - { - suspendedCallers = node.NeedsRemoval && RemoveNode(node) - ? DrainWaitQueue() - : null; - - pool.Return(node); - } - - suspendedCallers?.Unwind(); - } - - /// - /// Gets state of this trigger. - /// - public TState State { get; } - - private LinkedValueTaskCompletionSource? DrainWaitQueue() - { - Debug.Assert(Monitor.IsEntered(SyncRoot)); - Debug.Assert(WaitQueueHead is null or WaitNode); - - var detachedQueue = new LinkedValueTaskCompletionSource.LinkedList(); - - for (WaitNode? current = Unsafe.As(WaitQueueHead), next; current is not null; current = next) - { - Debug.Assert(current.Next is null or WaitNode); - - next = Unsafe.As(current.Next); - - if (current.IsCompleted || current.Transition is not { } transition) - { - RemoveNode(current); - continue; - } - - if (!transition.Test(State)) - break; - - if (RemoveAndSignal(current, out var resumable)) - transition.Transit(State); - - if (resumable) - detachedQueue.Add(current); - } - - return detachedQueue.First; - } - - /// - /// Performs unconditional transition. - /// - /// This trigger has been disposed. - public void Signal() - { - ThrowIfDisposed(); - - LinkedValueTaskCompletionSource? suspendedCallers; - lock (SyncRoot) - { - suspendedCallers = DrainWaitQueue(); - - if (IsDisposing && IsReadyToDispose) - Dispose(true); - } - - suspendedCallers?.Unwind(); - } - - /// - /// Performs unconditional transition. - /// - /// The transition action. - /// This trigger has been disposed. - /// is . - public void Signal(Action transition) - { - ArgumentNullException.ThrowIfNull(transition); - - ThrowIfDisposed(); - - LinkedValueTaskCompletionSource? suspendedCallers; - lock (SyncRoot) - { - transition(State); - suspendedCallers = DrainWaitQueue(); - - if (IsDisposing && IsReadyToDispose) - Dispose(true); - } - - suspendedCallers?.Unwind(); - } - - /// - /// Performs unconditional transition. - /// - /// The type of the argument to be passed to the transition. - /// The transition action. - /// The argument to be passed to the transition. - /// This trigger has been disposed. - /// is . - public void Signal(Action transition, T arg) - { - ArgumentNullException.ThrowIfNull(transition); - - ThrowIfDisposed(); - LinkedValueTaskCompletionSource? suspendedCallers; - lock (SyncRoot) - { - transition(State, arg); - suspendedCallers = DrainWaitQueue(); - - if (IsDisposing && IsReadyToDispose) - Dispose(true); - } - - suspendedCallers?.Unwind(); - } - - /// - /// Performs conditional transition synchronously. - /// - /// The condition to be examined immediately. - /// The result of invocation. - /// This trigger has been disposed. - /// is . - public bool TrySignal(ITransition transition) - { - ArgumentNullException.ThrowIfNull(transition); - - ThrowIfDisposed(); - lock (SyncRoot) - { - var manager = new LockManager(State, transition); - return TryAcquire(ref manager); - } - } - - /// - /// Performs conditional transition asynchronously. - /// - /// The conditional transition. - /// The time to wait for the signal. - /// The token that can be used to cancel the operation. - /// if event is triggered in timely manner; if timeout occurred. - /// This trigger has been disposed. - /// The operation has been canceled. - /// is . - /// - public ValueTask WaitAsync(ITransition transition, TimeSpan timeout, CancellationToken token = default) - { - var manager = new LockManager(State, transition); - return TryAcquireAsync(ref pool, ref manager, new TimeoutAndCancellationToken(timeout, token)); - } - - /// - /// Suspends the caller and waits for the signal. - /// - /// The conditional transition. - /// The token that can be used to cancel the operation. - /// The task representing asynchronous execution of this method. - /// This trigger has been disposed. - /// The operation has been canceled. - /// is . - /// - public ValueTask WaitAsync(ITransition transition, CancellationToken token = default) - { - var manager = new LockManager(State, transition); - return AcquireAsync(ref pool, ref manager, new CancellationTokenOnly(token)); - } - - private protected sealed override bool IsReadyToDispose => WaitQueueHead is null; -} +} \ No newline at end of file diff --git a/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs b/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs index 96d523ca0..85559becb 100644 --- a/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs +++ b/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs @@ -36,16 +36,10 @@ protected PersistentChannel(PersistentChannelOptions options) if (!location.Exists) location.Create(); var writer = new PersistentChannelWriter(this, options.SingleWriter, options.InitialPartitionSize); -#pragma warning disable CS0618 - var reader = new PersistentChannelReader(this, options.SingleReader, options.ReliableEnumeration, options.ReadRateCounter); -#pragma warning restore CS0618 + var reader = new PersistentChannelReader(this, options.SingleReader, options.ReliableEnumeration); Reader = reader; Writer = writer; readTrigger = new AsyncCounter(writer.Position - reader.Position); -#pragma warning disable CS0618 - writeRate = options.WriteRateCounter; -#pragma warning restore CS0618 - completionTask = new(TaskCreationOptions.RunContinuationsAsynchronously); measurementTags = options.MeasurementTags; IChannel.SetTags(ref measurementTags, location.FullName); diff --git a/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs b/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs index 70567a2fd..618c4b778 100644 --- a/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs +++ b/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs @@ -65,26 +65,6 @@ public long InitialPartitionSize set; } - /// - /// Specifies counter for write operations. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? WriteRateCounter - { - get; - set; - } - - /// - /// Specifiies counter for read operations. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? ReadRateCounter - { - get; - set; - } - /// /// Gets or sets a list of tags to be associated with each measurement. /// diff --git a/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs b/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs index 452c4f824..b0ed386ad 100644 --- a/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs +++ b/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs @@ -58,13 +58,12 @@ private sealed class MultipleReadersBuffer : ConcurrentQueue, IReadBuffer private readonly IReadBuffer buffer; private readonly FileStreamFactory fileFactory; private readonly IChannelReader reader; - private readonly IncrementingEventCounter? readRate; private readonly bool reliableEnumeration; private AsyncLock readLock; private Partition? readTopic; private ChannelCursor cursor; - internal PersistentChannelReader(IChannelReader reader, bool singleReader, bool reliableEnumeration, IncrementingEventCounter? readRate) + internal PersistentChannelReader(IChannelReader reader, bool singleReader, bool reliableEnumeration) { this.reader = reader; if (singleReader) @@ -87,7 +86,6 @@ internal PersistentChannelReader(IChannelReader reader, bool singleReader, bo }; cursor = new(reader.Location, StateFileName); - this.readRate = readRate; this.reliableEnumeration = reliableEnumeration; } @@ -105,7 +103,6 @@ internal PersistentChannelReader(IChannelReader reader, bool singleReader, bo public override bool TryRead([MaybeNullWhen(false)] out T item) { var result = buffer.TryRead(out item); - readRate?.Increment(); IChannel.ReadRateMeter.Add(1, reader.MeasurementTags); return result; } @@ -134,9 +131,7 @@ public override async ValueTask ReadAsync(CancellationToken token) await EndReadAsync(readTopic.Stream.Position, token).ConfigureAwait(false); } - readRate?.Increment(); IChannel.ReadRateMeter.Add(1, reader.MeasurementTags); - return result; } @@ -256,7 +251,6 @@ public async ValueTask MoveNextAsync() else { await reader.EndReadAsync(offset).ConfigureAwait(false); - reader.readRate?.Increment(); IChannel.ReadRateMeter.Add(1, reader.reader.MeasurementTags); dryRun = false; } diff --git a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs index 955af0129..272c78807 100644 --- a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs +++ b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs @@ -27,7 +27,6 @@ public class QueuedSynchronizer : Disposable private static readonly Counter SuspendedCallersMeter; private static readonly Histogram LockDurationMeter; - private readonly Action? contentionCounter, lockDurationCounter; private readonly TagList measurementTags; private readonly TaskCompletionSource disposeTask; private CallerInformationStorage? callerInfo; @@ -132,31 +131,12 @@ public void SetCallerInformation(object information) public IReadOnlyList GetSuspendedCallers() => callerInfo is null ? Array.Empty() : GetSuspendedCallersCore(); - /// - /// Sets counter for lock contention. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? LockContentionCounter - { - init => contentionCounter = value is not null ? value.Increment : null; - } - - /// - /// Sets counter of lock duration, in milliseconds. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public EventCounter? LockDurationCounter - { - init => lockDurationCounter = value is not null ? value.WriteMetric : null; - } - private protected bool RemoveNode(LinkedValueTaskCompletionSource node) => waitQueue.Remove(node); private protected void EnqueueNode(WaitNode node) { waitQueue.Add(node); - contentionCounter?.Invoke(1D); SuspendedCallersMeter.Add(1, measurementTags); } @@ -530,9 +510,7 @@ private protected static void AfterConsumed(T node) // report lock duration if (node.owner.TryGetTarget(out var owner)) { - var duration = node.createdAt.ElapsedMilliseconds; - owner.lockDurationCounter?.Invoke(duration); - LockDurationMeter.Record(duration, owner.measurementTags); + LockDurationMeter.Record(node.createdAt.ElapsedMilliseconds, owner.measurementTags); } node.OnConsumed?.Invoke(node); diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs b/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs index 1a7caef06..db87b468a 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/IUnmanagedArray.cs @@ -45,7 +45,7 @@ ref T this[nint index] /// /// The array allocated in managed heap containing copied elements from unmanaged memory. /// The underlying unmanaged memory has been released. - T[] ToArray() => Pointer.ToArray(Length); + T[] ToArray() => Pointer.ToArray((uint)Length); /// T[] ISupplier.Invoke() => ToArray(); diff --git a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs index dd60ad631..c7c0f37b8 100644 --- a/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs +++ b/src/DotNext.Unsafe/Runtime/InteropServices/Pointer.cs @@ -288,17 +288,6 @@ public unsafe void Clear(nuint count) /// This pointer is equal to zero. public void Clear() => Value = default; - /// - /// Copies block of memory from the source address to the destination address. - /// - /// Destination address. - /// The number of elements to be copied. - /// This pointer is equal to zero. - /// Destination pointer is zero. - [Obsolete("Use CopyTo method instead")] - public void WriteTo(Pointer destination, long count) - => CopyTo(destination, new IntPtr(count)); - /// /// Copies a block of memory identifier by this pointer to the specified location. /// @@ -346,31 +335,6 @@ public unsafe void CopyTo(Pointer destination, nuint count) Intrinsics.Copy(in value[0], out destination.value[0], count); } - /// - /// Copies elements from the memory location identified - /// by this pointer to managed array. - /// - /// The array to be modified. - /// The position in the destination array from which copying begins. - /// The number of elements of type to be copied. - /// Actual number of copied elements. - /// This pointer is equal to zero. - /// or is less than zero. - [Obsolete("Use CopyTo method instead")] - public unsafe long WriteTo(T[] destination, long offset, long count) - { - if (IsNull) - ThrowNullPointerException(); - if (count < 0L) - throw new ArgumentOutOfRangeException(nameof(count)); - if (offset < 0L) - throw new ArgumentOutOfRangeException(nameof(offset)); - if (count is 0L || (ulong)(offset + count) > (ulong)destination.LongLength) - return 0L; - Intrinsics.Copy(in value[0], out destination[offset], (nuint)count); - return count; - } - /// /// Copies bytes from the memory location identified by this pointer to the stream. /// @@ -434,31 +398,6 @@ static async ValueTask WriteToAsync(nint source, long length, Stream destination } } - /// - /// Copies elements from the specified array into - /// the memory block identified by this pointer. - /// - /// The source array. - /// The position in the source array from which copying begins. - /// The number of elements of type to be copied. - /// Actual number of copied elements. - /// This pointer is equal to zero. - /// or is less than zero. - [Obsolete("Use CopyTo method instead")] - public unsafe long ReadFrom(T[] source, long offset, long count) - { - if (IsNull) - ThrowNullPointerException(); - if (count < 0L) - throw new ArgumentOutOfRangeException(nameof(count)); - if (offset < 0L) - throw new ArgumentOutOfRangeException(nameof(offset)); - if (count is 0L || (ulong)(count + offset) > (ulong)source.LongLength) - return 0L; - Intrinsics.Copy(in source[offset], out value[0], (nuint)count); - return count; - } - /// /// Copies bytes from the given stream to the memory location identified by this pointer. /// @@ -570,24 +509,6 @@ public unsafe Stream AsStream(long count, FileAccess access = FileAccess.ReadWri return new UnmanagedMemoryStream((byte*)value, count, count, access); } - /// - /// Copies the block of memory referenced by this pointer - /// into managed heap as array of bytes. - /// - /// Number of elements to copy. - /// A copy of memory block in the form of byte array. - [Obsolete("Use ToByteArray overload with native-sized integer parameter")] - public byte[] ToByteArray(long length) => ToByteArray(new IntPtr(length)); - - /// - /// Copies the block of memory referenced by this pointer - /// into managed heap as array of bytes. - /// - /// Number of elements to copy. - /// A copy of memory block in the form of byte array. - public byte[] ToByteArray(nint length) - => length >= 0 ? ToByteArray((nuint)length) : throw new ArgumentOutOfRangeException(nameof(length)); - /// /// Copies the block of memory referenced by this pointer /// into managed heap as array of bytes. @@ -599,38 +520,20 @@ public unsafe byte[] ToByteArray(nuint length) { byte[] result; - if (IsNull || length is 0) + if (IsNull || length is 0U) { - result = Array.Empty(); + result = []; } else { length = checked((nuint)sizeof(T) * length); - result = new byte[length]; - Intrinsics.Copy(in ((byte*)value)[0], out MemoryMarshal.GetArrayDataReference(result), length); + result = length <= (nuint)Array.MaxLength ? GC.AllocateUninitializedArray((int)length, pinned: true) : new byte[length]; + Intrinsics.Copy(in Unsafe.AsRef(value), out MemoryMarshal.GetArrayDataReference(result), length); } return result; } - /// - /// Copies the block of memory referenced by this pointer - /// into managed heap as a pinned array. - /// - /// The length of the memory block to be copied. - /// The array containing elements from the memory block referenced by this pointer. - [Obsolete("Use ToByteArray overload with native-sized integer parameter")] - public T[] ToArray(long length) => ToArray(new IntPtr(length)); - - /// - /// Copies the block of memory referenced by this pointer - /// into managed heap as a pinned array. - /// - /// The length of the memory block to be copied. - /// The array containing elements from the memory block referenced by this pointer. - public T[] ToArray(nint length) - => length >= 0 ? ToArray((nuint)length) : throw new ArgumentOutOfRangeException(nameof(length)); - /// /// Copies the block of memory referenced by this pointer /// into managed heap as a pinned array. @@ -644,7 +547,7 @@ public unsafe T[] ToArray(nuint length) if (IsNull || length is 0) { - result = Array.Empty(); + result = []; } else { diff --git a/src/DotNext/Collections/Generic/AsyncEnumerable.cs b/src/DotNext/Collections/Generic/AsyncEnumerable.cs index 81fd4a04b..8e3aca7e4 100644 --- a/src/DotNext/Collections/Generic/AsyncEnumerable.cs +++ b/src/DotNext/Collections/Generic/AsyncEnumerable.cs @@ -73,19 +73,6 @@ public static async ValueTask ForEachAsync(this IAsyncEnumerable collectio return result; } - /// - /// Obtains first value in the sequence; or - /// if sequence is empty. - /// - /// Type of elements in the sequence. - /// A sequence to check. Cannot be . - /// The token that can be used to cancel enumeration. - /// The first element in the sequence; or if sequence is empty. - /// The operation has been canceled. - [Obsolete("Use FirstOrNoneAsync() extension method instead")] - public static ValueTask> FirstOrEmptyAsync(this IAsyncEnumerable seq, CancellationToken token = default) - => FirstOrNoneAsync(seq, token); - /// /// Obtains first element in the sequence; or /// if sequence is empty. @@ -120,19 +107,6 @@ public static async ValueTask> LastOrNoneAsync(this IAsyncEnumera return result; } - /// - /// Returns the first element in a sequence that satisfies a specified condition. - /// - /// The type of the elements of source. - /// A collection to return an element from. - /// A function to test each element for a condition. - /// The token that can be used to cancel enumeration. - /// The first element in the sequence that matches to the specified filter; or empty value. - /// The operation has been canceled. - [Obsolete("Use FirstOrNoneAsync() extension method instead")] - public static ValueTask> FirstOrEmptyAsync(this IAsyncEnumerable seq, Predicate filter, CancellationToken token = default) - => FirstOrNoneAsync(seq, filter); - /// /// Returns the first element in a sequence that satisfies a specified condition. /// diff --git a/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs b/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs index 78628a2b3..6359dc82f 100644 --- a/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs +++ b/src/DotNext/Collections/Specialized/ConcurrentTypeMap.cs @@ -313,16 +313,6 @@ private bool Set(int index, TValue newValue, [MaybeNullWhen(false)] out TValue o public bool Set(TValue newValue, [MaybeNullWhen(false)] out TValue oldValue) => Set(ITypeMap.GetIndex(), newValue, out oldValue); - /// - /// Replaces the existing value with a new value. - /// - /// The type acting as a key. - /// A new value. - /// The replaced value. - [Obsolete("Use Set overload instead")] - public Optional Replace(TValue newValue) - => Set(ITypeMap.GetIndex(), newValue, out var oldValue) ? Optional.Some(oldValue!) : Optional.None(); - private bool Remove(int index, [MaybeNullWhen(false)] out TValue value) { for (Entry[] entries; ;) diff --git a/src/DotNext/Collections/Specialized/ITypeMap.cs b/src/DotNext/Collections/Specialized/ITypeMap.cs index 3ce0e46af..e5f97670d 100644 --- a/src/DotNext/Collections/Specialized/ITypeMap.cs +++ b/src/DotNext/Collections/Specialized/ITypeMap.cs @@ -34,16 +34,6 @@ public interface ITypeMap : IReadOnlyTypeMap /// if value is replaced; if a new value is added without replacement. bool Set(TValue newValue, [MaybeNullWhen(false)] out TValue oldValue); - /// - /// Replaces the existing value with a new value. - /// - /// The type acting as a key. - /// A new value. - /// The replaced value. - [Obsolete("Use Set overload instead")] - Optional Replace(TValue newValue) - => Set(newValue, out var oldValue) ? Optional.Some(oldValue!) : Optional.None(); - /// /// Attempts to remove the value from the map. /// diff --git a/src/DotNext/Collections/Specialized/TypeMap.cs b/src/DotNext/Collections/Specialized/TypeMap.cs index b0e58dafd..bb40654bc 100644 --- a/src/DotNext/Collections/Specialized/TypeMap.cs +++ b/src/DotNext/Collections/Specialized/TypeMap.cs @@ -134,16 +134,6 @@ private bool Set(int index, TValue newValue, [MaybeNullWhen(false)] out TValue o public bool Set(TValue newValue, [MaybeNullWhen(false)] out TValue oldValue) => Set(ITypeMap.GetIndex(), newValue, out oldValue); - /// - /// Replaces the existing value with a new value. - /// - /// The type acting as a key. - /// A new value. - /// The replaced value. - [Obsolete("Use Set overload instead")] - public Optional Replace(TValue newValue) - => Set(ITypeMap.GetIndex(), newValue, out var oldValue) ? Optional.Some(oldValue!) : Optional.None(); - /// /// Determines whether the map has association between the value and the specified type. /// diff --git a/src/DotNext/Diagnostics/Timestamp.cs b/src/DotNext/Diagnostics/Timestamp.cs index 4a168c867..fb505f459 100644 --- a/src/DotNext/Diagnostics/Timestamp.cs +++ b/src/DotNext/Diagnostics/Timestamp.cs @@ -58,12 +58,6 @@ public Timestamp(TimeSpan ts) /// public bool IsPast => ticks < GetTimestamp(); - /// - /// Gets the current point in time. - /// - [Obsolete("Use public parameterless constructor instead")] - public static Timestamp Current => new(); - private static long ToTicks(double duration) => unchecked((long)(TickFrequency * duration)); diff --git a/src/DotNext/Predicate.cs b/src/DotNext/Predicate.cs index 8cb5e3a87..e642c14c4 100644 --- a/src/DotNext/Predicate.cs +++ b/src/DotNext/Predicate.cs @@ -72,28 +72,6 @@ public static Predicate Constant(bool value) static bool False(T value) => false; } - /// - /// Returns a predicate which always returns . - /// - /// Type of predicate argument. - /// A predicate which always returns . - /// - /// This method returns the same instance of predicate on every call. - /// - [Obsolete("Use Constant method instead.")] - public static Predicate True() => Constant(value: true); - - /// - /// Returns a predicate which always returns . - /// - /// Type of predicate argument. - /// A predicate which always returns . - /// - /// This method returns the same instance of predicate on every call. - /// - [Obsolete("Use Constant method instead.")] - public static Predicate False() => Constant(value: false); - /// /// Represents predicate as type . /// diff --git a/src/DotNext/Runtime/CompilerServices/Shared.cs b/src/DotNext/Runtime/CompilerServices/Shared.cs deleted file mode 100644 index c507bb20d..000000000 --- a/src/DotNext/Runtime/CompilerServices/Shared.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; - -namespace DotNext.Runtime.CompilerServices; - -/// -/// Represents container for value type. -/// -/// The value type. -[EditorBrowsable(EditorBrowsableState.Advanced)] -[Obsolete("Use BoxedValue data type instead")] -public sealed class Shared - where T : struct -{ - /// - /// Represents a value in the container. - /// - public T Value; - - /// - /// Boxes nullable value type. - /// - /// The value to be placed to the container. - /// The boxed representation of the value; or if is . - [return: NotNullIfNotNull(nameof(value))] - public static implicit operator Shared?(in T? value) - => value.HasValue ? new() { Value = value.GetValueOrDefault() } : null; - - /// - /// Places the value to the container. - /// - /// The value to be placed to the container. - /// The boxed representation of the value. - public static implicit operator Shared(T value) => new() { Value = value }; - - /// - /// Converts the value in this container to string. - /// - /// converted to the string. - public override string? ToString() => Value.ToString(); -} \ No newline at end of file diff --git a/src/DotNext/StringExtensions.cs b/src/DotNext/StringExtensions.cs index 04f8e3c51..42aaa327b 100644 --- a/src/DotNext/StringExtensions.cs +++ b/src/DotNext/StringExtensions.cs @@ -9,23 +9,6 @@ namespace DotNext; /// public static class StringExtensions { - /// - /// Returns alternative string if first string argument - /// is or empty. - /// - /// - /// This method is equivalent to the following code: - /// - /// var result = string.IsNullOrEmpty(str) ? alt : str; - /// - /// - /// A string to check. - /// Alternative string to be returned if original string is or empty. - /// Original or alternative string. - [Obsolete("This method is easily replaceable with pattern matching: expression is { Length: > 0 } str ? str : alt")] - public static string IfNullOrEmpty(this string? str, string alt) - => str is { Length: > 0 } ? str : alt; - /// /// Reverse string characters. /// diff --git a/src/DotNext/Text/Base64Decoder.Unicode.cs b/src/DotNext/Text/Base64Decoder.Unicode.cs deleted file mode 100644 index fb0f0e4df..000000000 --- a/src/DotNext/Text/Base64Decoder.Unicode.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Buffers; - -namespace DotNext.Text; - -using Buffers; -using NewBase64Decoder = Buffers.Text.Base64Decoder; - -public partial struct Base64Decoder -{ - /// - /// Decodes base64 characters. - /// - /// The span containing base64-encoded bytes. - /// The output growable buffer used to write decoded bytes. - /// is . - /// The input base64 string is malformed. - public void Decode(ReadOnlySpan chars, IBufferWriter output) - => decoder.DecodeFromUtf16(chars, output); - - /// - /// Decodes base64 characters. - /// - /// The span containing base64-encoded bytes. - /// The output growable buffer used to write decoded bytes. - /// is . - /// The input base64 string is malformed. - public void Decode(in ReadOnlySequence chars, IBufferWriter output) - => decoder.DecodeFromUtf16(in chars, output); - - /// - /// Decodes base64 characters. - /// - /// The span containing base64-encoded bytes. - /// The allocator of the result buffer. - /// The input base64 string is malformed. - /// A buffer containing decoded bytes. - public MemoryOwner Decode(ReadOnlySpan chars, MemoryAllocator? allocator = null) - => decoder.DecodeFromUtf16(chars, allocator); - - /// - /// Decodes base64-encoded bytes. - /// - /// The type of the consumer. - /// The span containing base64-encoded bytes. - /// The consumer called for decoded portion of data. - /// The input base64 string is malformed. - public void Decode(ReadOnlySpan chars, TConsumer output) - where TConsumer : notnull, IReadOnlySpanConsumer - => decoder.DecodeFromUtf16(chars, output); - - /// - /// Decodes base64-encoded bytes. - /// - /// The type of the argument to be passed to the callback. - /// The span containing base64-encoded bytes. - /// The callback called for decoded portion of data. - /// The argument to be passed to the callback. - /// The input base64 string is malformed. - public void Decode(ReadOnlySpan chars, ReadOnlySpanAction callback, TArg arg) - => decoder.DecodeFromUtf16(chars, callback, arg); - - /// - /// Decodes base64-encoded bytes. - /// - /// The type of the argument to be passed to the callback. - /// The span containing base64-encoded bytes. - /// The callback called for decoded portion of data. - /// The argument to be passed to the callback. - /// The input base64 string is malformed. - [CLSCompliant(false)] - public unsafe void Decode(ReadOnlySpan chars, delegate*, TArg, void> callback, TArg arg) - => decoder.DecodeFromUtf16(chars, callback, arg); - - /// - /// Decodes a sequence of base64-encoded bytes. - /// - /// A sequence of base64-encoded bytes. - /// The allocator of the buffer used for decoded bytes. - /// The token that can be used to cancel the operation. - /// A sequence of decoded bytes. - /// The input base64 string is malformed. - /// The operation has been canceled. - public static IAsyncEnumerable> DecodeAsync(IAsyncEnumerable> chars, MemoryAllocator? allocator = null, CancellationToken token = default) - => NewBase64Decoder.DecodeFromUtf16Async(chars, allocator, token); -} \ No newline at end of file diff --git a/src/DotNext/Text/Base64Decoder.Utf8.cs b/src/DotNext/Text/Base64Decoder.Utf8.cs deleted file mode 100644 index 6a411a668..000000000 --- a/src/DotNext/Text/Base64Decoder.Utf8.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Buffers; - -namespace DotNext.Text; - -using Buffers; -using NewBase64Decoder = Buffers.Text.Base64Decoder; - -public partial struct Base64Decoder -{ - /// - /// Decodes UTF-8 encoded base64 string. - /// - /// UTF-8 encoded portion of base64 string. - /// The output growable buffer used to write decoded bytes. - /// is . - /// The input base64 string is malformed. - public void Decode(ReadOnlySpan utf8Chars, IBufferWriter output) - => decoder.DecodeFromUtf8(utf8Chars, output); - - /// - /// Decodes UTF-8 encoded base64 string. - /// - /// UTF-8 encoded portion of base64 string. - /// The output growable buffer used to write decoded bytes. - /// is . - /// The input base64 string is malformed. - public void Decode(in ReadOnlySequence utf8Chars, IBufferWriter output) - => decoder.DecodeFromUtf8(in utf8Chars, output); - - /// - /// Decoes UTF-8 encoded base64 string. - /// - /// UTF-8 encoded portion of base64 string. - /// The allocator of the result buffer. - /// A buffer containing decoded bytes. - public MemoryOwner Decode(ReadOnlySpan utf8Chars, MemoryAllocator? allocator = null) - => decoder.DecodeFromUtf8(utf8Chars, allocator); - - /// - /// Decodes base64-encoded bytes. - /// - /// The type of the consumer. - /// The span containing base64-encoded bytes. - /// The consumer called for decoded portion of data. - /// The input base64 string is malformed. - public void Decode(ReadOnlySpan utf8Chars, TConsumer output) - where TConsumer : notnull, IReadOnlySpanConsumer - => decoder.DecodeFromUtf8(utf8Chars, output); - - /// - /// Decodes UTF-8 encoded base64 string. - /// - /// The type of the argument to be passed to the callback. - /// UTF-8 encoded portion of base64 string. - /// The callback called for decoded portion of data. - /// The argument to be passed to the callback. - /// The input base64 string is malformed. - public void Decode(ReadOnlySpan utf8Chars, ReadOnlySpanAction output, TArg arg) - => decoder.DecodeFromUtf8(utf8Chars, output, arg); - - /// - /// Decodes UTF-8 encoded base64 string. - /// - /// The type of the argument to be passed to the callback. - /// UTF-8 encoded portion of base64 string. - /// The callback called for decoded portion of data. - /// The argument to be passed to the callback. - /// The input base64 string is malformed. - [CLSCompliant(false)] - public unsafe void Decode(ReadOnlySpan utf8Chars, delegate*, TArg, void> output, TArg arg) - => decoder.DecodeFromUtf8(utf8Chars, output, arg); - - /// - /// Decodes UTF-8 encoded base64 string and writes result to the stream synchronously. - /// - /// UTF-8 encoded portion of base64 string. - /// The stream used as destination for decoded bytes. - /// The input base64 string is malformed. - public void Decode(ReadOnlySpan utf8Chars, Stream output) - => decoder.DecodeFromUtf8(utf8Chars, output); - - /// - /// Decodes a sequence of base64-encoded bytes. - /// - /// A sequence of base64-encoded bytes. - /// The allocator of the buffer used for decoded bytes. - /// The token that can be used to cancel the operation. - /// A sequence of decoded bytes. - /// The input base64 string is malformed. - /// The operation has been canceled. - public static IAsyncEnumerable> DecodeAsync(IAsyncEnumerable> utf8Chars, MemoryAllocator? allocator = null, CancellationToken token = default) - => NewBase64Decoder.DecodeFromUtf8Async(utf8Chars, allocator, token); -} \ No newline at end of file diff --git a/src/DotNext/Text/Base64Decoder.cs b/src/DotNext/Text/Base64Decoder.cs deleted file mode 100644 index 2bd7822bc..000000000 --- a/src/DotNext/Text/Base64Decoder.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Text; - -using NewBase64Decoder = Buffers.Text.Base64Decoder; - -/// -/// Represents base64 decoder suitable for decoding large base64-encoded binary -/// data using streaming approach. -/// -/// -/// This type maintains internal state for correct decoding of streaming data. -/// Therefore, it must be passed by reference to any routine. It's not a ref struct -/// to allow construction of high-level decoders in the form of classes. -/// Base64-encoded bytes can be accepted as UTF-8 or Unicode characters. -/// Decoding methods should not be intermixed by the caller code. -/// -[StructLayout(LayoutKind.Auto)] -[Obsolete("Use DotNext.Buffers.Text.Base64Decoder type instead.")] -public partial struct Base64Decoder -{ - private NewBase64Decoder decoder; - - /// - /// Indicates that decoders expected additional data to decode. - /// - public readonly bool NeedMoreData => decoder.NeedMoreData; - - /// - /// Resets the internal state of the decoder. - /// - public void Reset() => decoder.Reset(); -} \ No newline at end of file diff --git a/src/DotNext/Text/Base64Encoder.Unicode.cs b/src/DotNext/Text/Base64Encoder.Unicode.cs deleted file mode 100644 index 4d05c7225..000000000 --- a/src/DotNext/Text/Base64Encoder.Unicode.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System.Buffers; -using System.Text; - -namespace DotNext.Text; - -using Buffers; -using NewBase64Encoder = Buffers.Text.Base64Encoder; - -public partial struct Base64Encoder -{ - /// - /// Encodes a block of bytes to base64-encoded characters. - /// - /// A block of bytes to encode. - /// The output buffer. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - /// is . - /// The length of is greater than . - public void EncodeToChars(ReadOnlySpan bytes, IBufferWriter output, bool flush = false) - => encoder.EncodeToChars(bytes, output, flush); - - /// - /// Encodes a block of bytes to base64-encoded characters. - /// - /// A block of bytes to encode. - /// The allocator of the result buffer. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - /// The buffer containing encoded bytes. - /// The length of is greater than . - public MemoryOwner EncodeToChars(ReadOnlySpan bytes, MemoryAllocator? allocator = null, bool flush = false) - => encoder.EncodeToChars(bytes, allocator, flush); - - /// - /// Encodes a block of bytes to base64-encoded characters. - /// - /// The type of the consumer. - /// A block of bytes to encode. - /// The consumer called for encoded portion of data. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - public void EncodeToChars(ReadOnlySpan bytes, TConsumer output, bool flush = false) - where TConsumer : notnull, IReadOnlySpanConsumer - => encoder.EncodeToChars(bytes, output, flush); - - /// - /// Encodes a block of bytes to base64-encoded characters. - /// - /// The type of the argument to be passed to the callback. - /// A block of bytes to encode. - /// The consumer called for encoded portion of data. - /// The argument to be passed to the callback. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - public void EncodeToChars(ReadOnlySpan bytes, ReadOnlySpanAction output, TArg arg, bool flush = false) - => encoder.EncodeToChars(bytes, output, arg, flush); - - /// - /// Encodes a block of bytes to base64-encoded characters. - /// - /// The type of the argument to be passed to the callback. - /// A block of bytes to encode. - /// The consumer called for encoded portion of data. - /// The argument to be passed to the callback. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - [CLSCompliant(false)] - public unsafe void EncodeToChars(ReadOnlySpan bytes, delegate*, TArg, void> output, TArg arg, bool flush = false) - => encoder.EncodeToChars(bytes, output, arg, flush); - - /// - /// Encodes a block of bytes to base64-encoded characters. - /// - /// A block of bytes to encode. - /// The writer used as a destination for encoded data. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - public void EncodeToChars(ReadOnlySpan bytes, TextWriter output, bool flush = false) - => encoder.EncodeToChars(bytes, output, flush); - - /// - /// Encodes a block of bytes to base64-encoded characters. - /// - /// A block of bytes to encode. - /// The builder used as a destination for encoded data. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - public void EncodeToChars(ReadOnlySpan bytes, StringBuilder output, bool flush = false) - => encoder.EncodeToChars(bytes, output, flush); - - /// - /// Encodes a sequence of bytes to characters using base64 encoding. - /// - /// A collection of buffers. - /// Characters buffer allocator. - /// The token that can be used to cancel the encoding. - /// A collection of encoded bytes. - /// The operation has been canceled. - public static IAsyncEnumerable> EncodeToCharsAsync(IAsyncEnumerable> bytes, MemoryAllocator? allocator = null, CancellationToken token = default) - => NewBase64Encoder.EncodeToCharsAsync(bytes, allocator, token); - - /// - /// Flushes the buffered data as base64-encoded characters to the output buffer. - /// - /// The buffer of characters. - /// The number of written characters. - public int Flush(Span output) - => encoder.Flush(output); -} \ No newline at end of file diff --git a/src/DotNext/Text/Base64Encoder.Utf8.cs b/src/DotNext/Text/Base64Encoder.Utf8.cs deleted file mode 100644 index fda1298fd..000000000 --- a/src/DotNext/Text/Base64Encoder.Utf8.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System.Buffers; - -namespace DotNext.Text; - -using Buffers; -using NewBase64Encoder = Buffers.Text.Base64Encoder; - -public partial struct Base64Encoder -{ - /// - /// Encodes a block of bytes to base64-encoded UTF-8 characters. - /// - /// A block of bytes to encode. - /// The output buffer. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - /// is . - /// The length of is greater than . - public void EncodeToUtf8(ReadOnlySpan bytes, IBufferWriter output, bool flush = false) - => encoder.EncodeToUtf8(bytes, output, flush); - - /// - /// Encodes a block of bytes to base64-encoded UTF-8 characters. - /// - /// A block of bytes to encode. - /// The allocator of the result buffer. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - /// The buffer containing encoded bytes. - /// The length of is greater than . - public MemoryOwner EncodeToUtf8(ReadOnlySpan bytes, MemoryAllocator? allocator = null, bool flush = false) - => encoder.EncodeToUtf8(bytes, allocator, flush); - - /// - /// Encodes a block of bytes to base64-encoded UTF-8 characters. - /// - /// The type of the consumer. - /// A block of bytes to encode. - /// The consumer called for encoded portion of data. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - public void EncodeToUtf8(ReadOnlySpan bytes, TConsumer output, bool flush = false) - where TConsumer : notnull, IReadOnlySpanConsumer - => encoder.EncodeToUtf8(bytes, output, flush); - - /// - /// Encodes a block of bytes to base64-encoded UTF-8 characters. - /// - /// The type of the argument to be passed to the callback. - /// A block of bytes to encode. - /// The consumer called for encoded portion of data. - /// The argument to be passed to the callback. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - public void EncodeToUtf8(ReadOnlySpan bytes, ReadOnlySpanAction output, TArg arg, bool flush = false) - => encoder.EncodeToUtf8(bytes, output, arg, flush); - - /// - /// Encodes a block of bytes to base64-encoded UTF-8 characters. - /// - /// The type of the argument to be passed to the callback. - /// A block of bytes to encode. - /// The consumer called for encoded portion of data. - /// The argument to be passed to the callback. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - [CLSCompliant(false)] - public unsafe void EncodeToUtf8(ReadOnlySpan bytes, delegate*, TArg, void> output, TArg arg, bool flush = false) - => encoder.EncodeToUtf8(bytes, output, arg, flush); - - /// - /// Encodes a block of bytes to base64-encoded UTF-8 characters. - /// - /// A block of bytes to encode. - /// The stream used as a destination for encoded data. - /// - /// to encode the final block and insert padding if necessary; - /// to encode a fragment without padding. - /// - public void EncodeToUtf8(ReadOnlySpan bytes, Stream output, bool flush = false) - => encoder.EncodeToUtf8(bytes, output, flush); - - /// - /// Encodes a sequence of bytes to characters using base64 encoding. - /// - /// A collection of buffers. - /// Characters buffer allocator. - /// The token that can be used to cancel the encoding. - /// A collection of encoded bytes. - /// The operation has been canceled. - public static IAsyncEnumerable> EncodeToUtf8Async(IAsyncEnumerable> bytes, MemoryAllocator? allocator = null, CancellationToken token = default) - => NewBase64Encoder.EncodeToUtf8Async(bytes, allocator, token); - - /// - /// Flushes the buffered data as base64-encoded UTF-8 characters to the output buffer. - /// - /// The output buffer of size 4. - /// The number of written bytes. - public int Flush(Span output) - => encoder.Flush(output); -} \ No newline at end of file diff --git a/src/DotNext/Text/Base64Encoder.cs b/src/DotNext/Text/Base64Encoder.cs deleted file mode 100644 index e2ba27aa1..000000000 --- a/src/DotNext/Text/Base64Encoder.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Runtime.InteropServices; - -namespace DotNext.Text; - -using NewBase64Encoder = Buffers.Text.Base64Encoder; - -/// -/// Represents base64 encoder suitable for encoding large binary -/// data using streaming approach. -/// -/// -/// This type maintains internal state for correct encoding of streaming data. -/// Therefore, it must be passed by reference to any routine. It's not a ref struct -/// to allow construction of high-level encoders in the form of classes. -/// The output can be in the form of UTF-8 encoded bytes or Unicode characters. -/// Encoding methods should not be intermixed by the caller code. -/// -[StructLayout(LayoutKind.Auto)] -[Obsolete("Use DotNext.Buffers.Text.Base64Encoder type instead.")] -public partial struct Base64Encoder -{ - /// - /// Gets the maximum number of bytes that can be buffered by the encoder. - /// - public const int MaxBufferedDataSize = NewBase64Encoder.MaxBufferedDataSize; - - /// - /// Gets the maximum number of characters that can be produced by - /// or methods. - /// - public const int MaxCharsToFlush = NewBase64Encoder.MaxCharsToFlush; - - /// - /// Gets the maximum size of the input block of bytes to encode. - /// - public const int MaxInputSize = NewBase64Encoder.MaxInputSize; - - private NewBase64Encoder encoder; - - /// - /// Indicates that the size of the encoded data is not a multiple of 3 - /// and the encoder. - /// - public readonly bool HasBufferedData => encoder.HasBufferedData; - - /// - /// Gets the number of buffered bytes. - /// - /// - /// The range of the returned value is [0..]. - /// - public readonly int BufferedDataSize => encoder.BufferedDataSize; - - /// - /// Gets the buffered data. - /// - /// The output buffer. - /// The number of bytes copied to . - /// is not large enough. - public readonly int GetBufferedData(Span output) - => encoder.GetBufferedData(output); - - /// - /// Resets the internal state of the encoder. - /// - public void Reset() => encoder.Reset(); -} \ No newline at end of file diff --git a/src/DotNext/UserDataSlot.cs b/src/DotNext/UserDataSlot.cs index 3a5a69e65..bb2e0a46b 100644 --- a/src/DotNext/UserDataSlot.cs +++ b/src/DotNext/UserDataSlot.cs @@ -35,17 +35,10 @@ internal static string ToString(int typeIndex, int valueIndex) internal int ValueIndex => valueIndex - 1; - /// - /// Allocates a new data slot. - /// - /// Allocated data slot. - [Obsolete("Use public constructor to allocate the slot")] - public static UserDataSlot Allocate() => new(); - /// /// Gets a value indicating that this object was constructed using constructor. /// - public bool IsAllocated => valueIndex != 0; + public bool IsAllocated => valueIndex is not 0; /// /// Gets textual representation of this data slot diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/ClusterMemberConfiguration.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/ClusterMemberConfiguration.cs index b43fbe9bd..0b02fc5cb 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/ClusterMemberConfiguration.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/ClusterMemberConfiguration.cs @@ -66,16 +66,6 @@ public double ClockDriftBound /// ElectionTimeout IClusterMemberConfiguration.ElectionTimeout => electionTimeout; - /// - /// Indicates that each part of cluster in partitioned network allow to elect its own leader. - /// - /// - /// value allows to build CA distributed cluster - /// while value allows to build CP/AP distributed cluster. - /// - [Obsolete("This property is no longer supported.", error: true)] - public bool Partitioning { get; set; } - /// /// Gets metadata associated with local cluster member. /// diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs index d23475db4..77e605063 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs @@ -106,7 +106,7 @@ private static Optional ParseHeaderCore(IDictionary? { foreach (var header in values) { - if (parser(header, out var result)) + if (header is not null && parser(header, out var result)) return result; } } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMetricsCollector.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMetricsCollector.cs deleted file mode 100644 index e46dc31ad..000000000 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMetricsCollector.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Diagnostics.Tracing; - -namespace DotNext.Net.Cluster.Consensus.Raft.Http; - -using IClientMetricsCollector = Metrics.IClientMetricsCollector; - -/// -/// Contains a set of callbacks that can be used to report -/// runtime metrics generated by HTTP-specific implementation of Raft cluster node. -/// -/// -/// You need to register singleton service of type -/// to collect metrics produced by Raft node. -/// -[Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] -public class HttpMetricsCollector : MetricsCollector, IClientMetricsCollector -{ - /// - public virtual void ReportResponseTime(TimeSpan value) - { - } - - /// - void IClientMetricsCollector.ReportResponseTime(TimeSpan value) - { - ResponseTimeCounter?.WriteMetric(value.TotalMilliseconds); - ReportResponseTime(value); - } - - /// - /// Gets or sets counter that allows to count response time from every cluster node. - /// - public EventCounter? ResponseTimeCounter - { - get; - set; - } -} \ No newline at end of file diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/IHttpMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/IHttpMessage.cs index 82fcf08ac..0cdbda1db 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/IHttpMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/IHttpMessage.cs @@ -1,11 +1,9 @@ -using System.Runtime.Versioning; using HttpStatusCode = System.Net.HttpStatusCode; namespace DotNext.Net.Cluster.Consensus.Raft.Http; internal interface IHttpMessage { - [RequiresPreviewFeatures] static abstract string MessageType { get; } void PrepareRequest(HttpRequestMessage request); @@ -14,7 +12,6 @@ internal interface IHttpMessage /// Interprets produced by HTTP client. /// /// to handle the response as . - [RequiresPreviewFeatures] static abstract bool IsMemberUnavailable(HttpStatusCode? code); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs index 8462acb15..d9697e8ff 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs @@ -89,9 +89,6 @@ public RaftClusterHttpHost(IServiceProvider activationContext, string loggingCat configurator: activationContext.GetService(), configStorage: activationContext.GetService>(), httpHandlerFactory: activationContext.GetService(), -#pragma warning disable CS0618 - metrics: activationContext.GetService(), -#pragma warning restore CS0618 announcer: activationContext.GetService>()) { FailureDetectorFactory = activationContext.GetService>(), diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs index dade85ed3..c413ddea2 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs @@ -10,7 +10,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.Http; using Messaging; using Net.Http; using Runtime.Serialization; -using IClientMetricsCollector = Metrics.IClientMetricsCollector; using Timestamp = Diagnostics.Timestamp; internal sealed class RaftClusterMember : HttpPeerClient, IRaftClusterMember, ISubscriber @@ -30,9 +29,6 @@ internal sealed class RaftClusterMember : HttpPeerClient, IRaftClusterMember, IS private InvocationList>> memberStatusChanged; private IRaftClusterMember.ReplicationState state; - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.")] - internal IClientMetricsCollector? Metrics; - static RaftClusterMember() { var meter = new Meter("DotNext.Net.Cluster.Consensus.Raft.Client"); @@ -137,14 +133,9 @@ private async Task SendAsync(TMessage message, C request.Dispose(); var responseTime = timeStamp.ElapsedMilliseconds; -#pragma warning disable CS0618 - Metrics?.ReportResponseTime(TimeSpan.FromMilliseconds(responseTime)); -#pragma warning restore CS0618 ResponseTimeMeter.Record( responseTime, -#pragma warning disable CA2252 new(IRaftClusterMember.MessageTypeAttributeName, TMessage.MessageType), -#pragma warning restore CA2252 cachedRemoteAddressAttribute); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftHttpCluster.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftHttpCluster.cs index 133fd19ec..3fe0c08e8 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftHttpCluster.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftHttpCluster.cs @@ -17,13 +17,12 @@ namespace DotNext.Net.Cluster.Consensus.Raft.Http; using Net.Http; using IFailureDetector = Diagnostics.IFailureDetector; using HttpProtocolVersion = Net.Http.HttpProtocolVersion; -using IClientMetricsCollector = Metrics.IClientMetricsCollector; [SuppressMessage("Performance", "CA1812", Justification = "This class is instantiated by DI container")] internal sealed partial class RaftHttpCluster : RaftCluster, IRaftHttpCluster, IHostedService, IHostingContext { private readonly IClusterMemberLifetime? configurator; - private readonly IDisposable configurationTracker; + private readonly IDisposable? configurationTracker; private readonly IHttpMessageHandlerFactory? httpHandlerFactory; private readonly TimeSpan requestTimeout, raftRpcTimeout, connectTimeout; private readonly bool openConnectionForEachRequest, coldStart; @@ -43,12 +42,9 @@ public RaftHttpCluster( IPersistentState? auditTrail = null, IClusterConfigurationStorage? configStorage = null, IHttpMessageHandlerFactory? httpHandlerFactory = null, -#pragma warning disable CS0618 - MetricsCollector? metrics = null, -#pragma warning restore CS0618 ClusterMemberAnnouncer? announcer = null, Func? failureDetectorFactory = null) - : this(config, messageHandlers, loggerFactory.CreateLogger(), configurator, auditTrail, configStorage, httpHandlerFactory, metrics, announcer) + : this(config, messageHandlers, loggerFactory.CreateLogger(), configurator, auditTrail, configStorage, httpHandlerFactory, announcer) { FailureDetectorFactory = failureDetectorFactory; } @@ -61,9 +57,6 @@ internal RaftHttpCluster( IPersistentState? auditTrail = null, IClusterConfigurationStorage? configStorage = null, IHttpMessageHandlerFactory? httpHandlerFactory = null, -#pragma warning disable CS0618 - MetricsCollector? metrics = null, -#pragma warning restore CS0618 ClusterMemberAnnouncer? announcer = null) : base(config.CurrentValue, GetMeasurementTags(config.CurrentValue, out var localNode)) { @@ -94,9 +87,6 @@ internal RaftHttpCluster( ConfigurationStorage = configStorage ?? new InMemoryClusterConfigurationStorage(); this.httpHandlerFactory = httpHandlerFactory; Logger = logger; -#pragma warning disable CS0618 - Metrics = metrics; -#pragma warning restore CS0618 this.announcer = announcer; // track changes in configuration, do not track membership @@ -123,9 +113,6 @@ private RaftClusterMember CreateMember(UriEndPoint address) var result = new RaftClusterMember(this, address) { Timeout = requestTimeout, -#pragma warning disable CS0618 - Metrics = Metrics as IClientMetricsCollector, -#pragma warning restore CS0618 }; result.DefaultRequestHeaders.ConnectionClose = openConnectionForEachRequest; @@ -142,7 +129,7 @@ private RaftClusterMember CreateMember(UriEndPoint address) ISubscriber? IMessageBus.Leader => Leader; - private void ConfigurationChanged(HttpClusterMemberConfiguration configuration, string name) + private void ConfigurationChanged(HttpClusterMemberConfiguration configuration, string? name) { metadata = new(configuration.Metadata); } @@ -248,7 +235,7 @@ protected override void Dispose(bool disposing) { if (disposing) { - configurationTracker.Dispose(); + configurationTracker?.Dispose(); duplicationDetector.Dispose(); messageHandlers = ImmutableList.Empty; configurationEvents.Writer.TryComplete(new ObjectDisposedException(GetType().Name)); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs index 5c8da99cf..cbf10f312 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/ConsensusOnlyState.cs @@ -371,14 +371,6 @@ ValueTask IAuditTrail.WaitForCommitAsync(CancellationToken token) ValueTask IAuditTrail.WaitForCommitAsync(long index, CancellationToken token) => commitEvent.SpinWaitAsync(new CommitChecker(this, index), token); - /// - [Obsolete("Use IRaftCluster.ApplyReadBarrierAsync instead.")] - async ValueTask IPersistentState.EnsureConsistencyAsync(CancellationToken token) - { - while (Term != Volatile.Read(in lastTerm)) - await commitEvent.WaitAsync(token).ConfigureAwait(false); - } - /// /// Releases all resources associated with this audit trail. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs index afd7e13ee..cda151bcd 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs @@ -13,7 +13,6 @@ internal sealed class FollowerState : RaftState private readonly AsyncManualResetEvent suppressionEvent; private readonly CancellationTokenSource trackerCancellation; private Task? tracker; - internal IFollowerStateMetrics? Metrics; private volatile bool timedOut; internal FollowerState(IRaftStateMachine stateMachine) @@ -82,7 +81,6 @@ internal void Refresh() { Logger.TimeoutReset(); refreshEvent.Set(); - Metrics?.ReportHeartbeat(); FollowerState.HeartbeatRateMeter.Add(1, MeasurementTags); } @@ -111,7 +109,6 @@ protected override void Dispose(bool disposing) suppressionEvent.Dispose(); trackerCancellation.Dispose(); tracker = null; - Metrics = null; } base.Dispose(disposing); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IClusterMemberConfiguration.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IClusterMemberConfiguration.cs index 07c81930d..3a0579ca7 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IClusterMemberConfiguration.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IClusterMemberConfiguration.cs @@ -7,12 +7,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; /// public interface IClusterMemberConfiguration { - /// - /// Indicates that each part of cluster in partitioned network allow to elect its own leader. - /// - [Obsolete("This property is no longer supported.", error: true)] - bool Partitioning => false; - /// /// Gets or sets threshold of the heartbeat timeout. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IPersistentState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IPersistentState.cs index 6ab9ae1c2..e44dc9cbb 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IPersistentState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IPersistentState.cs @@ -47,16 +47,6 @@ public interface IPersistentState : IO.Log.IAuditTrail /// The task representing state of the asynchronous execution. ValueTask UpdateVotedForAsync(ClusterMemberId member); - /// - /// Suspens the caller until the log entry with term equal to - /// will be committed. - /// - /// The token that can be used to cancel the operation. - /// The task representing state of the asynchronous execution. - /// The operation has been canceled. - [Obsolete("Use IRaftCluster.ApplyReadBarrierAsync instead.")] - ValueTask EnsureConsistencyAsync(CancellationToken token = default); - internal static bool IsVotedFor(BoxedClusterMemberId? lastVote, in ClusterMemberId expected) => lastVote is null || lastVote.Value == expected; } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IRaftCluster.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IRaftCluster.cs index da143716f..386c42a4f 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IRaftCluster.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/IRaftCluster.cs @@ -28,12 +28,6 @@ public interface IRaftCluster : IReplicationCluster, IPeerMesh IReadOnlyCollection Members { get; } - /// - /// Establishes metrics collector. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - MetricsCollector Metrics { init; } - /// /// Defines persistent state for the Raft-based cluster. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs index c4050e82b..65131ac8a 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs @@ -39,12 +39,6 @@ internal LeaderState(IRaftStateMachine stateMachine, long term, TimeSpa replicatorFactory = localReplicatorFactory = CreateDefaultReplicator; } - internal ILeaderStateMetrics? Metrics - { - private get; - init; - } - private (long, long, int) ForkHeartbeats(TaskCompletionPipe>> responsePipe, IAuditTrail auditTrail, IClusterConfigurationStorage configurationStorage, IEnumerator members, CancellationToken token) { Replicator replicator; @@ -217,7 +211,6 @@ private async Task DoHeartbeats(TimeSpan period, IAuditTrail audi if (forced) GCSettings.LatencyMode = latencyMode; - Metrics?.ReportBroadcastTime(TimeSpan.FromMilliseconds(broadcastTime)); LeaderState.BroadcastTimeMeter.Record(broadcastTime, MeasurementTags); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs index d039c6fe4..6c5e436be 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs @@ -129,15 +129,5 @@ public LogEntryCacheEvictionPolicy CacheEvictionPolicy get; set; } - - /// - /// Gets or sets the counter used to measure the number of squashed log entries. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? CompactionCounter - { - get; - set; - } } } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.SnapshotBuilder.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.SnapshotBuilder.cs index 33a0ac2d7..e5fca9439 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.SnapshotBuilder.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.SnapshotBuilder.cs @@ -171,13 +171,6 @@ internal sealed override async ValueTask BuildAsync(long snaps /// protected abstract class InlineSnapshotBuilder : SnapshotBuilder { - /// - /// Gets the offset from the start of the snapshot file that is reserved - /// and should not be used for storing data. - /// - [Obsolete("Snapshot payload has no offset within the file")] - protected static long SnapshotOffset => 0L; - /// /// Initializes a new snapshot builder. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs index bdca1e946..c11831557 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs @@ -41,7 +41,6 @@ public abstract partial class MemoryBasedStateMachine : PersistentState private readonly CompactionMode compaction; private readonly bool replayOnInitialize, evictOnCommit; private readonly int snapshotBufferSize; - private readonly Action? compactionCounter; private long lastTerm; // term of last committed entry, volatile @@ -65,10 +64,6 @@ protected MemoryBasedStateMachine(DirectoryInfo path, int recordsPerPartition, O // with concurrent compaction, we will release cached log entries according to partition lifetime evictOnCommit = compaction is not CompactionMode.Incremental && configuration.CacheEvictionPolicy is LogEntryCacheEvictionPolicy.OnCommit; -#pragma warning disable CS0618 - compactionCounter = ToDelegate(configuration.CompactionCounter); -#pragma warning restore CS0618 - snapshot = new(path, snapshotBufferSize, in bufferManager, concurrentReads, configuration.WriteMode, initialSize: configuration.InitialPartitionSize); } @@ -112,10 +107,7 @@ private async ValueTask BuildSnapshotAsync(int sessionId, long upperBoundIndex, await ApplyIfNotEmptyAsync(builder, current.Read(sessionId, currentIndex)).ConfigureAwait(false); } - // update counter - var count = upperBoundIndex - SnapshotInfo.Index; - compactionCounter?.Invoke(count); - CompactionRateMeter.Add(count, measurementTags); + CompactionRateMeter.Add(upperBoundIndex - SnapshotInfo.Index, measurementTags); } private bool TryGetPartition(SnapshotBuilder builder, long startIndex, long endIndex, ref long currentIndex, [NotNullWhen(true)] ref Partition? partition) @@ -611,10 +603,7 @@ private async Task ForceIncrementalCompactionAsync(long upperBoundIndex, C incrementalBuilder.Builder.RefreshTimestamp(); UpdateSnapshotInfo(await incrementalBuilder.Builder.BuildAsync(upperBoundIndex).ConfigureAwait(false)); - var count = upperBoundIndex - SnapshotInfo.Index; - compactionCounter?.Invoke(count); - CompactionRateMeter.Add(count, measurementTags); - + CompactionRateMeter.Add(upperBoundIndex - SnapshotInfo.Index, measurementTags); return true; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Metrics/IClientMetricsCollector.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Metrics/IClientMetricsCollector.cs deleted file mode 100644 index 0ade993d1..000000000 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Metrics/IClientMetricsCollector.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace DotNext.Net.Cluster.Consensus.Raft.Metrics; - -/// -/// Optional interface that can be implemented by class derived from -/// to collect metrics about response time reported -/// by the Raft client. -/// -[Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] -public interface IClientMetricsCollector -{ - /// - /// Reports about response time. - /// - /// The response time. - void ReportResponseTime(TimeSpan value); -} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MetricsCollector.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MetricsCollector.cs deleted file mode 100644 index 6e66a475e..000000000 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MetricsCollector.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System.Diagnostics.Tracing; - -namespace DotNext.Net.Cluster.Consensus.Raft; - -internal interface ILeaderStateMetrics -{ - void ReportBroadcastTime(TimeSpan value); -} - -internal interface IFollowerStateMetrics -{ - void ReportHeartbeat(); -} - -/// -/// Contains a set of callbacks that can be used to report -/// runtime metrics generated by Raft cluster node. -/// -[Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] -public class MetricsCollector : ILeaderStateMetrics, IFollowerStateMetrics -{ - /// - /// Reports about broadcast time. - /// - /// - /// Broadcast time is the time spent accessing the cluster nodes caused by Leader states. - /// - /// The broadcast time. - public virtual void ReportBroadcastTime(TimeSpan value) - { - } - - /// - /// Sets counter that allows to track the broadcast time. - /// - public EventCounter? BroadcastTimeCounter - { - private get; - init; - } - - /// - void ILeaderStateMetrics.ReportBroadcastTime(TimeSpan value) - { - BroadcastTimeCounter?.WriteMetric(value.TotalMilliseconds); - ReportBroadcastTime(value); - } - - /// - /// Reports that node becomes a candidate. - /// - public virtual void MovedToCandidateState() - => CandidateStateCounter?.Increment(); - - /// - /// Sets counter that allows to track the number of transitions to candidate state. - /// - public IncrementingEventCounter? CandidateStateCounter - { - init; - private get; - } - - /// - /// Reports that node becomes a follower. - /// - public virtual void MovedToFollowerState() - => FollowerStateCounter?.Increment(); - - /// - /// Sets counter that allows to track the number of transitions to follower state. - /// - public IncrementingEventCounter? FollowerStateCounter - { - private get; - init; - } - - /// - /// Reports that node becomes a leader. - /// - public virtual void MovedToLeaderState() - => LeaderStateCounter?.Increment(); - - /// - /// Sets counter that allows to track the number of transitions to leader state. - /// - public IncrementingEventCounter? LeaderStateCounter - { - private get; - init; - } - - /// - /// Reports that node receives a heartbeat from leader node. - /// - public virtual void ReportHeartbeat() - { - } - - /// - /// Sets counter that allows to track the number of received hearbeats from leader state. - /// - public IncrementingEventCounter? HeartbeatCounter - { - private get; - init; - } - - /// - void IFollowerStateMetrics.ReportHeartbeat() - { - HeartbeatCounter?.Increment(); - ReportHeartbeat(); - } -} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs index d1e34d24a..4e7a1f0f3 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs @@ -49,19 +49,6 @@ public class Options : IBufferManagerSettings private long partitionSize; private bool parallelIO; - /// - /// Gets or sets a value indicating usage of intermediate buffers during I/O. - /// - /// - /// to bypass intermediate buffers for disk writes. - /// - [Obsolete("Use WriteMode property instead.")] - public bool WriteThrough - { - get => WriteMode is WriteMode.WriteThrough; - set => WriteMode = value ? WriteMode.WriteThrough : WriteMode.NoFlush; - } - /// /// Gets or sets a value indicating how the log interacts with underlying storage device. /// @@ -157,56 +144,6 @@ public RaftLogEntriesBufferingOptions? CopyOnReadOptions set; } - /// - /// Gets or sets lock contention counter. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? LockContentionCounter - { - get; - set; - } - - /// - /// Gets or sets lock duration counter. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public EventCounter? LockDurationCounter - { - get; - set; - } - - /// - /// Gets or sets the counter used to measure the number of retrieved log entries. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? ReadCounter - { - get; - set; - } - - /// - /// Gets or sets the counter used to measure the number of written log entries. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? WriteCounter - { - get; - set; - } - - /// - /// Gets or sets the counter used to measure the number of committed log entries. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public IncrementingEventCounter? CommitCounter - { - get; - set; - } - /// /// Gets or sets a list of tags to be associated with each measurement. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs index f0041895b..2e598c013 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs @@ -30,9 +30,6 @@ public abstract partial class PersistentState : Disposable, IPersistentState private protected readonly WriteMode writeMode; private readonly bool parallelIO; - // diagnostic counters - private readonly Action? readCounter, writeCounter, commitCounter; - static PersistentState() { var meter = new Meter("DotNext.IO.WriteAheadLog"); @@ -65,10 +62,6 @@ private protected PersistentState(DirectoryInfo path, int recordsPerPartition, O syncRoot = new(configuration.MaxConcurrentReads) { MeasurementTags = configuration.MeasurementTags, -#pragma warning disable CS0618 - LockContentionCounter = configuration.LockContentionCounter, - LockDurationCounter = configuration.LockDurationCounter, -#pragma warning restore CS0618 }; var partitionTable = new SortedSet(Comparer.Create(ComparePartitions)); @@ -102,21 +95,11 @@ private protected PersistentState(DirectoryInfo path, int recordsPerPartition, O partitionTable.Clear(); state = new(path, bufferManager.BufferAllocator, configuration.IntegrityCheck, writeMode is not WriteMode.NoFlush); - - // counters -#pragma warning disable CS0618 - readCounter = ToDelegate(configuration.ReadCounter); - writeCounter = ToDelegate(configuration.WriteCounter); - commitCounter = ToDelegate(configuration.CommitCounter); -#pragma warning restore CS0618 measurementTags = configuration.MeasurementTags; static int ComparePartitions(Partition x, Partition y) => x.PartitionNumber.CompareTo(y.PartitionNumber); } - private protected static Action? ToDelegate(IncrementingEventCounter? counter) - => counter is null ? null : counter.Increment; - private protected static Meter MeterRoot => ReadRateMeter.Meter; /// @@ -191,7 +174,6 @@ private ValueTask UnsafeReadAsync(ILogEntryConsumer int.MaxValue) return ValueTask.FromException(new InternalBufferOverflowException(ExceptionMessages.RangeTooBig)); - readCounter?.Invoke(length); ReadRateMeter.Add(length, measurementTags); SingletonList list; @@ -362,9 +344,7 @@ private protected ValueTask UnsafeAppendAsync(ILogEntryProducer result = new(Task.WhenAll(bufferingSupplier.BufferizeAsync(), AppendUncachedAsync(bufferingSupplier, startIndex, skipCommitted, token))); } - writeCounter?.Invoke(count); WriteRateMeter.Add(count, measurementTags); - return result; } @@ -515,7 +495,6 @@ private async ValueTask UnsafeAppendAsync(TEntry entry, long startIndex, state.LastIndex = startIndex; await state.FlushAsync(in NodeState.IndexesRange).ConfigureAwait(false); - writeCounter?.Invoke(1D); WriteRateMeter.Add(1L, measurementTags); } @@ -616,9 +595,7 @@ private async ValueTask AppendUncachedAsync(TEntry entry, Cancella syncRoot.Release(LockType.WriteLock); } - writeCounter?.Invoke(1D); WriteRateMeter.Add(1L, measurementTags); - return startIndex; } @@ -644,9 +621,7 @@ private async ValueTask AppendCachedAsync(CachedLogEntry cachedEntry, Canc syncRoot.Release(LockType.WriteLock); } - writeCounter?.Invoke(1D); WriteRateMeter.Add(1L, measurementTags); - return startIndex; } @@ -873,29 +848,9 @@ private protected void OnCommit(long count) Debug.Assert(count > 0L); commitEvent.Signal(resumeAll: true); - commitCounter?.Invoke(count); CommitRateMeter.Add(count, measurementTags); } - /// - /// Suspends the caller until the log entry with term equal to - /// will be committed. - /// - /// The token that can be used to cancel the operation. - /// The task representing state of the asynchronous execution. - /// The operation has been canceled. - /// Timeout occurred. - [Obsolete("Use IRaftCluster.ApplyReadBarrierAsync instead.")] - public async ValueTask EnsureConsistencyAsync(CancellationToken token) - { - ThrowIfDisposed(); - - for (var condition = new DelegatingSupplier(IsConsistent); !IsConsistent();) - await commitEvent.SpinWaitAsync(condition, token).ConfigureAwait(false); - - bool IsConsistent() => state.Term == LastTerm && state.CommitIndex == state.LastApplied; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private protected long GetCommitIndexAndCount(in long? endIndex, out long commitIndex) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Configuration.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Configuration.cs index 379564764..6ff0d93d0 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Configuration.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.Configuration.cs @@ -18,7 +18,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; using TransportServices; using TransportServices.Datagram; using Udp; -using IClientMetricsCollector = Metrics.IClientMetricsCollector; public partial class RaftCluster { @@ -43,16 +42,6 @@ private protected NodeConfiguration() warmupRounds = 10; } - /// - /// Gets or sets metrics collector. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public MetricsCollector? Metrics - { - get; - set; - } - /// /// Gets the address used for hosting local member. /// @@ -90,16 +79,6 @@ public InMemoryClusterConfigurationStorage UseInMemoryConfigurationSto return storage; } - /// - /// Indicates that each part of cluster in partitioned network allow to elect its own leader. - /// - /// - /// value allows to build CA distributed cluster - /// while value allows to build CP/AP distributed cluster. - /// - [Obsolete("This property is no longer supported.", error: true)] - public bool Partitioning { get; set; } - /// /// Gets or sets threshold of the heartbeat timeout. /// @@ -206,9 +185,7 @@ public ILoggerFactory LoggerFactory /// public bool AggressiveLeaderStickiness { get; set; } -#pragma warning disable CS0618 - internal abstract RaftClusterMember CreateClient(ILocalMember localMember, EndPoint endPoint, IClientMetricsCollector? metrics); -#pragma warning restore CS0618 + internal abstract RaftClusterMember CreateClient(ILocalMember localMember, EndPoint endPoint); internal abstract IServer CreateServer(ILocalMember localMember); } @@ -272,10 +249,8 @@ public IEqualityComparer EndPointComparer /// IEqualityComparer IClusterMemberConfiguration.EndPointComparer => EndPointComparer; -#pragma warning disable CS0618 - internal override GenericClient CreateClient(ILocalMember localMember, EndPoint endPoint, IClientMetricsCollector? metrics) + internal override GenericClient CreateClient(ILocalMember localMember, EndPoint endPoint) => new(localMember, endPoint, clientFactory, MemoryAllocator) { ConnectTimeout = ConnectTimeout }; -#pragma warning restore CS0618 internal override GenericServer CreateServer(ILocalMember localMember) => new(HostEndPoint, serverFactory, localMember, MemoryAllocator, LoggerFactory) { ReceiveTimeout = RequestTimeout }; @@ -436,15 +411,12 @@ private UdpClient CreateClient(EndPoint address) return client; } -#pragma warning disable CS0618 - internal override ExchangePeer CreateClient(ILocalMember localMember, EndPoint endPoint, IClientMetricsCollector? metrics) + internal override ExchangePeer CreateClient(ILocalMember localMember, EndPoint endPoint) => new(localMember, endPoint, CreateClient) { RequestTimeout = RequestTimeout, - Metrics = metrics, PipeConfig = PipeConfig, }; -#pragma warning restore CS0618 internal override UdpServer CreateServer(ILocalMember localMember) { @@ -535,8 +507,7 @@ public TimeSpan ConnectTimeout set => connectTimeout = value > TimeSpan.Zero ? value : throw new ArgumentOutOfRangeException(nameof(value)); } -#pragma warning disable CS0618 - internal override TcpClient CreateClient(ILocalMember localMember, EndPoint endPoint, IClientMetricsCollector? metrics) + internal override TcpClient CreateClient(ILocalMember localMember, EndPoint endPoint) => new(localMember, endPoint, MemoryAllocator) { TransmissionBlockSize = TransmissionBlockSize, @@ -545,9 +516,7 @@ internal override TcpClient CreateClient(ILocalMember localMember, EndPoint endP SslOptions = SslOptions?.ClientOptions, RequestTimeout = RequestTimeout, ConnectTimeout = ConnectTimeout, - Metrics = metrics, }; -#pragma warning restore CS0618 internal override TcpServer CreateServer(ILocalMember localMember) => new(HostEndPoint, ServerBacklog, localMember, MemoryAllocator, LoggerFactory) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs index 110e67db4..9ed4dfd1f 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs @@ -12,7 +12,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; using Membership; using Microsoft.Extensions.Logging; using TransportServices; -using IClientMetricsCollector = Metrics.IClientMetricsCollector; /// /// Represents default implementation of Raft-based cluster. @@ -64,9 +63,7 @@ protected override void Dispose(bool disposing) } private readonly IReadOnlyDictionary metadata; -#pragma warning disable CS0618 - private readonly Func clientFactory; -#pragma warning restore CS0618 + private readonly Func clientFactory; private readonly Func serverFactory; private readonly MemoryAllocator? allocator; private readonly ClusterMemberAnnouncer? announcer; @@ -85,9 +82,6 @@ protected override void Dispose(bool disposing) public RaftCluster(NodeConfiguration configuration) : base(configuration, GetMeasurementTags(configuration)) { -#pragma warning disable CS0618 - Metrics = configuration.Metrics; -#pragma warning restore CS0618 metadata = new Dictionary(configuration.Metadata, StringComparer.Ordinal); // TODO: Migrate to FrozenDictionary in .NET 8 clientFactory = configuration.CreateClient; serverFactory = configuration.CreateServer; @@ -199,9 +193,7 @@ async Task StopAsync() } private RaftClusterMember CreateMember(EndPoint address) -#pragma warning disable CS0618 - => clientFactory.Invoke(this, address, Metrics as IClientMetricsCollector); -#pragma warning restore CS0618 + => clientFactory.Invoke(this, address); /// /// Announces a new member in the cluster. diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs index 49d299c7c..9b7703f3d 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs @@ -129,12 +129,6 @@ public Func? FailureDetectorFactory private TimeSpan LeaderLeaseDuration => TimeSpan.FromMilliseconds(electionTimeout / clockDriftBound); - /// - /// Indicates that the local member is a leader. - /// - [Obsolete("Use LeadershipToken property instead.")] - protected bool IsLeaderLocal => state is LeaderState; - /// /// Gets the lease that can be used for linearizable read. /// @@ -178,16 +172,6 @@ public IPersistentState AuditTrail /// IReadOnlyCollection IRaftCluster.Members => Members; - /// - /// Establishes metrics collector. - /// - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.", UrlFormat = "https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics")] - public MetricsCollector? Metrics - { - protected get; - init; - } - /// /// Gets Term value maintained by local member. /// @@ -305,9 +289,7 @@ private ValueTask UnfreezeAsync() async ValueTask UnfreezeCoreAsync() { -#pragma warning disable CS0618 - var newState = new FollowerState(this) { Metrics = Metrics }; -#pragma warning restore CS0618 + var newState = new FollowerState(this); await UpdateStateAsync(newState).ConfigureAwait(false); newState.StartServing(ElectionTimeout, LifecycleToken); readinessProbe.TrySetResult(); @@ -356,21 +338,6 @@ public virtual async Task StartAsync(CancellationToken token) /// protected void StartFollowing() => (state as FollowerState)?.StartServing(ElectionTimeout, LifecycleToken); - /// - /// Turns this node into regular state when the node can be elected as leader. - /// - /// - /// Initially, the node can be started in standby mode when it cannot be elected as a leader. - /// This can be helpful if you need to wait for full replication with existing leader node. - /// When replication finished, you can turn this node into regular state. - /// - /// The token that can be used to cancel the operation. - /// The task representing asynchronous execution of this operation. - /// This object has been disposed. - [Obsolete("Use RevertToNormalModeAsync(CancellationToken) method instead.")] - public async ValueTask TurnIntoRegularNodeAsync(CancellationToken token) - => await RevertToNormalModeAsync(token).ConfigureAwait(false); - /// public async ValueTask RevertToNormalModeAsync(CancellationToken token = default) { @@ -388,9 +355,7 @@ public async ValueTask RevertToNormalModeAsync(CancellationToken token = d // ensure that we trying to update the same state if (TryGetLocalMember() is not null && ReferenceEquals(state, standbyState)) { -#pragma warning disable CS0618 - var newState = new FollowerState(this) { Metrics = Metrics }; -#pragma warning restore CS0618 + var newState = new FollowerState(this); await UpdateStateAsync(newState).ConfigureAwait(false); newState.StartServing(ElectionTimeout, LifecycleToken); return true; @@ -530,14 +495,9 @@ private async ValueTask StepDown() followerState.Refresh(); break; case LeaderState or CandidateState: -#pragma warning disable CS0618 - var newState = new FollowerState(this) { Metrics = Metrics }; -#pragma warning restore CS0618 + var newState = new FollowerState(this); await UpdateStateAsync(newState).ConfigureAwait(false); newState.StartServing(ElectionTimeout, LifecycleToken); -#pragma warning disable CS0618 - Metrics?.MovedToFollowerState(); -#pragma warning restore CS0618 FollowerState.TransitionRateMeter.Add(1, in measurementTags); break; } @@ -829,9 +789,6 @@ protected async ValueTask> VoteAsync(ClusterMemberId sender, long s } else if (state is StandbyState) { -#pragma warning disable CS0618 - Metrics?.ReportHeartbeat(); -#pragma warning restore CS0618 FollowerState.HeartbeatRateMeter.Add(1, in measurementTags); } else @@ -874,9 +831,7 @@ protected async ValueTask ResignAsync(CancellationToken token) if (ReferenceEquals(state, leaderState)) { -#pragma warning disable CS0618 - var newState = new FollowerState(this) { Metrics = Metrics }; -#pragma warning restore CS0618 + var newState = new FollowerState(this); await UpdateStateAsync(newState).ConfigureAwait(false); Leader = null; newState.StartServing(ElectionTimeout, LifecycleToken); @@ -1054,9 +1009,6 @@ async void IRaftStateMachine.MoveToCandidateState(IRaftStateMachine.IWe // vote for self newState.StartVoting(ElectionTimeout, auditTrail); -#pragma warning disable CS0618 - Metrics?.MovedToCandidateState(); -#pragma warning restore CS0618 CandidateState.TransitionRateMeter.Add(1, in measurementTags); Logger.TransitionToCandidateStateCompleted(Term); } @@ -1111,9 +1063,6 @@ async void IRaftStateMachine.MoveToLeaderState(IRaftStateMachine.IWeakC { var newState = new LeaderState(this, currentTerm, LeaderLeaseDuration) { -#pragma warning disable CS0618 - Metrics = Metrics, -#pragma warning restore CS0618 FailureDetectorFactory = FailureDetectorFactory, }; @@ -1123,9 +1072,6 @@ async void IRaftStateMachine.MoveToLeaderState(IRaftStateMachine.IWeakC await auditTrail.AppendNoOpEntry(LifecycleToken).ConfigureAwait(false); newState.StartLeading(HeartbeatTimeout, auditTrail, ConfigurationStorage, LifecycleToken); -#pragma warning disable CS0618 - Metrics?.MovedToLeaderState(); -#pragma warning restore CS0618 LeaderState.TransitionRateMeter.Add(1, in measurementTags); Logger.TransitionToLeaderStateCompleted(currentTerm); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs index 267984621..fb3b13be7 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftClusterMember.cs @@ -7,8 +7,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; using Collections.Specialized; using Membership; -using Metrics; -using Threading; using TransportServices; /// @@ -18,8 +16,6 @@ public abstract class RaftClusterMember : Disposable, IRaftClusterMember { private protected static readonly Histogram ResponseTimeMeter = Raft.Metrics.Instrumentation.ClientSide.CreateHistogram("response-time", unit: "ms", description: "Response Time"); - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.")] - private readonly IClientMetricsCollector? metrics; private protected readonly ILocalMember localMember; private readonly TimeSpan requestTimeout; internal readonly ClusterMemberId Id; @@ -42,13 +38,6 @@ private protected RaftClusterMember(ILocalMember localMember, EndPoint endPoint) IsRemote = localMember.Id != Id; } - [Obsolete("Use System.Diagnostics.Metrics infrastructure instead.")] - internal IClientMetricsCollector? Metrics - { - get => metrics; - init => metrics = value; - } - internal TimeSpan RequestTimeout { get => requestTimeout; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs index 1d01e1468..fbf85ecf5 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs @@ -115,12 +115,8 @@ private async Task RequestAsync(TExchange excha if (lockTaken) accessLock.Release(); - var responseTime = timeStamp.ElapsedMilliseconds; -#pragma warning disable CS0618 - Metrics?.ReportResponseTime(TimeSpan.FromMilliseconds(responseTime)); -#pragma warning restore CS0618 ResponseTimeMeter.Record( - responseTime, + timeStamp.ElapsedMilliseconds, new(IRaftClusterMember.MessageTypeAttributeName, TExchange.Name), cachedRemoteAddressAttribute); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs index 8488edfb4..646e37efd 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs @@ -54,12 +54,8 @@ private async Task SendAsync(TExchange exchange, Ca } finally { - var responseTime = timeStamp.ElapsedMilliseconds; -#pragma warning disable CS0618 - Metrics?.ReportResponseTime(TimeSpan.FromMilliseconds(responseTime)); -#pragma warning restore CS0618 ResponseTimeMeter.Record( - responseTime, + timeStamp.ElapsedMilliseconds, new(IRaftClusterMember.MessageTypeAttributeName, exchange.Name), cachedRemoteAddressAttribute); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/BinaryMessage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/BinaryMessage.cs index 61e105452..6d6b3f6d8 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/BinaryMessage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/BinaryMessage.cs @@ -3,8 +3,6 @@ namespace DotNext.Net.Cluster.Messaging; -using DataTransferObject = IO.DataTransferObject; - /// /// Represents binary message that encapsulated value of blittable type. /// @@ -12,12 +10,6 @@ namespace DotNext.Net.Cluster.Messaging; public class BinaryMessage : IO.BinaryTransferObject, IMessage where T : unmanaged { - /// - /// Gets the reader of the binary message. - /// - [Obsolete("This field can be replaced by delegate pointing to DataTransferObject.ToTypeAsync static method")] - public static readonly MessageReader Reader = DataTransferObject.ToTypeAsync; - /// /// Initializes a new binary message. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Security/SslOptions.cs b/src/cluster/DotNext.Net.Cluster/Net/Security/SslOptions.cs index 69e2dcc13..131cd3f0d 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Security/SslOptions.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Security/SslOptions.cs @@ -10,19 +10,6 @@ public class SslOptions private SslServerAuthenticationOptions? serverOptions; private SslClientAuthenticationOptions? clientOptions; - /// - /// Initializes a new SSL options with preconfigured options for server and client. - /// - /// Server-side SSL options. - /// Client-side SSL options. - /// or is . - [Obsolete("Use init-only properties to initialize the instance")] - public SslOptions(SslServerAuthenticationOptions serverOptions, SslClientAuthenticationOptions clientOptions) - { - this.serverOptions = serverOptions ?? throw new ArgumentNullException(nameof(serverOptions)); - this.clientOptions = clientOptions ?? throw new ArgumentNullException(nameof(clientOptions)); - } - /// /// Initializes empty SSL options for server and client. /// From eeabf3cc713c6191491a01402469794f3f92488f Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 20:41:30 +0200 Subject: [PATCH 061/155] Fixed compiler warning --- .../Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs index d9697e8ff..d573b88b6 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterHttpHost.cs @@ -63,10 +63,6 @@ public class RaftClusterHttpHost : Disposable, IHostedService, IAsyncDisposable /// /// /// - /// - /// Allows to capture runtime metrics associated with the local node. - /// - /// /// of type /// Allows to announce a new node the cluster leader. /// From 0d6b58b605d5759710c126a02c724a7638b3144a Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 21:05:48 +0200 Subject: [PATCH 062/155] Removed RequiredPreviewFeatures attribute usage --- src/DotNext.IO/IO/AsyncBufferWriter.cs | 2 -- src/DotNext.IO/IO/FileReader.Binary.cs | 4 --- src/DotNext.IO/IO/FileWriter.Binary.cs | 2 -- src/DotNext.IO/IO/IAsyncBinaryReader.cs | 2 -- src/DotNext.IO/IO/IAsyncBinaryWriter.cs | 2 -- .../IO/Pipelines/PipeExtensions.Readers.cs | 2 -- .../IO/Pipelines/PipeExtensions.Writers.cs | 2 -- .../IO/Pipelines/PipeReaderWriter.cs | 3 -- src/DotNext.IO/IO/SequenceReader.cs | 3 -- src/DotNext.IO/IO/StreamBinaryAccessor.cs | 3 -- src/DotNext.IO/IO/StreamExtensions.Writers.cs | 4 --- src/DotNext.IO/IO/StreamExtensions.cs | 5 ---- .../DeserializingTransformation.cs | 2 -- .../Runtime/Serialization/Serializable.cs | 4 --- src/DotNext.IO/Text/Json/IJsonSerializable.cs | 2 -- src/DotNext.IO/Text/Json/JsonSerializable.cs | 3 -- .../EqualityComparerBuilderTests.cs | 2 +- .../Threading/QueuedSynchronizer.cs | 28 ++----------------- .../Raft/Http/AppendEntriesMessage.cs | 2 -- .../Consensus/Raft/Http/CustomMessage.cs | 4 --- .../Consensus/Raft/Http/HttpMessage.cs | 2 -- .../Raft/Http/InstallSnapshotMessage.cs | 2 -- .../Consensus/Raft/Http/MetadataMessage.cs | 4 +-- .../Consensus/Raft/Http/PreVoteMessage.cs | 2 -- .../Consensus/Raft/Http/RaftClusterMember.cs | 6 ---- .../Consensus/Raft/Http/RequestVoteMessage.cs | 4 +-- .../Consensus/Raft/Http/ResignMessage.cs | 4 +-- .../Consensus/Raft/Http/SynchronizeMessage.cs | 2 -- .../Cluster/Consensus/Raft/BinaryLogEntry.cs | 2 -- .../Raft/Commands/CommandAttribute.cs | 18 ++++-------- .../CommandInterpreter.CommandHandler.cs | 11 +------- .../Raft/Commands/CommandRegistry.Builder.cs | 21 ++------------ .../Raft/PersistentState.NodeState.cs | 2 -- .../Client.AppendEntries.cs | 4 --- .../Client.InstallSnapshot.cs | 3 -- .../ConnectionOriented/Client.Metadata.cs | 4 --- .../ConnectionOriented/Client.Resign.cs | 4 --- .../ConnectionOriented/Client.Synchronize.cs | 3 -- .../ConnectionOriented/Client.Voting.cs | 4 --- .../ConnectionOriented/Client.cs | 3 -- .../ProtocolStreamExtensions.cs | 2 -- .../Datagram/ControlOctet.cs | 4 +-- .../Datagram/EntriesExchange.cs | 2 -- .../Datagram/MetadataExchange.cs | 2 -- .../Datagram/PacketHeaders.cs | 4 +-- .../Messaging/Gossip/RumorTimestamp.cs | 4 +-- .../Net/Cluster/Messaging/MessageAttribute.cs | 17 ++++------- .../Messaging/MessageHandler.Builder.cs | 11 ++------ .../Net/Cluster/Messaging/MessagingClient.cs | 2 -- 49 files changed, 26 insertions(+), 208 deletions(-) diff --git a/src/DotNext.IO/IO/AsyncBufferWriter.cs b/src/DotNext.IO/IO/AsyncBufferWriter.cs index 2ff2e8d8e..661a015f1 100644 --- a/src/DotNext.IO/IO/AsyncBufferWriter.cs +++ b/src/DotNext.IO/IO/AsyncBufferWriter.cs @@ -2,7 +2,6 @@ using System.IO.Pipelines; using System.Numerics; using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.IO; @@ -197,7 +196,6 @@ ValueTask IAsyncBinaryWriter.WriteFormattableAsync(T value, LengthFormat leng return result; } - [RequiresPreviewFeatures] ValueTask IAsyncBinaryWriter.WriteFormattableAsync(T value, CancellationToken token) { ValueTask result; diff --git a/src/DotNext.IO/IO/FileReader.Binary.cs b/src/DotNext.IO/IO/FileReader.Binary.cs index 21cfae920..326f54803 100644 --- a/src/DotNext.IO/IO/FileReader.Binary.cs +++ b/src/DotNext.IO/IO/FileReader.Binary.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using Debug = System.Diagnostics.Debug; namespace DotNext.IO; @@ -262,7 +261,6 @@ public async ValueTask ParseAsync(Parser parser, LengthFormat lengthFor /// The parsed value. /// The operation has been canceled. /// The underlying source doesn't contain necessary amount of bytes to decode the value. - [RequiresPreviewFeatures] public ValueTask ParseAsync(CancellationToken token = default) where T : notnull, IBinaryFormattable { @@ -295,7 +293,6 @@ public ValueTask ParseAsync(CancellationToken token = default) return result; } - [RequiresPreviewFeatures] private T ParseFast() where T : notnull, IBinaryFormattable { @@ -308,7 +305,6 @@ private T ParseFast() return result; } - [RequiresPreviewFeatures] private async ValueTask ParseSlowAsync(CancellationToken token) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/IO/FileWriter.Binary.cs b/src/DotNext.IO/IO/FileWriter.Binary.cs index 8dc357002..0dddf9e44 100644 --- a/src/DotNext.IO/IO/FileWriter.Binary.cs +++ b/src/DotNext.IO/IO/FileWriter.Binary.cs @@ -2,7 +2,6 @@ using System.IO.Pipelines; using System.Numerics; using System.Runtime.CompilerServices; -using System.Runtime.Versioning; using Debug = System.Diagnostics.Debug; using Encoder = System.Text.Encoder; @@ -250,7 +249,6 @@ public async ValueTask WriteFormattableAsync(T value, LengthFormat lengthForm /// The token that can be used to cancel the operation. /// The task representing state of asynchronous execution. /// The operation has been canceled. - [RequiresPreviewFeatures] public async ValueTask WriteFormattableAsync(T value, CancellationToken token = default) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/IO/IAsyncBinaryReader.cs b/src/DotNext.IO/IO/IAsyncBinaryReader.cs index 44d0739e9..17233df8a 100644 --- a/src/DotNext.IO/IO/IAsyncBinaryReader.cs +++ b/src/DotNext.IO/IO/IAsyncBinaryReader.cs @@ -2,7 +2,6 @@ using System.IO.Pipelines; using System.Numerics; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using Unsafe = System.Runtime.CompilerServices.Unsafe; namespace DotNext.IO; @@ -110,7 +109,6 @@ async ValueTask ParseAsync(Parser parser, LengthFormat lengthFormat, De /// The parsed value. /// The operation has been canceled. /// The underlying source doesn't contain necessary amount of bytes to decode the value. - [RequiresPreviewFeatures] async ValueTask ParseAsync(CancellationToken token = default) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/IO/IAsyncBinaryWriter.cs b/src/DotNext.IO/IO/IAsyncBinaryWriter.cs index 046685e8d..56e476155 100644 --- a/src/DotNext.IO/IO/IAsyncBinaryWriter.cs +++ b/src/DotNext.IO/IO/IAsyncBinaryWriter.cs @@ -1,7 +1,6 @@ using System.Buffers; using System.IO.Pipelines; using System.Numerics; -using System.Runtime.Versioning; using Unsafe = System.Runtime.CompilerServices.Unsafe; namespace DotNext.IO; @@ -178,7 +177,6 @@ ValueTask WriteFormattableAsync(T value, LengthFormat lengthFormat, EncodingC /// The value to be written as a sequence of bytes. /// The token that can be used to cancel the operation. /// The task representing state of asynchronous execution. - [RequiresPreviewFeatures] async ValueTask WriteFormattableAsync(T value, CancellationToken token = default) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs index 0e08c1974..1299c9b68 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using static System.Buffers.Binary.BinaryPrimitives; @@ -120,7 +119,6 @@ public static async ValueTask ParseAsync(this PipeReader reader, Parser /// The parsed value. /// The operation has been canceled. /// The underlying source doesn't contain necessary amount of bytes to decode the value. - [RequiresPreviewFeatures] public static ValueTask ParseAsync(this PipeReader reader, CancellationToken token = default) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs index 0900e1cc3..0d3a8b34b 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs @@ -1,7 +1,6 @@ using System.Buffers; using System.IO.Pipelines; using System.Numerics; -using System.Runtime.Versioning; using System.Text; namespace DotNext.IO.Pipelines; @@ -84,7 +83,6 @@ public static ValueTask WriteFormattableAsync(this PipeWriter wr /// The token that can be used to cancel the operation. /// The task representing state of asynchronous execution. /// The operation has been canceled. - [RequiresPreviewFeatures] public static ValueTask WriteFormattableAsync(this PipeWriter writer, T value, CancellationToken token = default) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs b/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs index c4bdb457c..1d771c582 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs @@ -2,7 +2,6 @@ using System.IO.Pipelines; using System.Numerics; using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.IO.Pipelines; @@ -44,7 +43,6 @@ ValueTask> IAsyncBinaryReader.ReadStringAsync(LengthFormat len ValueTask IAsyncBinaryReader.ParseAsync(Parser parser, LengthFormat lengthFormat, DecodingContext context, IFormatProvider? provider, CancellationToken token) => Reader.ParseAsync(parser, lengthFormat, context, provider, token); - [RequiresPreviewFeatures] ValueTask IAsyncBinaryReader.ParseAsync(CancellationToken token) => Reader.ParseAsync(token); @@ -231,7 +229,6 @@ static ValueTask WriteAndFlushOnceAsync(PipeWriter output, ReadOnly unsafe ValueTask IAsyncBinaryWriter.WriteFormattableAsync(T value, LengthFormat lengthFormat, EncodingContext context, string? format, IFormatProvider? provider, CancellationToken token) => WriteAsync(output, new Writer(value, lengthFormat, context, format, provider, &PipeExtensions.WriteFormattableAsync), token); - [RequiresPreviewFeatures] unsafe ValueTask IAsyncBinaryWriter.WriteFormattableAsync(T value, CancellationToken token) => WriteAsync(output, new Writer(value, &PipeExtensions.WriteFormattableAsync), token); diff --git a/src/DotNext.IO/IO/SequenceReader.cs b/src/DotNext.IO/IO/SequenceReader.cs index 52307e9e7..52632226b 100644 --- a/src/DotNext.IO/IO/SequenceReader.cs +++ b/src/DotNext.IO/IO/SequenceReader.cs @@ -3,7 +3,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.IO; @@ -102,7 +101,6 @@ public unsafe T Parse(Parser parser, LengthFormat lengthFormat, in Decodin /// /// The type of the result. /// The parsed value. - [RequiresPreviewFeatures] public T Parse() where T : notnull, IBinaryFormattable { @@ -600,7 +598,6 @@ ValueTask IAsyncBinaryReader.ParseAsync(Parser parser, LengthFormat len } /// - [RequiresPreviewFeatures] ValueTask IAsyncBinaryReader.ParseAsync(CancellationToken token) { ValueTask result; diff --git a/src/DotNext.IO/IO/StreamBinaryAccessor.cs b/src/DotNext.IO/IO/StreamBinaryAccessor.cs index e06b842fb..a465c18da 100644 --- a/src/DotNext.IO/IO/StreamBinaryAccessor.cs +++ b/src/DotNext.IO/IO/StreamBinaryAccessor.cs @@ -2,7 +2,6 @@ using System.IO.Pipelines; using System.Numerics; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using static System.Buffers.Binary.BinaryPrimitives; namespace DotNext.IO; @@ -113,7 +112,6 @@ unsafe ValueTask IAsyncBinaryReader.ReadInt64Async(bool littleEndian, Canc ValueTask IAsyncBinaryReader.ParseAsync(Parser parser, LengthFormat lengthFormat, DecodingContext context, IFormatProvider? provider, CancellationToken token) => StreamExtensions.ParseAsync(Stream, parser, lengthFormat, context, buffer, provider, token); - [RequiresPreviewFeatures] ValueTask IAsyncBinaryReader.ParseAsync(CancellationToken token) => StreamExtensions.ParseAsync(Stream, buffer, token); @@ -204,7 +202,6 @@ ValueTask IAsyncBinaryWriter.WriteBigIntegerAsync(BigInteger value, bool littleE ValueTask IAsyncBinaryWriter.WriteFormattableAsync(T value, LengthFormat lengthFormat, EncodingContext context, string? format, IFormatProvider? provider, CancellationToken token) => Stream.WriteFormattableAsync(value, lengthFormat, context, buffer, format, provider, token); - [RequiresPreviewFeatures] ValueTask IAsyncBinaryWriter.WriteFormattableAsync(T value, CancellationToken token) => Stream.WriteFormattableAsync(value, buffer, token); diff --git a/src/DotNext.IO/IO/StreamExtensions.Writers.cs b/src/DotNext.IO/IO/StreamExtensions.Writers.cs index 666a46f2f..194db12d5 100644 --- a/src/DotNext.IO/IO/StreamExtensions.Writers.cs +++ b/src/DotNext.IO/IO/StreamExtensions.Writers.cs @@ -2,7 +2,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Text; namespace DotNext.IO; @@ -223,7 +222,6 @@ public static void WriteFormattable(this Stream stream, T value, LengthFormat /// The type of formattable value. /// The stream to write into. /// The type value to be written as string. - [RequiresPreviewFeatures] public static void WriteFormattable(this Stream stream, T value) where T : notnull, IBinaryFormattable { @@ -465,7 +463,6 @@ public static async ValueTask WriteFormattableAsync(this Stream stream, T val /// The token that can be used to cancel the operation. /// The task representing state of asynchronous execution. /// The operation has been canceled. - [RequiresPreviewFeatures] public static async ValueTask WriteFormattableAsync(this Stream stream, T value, Memory buffer, CancellationToken token = default) where T : notnull, IBinaryFormattable { @@ -491,7 +488,6 @@ public static async ValueTask WriteFormattableAsync(this Stream stream, T val /// The token that can be used to cancel the operation. /// The task representing state of asynchronous execution. /// The operation has been canceled. - [RequiresPreviewFeatures] public static async ValueTask WriteFormattableAsync(this Stream stream, T value, CancellationToken token = default) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/IO/StreamExtensions.cs b/src/DotNext.IO/IO/StreamExtensions.cs index a9558a567..80fc1364f 100644 --- a/src/DotNext.IO/IO/StreamExtensions.cs +++ b/src/DotNext.IO/IO/StreamExtensions.cs @@ -2,7 +2,6 @@ using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Text; using Utf8 = System.Text.Unicode.Utf8; @@ -85,7 +84,6 @@ public static T Parse(this Stream stream, Parser parser, LengthFormat leng /// The buffer that is allocated by the caller. /// The decoded value. /// too small for decoding the value. - [RequiresPreviewFeatures] public static T Parse(this Stream stream, Span buffer) where T : notnull, IBinaryFormattable { @@ -105,7 +103,6 @@ public static T Parse(this Stream stream, Span buffer) /// The type of the result. /// The stream to read from. /// The decoded value. - [RequiresPreviewFeatures] public static T Parse(this Stream stream) where T : notnull, IBinaryFormattable { @@ -528,7 +525,6 @@ public static async ValueTask ParseAsync(this Stream stream, Parser par /// The operation has been canceled. /// The underlying source doesn't contain necessary amount of bytes to decode the value. /// too small for decoding value. - [RequiresPreviewFeatures] public static async ValueTask ParseAsync(this Stream stream, Memory buffer, CancellationToken token = default) where T : notnull, IBinaryFormattable { @@ -549,7 +545,6 @@ public static async ValueTask ParseAsync(this Stream stream, Memory /// The parsed value. /// The operation has been canceled. /// The underlying source doesn't contain necessary amount of bytes to decode the value. - [RequiresPreviewFeatures] public static async ValueTask ParseAsync(this Stream stream, CancellationToken token = default) where T : notnull, IBinaryFormattable { diff --git a/src/DotNext.IO/Runtime/Serialization/DeserializingTransformation.cs b/src/DotNext.IO/Runtime/Serialization/DeserializingTransformation.cs index 3c7954fe3..54e0ea264 100644 --- a/src/DotNext.IO/Runtime/Serialization/DeserializingTransformation.cs +++ b/src/DotNext.IO/Runtime/Serialization/DeserializingTransformation.cs @@ -1,5 +1,4 @@ using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.Runtime.Serialization; @@ -9,7 +8,6 @@ namespace DotNext.Runtime.Serialization; internal readonly struct DeserializingTransformation : IDataTransferObject.ITransformation where TOutput : notnull, ISerializable { - [RequiresPreviewFeatures] ValueTask IDataTransferObject.ITransformation.TransformAsync(TReader reader, CancellationToken token) => TOutput.ReadFromAsync(reader, token); } \ No newline at end of file diff --git a/src/DotNext.IO/Runtime/Serialization/Serializable.cs b/src/DotNext.IO/Runtime/Serialization/Serializable.cs index 8ebc318d5..c26b5d3bf 100644 --- a/src/DotNext.IO/Runtime/Serialization/Serializable.cs +++ b/src/DotNext.IO/Runtime/Serialization/Serializable.cs @@ -1,5 +1,4 @@ using System.IO.Pipelines; -using System.Runtime.Versioning; namespace DotNext.Runtime.Serialization; @@ -23,7 +22,6 @@ public static class Serializable /// The token that can be used to cancel the operation. /// Deserialized object. /// The operation has been canceled. - [RequiresPreviewFeatures] public static ValueTask ReadFromAsync(this Stream input, Memory buffer, CancellationToken token = default) where TObject : notnull, ISerializable => TObject.ReadFromAsync(new(input, buffer), token); @@ -37,7 +35,6 @@ public static ValueTask ReadFromAsync(this Stream input, Memor /// The token that can be used to cancel the operation. /// Deserialized object. /// The operation has been canceled. - [RequiresPreviewFeatures] public static async ValueTask ReadFromAsync(this Stream input, int bufferSize = 128, CancellationToken token = default) where TObject : notnull, ISerializable { @@ -53,7 +50,6 @@ public static async ValueTask ReadFromAsync(this Stream input, /// The token that can be used to cancel the operation. /// Deserialized object. /// The operation has been canceled. - [RequiresPreviewFeatures] public static ValueTask ReadFromAsync(this PipeReader reader, CancellationToken token = default) where TObject : notnull, ISerializable => TObject.ReadFromAsync(new(reader), token); diff --git a/src/DotNext.IO/Text/Json/IJsonSerializable.cs b/src/DotNext.IO/Text/Json/IJsonSerializable.cs index 9bd0dd62d..d6e31b25b 100644 --- a/src/DotNext.IO/Text/Json/IJsonSerializable.cs +++ b/src/DotNext.IO/Text/Json/IJsonSerializable.cs @@ -1,4 +1,3 @@ -using System.Runtime.Versioning; using System.Text.Json.Serialization.Metadata; namespace DotNext.Text.Json; @@ -13,6 +12,5 @@ public interface IJsonSerializable /// /// Gets the type information required by serialization or deserialization process. /// - [RequiresPreviewFeatures] static abstract JsonTypeInfo TypeInfo { get; } } \ No newline at end of file diff --git a/src/DotNext.IO/Text/Json/JsonSerializable.cs b/src/DotNext.IO/Text/Json/JsonSerializable.cs index c5a887199..d9f211bc6 100644 --- a/src/DotNext.IO/Text/Json/JsonSerializable.cs +++ b/src/DotNext.IO/Text/Json/JsonSerializable.cs @@ -1,7 +1,6 @@ using System.Buffers; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; using System.Text.Json; using PipeReader = System.IO.Pipelines.PipeReader; @@ -33,7 +32,6 @@ public record struct JsonSerializable : ISerializable>, I readonly long? IDataTransferObject.Length => null; /// - [RequiresPreviewFeatures] readonly ValueTask IDataTransferObject.WriteToAsync(TWriter writer, CancellationToken token) { ValueTask result; @@ -71,7 +69,6 @@ static void Serialize(T value, IBufferWriter writer) } /// - [RequiresPreviewFeatures] public static ValueTask> ReadFromAsync(TReader reader, CancellationToken token = default) where TReader : notnull, IAsyncBinaryReader { diff --git a/src/DotNext.Tests/EqualityComparerBuilderTests.cs b/src/DotNext.Tests/EqualityComparerBuilderTests.cs index fc5f3e583..99c21aaf9 100644 --- a/src/DotNext.Tests/EqualityComparerBuilderTests.cs +++ b/src/DotNext.Tests/EqualityComparerBuilderTests.cs @@ -43,7 +43,7 @@ internal ComplexClass(UnsafeClass obj, params UnsafeStruct[] array) { this.obj = obj; this.array = array; - objArray = new object[] { "1", "2" }; + objArray = ["1", "2"]; nullArray = null; } diff --git a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs index 272c78807..80c24ac44 100644 --- a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs +++ b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs @@ -1,10 +1,8 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.Metrics; -using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.Threading; @@ -191,9 +189,7 @@ private protected ValueTask AcquireAsync(ref Valu break; } -#pragma warning disable CA2252 interruptedCallers = TOptions.InterruptionRequired -#pragma warning restore CA2252 ? Interrupt(options.InterruptionReason) : null; @@ -220,9 +216,7 @@ private protected ValueTask AcquireAsync(ref Valu break; } -#pragma warning disable CA2252 interruptedCallers = TOptions.InterruptionRequired -#pragma warning restore CA2252 ? Interrupt(options.InterruptionReason) : null; @@ -266,9 +260,8 @@ private protected ValueTask TryAcquireAsync task = new(GetDisposedTask()); break; } -#pragma warning disable CA2252 + interruptedCallers = TOptions.InterruptionRequired -#pragma warning restore CA2252 ? Interrupt(options.InterruptionReason) : null; @@ -293,9 +286,7 @@ private protected ValueTask TryAcquireAsync break; } -#pragma warning disable CA2252 interruptedCallers = TOptions.InterruptionRequired -#pragma warning restore CA2252 ? Interrupt(options.InterruptionReason) : null; @@ -546,10 +537,9 @@ private protected interface IAcquisitionOptions TimeSpan Timeout { get; } - object? InterruptionReason { get; } + object? InterruptionReason => null; - [RequiresPreviewFeatures] - static abstract bool InterruptionRequired { get; } + static virtual bool InterruptionRequired => false; } [StructLayout(LayoutKind.Auto)] @@ -559,11 +549,6 @@ private protected interface IAcquisitionOptions public CancellationToken Token { get; } - [RequiresPreviewFeatures] - static bool IAcquisitionOptions.InterruptionRequired => false; - - object? IAcquisitionOptions.InterruptionReason => null; - TimeSpan IAcquisitionOptions.Timeout => new(Timeout.InfiniteTicks); } @@ -579,11 +564,6 @@ internal TimeoutAndCancellationToken(TimeSpan timeout, CancellationToken token) public CancellationToken Token { get; } public TimeSpan Timeout { get; } - - [RequiresPreviewFeatures] - static bool IAcquisitionOptions.InterruptionRequired => false; - - object? IAcquisitionOptions.InterruptionReason => null; } [StructLayout(LayoutKind.Auto)] @@ -599,7 +579,6 @@ internal InterruptionReasonAndCancellationToken(object? reason, CancellationToke public object? InterruptionReason { get; } - [RequiresPreviewFeatures] static bool IAcquisitionOptions.InterruptionRequired => true; TimeSpan IAcquisitionOptions.Timeout => new(Timeout.InfiniteTicks); @@ -619,7 +598,6 @@ internal TimeoutAndInterruptionReasonAndCancellationToken(object? reason, TimeSp public object? InterruptionReason { get; } - [RequiresPreviewFeatures] static bool IAcquisitionOptions.InterruptionRequired => true; public TimeSpan Timeout { get; } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs index f985d7517..e38f4be10 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs @@ -3,7 +3,6 @@ using System.IO.Pipelines; using System.Net; using System.Net.Http.Headers; -using System.Runtime.Versioning; using System.Text; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebUtilities; @@ -301,7 +300,6 @@ private static ILogEntryProducer CreateReader(HttpRequest request base.PrepareRequest(request); } - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; internal static Task SaveResponseAsync(HttpResponse response, in Result result, CancellationToken token) diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/CustomMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/CustomMessage.cs index 16edc1471..aa0a1dd45 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/CustomMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/CustomMessage.cs @@ -1,7 +1,6 @@ using System.Net; using System.Net.Http.Headers; using System.Net.Mime; -using System.Runtime.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using static System.Globalization.CultureInfo; @@ -157,7 +156,6 @@ private protected static async Task ParseResponseAsync(HttpResponseMessage } } - [RequiresPreviewFeatures] private protected static async Task ParseResponseAsync(HttpResponseMessage response, CancellationToken token) where T : notnull, ISerializable { @@ -172,7 +170,6 @@ private protected static async Task ParseResponseAsync(HttpResponseMessage } } - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; } @@ -195,7 +192,6 @@ internal CustomSerializableMessage(in ClusterMemberId sender, IMessage message) { } - [RequiresPreviewFeatures] Task IHttpMessage.ParseResponseAsync(HttpResponseMessage response, CancellationToken token) => ParseResponseAsync(response, token); } \ No newline at end of file diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs index 77e605063..f15deebeb 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/HttpMessage.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using System.Net; -using System.Runtime.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using static System.Globalization.CultureInfo; @@ -52,7 +51,6 @@ private protected HttpMessage(IDictionary headers) internal static string GetMessageType(HttpRequest request) => ParseHeader(request.Headers, MessageTypeHeader); - [RequiresPreviewFeatures] internal static void SetMessageType(HttpRequestMessage request) where TMessage : class, IHttpMessage => request.Headers.Add(MessageTypeHeader, TMessage.MessageType); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/InstallSnapshotMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/InstallSnapshotMessage.cs index ecca77bd0..29d03c52c 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/InstallSnapshotMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/InstallSnapshotMessage.cs @@ -1,6 +1,5 @@ using System.IO.Pipelines; using System.Net; -using System.Runtime.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using static System.Globalization.CultureInfo; @@ -108,7 +107,6 @@ internal InstallSnapshotMessage(HttpRequest request) Task> IHttpMessage>.ParseResponseAsync(HttpResponseMessage response, CancellationToken token) => ParseEnumResponseAsync(response, token); - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; internal static Task SaveResponseAsync(HttpResponse response, in Result result, CancellationToken token) diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/MetadataMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/MetadataMessage.cs index 606904e3b..f2daa8379 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/MetadataMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/MetadataMessage.cs @@ -1,5 +1,4 @@ -using System.Runtime.Versioning; -using System.Text.Json; +using System.Text.Json; using Microsoft.AspNetCore.Http; namespace DotNext.Net.Cluster.Consensus.Raft.Http; @@ -38,7 +37,6 @@ static async Task ParseAsync(HttpContent content, CancellationTo void IHttpMessage.PrepareRequest(HttpRequestMessage request) => PrepareRequest(request); - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; internal static Task SaveResponseAsync(HttpResponse response, MemberMetadata metadata, CancellationToken token) diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/PreVoteMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/PreVoteMessage.cs index 9521f445e..ff5c037b2 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/PreVoteMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/PreVoteMessage.cs @@ -1,4 +1,3 @@ -using System.Runtime.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using static System.Globalization.CultureInfo; @@ -40,7 +39,6 @@ internal PreVoteMessage(HttpRequest request) Task> IHttpMessage>.ParseResponseAsync(HttpResponseMessage response, CancellationToken token) => ParseEnumResponseAsync(response, token); - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; internal static Task SaveResponseAsync(HttpResponse response, Result result, CancellationToken token) => RaftHttpMessage.SaveResponseAsync(response, result, token); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs index c413ddea2..3ad6acf93 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RaftClusterMember.cs @@ -60,9 +60,7 @@ event Action IClusterMember.MemberStatusCha private async Task SendAsync(TMessage message, CancellationToken token) where TMessage : class, IHttpMessage { -#pragma warning disable CA2252 context.Logger.SendingRequestToMember(EndPoint, TMessage.MessageType); -#pragma warning restore CA2252 var request = new HttpRequestMessage { RequestUri = resourcePath, @@ -70,9 +68,7 @@ private async Task SendAsync(TMessage message, C VersionPolicy = DefaultVersionPolicy, }; -#pragma warning disable CA2252 HttpMessage.SetMessageType(request); -#pragma warning restore CA2252 message.PrepareRequest(request); // setup additional timeout control token needed if actual timeout @@ -105,9 +101,7 @@ private async Task SendAsync(TMessage message, C } catch (HttpRequestException e) { -#pragma warning disable CA2252 if (response is null || TMessage.IsMemberUnavailable(e.StatusCode)) -#pragma warning restore CA2252 throw MemberUnavailable(e); throw new UnexpectedStatusCodeException(response, e); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RequestVoteMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RequestVoteMessage.cs index 9db55caf2..45f27a02f 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RequestVoteMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/RequestVoteMessage.cs @@ -1,5 +1,4 @@ -using System.Runtime.Versioning; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using static System.Globalization.CultureInfo; @@ -42,7 +41,6 @@ internal RequestVoteMessage(HttpRequest request) Task> IHttpMessage>.ParseResponseAsync(HttpResponseMessage response, CancellationToken token) => ParseBoolResponseAsync(response, token); - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; internal static Task SaveResponseAsync(HttpResponse response, Result result, CancellationToken token) => RaftHttpMessage.SaveResponseAsync(response, result, token); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/ResignMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/ResignMessage.cs index f0a6c5cf5..47f4ec4d8 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/ResignMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/ResignMessage.cs @@ -1,5 +1,4 @@ -using System.Runtime.Versioning; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; namespace DotNext.Net.Cluster.Consensus.Raft.Http; @@ -19,7 +18,6 @@ internal ResignMessage(HttpRequest request) Task IHttpMessage.ParseResponseAsync(HttpResponseMessage response, CancellationToken token) => ParseBoolResponseAsync(response, token); - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; void IHttpMessage.PrepareRequest(HttpRequestMessage request) => PrepareRequest(request); diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs index dbe9ec7b4..c7c29621c 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs @@ -1,5 +1,4 @@ using System.Net.Mime; -using System.Runtime.Versioning; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using static System.Buffers.Binary.BinaryPrimitives; @@ -55,7 +54,6 @@ internal SynchronizeMessage(HttpRequest request) } } - [RequiresPreviewFeatures] static string IHttpMessage.MessageType => MessageType; internal static Task SaveResponseAsync(HttpResponse response, long? commitIndex, CancellationToken token) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs index 6b7de2544..11d5cd0a6 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs @@ -1,5 +1,4 @@ using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.Net.Cluster.Consensus.Raft; @@ -12,7 +11,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft; /// /// Binary-formattable type. [StructLayout(LayoutKind.Auto)] -[RequiresPreviewFeatures] public struct BinaryLogEntry : IBinaryLogEntry where T : struct, IBinaryFormattable { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandAttribute.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandAttribute.cs index ae1c7b469..60da8e81a 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandAttribute.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandAttribute.cs @@ -1,5 +1,3 @@ -using System.Runtime.Versioning; - namespace DotNext.Net.Cluster.Consensus.Raft.Commands; using Runtime.Serialization; @@ -28,18 +26,12 @@ public abstract class CommandAttribute : Attribute /// Registers command type in the interpreter. /// /// The type of the command. -[RequiresPreviewFeatures] -public sealed class CommandAttribute : CommandAttribute +/// +/// Initializes a new attribute. +/// +/// The identifier of the log entry. +public sealed class CommandAttribute(int id) : CommandAttribute(id) where TCommand : notnull, ISerializable { - /// - /// Initializes a new attribute. - /// - /// The identifier of the log entry. - public CommandAttribute(int id) - : base(id) - { - } - internal override Type CommandType => typeof(TCommand); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.CommandHandler.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.CommandHandler.cs index f3160fbec..57b730335 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.CommandHandler.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.CommandHandler.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Runtime.Versioning; namespace DotNext.Net.Cluster.Consensus.Raft.Commands; @@ -14,18 +13,10 @@ internal abstract ValueTask InterpretAsync(TReader reader, Cancellation where TReader : notnull, IAsyncBinaryReader; } - private sealed class CommandHandler : CommandHandler + private sealed class CommandHandler(Func handler) : CommandHandler where TCommand : notnull, ISerializable { - private readonly Func handler; - - public CommandHandler(Func handler) - { - this.handler = handler; - } - [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - [RequiresPreviewFeatures] internal override async ValueTask InterpretAsync(TReader reader, CancellationToken token) { var command = await TCommand.ReadFromAsync(reader, token).ConfigureAwait(false); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandRegistry.Builder.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandRegistry.Builder.cs index 2f1f2e0e8..d55343ab4 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandRegistry.Builder.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandRegistry.Builder.cs @@ -1,5 +1,3 @@ -using System.Runtime.Versioning; - namespace DotNext.Net.Cluster.Consensus.Raft.Commands; using Runtime.Serialization; @@ -11,19 +9,10 @@ public partial class CommandInterpreter : IBuildable public sealed class Builder : ISupplier, IResettable { - private readonly Dictionary interpreters; - private readonly Dictionary identifiers; + private readonly Dictionary interpreters = new(); + private readonly Dictionary identifiers = new(); private int? snapshotCommandId; - /// - /// Initializes a new builder. - /// - public Builder() - { - interpreters = new(); - identifiers = new(); - } - /// /// Registers command handler. /// @@ -52,15 +41,12 @@ public Builder Add(int commandId, Func /// Clears this builder so it can be reused. /// - public void Clear() + public void Reset() { interpreters.Clear(); identifiers.Clear(); } - /// - void IResettable.Reset() => Clear(); - /// /// Constructs an instance of . /// @@ -72,6 +58,5 @@ public void Clear() } /// - [RequiresPreviewFeatures] static Builder IBuildable.CreateBuilder() => new(); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs index 3d310220a..6f0b990af 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs @@ -228,9 +228,7 @@ internal void UpdateVotedFor(ClusterMemberId id) { votedFor = BoxedClusterMemberId.Box(id); buffer[LastVotePresenceOffset] = True; -#pragma warning disable CA2252 // TODO: Remove in .NET 7 IBinaryFormattable.Format(id, buffer.Span.Slice(LastVoteOffset)); -#pragma warning restore CA2252 } internal ref readonly SnapshotMetadata Snapshot => ref snapshot; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs index 3cf827936..6c24aba60 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.AppendEntries.cs @@ -1,5 +1,4 @@ using System.Runtime.CompilerServices; -using System.Runtime.Versioning; using Debug = System.Diagnostics.Debug; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriented; @@ -12,7 +11,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriente internal partial class Client : RaftClusterMember { // optimized version for empty heartbeats (it has no field to store empty entries) - [RequiresPreviewFeatures] private class AppendEntriesExchange : IClientExchange>, IResettable { private const string Name = "AppendEntries"; @@ -80,7 +78,6 @@ static ValueTask> IClientExchange>.Name => Name; } - [RequiresPreviewFeatures] private sealed class AppendEntriesExchange : AppendEntriesExchange, IClientExchange> where TEntry : notnull, IRaftLogEntry where TList : notnull, IReadOnlyList @@ -142,7 +139,6 @@ static int WriteLogEntryMetadata(Span buffer, TEntry entry) } } - [RequiresPreviewFeatures] private protected sealed override Task> AppendEntriesAsync(long term, TList entries, long prevLogIndex, long prevLogTerm, long commitIndex, IClusterConfiguration config, bool applyConfig, CancellationToken token) { if (entries.Count is 0) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.InstallSnapshot.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.InstallSnapshot.cs index 9f1571a7b..8f3781ee7 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.InstallSnapshot.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.InstallSnapshot.cs @@ -1,4 +1,3 @@ -using System.Runtime.Versioning; using Debug = System.Diagnostics.Debug; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriented; @@ -7,7 +6,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriente internal partial class Client : RaftClusterMember { - [RequiresPreviewFeatures] private sealed class InstallSnapshotExchange : IClientExchange> { private const string Name = "InstallSnapshot"; @@ -47,7 +45,6 @@ static ValueTask> IClientExchange>.Name => Name; } - [RequiresPreviewFeatures] private protected sealed override Task> InstallSnapshotAsync(long term, IRaftLogEntry snapshot, long snapshotIndex, CancellationToken token) => RequestAsync, InstallSnapshotExchange>(new(term, snapshot, snapshotIndex), token); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Metadata.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Metadata.cs index 4d0785290..82f200542 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Metadata.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Metadata.cs @@ -1,10 +1,7 @@ -using System.Runtime.Versioning; - namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriented; internal partial class Client : RaftClusterMember { - [RequiresPreviewFeatures] private sealed class MetadataExchange : IClientExchange> { private const string Name = "Metadata"; @@ -27,7 +24,6 @@ static ValueTask> IClientExchange>.Name => Name; } - [RequiresPreviewFeatures] private protected sealed override Task> GetMetadataAsync(CancellationToken token) => RequestAsync, MetadataExchange>(MetadataExchange.Instance, token); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Resign.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Resign.cs index 1bdc2abb7..f0d507791 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Resign.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Resign.cs @@ -1,10 +1,7 @@ -using System.Runtime.Versioning; - namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriented; internal partial class Client : RaftClusterMember { - [RequiresPreviewFeatures] private sealed class ResignExchange : IClientExchange { private const string Name = "Resign"; @@ -27,7 +24,6 @@ static ValueTask IClientExchange.ResponseAsync(ProtocolStream protoc static string IClientExchange.Name => Name; } - [RequiresPreviewFeatures] private protected sealed override Task ResignAsync(CancellationToken token) => RequestAsync(ResignExchange.Instance, token); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs index 9c8f5b04c..dab43c73e 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Synchronize.cs @@ -1,5 +1,4 @@ using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriented; @@ -8,7 +7,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriente internal partial class Client : RaftClusterMember { [StructLayout(LayoutKind.Auto)] - [RequiresPreviewFeatures] private readonly struct SynchronizeExchange : IClientExchange { private const string Name = "Synchronize"; @@ -32,7 +30,6 @@ internal partial class Client : RaftClusterMember static string IClientExchange.Name => Name; } - [RequiresPreviewFeatures] private protected sealed override Task SynchronizeAsync(long commitIndex, CancellationToken token) => RequestAsync(new(commitIndex), token); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Voting.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Voting.cs index ba98bf4af..df988a305 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Voting.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.Voting.cs @@ -1,11 +1,9 @@ using System.Runtime.InteropServices; -using System.Runtime.Versioning; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriented; internal partial class Client : RaftClusterMember { - [RequiresPreviewFeatures] [StructLayout(LayoutKind.Auto)] private readonly struct VoteExchange : IClientExchange>, IClientExchange> { @@ -45,11 +43,9 @@ static ValueTask> IClientExchange>.R static string IClientExchange>.Name => "PreVote"; } - [RequiresPreviewFeatures] private protected sealed override Task> VoteAsync(long term, long lastLogIndex, long lastLogTerm, CancellationToken token) => RequestAsync, VoteExchange>(new(term, lastLogIndex, lastLogTerm), token); - [RequiresPreviewFeatures] private protected sealed override Task> PreVoteAsync(long term, long lastLogIndex, long lastLogTerm, CancellationToken token) => RequestAsync, VoteExchange>(new(term, lastLogIndex, lastLogTerm), token); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs index fbf85ecf5..571a2d08b 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs @@ -1,6 +1,5 @@ using System.Net; using System.Runtime.CompilerServices; -using System.Runtime.Versioning; using Debug = System.Diagnostics.Debug; namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.ConnectionOriented; @@ -19,7 +18,6 @@ private protected interface IConnectionContext : IDisposable, IAsyncDisposable } // this interface helps to inline async request/response parsing pipeline to RequestAsync method - [RequiresPreviewFeatures] private interface IClientExchange { ValueTask RequestAsync(ILocalMember localMember, ProtocolStream protocol, Memory buffer, CancellationToken token); @@ -61,7 +59,6 @@ internal TimeSpan ConnectTimeout private protected abstract ValueTask ConnectAsync(CancellationToken token); - [RequiresPreviewFeatures] private async Task RequestAsync(TExchange exchange, CancellationToken token) where TExchange : notnull, IClientExchange { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs index ea8526435..07fa712fb 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStreamExtensions.cs @@ -100,7 +100,5 @@ internal static async ValueTask WriteDictionaryAsync(this ProtocolStream protoco } internal static async ValueTask> ReadDictionaryAsync(this ProtocolStream protocol, Memory buffer, CancellationToken token) -#pragma warning disable CA2252 // TODO: Remove in .NET 7 => (await Serializable.ReadFromAsync(protocol, buffer, token).ConfigureAwait(false)).Metadata; -#pragma warning restore CA2252 } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ControlOctet.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ControlOctet.cs index 990fc8b8f..8bd70577d 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ControlOctet.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ControlOctet.cs @@ -4,7 +4,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.Datagram; using Buffers; -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Auto)] internal readonly struct ControlOctet : IBinaryFormattable { @@ -30,5 +29,4 @@ internal ControlOctet(ref SpanReader input) internal FlowControl Control => (FlowControl)(value & FlowControlMask); public static implicit operator byte(ControlOctet value) => value.value; -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/EntriesExchange.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/EntriesExchange.cs index 8363de7a0..a900b1406 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/EntriesExchange.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/EntriesExchange.cs @@ -100,9 +100,7 @@ private protected EntriesExchange(long term, long prevLogIndex, long prevLogTerm } private static ValueTask WriteLogEntryMetadata(PipeWriter writer, TEntry entry, CancellationToken token) -#pragma warning disable CA2252 // TODO: Remove in .NET 7 => writer.WriteFormattableAsync(LogEntryMetadata.Create(entry), token); -#pragma warning restore CA2252 private static async ValueTask WriteLogEntryContent(PipeWriter writer, TEntry entry, CancellationToken token) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/MetadataExchange.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/MetadataExchange.cs index 5c7476038..7e9e04dee 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/MetadataExchange.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/MetadataExchange.cs @@ -24,9 +24,7 @@ ClusterMemberId IClientExchange>>.Sende string IClientExchange>>.Name => Name; async Task> ISupplier>>.Invoke(CancellationToken token) -#pragma warning disable CA2252 // TODO: Remove in .NET 7 => (await Serializable.ReadFromAsync(Reader, token).ConfigureAwait(false)).Metadata; -#pragma warning restore CA2252 public override async ValueTask ProcessInboundMessageAsync(PacketHeaders headers, ReadOnlyMemory payload, CancellationToken token) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/PacketHeaders.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/PacketHeaders.cs index 32b19c1b8..95200f399 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/PacketHeaders.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/PacketHeaders.cs @@ -2,7 +2,6 @@ namespace DotNext.Net.Cluster.Consensus.Raft.TransportServices.Datagram; using Buffers; -#pragma warning disable CA2252 // TODO: Remove in .NET 7 internal readonly struct PacketHeaders : IBinaryFormattable { /// @@ -27,5 +26,4 @@ internal PacketHeaders(ref SpanReader input) internal MessageType Type => control.Type; internal FlowControl Control => control.Control; -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs index 148ae891f..f159b3db5 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/Gossip/RumorTimestamp.cs @@ -11,7 +11,6 @@ namespace DotNext.Net.Cluster.Messaging.Gossip; /// Represents Lamport timestamp of the rumor mixed with the timestamp returned /// by the local clock. /// -#pragma warning disable CA2252 // TODO: Remove in .NET 7 [StructLayout(LayoutKind.Sequential)] public readonly struct RumorTimestamp : IEquatable, IBinaryFormattable, IComparable { @@ -244,5 +243,4 @@ public int CompareTo(RumorTimestamp other) /// A new timestamp that is greater than previous. public static RumorTimestamp Next(ref RumorTimestamp location) => new(location.timestamp, Interlocked.Increment(ref Unsafe.AsRef(in location.sequenceNumber))); -} -#pragma warning restore CA2252 \ No newline at end of file +} \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageAttribute.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageAttribute.cs index 33602dca6..5d8d26902 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageAttribute.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageAttribute.cs @@ -1,5 +1,4 @@ using System.Net.Mime; -using System.Runtime.Versioning; namespace DotNext.Net.Cluster.Messaging; @@ -45,18 +44,12 @@ public string MimeType /// The type of the message payload. /// /// -[RequiresPreviewFeatures] -public sealed class MessageAttribute : MessageAttribute +/// +/// Initializes a new instance of the attribute. +/// +/// The name of the message. +public sealed class MessageAttribute(string name) : MessageAttribute(name) where TMessage : notnull, ISerializable { - /// - /// Initializes a new instance of the attribute. - /// - /// The name of the message. - public MessageAttribute(string name) - : base(name) - { - } - internal override Type MessageType => typeof(TMessage); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.Builder.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.Builder.cs index 2ad780531..aa3254317 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.Builder.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.Builder.cs @@ -1,5 +1,3 @@ -using System.Runtime.Versioning; - namespace DotNext.Net.Cluster.Messaging; using Runtime.Serialization; @@ -156,15 +154,12 @@ public Builder Add(string inputMessageName, Func /// Clears this builder so it can be reused. /// - public void Clear() + public void Reset() { rpcHandlers.Clear(); signalHandlers.Clear(); } - /// - void IResettable.Reset() => Clear(); - /// /// Constructs message hander based on registered delegates. /// @@ -175,7 +170,5 @@ public void Clear() MessageHandler ISupplier.Invoke() => Build(); } - /// - [RequiresPreviewFeatures] - static Builder IBuildable.CreateBuilder() => new(); + static Builder IBuildable.CreateBuilder() => new(); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessagingClient.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessagingClient.cs index a4c2e52e7..f9e35fa99 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessagingClient.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessagingClient.cs @@ -1,6 +1,5 @@ using System.Net.Mime; using System.Reflection; -using System.Runtime.Versioning; namespace DotNext.Net.Cluster.Messaging; @@ -55,7 +54,6 @@ public MessagingClient RegisterMessage(string name, ContentType? type /// The type of the message payload. /// The information about message type. /// This client. - [RequiresPreviewFeatures] public MessagingClient RegisterMessage(MessageAttribute messageInfo) where TMessage : notnull, ISerializable => RegisterMessage(messageInfo.Name, new(messageInfo.MimeType)); From a8195c04b0ac238596b9be9755af53c08fd842f5 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 21:31:28 +0200 Subject: [PATCH 063/155] Improved compatibility with IL trimming --- src/DotNext/Dynamic/TaskResultBinder.cs | 10 +++------- .../Threading/Tasks/DynamicTaskAwaitable.cs | 18 +++++++++++++----- src/DotNext/Threading/Tasks/Synchronization.cs | 4 ++-- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/DotNext/Dynamic/TaskResultBinder.cs b/src/DotNext/Dynamic/TaskResultBinder.cs index 8bd969587..10fa2fa41 100644 --- a/src/DotNext/Dynamic/TaskResultBinder.cs +++ b/src/DotNext/Dynamic/TaskResultBinder.cs @@ -7,26 +7,24 @@ namespace DotNext.Dynamic; +[RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")] +[RequiresDynamicCode("DLR is required to resolve underlying task type at runtime")] internal sealed class TaskResultBinder : CallSiteBinder { private const string ResultPropertyName = nameof(Task.Result); private const BindingFlags ResultPropertyFlags = BindingFlags.Public | BindingFlags.Instance; - [RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")] private static Expression BindProperty(PropertyInfo resultProperty, Expression target, out Expression restrictions) { Debug.Assert(resultProperty.DeclaringType is not null); restrictions = Expression.TypeIs(target, resultProperty.DeclaringType); // reinterpret reference type without casting because it is protected by restriction - target = Expression.Call(typeof(Unsafe), nameof(Unsafe.As), new[] { resultProperty.DeclaringType }, target); + target = Expression.Call(typeof(Unsafe), nameof(Unsafe.As), [resultProperty.DeclaringType], target); target = Expression.Property(target, resultProperty); return target.Type.IsValueType ? Expression.Convert(target, typeof(object)) : target; } - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(Task<>))] - [DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(ValueTask<>))] - [RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")] private static Expression Bind(object targetValue, Expression target, LabelTarget returnLabel) { PropertyInfo? property = targetValue.GetType().GetProperty(ResultPropertyName, ResultPropertyFlags); @@ -38,7 +36,5 @@ private static Expression Bind(object targetValue, Expression target, LabelTarge return target; } - [RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")] - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2046", Justification = "No other way to express compatibility with timming")] public override Expression Bind(object[] args, ReadOnlyCollection parameters, LabelTarget returnLabel) => Bind(args[0], parameters[0], returnLabel); } \ No newline at end of file diff --git a/src/DotNext/Threading/Tasks/DynamicTaskAwaitable.cs b/src/DotNext/Threading/Tasks/DynamicTaskAwaitable.cs index 52fc1fed8..9d3e2cbbe 100644 --- a/src/DotNext/Threading/Tasks/DynamicTaskAwaitable.cs +++ b/src/DotNext/Threading/Tasks/DynamicTaskAwaitable.cs @@ -19,14 +19,14 @@ namespace DotNext.Threading.Tasks; [StructLayout(LayoutKind.Auto)] public readonly struct DynamicTaskAwaitable { - private static readonly CallSite> GetResultCallSite = CallSite>.Create(new TaskResultBinder()); - /// /// Provides an object that waits for the completion of an asynchronous task. /// [StructLayout(LayoutKind.Auto)] public readonly struct Awaiter : ICriticalNotifyCompletion { + private static CallSite>? getResultCallSite; + private readonly Task task; private readonly ConfiguredTaskAwaitable.ConfiguredTaskAwaiter awaiter; @@ -55,20 +55,28 @@ void ICriticalNotifyCompletion.UnsafeOnCompleted(Action continuation) private static bool IsTaskWithResult(Type type) => type != CompletedTaskType && type.IsConstructedGenericType; + [RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")] internal object? GetRawResult() => IsTaskWithResult(task.GetType()) ? - GetResultCallSite.Target.Invoke(GetResultCallSite, task) : + GetRawResult(task) : Missing.Value; + [RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")] + private static object? GetRawResult(Task task) + { + var callSite = getResultCallSite ??= CallSite>.Create(new TaskResultBinder()); + return callSite.Target(callSite, task); + } + /// /// Gets dynamically typed task result. /// - /// The result of the completed task; or if underlying task is not of type . + /// The result of the completed task; or if underlying task is not of type . [RequiresUnreferencedCode("Runtime binding may be incompatible with IL trimming")] public dynamic? GetResult() { if (IsTaskWithResult(task.GetType())) - return GetResultCallSite.Target.Invoke(GetResultCallSite, task); + return GetRawResult(task); awaiter.GetResult(); return Missing.Value; diff --git a/src/DotNext/Threading/Tasks/Synchronization.cs b/src/DotNext/Threading/Tasks/Synchronization.cs index 39ef49c59..ce1e3e94b 100644 --- a/src/DotNext/Threading/Tasks/Synchronization.cs +++ b/src/DotNext/Threading/Tasks/Synchronization.cs @@ -86,7 +86,7 @@ public static Result GetResult(this Task task, Cancel var awaiter = new DynamicTaskAwaitable.Awaiter(task, false); result = new(awaiter.GetRawResult()); } - catch (AggregateException e) when (e.InnerExceptions.Count == 1) + catch (AggregateException e) when (e.InnerExceptions.Count is 1) { result = new(e.InnerExceptions[0]); } @@ -117,7 +117,7 @@ public static Result GetResult(this Task task, Cancel var awaiter = new DynamicTaskAwaitable.Awaiter(task, false); result = new(awaiter.GetRawResult()); } - catch (AggregateException e) when (e.InnerExceptions.Count == 1) + catch (AggregateException e) when (e.InnerExceptions.Count is 1) { result = new(e.InnerExceptions[0]); } From cd1431c96228854a62339d97971b7e3f0696e661 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 21:56:44 +0200 Subject: [PATCH 064/155] Fixed TODOs --- src/DotNext.IO/Text/Json/JsonSerializable.cs | 2 +- .../Raft/Commands/CommandInterpreterTests.cs | 2 +- .../Net/EndPointFormatterTests.cs | 16 +--- .../Threading/AsyncTrigger.cs | 2 +- .../Tasks/TaskCompletionPipe.Consumer.cs | 77 ++++++++----------- .../Raft/Commands/CommandInterpreter.cs | 7 +- .../Commands/{LogEntry.cs => RaftLogEntry.cs} | 4 +- .../Consensus/Raft/RaftCluster.DefaultImpl.cs | 5 +- .../Net/Cluster/Consensus/Raft/Result.cs | 2 +- .../HyParView/PeerController.Command.cs | 2 +- .../Net/Cluster/Messaging/MessageHandler.cs | 6 +- 11 files changed, 49 insertions(+), 76 deletions(-) rename src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/{LogEntry.cs => RaftLogEntry.cs} (86%) diff --git a/src/DotNext.IO/Text/Json/JsonSerializable.cs b/src/DotNext.IO/Text/Json/JsonSerializable.cs index d9f211bc6..f02d319bd 100644 --- a/src/DotNext.IO/Text/Json/JsonSerializable.cs +++ b/src/DotNext.IO/Text/Json/JsonSerializable.cs @@ -23,7 +23,7 @@ public record struct JsonSerializable : ISerializable>, I /// /// Represents JSON serializable object. /// - public T Value; // TODO: Change to required in C# 11 + required public T Value; /// readonly T ISupplier.Invoke() => Value; diff --git a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/Commands/CommandInterpreterTests.cs b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/Commands/CommandInterpreterTests.cs index da87d2c01..65fa80697 100644 --- a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/Commands/CommandInterpreterTests.cs +++ b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/Commands/CommandInterpreterTests.cs @@ -166,7 +166,7 @@ public TestPersistenceState() internal int Value => interpreter.Value; - internal LogEntry CreateLogEntry(TCommand command) + internal RaftLogEntry CreateLogEntry(TCommand command) where TCommand : struct, ISerializable => interpreter.CreateLogEntry(command, Term); diff --git a/src/DotNext.Tests/Net/EndPointFormatterTests.cs b/src/DotNext.Tests/Net/EndPointFormatterTests.cs index 50b4903ea..99c4407cd 100644 --- a/src/DotNext.Tests/Net/EndPointFormatterTests.cs +++ b/src/DotNext.Tests/Net/EndPointFormatterTests.cs @@ -10,18 +10,6 @@ namespace DotNext.Net; public sealed class EndPointFormatterTests : Test { - // TODO: Remove in .NET 7/8 because of https://github.com/dotnet/runtime/pull/69722 - private sealed class UnixDomainSocketEndPointComparer : IEqualityComparer - { - internal static readonly UnixDomainSocketEndPointComparer Instance = new(); - - bool IEqualityComparer.Equals(EndPoint x, EndPoint y) - => string.Equals(x?.ToString(), y?.ToString(), StringComparison.Ordinal); - - int IEqualityComparer.GetHashCode(EndPoint obj) - => string.GetHashCode(obj.ToString(), StringComparison.Ordinal); - } - public static IEnumerable GetTestEndPoints() { yield return new object[] { new DnsEndPoint("host", 3262), EqualityComparer.Default }; @@ -37,8 +25,8 @@ public static IEnumerable GetTestEndPoints() if (Socket.OSSupportsUnixDomainSockets) { - yield return new object[] { new UnixDomainSocketEndPoint("@abstract"), UnixDomainSocketEndPointComparer.Instance }; - yield return new object[] { new UnixDomainSocketEndPoint(Path.GetTempFileName()), UnixDomainSocketEndPointComparer.Instance }; + yield return new object[] { new UnixDomainSocketEndPoint("@abstract"), EqualityComparer.Default }; + yield return new object[] { new UnixDomainSocketEndPoint(Path.GetTempFileName()), EqualityComparer.Default }; } } diff --git a/src/DotNext.Threading/Threading/AsyncTrigger.cs b/src/DotNext.Threading/Threading/AsyncTrigger.cs index 5d16cef13..a19e63153 100644 --- a/src/DotNext.Threading/Threading/AsyncTrigger.cs +++ b/src/DotNext.Threading/Threading/AsyncTrigger.cs @@ -306,7 +306,7 @@ private async ValueTask SpinWaitAsync(ConditionalLockManager : ILockManager where TCondition : notnull, ISupplier { - internal TCondition Condition; // TODO: Change to required in C# 12 + required internal TCondition Condition; bool ILockManager.IsLockAllowed => Condition.Invoke(); diff --git a/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.Consumer.cs b/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.Consumer.cs index ba95a6c11..f39f19d3f 100644 --- a/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.Consumer.cs +++ b/src/DotNext.Threading/Threading/Tasks/TaskCompletionPipe.Consumer.cs @@ -1,70 +1,53 @@ using System.Runtime.InteropServices; using Debug = System.Diagnostics.Debug; -using Unsafe = System.Runtime.CompilerServices.Unsafe; namespace DotNext.Threading.Tasks; -using Intrinsics = Runtime.Intrinsics; - -public partial class TaskCompletionPipe : IDynamicInterfaceCastable -{ - internal static Tuple? RuntimeEnumerableInfo; - - /// - bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) - { - var info = RuntimeEnumerableInfo; - return info is not null && interfaceType.Equals(info.Item1) || (throwIfNotImplemented ? throw new InvalidCastException() : false); - } - - /// - RuntimeTypeHandle IDynamicInterfaceCastable.GetInterfaceImplementation(RuntimeTypeHandle interfaceType) - { - var info = RuntimeEnumerableInfo; - return info is not null && interfaceType.Equals(info.Item1) ? info.Item2 : default; - } -} - /// /// Provides various extension methods for class. /// public static class TaskCompletionPipe { - [DynamicInterfaceCastableImplementation] - private interface ITypedTaskCompletionPipe : IAsyncEnumerable + /// + /// Gets asynchronous consumer. + /// + /// The type of the elements in the consuming collection. + /// The task completion pipe with typed tasks. + /// The asynchronous consuming collection. + public static Consumer GetConsumer(this TaskCompletionPipe> pipe) + => new(pipe); + + private static async IAsyncEnumerator GetAsyncEnumerator(TaskCompletionPipe> pipe, uint expectedVersion, CancellationToken token) { - private static async IAsyncEnumerator GetAsyncEnumerator(TaskCompletionPipe> pipe, uint expectedVersion, CancellationToken token) + while (await pipe.TryDequeue(expectedVersion, out var task, token).ConfigureAwait(false)) { - while (await pipe.TryDequeue(expectedVersion, out var task, token).ConfigureAwait(false)) + if (task is not null) { - if (task is not null) - { - Debug.Assert(task.IsCompleted); + Debug.Assert(task.IsCompleted); - yield return await task.ConfigureAwait(false); - } + yield return await task.ConfigureAwait(false); } } - - IAsyncEnumerator IAsyncEnumerable.GetAsyncEnumerator(CancellationToken token) - { - Debug.Assert(this is TaskCompletionPipe>); - - var pipe = Unsafe.As>>(this); - return GetAsyncEnumerator(pipe, pipe.Version, token); - } } /// - /// Gets asynchronous consumer. + /// Represents asynchronous consumer for the pipe. /// - /// The type of the elements in the consuming collection. - /// The task completion pipe with typed tasks. - /// The asynchronous consuming collection. - public static IAsyncEnumerable GetConsumer(this TaskCompletionPipe> pipe) // TODO: Change to struct and remove IDynamicInterfaceCastable + /// The type of the result produced by the pipe. + [StructLayout(LayoutKind.Auto)] + public readonly struct Consumer : IAsyncEnumerable { - // dynamic interface dispatch must be compatible with AOT. Thus, we cannot use things like Type.MakeGenericType - TaskCompletionPipe>.RuntimeEnumerableInfo ??= new(Intrinsics.TypeOf>(), Intrinsics.TypeOf>()); - return (IAsyncEnumerable)pipe; + private readonly TaskCompletionPipe> pipe; + + internal Consumer(TaskCompletionPipe> pipe) + => this.pipe = pipe; + + /// + /// Gets asynchronous enumerator over completed tasks. + /// + /// The token that can be used to cancel the operation. + /// The asynchronous enumerator over completed tasks. + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken token = default) + => GetAsyncEnumerator(pipe, pipe.Version, token); } } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs index d78d55b4e..e9a4ff7f6 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/CommandInterpreter.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -88,7 +89,7 @@ protected CommandInterpreter() private CommandInterpreter(IDictionary interpreters, IDictionary identifiers, int? snapshotCommandId) { this.interpreters = CreateRegistry(interpreters); - this.identifiers = new Dictionary(identifiers); // TODO: Migrate to FrozenDictionary in .NET 8 + this.identifiers = identifiers.ToFrozenDictionary(); this.snapshotCommandId = snapshotCommandId; } @@ -100,10 +101,10 @@ private CommandInterpreter(IDictionary interpreters, IDicti /// The type of the command. /// The instance of the log entry containing the command. /// is not registered with . - public LogEntry CreateLogEntry(TCommand command, long term) + public RaftLogEntry CreateLogEntry(TCommand command, long term) where TCommand : notnull, ISerializable => identifiers.TryGetValue(typeof(TCommand), out var id) ? - new LogEntry(term, command, id) : + new RaftLogEntry(term, command, id) : throw new GenericArgumentException(ExceptionMessages.MissingCommandId, nameof(command)); private bool TryGetCommandId(ref TEntry entry, out int commandId) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/LogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/RaftLogEntry.cs similarity index 86% rename from src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/LogEntry.cs rename to src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/RaftLogEntry.cs index 8bc066208..869d5afac 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/LogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Commands/RaftLogEntry.cs @@ -10,10 +10,10 @@ namespace DotNext.Net.Cluster.Consensus.Raft.Commands; /// /// The type of the command encoded by the log entry. [StructLayout(LayoutKind.Auto)] -public readonly struct LogEntry : IRaftLogEntry // TODO: Rename to RaftLogEntry +public readonly struct RaftLogEntry : IRaftLogEntry where TCommand : notnull, ISerializable { - internal LogEntry(long term, TCommand command, int id) + internal RaftLogEntry(long term, TCommand command, int id) { Term = term; Timestamp = DateTimeOffset.UtcNow; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs index 9ed4dfd1f..be76d2294 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Diagnostics; using System.Net; using System.Runtime.CompilerServices; @@ -62,7 +63,7 @@ protected override void Dispose(bool disposing) } } - private readonly IReadOnlyDictionary metadata; + private readonly FrozenDictionary metadata; private readonly Func clientFactory; private readonly Func serverFactory; private readonly MemoryAllocator? allocator; @@ -82,7 +83,7 @@ protected override void Dispose(bool disposing) public RaftCluster(NodeConfiguration configuration) : base(configuration, GetMeasurementTags(configuration)) { - metadata = new Dictionary(configuration.Metadata, StringComparer.Ordinal); // TODO: Migrate to FrozenDictionary in .NET 8 + metadata = configuration.Metadata.ToFrozenDictionary(StringComparer.Ordinal); clientFactory = configuration.CreateClient; serverFactory = configuration.CreateServer; localMemberId = ClusterMemberId.FromEndPoint(LocalMemberAddress = configuration.PublicEndPoint); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Result.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Result.cs index 89a152f37..162d88164 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Result.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Result.cs @@ -12,7 +12,7 @@ public readonly struct Result /// /// Gets term of the remote member. /// - public long Term { get; init; } // TODO: Change to required init in C# 12 + required public long Term { get; init; } /// /// Gets RPC response. diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs index 14f3d1bdd..82567e70b 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Discovery/HyParView/PeerController.Command.cs @@ -35,7 +35,7 @@ private enum CommandType [StructLayout(LayoutKind.Auto)] private readonly struct Command { - internal CommandType Type { get; private init; } // TODO: Change to required in C# 11 + required internal CommandType Type { get; init; } [DisallowNull] private EndPoint? Address1 { get; init; } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.cs index 9bb7435ed..7c6280d73 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Messaging/MessageHandler.cs @@ -1,3 +1,4 @@ +using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using System.Reflection; @@ -150,9 +151,8 @@ protected MessageHandler() private MessageHandler(IDictionary rpcHandlers, IDictionary signalHandlers) { - // TODO: Migrate to FrozenDictionary in .NET 8 - this.rpcHandlers = new Dictionary(rpcHandlers, NameComparer); - this.signalHandlers = new Dictionary(signalHandlers, NameComparer); + this.rpcHandlers = rpcHandlers.ToFrozenDictionary(NameComparer); + this.signalHandlers = signalHandlers.ToFrozenDictionary(NameComparer); } /// From b9f25a3ceb10f1c02ba1a4ecdfc1b2d15c7e6dbd Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 21:59:28 +0200 Subject: [PATCH 065/155] Fixed visibility for type --- .../LinkedCancellationTokenSource.cs | 25 ----------------- .../Threading/LinkedTokenSourceFactory.cs | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/DotNext.Threading/Threading/LinkedCancellationTokenSource.cs b/src/DotNext.Threading/Threading/LinkedCancellationTokenSource.cs index 40e8d53b8..5ef1db5c1 100644 --- a/src/DotNext.Threading/Threading/LinkedCancellationTokenSource.cs +++ b/src/DotNext.Threading/Threading/LinkedCancellationTokenSource.cs @@ -75,31 +75,6 @@ protected override void Dispose(bool disposing) } status = UnsetStatus; - base.Dispose(disposing); - } -} - -internal sealed class Linked2CancellationTokenSource : LinkedCancellationTokenSource -{ - private readonly CancellationTokenRegistration registration1, registration2; - - internal Linked2CancellationTokenSource(in CancellationToken token1, in CancellationToken token2) - { - Debug.Assert(token1.CanBeCanceled); - Debug.Assert(token2.CanBeCanceled); - - registration1 = token1.UnsafeRegister(CancellationCallback, this); - registration2 = token2.UnsafeRegister(CancellationCallback, this); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - registration1.Dispose(); - registration2.Dispose(); - } - base.Dispose(disposing); } } \ No newline at end of file diff --git a/src/DotNext.Threading/Threading/LinkedTokenSourceFactory.cs b/src/DotNext.Threading/Threading/LinkedTokenSourceFactory.cs index 45a001c66..f4362f5da 100644 --- a/src/DotNext.Threading/Threading/LinkedTokenSourceFactory.cs +++ b/src/DotNext.Threading/Threading/LinkedTokenSourceFactory.cs @@ -1,3 +1,5 @@ +using Debug = System.Diagnostics.Debug; + namespace DotNext.Threading; /// @@ -87,4 +89,29 @@ public static class LinkedTokenSourceFactory return result; } + + private sealed class Linked2CancellationTokenSource : LinkedCancellationTokenSource + { + private readonly CancellationTokenRegistration registration1, registration2; + + internal Linked2CancellationTokenSource(in CancellationToken token1, in CancellationToken token2) + { + Debug.Assert(token1.CanBeCanceled); + Debug.Assert(token2.CanBeCanceled); + + registration1 = token1.UnsafeRegister(CancellationCallback, this); + registration2 = token2.UnsafeRegister(CancellationCallback, this); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + registration1.Dispose(); + registration2.Dispose(); + } + + base.Dispose(disposing); + } + } } \ No newline at end of file From 9a837a8a3a29c1f35e45741d88c40131a88809fa Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 22:18:23 +0200 Subject: [PATCH 066/155] Migration to file-local types --- .../MemoryMappedViewAccessorExtensions.cs | 12 ------------ .../IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs | 9 +++++++++ .../Authentication/AuthenticationMiddleware.cs | 1 - .../Metaprogramming/ExpressionHelpers.cs | 11 ----------- .../Metaprogramming/MatchBuilder.cs | 8 ++++++++ .../Buffers/UnmanagedMemoryAllocator.cs | 2 +- src/DotNext/UserDataStorage.BackingStorage.cs | 3 +-- .../Net/Cluster/Consensus/Raft/CandidateState.cs | 3 ++- .../Net/Cluster/Consensus/Raft/FollowerState.cs | 2 ++ .../Net/Cluster/Consensus/Raft/LeaderState.cs | 3 ++- .../Net/Cluster/Consensus/Raft/RaftCluster.cs | 3 --- 11 files changed, 25 insertions(+), 32 deletions(-) delete mode 100644 src/DotNext.IO/IO/MemoryMappedFiles/MemoryMappedViewAccessorExtensions.cs delete mode 100644 src/DotNext.Metaprogramming/Metaprogramming/ExpressionHelpers.cs diff --git a/src/DotNext.IO/IO/MemoryMappedFiles/MemoryMappedViewAccessorExtensions.cs b/src/DotNext.IO/IO/MemoryMappedFiles/MemoryMappedViewAccessorExtensions.cs deleted file mode 100644 index a9cb8b554..000000000 --- a/src/DotNext.IO/IO/MemoryMappedFiles/MemoryMappedViewAccessorExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.IO.MemoryMappedFiles; - -namespace DotNext.IO.MemoryMappedFiles; - -internal static class MemoryMappedViewAccessorExtensions -{ - internal static void ReleasePointerAndDispose(this MemoryMappedViewAccessor accessor) - { - accessor.SafeMemoryMappedViewHandle.ReleasePointer(); - accessor.Dispose(); - } -} \ No newline at end of file diff --git a/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs b/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs index 30fe7238e..c13ad02a5 100644 --- a/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs +++ b/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs @@ -233,4 +233,13 @@ protected override unsafe void Dispose(bool disposing) ptr = null; base.Dispose(disposing); } +} + +file static class MemoryMappedViewAccessorExtensions +{ + internal static void ReleasePointerAndDispose(this MemoryMappedViewAccessor accessor) + { + accessor.SafeMemoryMappedViewHandle.ReleasePointer(); + accessor.Dispose(); + } } \ No newline at end of file diff --git a/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/AuthenticationMiddleware.cs b/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/AuthenticationMiddleware.cs index a9eee7c99..bab77e305 100644 --- a/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/AuthenticationMiddleware.cs +++ b/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/AuthenticationMiddleware.cs @@ -4,7 +4,6 @@ namespace DotNext.Maintenance.CommandLine.Authentication; using IMaintenanceConsole = IO.IMaintenanceConsole; -using AnonymousPrincipal = Security.Principal.AnonymousPrincipal; internal static class AuthenticationMiddleware { diff --git a/src/DotNext.Metaprogramming/Metaprogramming/ExpressionHelpers.cs b/src/DotNext.Metaprogramming/Metaprogramming/ExpressionHelpers.cs deleted file mode 100644 index 10f46f8e9..000000000 --- a/src/DotNext.Metaprogramming/Metaprogramming/ExpressionHelpers.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; - -namespace DotNext.Metaprogramming; - -internal static class ExpressionHelpers -{ - [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313", Justification = "Underscore is used to indicate that the parameter is unused")] - internal static Expression TrivialCaseStatement(this Expression value, ParameterExpression _) - => value; -} \ No newline at end of file diff --git a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs index 90331ece9..4ce80f4a9 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices; @@ -627,4 +628,11 @@ internal MatchByTypeWithConditionStatement(MatchBuilder builder, Type expectedTy private protected override MatchBuilder Build(MatchBuilder builder, Action scope) => builder.MatchByType(expectedType, condition, new CaseStatementBuilder(this, scope)); } +} + +file static class ExpressionHelpers +{ + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313", Justification = "Underscore is used to indicate that the parameter is unused")] + internal static Expression TrivialCaseStatement(this Expression value, ParameterExpression _) + => value; } \ No newline at end of file diff --git a/src/DotNext.Unsafe/Buffers/UnmanagedMemoryAllocator.cs b/src/DotNext.Unsafe/Buffers/UnmanagedMemoryAllocator.cs index 68c3e8cbf..338175088 100644 --- a/src/DotNext.Unsafe/Buffers/UnmanagedMemoryAllocator.cs +++ b/src/DotNext.Unsafe/Buffers/UnmanagedMemoryAllocator.cs @@ -32,7 +32,7 @@ public static MemoryAllocator GetAllocator(bool zeroMem) => zeroMem ? UnmanagedMemoryAllocator.ZeroedAllocator : UnmanagedMemoryAllocator.Allocator; } -internal static class UnmanagedMemoryAllocator +file static class UnmanagedMemoryAllocator where T : unmanaged { internal static readonly MemoryAllocator Allocator, ZeroedAllocator; diff --git a/src/DotNext/UserDataStorage.BackingStorage.cs b/src/DotNext/UserDataStorage.BackingStorage.cs index 7cd240c4f..05b8c4f2d 100644 --- a/src/DotNext/UserDataStorage.BackingStorage.cs +++ b/src/DotNext/UserDataStorage.BackingStorage.cs @@ -21,8 +21,7 @@ internal void CopyTo(int typeIndex, Dictionary output) for (var i = 0; i < array.Length; i++) { - var value = (array.GetValue(i) as ISupplier)?.Invoke(); - if (value is not null) + if ((array.GetValue(i) as ISupplier)?.Invoke() is { } value) output[UserDataSlot.ToString(typeIndex, i + 1)] = value; } } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/CandidateState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/CandidateState.cs index 68b3b6e28..fd0a75614 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/CandidateState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/CandidateState.cs @@ -131,6 +131,7 @@ private async Task EndVoting(IAsyncEnumerable<(TMember, long, VotingResult)> vot /// The local transaction log. internal void StartVoting(TimeSpan timeout, IAuditTrail auditTrail) { + CandidateState.TransitionRateMeter.Add(1, in MeasurementTags); Logger.VotingStarted(timeout, Term); votingTask = VoteAsync(timeout, auditTrail); } @@ -163,7 +164,7 @@ protected override void Dispose(bool disposing) } } -internal static class CandidateState +file static class CandidateState { internal static readonly Counter TransitionRateMeter = Metrics.Instrumentation.ServerSide.CreateCounter("transitions-to-candidate-count", description: "Number of Transitions to Candidate State"); } \ No newline at end of file diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs index cda151bcd..b75d49760 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/FollowerState.cs @@ -71,6 +71,8 @@ internal void StartServing(TimeSpan timeout, CancellationToken token) timedOut = false; tracker = Track(timeout, token); } + + FollowerState.TransitionRateMeter.Add(1, in MeasurementTags); } internal bool IsExpired => timedOut; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs index 65131ac8a..f6a119ae1 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/LeaderState.cs @@ -265,6 +265,7 @@ internal void StartLeading(TimeSpan period, IAuditTrail transacti } heartbeatTask = DoHeartbeats(period, transactionLog, configurationStorage, members, token); + LeaderState.TransitionRateMeter.Add(1, in MeasurementTags); } protected override async ValueTask DisposeAsyncCore() @@ -313,7 +314,7 @@ private enum MemberResponse } } -internal static class LeaderState +file static class LeaderState { internal static readonly Histogram BroadcastTimeMeter = Metrics.Instrumentation.ServerSide.CreateHistogram("broadcast-time", unit: "ms", description: "Heartbeat Broadcasting Time"); internal static readonly Counter TransitionRateMeter = Metrics.Instrumentation.ServerSide.CreateCounter("transitions-to-leader-count", description: "Number of Transitions of Leader State"); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs index 9b7703f3d..46e9b5229 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs @@ -498,7 +498,6 @@ private async ValueTask StepDown() var newState = new FollowerState(this); await UpdateStateAsync(newState).ConfigureAwait(false); newState.StartServing(ElectionTimeout, LifecycleToken); - FollowerState.TransitionRateMeter.Add(1, in measurementTags); break; } @@ -1009,7 +1008,6 @@ async void IRaftStateMachine.MoveToCandidateState(IRaftStateMachine.IWe // vote for self newState.StartVoting(ElectionTimeout, auditTrail); - CandidateState.TransitionRateMeter.Add(1, in measurementTags); Logger.TransitionToCandidateStateCompleted(Term); } else @@ -1072,7 +1070,6 @@ async void IRaftStateMachine.MoveToLeaderState(IRaftStateMachine.IWeakC await auditTrail.AppendNoOpEntry(LifecycleToken).ConfigureAwait(false); newState.StartLeading(HeartbeatTimeout, auditTrail, ConfigurationStorage, LifecycleToken); - LeaderState.TransitionRateMeter.Add(1, in measurementTags); Logger.TransitionToLeaderStateCompleted(currentTerm); } } From 1e0f812bd2cb277441fca308ac80c8aa7317551e Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 22:24:34 +0200 Subject: [PATCH 067/155] Fixed type visibility --- .../Cluster/Consensus/Raft/PersistentState.LockManager.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LockManager.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LockManager.cs index 2e8050014..032a5d4d7 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LockManager.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LockManager.cs @@ -22,7 +22,12 @@ internal enum LockType : int // Compaction lock - disallow reads, allow writes (to the end of the log), disallow compaction // Exclusive lock - disallow everything // Write lock + Compaction lock = exclusive lock - internal sealed class LockManager : QueuedSynchronizer +#if DEBUG + internal +#else + private protected +#endif + sealed class LockManager : QueuedSynchronizer { private readonly uint maxReadCount; private uint readerCount; From f3b62bd22f5cd2284be4e826a3cfb6f4496ccd74 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 22:33:54 +0200 Subject: [PATCH 068/155] Removed unused warning suppressions --- src/DotNext.IO/IO/FileBufferingWriter.Options.cs | 1 - src/DotNext.IO/IO/FileBufferingWriter.cs | 3 --- .../Linq/Expressions/AwaitExpression.cs | 1 - .../Runtime/CompilerServices/AsyncStateMachineBuilder.cs | 1 - .../Runtime/CompilerServices/ClosureAnalyzer.cs | 1 - .../Runtime/CompilerServices/ExpressionAttributes.cs | 4 +--- .../Runtime/CompilerServices/VisitorContext.cs | 2 -- src/DotNext.Threading/Threading/Channels/PersistentChannel.cs | 3 --- .../Threading/Channels/PersistentChannelOptions.cs | 1 - .../Threading/Channels/PersistentChannelReader.cs | 1 - src/DotNext/Buffers/ReadOnlySequencePartitioner.cs | 2 +- src/DotNext/Threading/LockAcquisition.cs | 4 +--- .../Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs | 2 -- .../Net/Cluster/Consensus/Raft/PersistentState.Options.cs | 1 - .../Net/Cluster/Consensus/Raft/PersistentState.cs | 1 - 15 files changed, 3 insertions(+), 25 deletions(-) diff --git a/src/DotNext.IO/IO/FileBufferingWriter.Options.cs b/src/DotNext.IO/IO/FileBufferingWriter.Options.cs index 3457d9e40..bbb35d211 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.Options.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.Options.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Diagnostics.Tracing; using System.Runtime.InteropServices; namespace DotNext.IO; diff --git a/src/DotNext.IO/IO/FileBufferingWriter.cs b/src/DotNext.IO/IO/FileBufferingWriter.cs index 71dbe53d3..ce63ae661 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Metrics; -using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -165,7 +164,6 @@ static FileBufferingWriter() private readonly BackingFileProvider fileProvider; private readonly int memoryThreshold; private readonly MemoryAllocator? allocator; - private readonly EventCounter? allocationCounter; private MemoryOwner buffer; private int position; private FileStream? fileBackend; @@ -344,7 +342,6 @@ private MemoryEvaluationResult PrepareMemory(int size, out Memory output) newSize = bufLen; buffer.Resize(newSize, exactSize: false, allocator: allocator); - allocationCounter?.WriteMetric(buffer.Length); AllocationMeter.Record(buffer.Length, measurementTags); } diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/AwaitExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/AwaitExpression.cs index 7975610b1..4004722f1 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/AwaitExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/AwaitExpression.cs @@ -14,7 +14,6 @@ namespace DotNext.Linq.Expressions; /// Await expression public sealed class AwaitExpression : CustomExpression { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot IsAwaiterVarSlot = new(); /// diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs index c58001ef1..7af7fe79e 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/AsyncStateMachineBuilder.cs @@ -24,7 +24,6 @@ namespace DotNext.Runtime.CompilerServices; /// internal sealed class AsyncStateMachineBuilder : ExpressionVisitor, IDisposable { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot ParameterPositionSlot = new(); // small optimization - reuse variable for awaiters of the same type diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs index b8c382769..a064b4c28 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/ClosureAnalyzer.cs @@ -6,7 +6,6 @@ namespace DotNext.Runtime.CompilerServices; internal sealed class ClosureAnalyzer : ExpressionVisitor { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot ClosureVariableSlot = new(); private readonly ICollection locals; diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/ExpressionAttributes.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/ExpressionAttributes.cs index 75dda9ee9..392355a3f 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/ExpressionAttributes.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/ExpressionAttributes.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; +using System.Linq.Expressions; namespace DotNext.Runtime.CompilerServices; @@ -8,7 +7,6 @@ namespace DotNext.Runtime.CompilerServices; /// internal class ExpressionAttributes { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot AttributesSlot = new(); /// diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs index d1d43421b..7b08b9620 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/VisitorContext.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace DotNext.Runtime.CompilerServices; @@ -7,7 +6,6 @@ namespace DotNext.Runtime.CompilerServices; internal sealed class VisitorContext : Disposable { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot StateIdPlaceholder = new(); private readonly Stack attributes; private readonly Stack statements; diff --git a/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs b/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs index 85559becb..5a6613535 100644 --- a/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs +++ b/src/DotNext.Threading/Threading/Channels/PersistentChannel.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; using System.Threading.Channels; namespace DotNext.Threading.Channels; @@ -20,7 +19,6 @@ public abstract class PersistentChannel : Channel.MessageReady() { readTrigger.Signal(); - writeRate?.Increment(); IChannel.WriteRateMeter.Add(1, measurementTags); } diff --git a/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs b/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs index 618c4b778..3dc84e2fa 100644 --- a/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs +++ b/src/DotNext.Threading/Threading/Channels/PersistentChannelOptions.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Diagnostics.Tracing; using System.Threading.Channels; namespace DotNext.Threading.Channels; diff --git a/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs b/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs index b0ed386ad..ec785bdd6 100644 --- a/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs +++ b/src/DotNext.Threading/Threading/Channels/PersistentChannelReader.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Threading.Channels; using Debug = System.Diagnostics.Debug; diff --git a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs index b3c10bb80..6b3f868c0 100644 --- a/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs +++ b/src/DotNext/Buffers/ReadOnlySequencePartitioner.cs @@ -8,7 +8,7 @@ namespace DotNext.Buffers; using Enumerator = Collections.Generic.Enumerator; -internal sealed class ReadOnlySequencePartitioner : OrderablePartitioner +file sealed class ReadOnlySequencePartitioner : OrderablePartitioner { private sealed class SegmentProvider : IEnumerable> { diff --git a/src/DotNext/Threading/LockAcquisition.cs b/src/DotNext/Threading/LockAcquisition.cs index 552246f68..5c26e88b3 100644 --- a/src/DotNext/Threading/LockAcquisition.cs +++ b/src/DotNext/Threading/LockAcquisition.cs @@ -1,5 +1,4 @@ -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; namespace DotNext.Threading; @@ -8,7 +7,6 @@ namespace DotNext.Threading; /// public static class LockAcquisition { - [SuppressMessage("Performance", "CA1805", Justification = "https://github.com/dotnet/roslyn-analyzers/issues/5750")] private static readonly UserDataSlot ReaderWriterLock = new(); private sealed class ReaderWriterLockSlimWithRecursion : ReaderWriterLockSlim diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs index 6c5e436be..2dbdab176 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.Options.cs @@ -1,5 +1,3 @@ -using System.Diagnostics.Tracing; - namespace DotNext.Net.Cluster.Consensus.Raft; public partial class MemoryBasedStateMachine diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs index 4e7a1f0f3..217fcecfe 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Options.cs @@ -1,6 +1,5 @@ using System.Buffers; using System.Diagnostics; -using System.Diagnostics.Tracing; using System.IO.Compression; namespace DotNext.Net.Cluster.Consensus.Raft; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs index 2e598c013..e6b1c6985 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Metrics; -using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; namespace DotNext.Net.Cluster.Consensus.Raft; From 96e95ca2029b927574fea6f282f21b0f1ac69918 Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 22:43:27 +0200 Subject: [PATCH 069/155] Fixed naming convention --- src/DotNext.Tests/ServiceProviderFactoryTests.cs | 2 +- src/DotNext/ServiceProviderFactory.Builder.cs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/DotNext.Tests/ServiceProviderFactoryTests.cs b/src/DotNext.Tests/ServiceProviderFactoryTests.cs index 594872273..d7a23c976 100644 --- a/src/DotNext.Tests/ServiceProviderFactoryTests.cs +++ b/src/DotNext.Tests/ServiceProviderFactoryTests.cs @@ -16,7 +16,7 @@ public static void UseBuilderStyle() IsType(provider.GetService(typeof(IFormattable))); Null(provider.GetService(typeof(string))); - builder.Clear(); + builder.Reset(); Same(ServiceProviderFactory.Empty, builder.Build()); } diff --git a/src/DotNext/ServiceProviderFactory.Builder.cs b/src/DotNext/ServiceProviderFactory.Builder.cs index 5a326dc93..a3e45e912 100644 --- a/src/DotNext/ServiceProviderFactory.Builder.cs +++ b/src/DotNext/ServiceProviderFactory.Builder.cs @@ -41,10 +41,7 @@ public IServiceProvider Build(IServiceProvider? fallback = null) /// /// Clears internal state of this builder and makes it reusable for subsequent calls. /// - public void Clear() => services.Clear(); - - /// - void IResettable.Reset() => Clear(); + public void Reset() => services.Clear(); /// IServiceProvider ISupplier.Invoke() => Build(); From ddb8640682df12218a54d35c54b56677b041ee9e Mon Sep 17 00:00:00 2001 From: sakno Date: Fri, 24 Nov 2023 23:26:10 +0200 Subject: [PATCH 070/155] Migration to UnscopedRef --- src/DotNext.Tests/Buffers/Text/Base64Tests.cs | 18 +++---- .../Buffers/Text/Base64Decoder.Unicode.cs | 21 ++------ .../Buffers/Text/Base64Decoder.Utf8.cs | 19 ++----- src/DotNext/Buffers/Text/Base64Decoder.cs | 33 +++++++++++++ .../Buffers/Text/Base64Encoder.Unicode.cs | 10 ++-- .../Buffers/Text/Base64Encoder.Utf8.cs | 8 +-- src/DotNext/Buffers/Text/Base64Encoder.cs | 49 +++++++++---------- 7 files changed, 82 insertions(+), 76 deletions(-) diff --git a/src/DotNext.Tests/Buffers/Text/Base64Tests.cs b/src/DotNext.Tests/Buffers/Text/Base64Tests.cs index 205e2c2b3..754c4c645 100644 --- a/src/DotNext.Tests/Buffers/Text/Base64Tests.cs +++ b/src/DotNext.Tests/Buffers/Text/Base64Tests.cs @@ -198,7 +198,7 @@ public static void EncodeToBufferWriterUtf8(int size) encoder.EncodeToUtf8(expected.AsSpan(0, size / 2), writer, flush: false); encoder.EncodeToUtf8(expected.AsSpan().Slice(size / 2), writer, flush: true); - Equal(0, encoder.BufferedDataSize); + Equal(0, encoder.BufferedData.Length); var decoder = new Base64Decoder(); using var actual = decoder.DecodeFromUtf8(writer.WrittenSpan); @@ -271,15 +271,15 @@ public static void FlushToBytes() { var encoder = new Base64Encoder(); var writer = new ArrayBufferWriter(); - byte[] expected = { 1, 2 }; + byte[] expected = [1, 2]; encoder.EncodeToUtf8(expected, writer, flush: false); True(encoder.HasBufferedData); - Span base64 = stackalloc byte[4]; - Equal(2, encoder.GetBufferedData(base64)); - Equal(expected, base64.Slice(0, 2).ToArray()); + Equal(2, encoder.BufferedData.Length); + Equal(expected, encoder.BufferedData.Slice(0, 2).ToArray()); + Span base64 = stackalloc byte[Base64Encoder.MaxCharsToFlush]; Equal(4, encoder.Flush(base64)); } @@ -357,7 +357,7 @@ public static void EncodeToCallbackUtf8(int size) encoder.EncodeToUtf8(expected.AsSpan(0, size / 2), Write, writer, flush: false); encoder.EncodeToUtf8(expected.AsSpan().Slice(size / 2), Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); + Equal(0, encoder.BufferedData.Length); var decoder = new Base64Decoder(); using var actual = decoder.DecodeFromUtf8(writer.WrittenSpan); @@ -384,7 +384,7 @@ public static void EncodeToCallbackChars(int size) encoder.EncodeToChars(expected.AsSpan(0, size / 2), Write, writer, flush: false); encoder.EncodeToChars(expected.AsSpan().Slice(size / 2), Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); + Equal(0, encoder.BufferedData.Length); var decoder = new Base64Decoder(); using var actual = decoder.DecodeFromUtf16(writer.WrittenSpan); @@ -411,7 +411,7 @@ public static unsafe void EncodeToFunctionPointerUtf8(int size) encoder.EncodeToUtf8(expected.AsSpan(0, size / 2), &Write, writer, flush: false); encoder.EncodeToUtf8(expected.AsSpan().Slice(size / 2), &Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); + Equal(0, encoder.BufferedData.Length); var decoder = new Base64Decoder(); using var actual = decoder.DecodeFromUtf8(writer.WrittenSpan); @@ -438,7 +438,7 @@ public static unsafe void EncodeToFunctionPointerChars(int size) encoder.EncodeToChars(expected.AsSpan(0, size / 2), &Write, writer, flush: false); encoder.EncodeToChars(expected.AsSpan().Slice(size / 2), &Write, writer, flush: true); - Equal(0, encoder.BufferedDataSize); + Equal(0, encoder.BufferedData.Length); var decoder = new Base64Decoder(); using var actual = decoder.DecodeFromUtf16(writer.WrittenSpan); diff --git a/src/DotNext/Buffers/Text/Base64Decoder.Unicode.cs b/src/DotNext/Buffers/Text/Base64Decoder.Unicode.cs index e37516ca2..477255fa9 100644 --- a/src/DotNext/Buffers/Text/Base64Decoder.Unicode.cs +++ b/src/DotNext/Buffers/Text/Base64Decoder.Unicode.cs @@ -2,7 +2,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace DotNext.Buffers.Text; @@ -12,18 +11,6 @@ public partial struct Base64Decoder { private const char PaddingChar = '='; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Span AsChars(ref ulong value) - => MemoryMarshal.CreateSpan(ref Unsafe.As(ref value), sizeof(ulong) / sizeof(char)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlySpan AsChars(in ulong value, int length) - { - Debug.Assert((uint)length <= (uint)(sizeof(ulong) / sizeof(char))); - - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref Unsafe.AsRef(in value)), length); - } - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010", Justification = "False positive")] private bool DecodeFromUtf16Core(scoped ReadOnlySpan chars, scoped ref TWriter writer) where TWriter : notnull, IBufferWriter @@ -36,7 +23,7 @@ private bool DecodeFromUtf16Core(scoped ReadOnlySpan chars, scope // size of the rest size = chars.Length - size; var rest = chars.Slice(size); - rest.CopyTo(AsChars(ref reservedBuffer)); + rest.CopyTo(CharBuffer); reservedBufferSize = rest.Length; // keep the number of chars, not bytes chars = chars.Slice(0, size); } @@ -66,7 +53,7 @@ private bool CopyAndDecodeFromUtf16(scoped ReadOnlySpan chars, sc var newSize = reservedBufferSize + chars.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc char[newSize] : new MemoryRental(newSize); - AsChars(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedChars.CopyTo(tempBuffer.Span); chars.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); return DecodeFromUtf16Core(tempBuffer.Span, ref writer); } @@ -150,7 +137,7 @@ private void DecodeFromUtf16Core(scoped ReadOnlySpan chars, TCo { reservedBufferSize = chunk.Length - consumed; Debug.Assert(reservedBufferSize <= 4); - chunk.Slice(consumed).CopyTo(AsChars(ref reservedBuffer)); + chunk.Slice(consumed).CopyTo(CharBuffer); } if (consumed is 0 || produced is 0) @@ -195,7 +182,7 @@ private void CopyAndDecodeFromUtf16(scoped ReadOnlySpan chars, var newSize = reservedBufferSize + chars.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc char[newSize] : new MemoryRental(newSize); - AsChars(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedChars.CopyTo(tempBuffer.Span); chars.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); DecodeFromUtf16Core(tempBuffer.Span, output); } diff --git a/src/DotNext/Buffers/Text/Base64Decoder.Utf8.cs b/src/DotNext/Buffers/Text/Base64Decoder.Utf8.cs index 5c0459444..bc89482e8 100644 --- a/src/DotNext/Buffers/Text/Base64Decoder.Utf8.cs +++ b/src/DotNext/Buffers/Text/Base64Decoder.Utf8.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace DotNext.Buffers.Text; @@ -14,14 +13,6 @@ public partial struct Base64Decoder { private const byte PaddingByte = (byte)PaddingChar; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlySpan AsReadOnlyBytes(in ulong value, int length) - { - Debug.Assert((uint)length <= (uint)sizeof(ulong)); - - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref Unsafe.AsRef(in value)), length); - } - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1010", Justification = "False positive")] private bool DecodeFromUtf8Core(scoped ReadOnlySpan utf8Chars, scoped ref TWriter writer) where TWriter : notnull, IBufferWriter @@ -45,7 +36,7 @@ private bool DecodeFromUtf8Core(scoped ReadOnlySpan utf8Chars, sc case OperationStatus.NeedMoreData: reservedBufferSize = utf8Chars.Length - consumed; Debug.Assert(reservedBufferSize <= sizeof(ulong)); - utf8Chars.Slice(consumed).CopyTo(Span.AsBytes(ref reservedBuffer)); + utf8Chars.Slice(consumed).CopyTo(ByteBuffer); break; } @@ -61,7 +52,7 @@ private bool CopyAndDecodeFromUtf8(scoped ReadOnlySpan utf8Chars, var newSize = reservedBufferSize + utf8Chars.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc byte[newSize] : new MemoryRental(newSize); - AsReadOnlyBytes(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedBytes.CopyTo(tempBuffer.Span); utf8Chars.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); return DecodeFromUtf8Core(tempBuffer.Span, ref writer); } @@ -152,8 +143,8 @@ private void DecodeFromUtf8Core(scoped ReadOnlySpan utf8Chars, break; case OperationStatus.NeedMoreData: reservedBufferSize = utf8Chars.Length - consumed; - Debug.Assert(reservedBufferSize <= sizeof(ulong) / sizeof(char)); - utf8Chars.Slice(consumed).CopyTo(Span.AsBytes(ref reservedBuffer)); + Debug.Assert(reservedBufferSize <= sizeof(ulong)); + utf8Chars.Slice(consumed).CopyTo(ByteBuffer); break; } @@ -174,7 +165,7 @@ private void CopyAndDecodeFromUtf8(scoped ReadOnlySpan utf8Char var newSize = reservedBufferSize + utf8Chars.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc byte[newSize] : new MemoryRental(newSize); - AsReadOnlyBytes(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedBytes.CopyTo(tempBuffer.Span); utf8Chars.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); DecodeFromUtf8Core(tempBuffer.Span, output); } diff --git a/src/DotNext/Buffers/Text/Base64Decoder.cs b/src/DotNext/Buffers/Text/Base64Decoder.cs index 2585d92ea..eadd86ced 100644 --- a/src/DotNext/Buffers/Text/Base64Decoder.cs +++ b/src/DotNext/Buffers/Text/Base64Decoder.cs @@ -1,8 +1,12 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DotNext.Buffers.Text; +using static Runtime.Intrinsics; + /// /// Represents base64 decoder suitable for decoding large base64-encoded binary /// data using streaming approach. @@ -36,4 +40,33 @@ public partial struct Base64Decoder : IResettable /// Resets the internal state of the decoder. /// public void Reset() => reservedBufferSize = 0; + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private Span CharBuffer + => MemoryMarshal.CreateSpan(ref Unsafe.As(ref reservedBuffer), sizeof(ulong) / sizeof(char)); + + private readonly ReadOnlySpan BufferedChars + { + get + { + Debug.Assert((uint)reservedBufferSize <= sizeof(ulong) / sizeof(char)); + + return MemoryMarshal.CreateReadOnlySpan(in InToRef(in reservedBuffer), reservedBufferSize); + } + } + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [UnscopedRef] + private Span ByteBuffer + => Span.AsBytes(ref reservedBuffer); + + private readonly ReadOnlySpan BufferedBytes + { + get + { + Debug.Assert((uint)reservedBufferSize <= sizeof(ulong)); + + return MemoryMarshal.CreateReadOnlySpan(in InToRef(ref Unsafe.AsRef(in reservedBuffer)), reservedBufferSize); + } + } } \ No newline at end of file diff --git a/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs b/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs index 1938fe563..ef099ba73 100644 --- a/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs +++ b/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs @@ -27,7 +27,7 @@ private void EncodeToCharsCore(scoped ReadOnlySpan bytes, scoped // size of the rest size = bytes.Length - size; var rest = bytes.Slice(size); - rest.CopyTo(Span.AsBytes(ref reservedBuffer)); + rest.CopyTo(Buffer); reservedBufferSize = rest.Length; bytes = bytes.Slice(0, size); } @@ -42,7 +42,7 @@ private void CopyAndEncodeToChars(scoped ReadOnlySpan bytes, scop { var newSize = reservedBufferSize + bytes.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc byte[newSize] : new MemoryRental(newSize); - AsReadOnlyBytes(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedData.CopyTo(tempBuffer.Span); bytes.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); EncodeToCharsCore(tempBuffer.Span, ref writer, flush); } @@ -117,7 +117,7 @@ private void EncodeToCharsCore(scoped ReadOnlySpan bytes, TCons { reservedBufferSize = chunk.Length - consumed; Debug.Assert(reservedBufferSize <= MaxBufferedDataSize); - chunk.Slice(consumed).CopyTo(Span.AsBytes(ref reservedBuffer)); + chunk.Slice(consumed).CopyTo(Buffer); } if (consumed > 0 && produced > 0) @@ -130,7 +130,7 @@ private void EncodeToCharsCore(scoped ReadOnlySpan bytes, TCons // flush the rest of the buffer if (HasBufferedData && flush) { - Convert.TryToBase64Chars(AsReadOnlyBytes(in reservedBuffer, reservedBufferSize), buffer, out produced); + Convert.TryToBase64Chars(BufferedData, buffer, out produced); Reset(); output.Invoke(buffer.Slice(0, produced)); } @@ -158,7 +158,7 @@ private void CopyAndEncodeToChars(scoped ReadOnlySpan bytes, TC { var newSize = reservedBufferSize + bytes.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc byte[newSize] : new MemoryRental(newSize); - AsReadOnlyBytes(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedData.CopyTo(tempBuffer.Span); bytes.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); EncodeToCharsCore(tempBuffer.Span, output, flush); } diff --git a/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs b/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs index e6609c3bb..cbc0d4109 100644 --- a/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs +++ b/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs @@ -37,7 +37,7 @@ private void CopyAndEncodeToUtf8(scoped ReadOnlySpan bytes, scope { var newSize = reservedBufferSize + bytes.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc byte[newSize] : new MemoryRental(newSize); - AsReadOnlyBytes(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedData.CopyTo(tempBuffer.Span); bytes.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); EncodeToUtf8Core(tempBuffer.Span, ref writer, flush); } @@ -125,7 +125,7 @@ private void EncodeToUtf8Core(scoped ReadOnlySpan bytes, TConsu // flush the rest of the buffer if (HasBufferedData && flush) { - Base64.EncodeToUtf8(AsReadOnlyBytes(in reservedBuffer, reservedBufferSize), buffer, out consumed, out produced); + Base64.EncodeToUtf8(BufferedData, buffer, out consumed, out produced); Reset(); output.Invoke(buffer.Slice(0, produced)); } @@ -137,7 +137,7 @@ private void CopyAndEncodeToUtf8(scoped ReadOnlySpan bytes, TCo { var newSize = reservedBufferSize + bytes.Length; using var tempBuffer = (uint)newSize <= (uint)MemoryRental.StackallocThreshold ? stackalloc byte[newSize] : new MemoryRental(newSize); - AsReadOnlyBytes(in reservedBuffer, reservedBufferSize).CopyTo(tempBuffer.Span); + BufferedData.CopyTo(tempBuffer.Span); bytes.CopyTo(tempBuffer.Span.Slice(reservedBufferSize)); EncodeToUtf8Core(tempBuffer.Span, output, flush); } @@ -246,7 +246,7 @@ public int Flush(scoped Span output) } else { - Base64.EncodeToUtf8(AsReadOnlyBytes(in reservedBuffer, reservedBufferSize), output, out var consumed, out bytesWritten); + Base64.EncodeToUtf8(BufferedData, output, out var consumed, out bytesWritten); reservedBufferSize -= consumed; } diff --git a/src/DotNext/Buffers/Text/Base64Encoder.cs b/src/DotNext/Buffers/Text/Base64Encoder.cs index 0c821be6c..15421155d 100644 --- a/src/DotNext/Buffers/Text/Base64Encoder.cs +++ b/src/DotNext/Buffers/Text/Base64Encoder.cs @@ -5,6 +5,8 @@ namespace DotNext.Buffers.Text; +using static Runtime.Intrinsics; + /// /// Represents base64 encoder suitable for encoding large binary /// data using streaming approach. @@ -17,7 +19,7 @@ namespace DotNext.Buffers.Text; /// Encoding methods should not be intermixed by the caller code. /// [StructLayout(LayoutKind.Auto)] -[DebuggerDisplay($"BufferedDataSize = {{{nameof(BufferedDataSize)}}}, BufferedData = {{{nameof(BufferedData)}}}")] +[DebuggerDisplay($"BufferedData = {{{nameof(BufferedDataString)}}}")] public partial struct Base64Encoder : IResettable { /// @@ -29,16 +31,16 @@ public partial struct Base64Encoder : IResettable /// Gets the maximum number of characters that can be produced by /// or methods. /// - public const int MaxCharsToFlush = ((MaxBufferedDataSize + 2) / 3) * 4; + public const int MaxCharsToFlush = (MaxBufferedDataSize + 2) / 3 * 4; /// /// Gets the maximum size of the input block of bytes to encode. /// - public const int MaxInputSize = (int.MaxValue / 4) * 3; + public const int MaxInputSize = int.MaxValue / 4 * 3; private const int DecodingBufferSize = 258; - private const int EncodingBufferSize = ((DecodingBufferSize + 2) / 3) * 4; + private const int EncodingBufferSize = (DecodingBufferSize + 2) / 3 * 4; // 2 bytes reserved if the input is not a multiple of 3 private ushort reservedBuffer; @@ -53,32 +55,33 @@ public partial struct Base64Encoder : IResettable public readonly bool HasBufferedData => reservedBufferSize > 0; /// - /// Gets the number of buffered bytes. + /// Gets the buffered data. /// /// - /// The range of the returned value is [0..]. + /// The length of returned span is in [0..] range. /// - public readonly int BufferedDataSize => reservedBufferSize; - - /// - /// Gets the buffered data. - /// - /// The output buffer. - /// The number of bytes copied to . - /// is not large enough. - public readonly int GetBufferedData(scoped Span output) + [UnscopedRef] + public readonly ReadOnlySpan BufferedData { - AsReadOnlyBytes(in reservedBuffer, reservedBufferSize).CopyTo(output); - return reservedBufferSize; + get + { + Debug.Assert((uint)reservedBufferSize <= sizeof(ushort)); + + return MemoryMarshal.CreateReadOnlySpan(in InToRef(in reservedBuffer), reservedBufferSize); + } } + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + [UnscopedRef] + private Span Buffer => Span.AsBytes(ref reservedBuffer); + [ExcludeFromCodeCoverage] [DebuggerBrowsable(DebuggerBrowsableState.Never)] - private readonly string BufferedData + private readonly string BufferedDataString { get { - var bufferedData = AsReadOnlyBytes(in reservedBuffer, reservedBufferSize); + var bufferedData = BufferedData; return bufferedData.IsEmpty ? string.Empty : Convert.ToBase64String(bufferedData); } } @@ -87,12 +90,4 @@ private readonly string BufferedData /// Resets the internal state of the encoder. /// public void Reset() => reservedBufferSize = 0; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlySpan AsReadOnlyBytes(in ushort value, int length) - { - Debug.Assert((uint)length <= (uint)sizeof(ushort)); - - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref Unsafe.AsRef(in value)), length); - } } \ No newline at end of file From 6012506be6d4d4a9d8e63c9a38baedfe318da8a5 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 02:53:09 +0200 Subject: [PATCH 071/155] Reuse VectorXYZ static methods --- src/DotNext/Buffers/Text/Hex.Unicode.cs | 24 ++++++++++++------------ src/DotNext/Buffers/Text/Hex.Utf8.cs | 20 ++++++++++---------- src/DotNext/Buffers/Text/Hex.cs | 8 ++++---- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/DotNext/Buffers/Text/Hex.Unicode.cs b/src/DotNext/Buffers/Text/Hex.Unicode.cs index 00005796f..5d17689f1 100644 --- a/src/DotNext/Buffers/Text/Hex.Unicode.cs +++ b/src/DotNext/Buffers/Text/Hex.Unicode.cs @@ -15,7 +15,7 @@ public static partial class Hex /// The buffer used to write hexadecimal representation of bytes. /// to return lowercased hex string; to return uppercased hex string. /// The actual number of characters in written by the method. - public static unsafe int EncodeToUtf16(scoped ReadOnlySpan bytes, scoped Span output, bool lowercased = false) + public static int EncodeToUtf16(scoped ReadOnlySpan bytes, scoped Span output, bool lowercased = false) { if (bytes.IsEmpty || output.IsEmpty) return 0; @@ -40,7 +40,7 @@ public static unsafe int EncodeToUtf16(scoped ReadOnlySpan bytes, scoped S var nibbles256 = Vector256.Create(nibbles, nibbles); var lowNibbleMask = Vector256.Create(NibbleMaxValue); var utf16Mask = Vector256.Create( - (byte)0, + 0, byte.MaxValue, 1, byte.MaxValue, @@ -76,16 +76,16 @@ public static unsafe int EncodeToUtf16(scoped ReadOnlySpan bytes, scoped S do { var lowNibbles = Fetch(ref bytePtr); - var highNibbles = Avx2.ShiftRightLogical(lowNibbles.AsUInt32(), 4).AsByte(); + var highNibbles = Vector256.ShiftRightLogical(lowNibbles.AsUInt32(), 4).AsByte(); // combine high nibbles and low nibbles, then do table lookup var result = Avx2.UnpackLow(highNibbles, lowNibbles); - result = Avx2.And(result, lowNibbleMask); + result &= lowNibbleMask; result = Avx2.Shuffle(nibbles256, result); result = Avx2.Shuffle(result, utf16Mask); // save vector back to memory block - Unsafe.WriteUnaligned(ref Unsafe.As(ref charPtr), result); + Vector256.StoreUnsafe(result, ref As(ref charPtr)); bytePtr = ref Add(ref bytePtr, bytesCountPerIteration); charPtr = ref Add(ref charPtr, charsCountPerIteration); @@ -113,7 +113,7 @@ static Vector256 Fetch(ref byte bytePtr) var lowNibbleMask = Vector128.Create(NibbleMaxValue); var utf16Mask = Vector128.Create( - (byte)0, + 0, byte.MaxValue, 1, byte.MaxValue, @@ -133,16 +133,16 @@ static Vector256 Fetch(ref byte bytePtr) do { var lowNibbles = Vector128.CreateScalarUnsafe(ReadUnaligned(ref bytePtr)).AsByte(); - var highNibbles = Sse2.ShiftRightLogical(lowNibbles.AsUInt32(), 4).AsByte(); + var highNibbles = Vector128.ShiftRightLogical(lowNibbles.AsUInt32(), 4).AsByte(); // combine high nibbles and low nibbles, then do table lookup var result = Sse2.UnpackLow(highNibbles, lowNibbles); - result = Sse2.And(result, lowNibbleMask); + result &= lowNibbleMask; result = Ssse3.Shuffle(nibbles, result); result = Ssse3.Shuffle(result, utf16Mask); // save vector back to memory block - Unsafe.WriteUnaligned(ref Unsafe.As(ref charPtr), result); + Vector128.StoreUnsafe(result, ref As(ref charPtr)); bytePtr = ref Add(ref bytePtr, bytesCountPerIteration); charPtr = ref Add(ref charPtr, charsCountPerIteration); @@ -160,7 +160,7 @@ static Vector256 Fetch(ref byte bytePtr) ref char hexTable = ref MemoryMarshal.GetArrayDataReference(NibbleToUtf16CharLookupTable); if (!lowercased) - hexTable = ref Unsafe.Add(ref hexTable, 16); + hexTable = ref Add(ref hexTable, 16); for (byte value; offset < bytesCount; offset++, charPtr = ref Add(ref charPtr, 1), bytePtr = ref Add(ref bytePtr, 1)) { @@ -213,8 +213,8 @@ public static int DecodeFromUtf16(scoped ReadOnlySpan chars, scoped Span bytes, scoped Sp do { var lowNibbles = Fetch(ref bytePtr); - var highNibbles = Avx2.ShiftRightLogical(lowNibbles.AsUInt32(), 4).AsByte(); + var highNibbles = Vector256.ShiftRightLogical(lowNibbles.AsUInt32(), 4).AsByte(); // combine high nibbles and low nibbles, then do table lookup var result = Avx2.UnpackLow(highNibbles, lowNibbles); - result = Avx2.And(result, lowNibbleMask); + result &= lowNibbleMask; result = Avx2.Shuffle(nibbles256, result); // save vector back to memory block - Unsafe.WriteUnaligned(ref charPtr, result); + Vector256.StoreUnsafe(result, ref charPtr); bytePtr = ref Add(ref bytePtr, bytesCountPerIteration); charPtr = ref Add(ref charPtr, charsCountPerIteration); @@ -80,15 +80,15 @@ static Vector256 Fetch(ref byte bytePtr) do { var lowNibbles = Vector128.CreateScalarUnsafe(ReadUnaligned(ref bytePtr)).AsByte(); - var highNibbles = Sse2.ShiftRightLogical(lowNibbles.AsUInt64(), 4).AsByte(); + var highNibbles = Vector128.ShiftRightLogical(lowNibbles.AsUInt64(), 4).AsByte(); // combine high nibbles and low nibbles, then do table lookup var result = Sse2.UnpackLow(highNibbles, lowNibbles); - result = Sse2.And(result, lowNibbleMask); + result &= lowNibbleMask; result = Ssse3.Shuffle(nibbles, result); // save vector back to memory block - Unsafe.WriteUnaligned(ref charPtr, result); + Vector128.StoreUnsafe(result, ref charPtr); bytePtr = ref Add(ref bytePtr, bytesCountPerIteration); charPtr = ref Add(ref charPtr, charsCountPerIteration); @@ -106,7 +106,7 @@ static Vector256 Fetch(ref byte bytePtr) ref char hexTable = ref MemoryMarshal.GetArrayDataReference(NibbleToUtf16CharLookupTable); if (!lowercased) - hexTable = ref Unsafe.Add(ref hexTable, 16); + hexTable = ref Add(ref hexTable, 16); for (; offset < bytesCount; offset++, charPtr = ref Add(ref charPtr, 1), bytePtr = ref Add(ref bytePtr, 1)) { @@ -132,7 +132,7 @@ public static byte[] EncodeToUtf8(scoped ReadOnlySpan bytes, bool lowercas var count = bytes.Length << 1; if (count is 0) { - result = Array.Empty(); + result = []; } else { @@ -159,8 +159,8 @@ public static int DecodeFromUtf8(scoped ReadOnlySpan chars, scoped Span CharToNibbleLookupTable => new byte[] - { + private static ReadOnlySpan CharToNibbleLookupTable => + [ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47 @@ -24,7 +24,7 @@ public static partial class Hex 0xFF, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95 0xFF, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, // 102 - }; + ]; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 NibbleToUtf8CharLookupTable(bool lowercased) => lowercased From 421a86b5f76026b441fe998b437bfe02db951beb Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 12:29:15 +0200 Subject: [PATCH 072/155] Added optimization path for UTF-8 + fixed method names --- src/DotNext.IO/Buffers/BufferWriter.cs | 11 +-- .../EncodingInterpolatedStringHandler.cs | 88 +++++++++++++------ src/DotNext.IO/IO/AsyncBufferWriter.cs | 4 +- .../IO/Pipelines/PipeExtensions.Writers.cs | 2 +- .../IO/Pipelines/PipeReaderWriter.cs | 2 +- .../Buffers/BufferWriterTests.cs | 46 +++++----- .../Cluster/Consensus/Raft/JsonLogEntry.cs | 4 +- .../MetadataTransferObject.cs | 4 +- .../Net/Cluster/Messaging/TextMessage.cs | 2 +- 9 files changed, 101 insertions(+), 62 deletions(-) diff --git a/src/DotNext.IO/Buffers/BufferWriter.cs b/src/DotNext.IO/Buffers/BufferWriter.cs index 000e88945..8ff5f4189 100644 --- a/src/DotNext.IO/Buffers/BufferWriter.cs +++ b/src/DotNext.IO/Buffers/BufferWriter.cs @@ -159,7 +159,7 @@ internal static int WriteLength(this IBufferWriter writer, ReadOnlySpanThe encoding context. /// String length encoding format; or to prevent encoding of string length. /// The number of written bytes. - public static long WriteString(this IBufferWriter writer, ReadOnlySpan value, in EncodingContext context, LengthFormat? lengthFormat = null) + public static long Encode(this IBufferWriter writer, ReadOnlySpan value, in EncodingContext context, LengthFormat? lengthFormat = null) { var result = lengthFormat.HasValue ? WriteLength(writer, value, lengthFormat.GetValueOrDefault(), context.Encoding) @@ -201,7 +201,7 @@ private static bool TryWriteFormattable(IBufferWriter writer, T value, /// The format provider. /// The number of written bytes. [SkipLocalsInit] - public static long WriteFormattable(this IBufferWriter writer, T value, LengthFormat? lengthFormat, in EncodingContext context, ReadOnlySpan format = default, IFormatProvider? provider = null) + public static long Encode(this IBufferWriter writer, T value, LengthFormat? lengthFormat, in EncodingContext context, ReadOnlySpan format = default, IFormatProvider? provider = null) where T : notnull, ISpanFormattable { // attempt to allocate char buffer on the stack @@ -229,7 +229,7 @@ public static long WriteFormattable(this IBufferWriter writer, T value, /// The format provider. /// The interpolated string handler. /// The number of produced bytes. - public static int WriteString(this IBufferWriter writer, in EncodingContext context, Span buffer, IFormatProvider? provider, [InterpolatedStringHandlerArgument(nameof(writer), nameof(context), nameof(buffer), nameof(provider))] ref EncodingInterpolatedStringHandler handler) + public static int Interpolate(this IBufferWriter writer, in EncodingContext context, Span buffer, IFormatProvider? provider, [InterpolatedStringHandlerArgument(nameof(writer), nameof(context), nameof(buffer), nameof(provider))] ref EncodingInterpolatedStringHandler handler) => handler.WrittenCount; /// @@ -237,8 +237,9 @@ public static int WriteString(this IBufferWriter writer, in EncodingContex /// /// The output buffer. /// The encoding context. + /// The preallocated buffer to be used for placing characters during encoding. /// The interpolated string handler. /// The number of produced bytes. - public static int WriteString(this IBufferWriter writer, in EncodingContext context, [InterpolatedStringHandlerArgument(nameof(writer), nameof(context))] ref EncodingInterpolatedStringHandler handler) - => WriteString(writer, in context, Span.Empty, null, ref handler); + public static int Interpolate(this IBufferWriter writer, in EncodingContext context, Span buffer, [InterpolatedStringHandlerArgument(nameof(writer), nameof(context), nameof(buffer))] ref EncodingInterpolatedStringHandler handler) + => Interpolate(writer, in context, buffer, provider: null, ref handler); } \ No newline at end of file diff --git a/src/DotNext.IO/Buffers/EncodingInterpolatedStringHandler.cs b/src/DotNext.IO/Buffers/EncodingInterpolatedStringHandler.cs index 2eb1812f6..c64322106 100644 --- a/src/DotNext.IO/Buffers/EncodingInterpolatedStringHandler.cs +++ b/src/DotNext.IO/Buffers/EncodingInterpolatedStringHandler.cs @@ -1,5 +1,6 @@ using System.Buffers; using System.ComponentModel; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; @@ -19,7 +20,8 @@ namespace DotNext.Buffers; public ref struct EncodingInterpolatedStringHandler { private const int MaxBufferSize = int.MaxValue / 2; - private const char Whitespace = ' '; + private const char WhitespaceUtf16 = ' '; + private const byte WhitespaceUtf8 = (byte)' '; private readonly IBufferWriter buffer; private readonly IFormatProvider? provider; @@ -87,15 +89,30 @@ public void AppendFormatted(ReadOnlySpan value) /// The format string. public void AppendFormatted(T value, string? format = null) { + int bufferSize, charsWritten; switch (value) { + case IUtf8SpanFormattable when ReferenceEquals(encoding, Encoding.UTF8): + for (bufferSize = 0; ; bufferSize = bufferSize <= MaxBufferSize ? bufferSize << 1 : throw new InsufficientMemoryException()) + { + var span = buffer.GetSpan(bufferSize); + + // constrained call avoiding boxing for value types + if (((IUtf8SpanFormattable)value).TryFormat(span, out charsWritten, format, provider)) + { + buffer.Advance(charsWritten); + break; + } + } + + break; case ISpanFormattable: - for (int bufferSize = 0; ; bufferSize = bufferSize <= MaxBufferSize ? bufferSize * 2 : throw new InsufficientMemoryException()) + for (bufferSize = 0; ; bufferSize = bufferSize <= MaxBufferSize ? bufferSize * 2 : throw new InsufficientMemoryException()) { using var tempBuffer = bufferSize <= charBuffer.Length ? charBuffer : new MemoryRental(bufferSize, false); // constrained call avoiding boxing for value types - if (((ISpanFormattable)value).TryFormat(tempBuffer.Span, out var charsWritten, format, provider)) + if (((ISpanFormattable)value).TryFormat(tempBuffer.Span, out charsWritten, format, provider)) { AppendFormatted(tempBuffer.Span.Slice(0, charsWritten)); break; @@ -129,7 +146,7 @@ private void AppendFormatted(ReadOnlySpan value, int alignment, bool leftA ? span.Slice(value.Length, padding) : span.TrimLength(padding, out span); - filler.Fill(Whitespace); + filler.Fill(WhitespaceUtf16); value.CopyTo(span); AppendFormatted(span); @@ -153,6 +170,29 @@ public void AppendFormatted(ReadOnlySpan value, int alignment) AppendFormatted(value, alignment, leftAlign); } + private static void Align(Span buffer, T whitespace, ref int alignment, int charsWritten, bool leftAlign) + where T : struct, IBinaryNumber + { + Span filler; + var padding = alignment - charsWritten; + + if (padding <= 0) + { + alignment = charsWritten; + } + else if (leftAlign) + { + filler = buffer.Slice(charsWritten, padding); + filler.Fill(whitespace); + } + else + { + filler = buffer.TrimLength(padding, out var rest); + buffer.Slice(0, charsWritten).CopyTo(rest); + filler.Fill(whitespace); + } + } + /// /// Writes the specified value to the handler. /// @@ -165,6 +205,7 @@ public void AppendFormatted(ReadOnlySpan value, int alignment) /// The format string. public void AppendFormatted(T value, int alignment, string? format = null) { + int bufferSize, charsWritten; bool leftAlign; if (leftAlign = alignment < 0) @@ -172,32 +213,29 @@ public void AppendFormatted(T value, int alignment, string? format = null) switch (value) { + case IUtf8SpanFormattable when ReferenceEquals(encoding, Encoding.UTF8): + for (bufferSize = alignment; ; bufferSize = bufferSize <= MaxBufferSize ? bufferSize << 1 : throw new InsufficientMemoryException()) + { + var span = buffer.GetSpan(bufferSize); + if (((IUtf8SpanFormattable)value).TryFormat(span, out charsWritten, format, provider)) + { + Align(span, WhitespaceUtf8, ref alignment, charsWritten, leftAlign); + buffer.Advance(alignment); + count += alignment; + break; + } + } + + break; case ISpanFormattable: - for (int bufferSize = alignment; ; bufferSize = bufferSize <= MaxBufferSize ? bufferSize * 2 : throw new InsufficientMemoryException()) + for (bufferSize = alignment; ; bufferSize = bufferSize <= MaxBufferSize ? bufferSize * 2 : throw new InsufficientMemoryException()) { using var tempBuffer = bufferSize <= charBuffer.Length ? charBuffer : new MemoryRental(bufferSize, false); - Span span = tempBuffer.Span, filler; + var span = tempBuffer.Span; - if (((ISpanFormattable)value).TryFormat(span, out var charsWritten, format, provider)) + if (((ISpanFormattable)value).TryFormat(span, out charsWritten, format, provider)) { - var padding = alignment - charsWritten; - - if (padding <= 0) - { - alignment = charsWritten; - } - else if (leftAlign) - { - filler = span.Slice(charsWritten, padding); - filler.Fill(Whitespace); - } - else - { - filler = span.TrimLength(padding, out var rest); - span.Slice(0, charsWritten).CopyTo(rest); - filler.Fill(Whitespace); - } - + Align(span, WhitespaceUtf16, ref alignment, charsWritten, leftAlign); AppendFormatted(span.Slice(0, alignment)); break; } diff --git a/src/DotNext.IO/IO/AsyncBufferWriter.cs b/src/DotNext.IO/IO/AsyncBufferWriter.cs index 661a015f1..6b777de14 100644 --- a/src/DotNext.IO/IO/AsyncBufferWriter.cs +++ b/src/DotNext.IO/IO/AsyncBufferWriter.cs @@ -162,7 +162,7 @@ ValueTask IAsyncBinaryWriter.WriteStringAsync(ReadOnlyMemory chars, Encodi result = new(); try { - writer.WriteString(chars.Span, in context, lengthFormat: lengthFormat); + writer.Encode(chars.Span, in context, lengthFormat: lengthFormat); } catch (Exception e) { @@ -185,7 +185,7 @@ ValueTask IAsyncBinaryWriter.WriteFormattableAsync(T value, LengthFormat leng result = new(); try { - writer.WriteFormattable(value, lengthFormat, in context, format, provider); + writer.Encode(value, lengthFormat, in context, format, provider); } catch (Exception e) { diff --git a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs index 0d3a8b34b..5f8696b76 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Writers.cs @@ -70,7 +70,7 @@ public static async Task WriteAsync(this PipeWriter writer, Func WriteFormattableAsync(this PipeWriter writer, T value, LengthFormat lengthFormat, EncodingContext context, string? format = null, IFormatProvider? provider = null, CancellationToken token = default) where T : notnull, ISpanFormattable { - writer.WriteFormattable(value, lengthFormat, in context, format, provider); + writer.Encode(value, lengthFormat, in context, format, provider); return writer.FlushAsync(token); } diff --git a/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs b/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs index 1d771c582..dcb207d1a 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs @@ -221,7 +221,7 @@ public unsafe ValueTask WriteStringAsync(ReadOnlyMemory chars, EncodingCon static ValueTask WriteAndFlushOnceAsync(PipeWriter output, ReadOnlyMemory chars, EncodingContext context, LengthFormat? lengthFormat, CancellationToken token) { - output.WriteString(chars.Span, context, lengthFormat: lengthFormat); + output.Encode(chars.Span, context, lengthFormat: lengthFormat); return output.FlushAsync(token); } } diff --git a/src/DotNext.Tests/Buffers/BufferWriterTests.cs b/src/DotNext.Tests/Buffers/BufferWriterTests.cs index 69e814eaf..c252abafe 100644 --- a/src/DotNext.Tests/Buffers/BufferWriterTests.cs +++ b/src/DotNext.Tests/Buffers/BufferWriterTests.cs @@ -37,7 +37,7 @@ public static async Task ReadBlittableTypes(bool littleEndian) private static async Task ReadWriteStringUsingEncodingAsync(string value, Encoding encoding, LengthFormat? lengthEnc) { var writer = new ArrayBufferWriter(); - writer.WriteString(value.AsSpan(), encoding, lengthEnc); + writer.Encode(value.AsSpan(), encoding, lengthEnc); IAsyncBinaryReader reader = IAsyncBinaryReader.Create(writer.WrittenMemory); var result = await (lengthEnc is null ? reader.ReadStringAsync(encoding.GetByteCount(value), encoding) : @@ -143,27 +143,27 @@ static void EncodeDecode(TBuffer writer, Encoding encoding) var bi = new BigInteger(RandomBytes(64)); var dt = DateTime.Now; var dto = DateTimeOffset.Now; - writer.WriteFormattable(42L, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(12UL, LengthFormat.PlainLittleEndian, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(34, LengthFormat.PlainBigEndian, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(78, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(90, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(12, LengthFormat.Plain, in encodingContext, format: "X", provider: InvariantCulture); - writer.WriteFormattable(12, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(10, LengthFormat.Plain, in encodingContext, format: "X", provider: InvariantCulture); - writer.WriteFormattable(11, LengthFormat.Plain, in encodingContext, format: "X", provider: InvariantCulture); - writer.WriteFormattable(10, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(11, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(g, LengthFormat.Plain, in encodingContext); - writer.WriteFormattable(g, LengthFormat.Plain, in encodingContext, format: "X"); - writer.WriteFormattable(dt, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); - writer.WriteFormattable(dto, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); - writer.WriteFormattable(dt, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); - writer.WriteFormattable(dto, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); - writer.WriteFormattable(42.5M, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(32.2F, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(56.6D, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); - writer.WriteFormattable(bi, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(42L, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(12UL, LengthFormat.PlainLittleEndian, in encodingContext, provider: InvariantCulture); + writer.Encode(34, LengthFormat.PlainBigEndian, in encodingContext, provider: InvariantCulture); + writer.Encode(78, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(90, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(12, LengthFormat.Plain, in encodingContext, format: "X", provider: InvariantCulture); + writer.Encode(12, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(10, LengthFormat.Plain, in encodingContext, format: "X", provider: InvariantCulture); + writer.Encode(11, LengthFormat.Plain, in encodingContext, format: "X", provider: InvariantCulture); + writer.Encode(10, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(11, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(g, LengthFormat.Plain, in encodingContext); + writer.Encode(g, LengthFormat.Plain, in encodingContext, format: "X"); + writer.Encode(dt, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); + writer.Encode(dto, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); + writer.Encode(dt, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); + writer.Encode(dto, LengthFormat.Plain, in encodingContext, format: "O", provider: InvariantCulture); + writer.Encode(42.5M, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(32.2F, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(56.6D, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); + writer.Encode(bi, LengthFormat.Plain, in encodingContext, provider: InvariantCulture); var decodingContext = new DecodingContext(encoding, true); True(writer.TryGetWrittenContent(out var writtenMemory)); @@ -288,7 +288,7 @@ public static void EncodeInterpolatedString(int bufferSize, string encoding, int Span buffer = stackalloc char[bufferSize]; var context = new EncodingContext(Encoding.GetEncoding(encoding), true); - True(writer.WriteString(in context, buffer, null, $"{x,4:X} = {y,-3:X}") > 0); + True(writer.Interpolate(in context, buffer, $"{x,4:X} = {y,-3:X}") > 0); Equal($"{x,4:X} = {y,-3:X}", context.Encoding.GetString(writer.WrittenSpan)); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/JsonLogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/JsonLogEntry.cs index ab465a8c2..3b8752eb3 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/JsonLogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/JsonLogEntry.cs @@ -54,7 +54,7 @@ private static Type LoadType(string typeId, Func? typeLoader) static void Serialize(string typeId, T value, IBufferWriter buffer, JsonSerializerOptions? options) { // serialize type identifier - buffer.WriteString(typeId, DefaultEncoding, lengthFormat: LengthEncoding); + buffer.Encode(typeId, DefaultEncoding, lengthFormat: LengthEncoding); // serialize object to JSON using var jsonWriter = new Utf8JsonWriter(buffer, options?.GetWriterOptions() ?? DefaultWriterOptions); @@ -95,7 +95,7 @@ internal static ValueTask SerializeAsync(TWriter writer, string type static void Serialize(string typeId, T value, IBufferWriter buffer, JsonTypeInfo typeInfo) { // serialize type identifier - buffer.WriteString(typeId, DefaultEncoding, lengthFormat: LengthEncoding); + buffer.Encode(typeId, DefaultEncoding, lengthFormat: LengthEncoding); // serialize object to JSON using var jsonWriter = new Utf8JsonWriter(buffer, DefaultWriterOptions); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs index 08e1c35a3..7b7a7f9d2 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/MetadataTransferObject.cs @@ -35,8 +35,8 @@ private static void Write(IBufferWriter writer, IReadOnlyDictionary(TWriter writer, Cancellation result = ValueTask.CompletedTask; try { - bufferWriter.WriteString(Content.AsSpan(), Type.GetEncoding()); + bufferWriter.Encode(Content.AsSpan(), Type.GetEncoding()); } catch (Exception e) { From 559abe6da6f5ec9cf550c4d4070d4103144d5b91 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 13:06:04 +0200 Subject: [PATCH 073/155] Added UTF-8 support --- src/DotNext.IO/Buffers/BufferWriter.cs | 29 ++++++++++++++++++ .../Buffers/BufferWriterSlimTests.cs | 9 ++++++ .../Buffers/BufferWriterTests.cs | 9 ++++++ .../Buffers/BufferHelpers.BufferWriterSlim.cs | 30 +++++++++++++++++++ 4 files changed, 77 insertions(+) diff --git a/src/DotNext.IO/Buffers/BufferWriter.cs b/src/DotNext.IO/Buffers/BufferWriter.cs index 8ff5f4189..5ec61822f 100644 --- a/src/DotNext.IO/Buffers/BufferWriter.cs +++ b/src/DotNext.IO/Buffers/BufferWriter.cs @@ -220,6 +220,35 @@ public static long Encode(this IBufferWriter writer, T value, LengthFor return bytesWritten; } + /// + /// Encodes formatted value as a set of UTF-8 bytes using the specified encoding. + /// + /// The type of formattable value. + /// The buffer writer. + /// The type value to be written as string. + /// The format of the value. + /// The format provider. + /// The number of written bytes. + /// has not enough free space to place UTF-8 bytes. + public static int EncodeAsUtf8(this IBufferWriter writer, T value, ReadOnlySpan format = default, IFormatProvider? provider = null) + where T : notnull, IUtf8SpanFormattable + { + int bytesWritten; + for (var bufferSize = 0; ; bufferSize = bufferSize <= MaxBufferSize ? bufferSize << 1 : throw new InsufficientMemoryException()) + { + var span = writer.GetSpan(bufferSize); + + // constrained call avoiding boxing for value types + if (((IUtf8SpanFormattable)value).TryFormat(span, out bytesWritten, format, provider)) + { + writer.Advance(bytesWritten); + break; + } + } + + return bytesWritten; + } + /// /// Encodes formattable string as a sequence of bytes. /// diff --git a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs index f200447b3..41e70ad5a 100644 --- a/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs +++ b/src/DotNext.Tests/Buffers/BufferWriterSlimTests.cs @@ -295,4 +295,13 @@ public static void ChangeWrittenCount() buffer.WrittenCount = 1; Equal(42, buffer[0]); } + + [Fact] + public static void EncodeAsUtf8() + { + var writer = new BufferWriterSlim(); + writer.EncodeAsUtf8(42); + Equal(2, writer.WrittenCount); + Equal(42, int.Parse(writer.WrittenSpan)); + } } \ No newline at end of file diff --git a/src/DotNext.Tests/Buffers/BufferWriterTests.cs b/src/DotNext.Tests/Buffers/BufferWriterTests.cs index c252abafe..6d361bd6b 100644 --- a/src/DotNext.Tests/Buffers/BufferWriterTests.cs +++ b/src/DotNext.Tests/Buffers/BufferWriterTests.cs @@ -341,4 +341,13 @@ public static void AdvanceRewind() buffer.Advance(1); Equal(42, buffer[0]); } + + [Fact] + public static void EncodeAsUtf8() + { + var writer = new ArrayBufferWriter(); + writer.EncodeAsUtf8(42); + Equal(2, writer.WrittenCount); + Equal(42, int.Parse(writer.WrittenSpan)); + } } \ No newline at end of file diff --git a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs index 4eac902ea..cced0f469 100644 --- a/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs +++ b/src/DotNext/Buffers/BufferHelpers.BufferWriterSlim.cs @@ -170,4 +170,34 @@ public static void Write(this ref BufferWriterSlim writer, scoped ReadO writer.Advance(output.WrittenCount); } + + /// + /// Encodes formatted value as a set of UTF-8 bytes using the specified encoding. + /// + /// The type of formattable value. + /// The buffer writer. + /// The type value to be written as string. + /// The format of the value. + /// The format provider. + /// The number of written bytes. + /// has not enough free space to place UTF-8 bytes. + public static int EncodeAsUtf8(this ref BufferWriterSlim writer, T value, ReadOnlySpan format = default, IFormatProvider? provider = null) + where T : notnull, IUtf8SpanFormattable + { + const int maxBufferSize = int.MaxValue / 2; + int bytesWritten; + for (var bufferSize = 0; ; bufferSize = bufferSize <= maxBufferSize ? bufferSize << 1 : throw new InsufficientMemoryException()) + { + var span = writer.InternalGetSpan(bufferSize); + + // constrained call avoiding boxing for value types + if (((IUtf8SpanFormattable)value).TryFormat(span, out bytesWritten, format, provider)) + { + writer.Advance(bytesWritten); + break; + } + } + + return bytesWritten; + } } \ No newline at end of file From b1c1bf9350ebd662073ebf67b58dd2c697f7cb5b Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 18:53:50 +0200 Subject: [PATCH 074/155] Migration to ReadExactly/ReadAtLeast from .NET Stream --- .../IO/Pipelines/PipeExtensions.Readers.cs | 30 +++-- .../IO/Pipelines/PipeReaderWriter.cs | 2 +- src/DotNext.IO/IO/StreamBinaryAccessor.cs | 2 +- src/DotNext.IO/IO/StreamExtensions.cs | 119 +++--------------- .../IO/FileBufferingWriterTests.cs | 2 +- .../IO/Pipelines/PipeExtensionsTests.cs | 10 +- .../IO/SequenceBinaryReaderTests.cs | 2 +- src/DotNext.Tests/IO/StreamExtensionsTests.cs | 26 +--- src/DotNext.Tests/IO/StreamSourceTests.cs | 4 +- .../Raft/Http/AppendEntriesMessage.cs | 6 +- .../Consensus/Raft/Http/SynchronizeMessage.cs | 2 +- .../Http/HttpPeerController.Disconnect.cs | 4 +- .../Http/HttpPeerController.ForwardJoin.cs | 4 +- .../HyParView/Http/HttpPeerController.Join.cs | 4 +- .../Http/HttpPeerController.Neighbor.cs | 4 +- .../Http/HttpPeerController.Shuffle.cs | 6 +- .../Consensus/Raft/Tcp/TcpProtocolStream.cs | 4 +- .../ConnectionOriented/Server.cs | 2 +- .../Datagram/ServerExchange.Configuration.cs | 2 +- 19 files changed, 70 insertions(+), 165 deletions(-) diff --git a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs index 1299c9b68..1bb412638 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs @@ -123,7 +123,7 @@ public static ValueTask ParseAsync(this PipeReader reader, CancellationTok where T : notnull, IBinaryFormattable { ValueTask result; - if (TryReadBlock(reader, T.Size, out var readResult)) + if (TryReadExactly(reader, T.Size, out var readResult)) { result = readResult.IsCanceled ? ValueTask.FromCanceled(token.IsCancellationRequested ? token : new(true)) @@ -142,7 +142,7 @@ public static ValueTask ParseAsync(this PipeReader reader, CancellationTok static async ValueTask ParseSlowAsync(PipeReader reader, CancellationToken token) { using var buffer = MemoryAllocator.Allocate(T.Size, true); - await ReadBlockAsync(reader, buffer.Memory, token).ConfigureAwait(false); + await ReadExactlyAsync(reader, buffer.Memory, token).ConfigureAwait(false); return IBinaryFormattable.Parse(buffer.Span); } } @@ -339,7 +339,7 @@ public static ValueTask ReadAsync(this PipeReader reader, CancellationToke { ValueTask result; - if (!TryReadBlock(reader, Unsafe.SizeOf(), out var readResult)) + if (!TryReadExactly(reader, Unsafe.SizeOf(), out var readResult)) { result = ReadSlowAsync(reader, token); } @@ -519,12 +519,12 @@ public static unsafe ValueTask ReadUInt16Async(this PipeReader reader, b /// The task representing asynchronous state of the operation. /// The operation has been canceled. /// Reader doesn't have enough data. - public static ValueTask ReadBlockAsync(this PipeReader reader, Memory output, CancellationToken token = default) + public static ValueTask ReadExactlyAsync(this PipeReader reader, Memory output, CancellationToken token = default) { if (output.IsEmpty) return ValueTask.CompletedTask; - if (TryReadBlock(reader, output.Length, out var result)) + if (TryReadExactly(reader, output.Length, out var result)) { result.Buffer.CopyTo(output.Span); reader.AdvanceTo(result.Buffer.GetPosition(output.Length)); @@ -536,7 +536,21 @@ public static ValueTask ReadBlockAsync(this PipeReader reader, Memory outp [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] static async ValueTask ReadBlockSlowAsync(PipeReader reader, Memory output, CancellationToken token) - => await ReadAsync(reader, new MemoryReader(output), token).ConfigureAwait(false); + { + var result = await reader.ReadAtLeastAsync(output.Length, token).ConfigureAwait(false); + result.ThrowIfCancellationRequested(reader, token); + Copy(reader, result.Buffer, output.Span); + } + + static void Copy(PipeReader reader, ReadOnlySequence source, Span output) + { + if (source.Length < output.Length) + throw new EndOfStreamException(); + + var blockEnd = source.GetPosition(output.Length); + source.Slice(source.Start, blockEnd).CopyTo(output); + reader.AdvanceTo(blockEnd, source.End); + } } /// @@ -557,7 +571,7 @@ public static async ValueTask> ReadBlockAsync(this PipeReader if (length > 0) { result = allocator.Invoke(length, true); - await ReadBlockAsync(reader, result.Memory, token).ConfigureAwait(false); + await ReadExactlyAsync(reader, result.Memory, token).ConfigureAwait(false); } else { @@ -583,7 +597,7 @@ public static async ValueTask> ReadBlockAsync(this PipeReader /// if the block of requested length is obtained successfully; /// otherwise, . /// - public static bool TryReadBlock(this PipeReader reader, long length, out ReadResult result) + public static bool TryReadExactly(this PipeReader reader, long length, out ReadResult result) { if (reader.TryRead(out result)) { diff --git a/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs b/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs index dcb207d1a..b3e0b02e8 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeReaderWriter.cs @@ -20,7 +20,7 @@ public ValueTask ReadAsync(CancellationToken token) => Reader.ReadAsync(token); public ValueTask ReadAsync(Memory output, CancellationToken token) - => Reader.ReadBlockAsync(output, token); + => Reader.ReadExactlyAsync(output, token); ValueTask IAsyncBinaryReader.SkipAsync(long length, CancellationToken token) => Reader.SkipAsync(length, token); diff --git a/src/DotNext.IO/IO/StreamBinaryAccessor.cs b/src/DotNext.IO/IO/StreamBinaryAccessor.cs index a465c18da..799c675dd 100644 --- a/src/DotNext.IO/IO/StreamBinaryAccessor.cs +++ b/src/DotNext.IO/IO/StreamBinaryAccessor.cs @@ -36,7 +36,7 @@ public ValueTask ReadAsync(CancellationToken token = default) => StreamExtensions.ReadAsync(Stream, buffer, token); public ValueTask ReadAsync(Memory output, CancellationToken token = default) - => StreamExtensions.ReadBlockAsync(Stream, output, token); + => Stream.ReadExactlyAsync(output, token); private async ValueTask SkipSlowAsync(long length, CancellationToken token) { diff --git a/src/DotNext.IO/IO/StreamExtensions.cs b/src/DotNext.IO/IO/StreamExtensions.cs index 80fc1364f..ee1c62624 100644 --- a/src/DotNext.IO/IO/StreamExtensions.cs +++ b/src/DotNext.IO/IO/StreamExtensions.cs @@ -91,7 +91,7 @@ public static T Parse(this Stream stream, Span buffer) throw new ArgumentException(ExceptionMessages.BufferTooSmall, nameof(buffer)); buffer = buffer.Slice(0, T.Size); - ReadBlock(stream, buffer); + stream.ReadExactly(buffer); var reader = new SpanReader(buffer); return T.Parse(ref reader); @@ -218,7 +218,7 @@ public static BigInteger ReadBigInteger(this Stream stream, int length, bool lit throw new ArgumentException(ExceptionMessages.BufferTooSmall, nameof(buffer)); buffer = buffer.Slice(0, length); - stream.ReadBlock(buffer); + stream.ReadExactly(buffer); return new BigInteger(buffer, isBigEndian: !littleEndian); } @@ -241,7 +241,7 @@ public static BigInteger ReadBigInteger(this Stream stream, int length, bool lit return BigInteger.Zero; using MemoryRental buffer = length <= MemoryRental.StackallocThreshold ? stackalloc byte[length] : new MemoryRental(length); - stream.ReadBlock(buffer.Span); + stream.ReadExactly(buffer.Span); return new BigInteger(buffer.Span, isBigEndian: !littleEndian); } @@ -384,7 +384,7 @@ public static string ReadString(this Stream stream, int length, Encoding encodin using (MemoryRental bytesBuffer = length <= MemoryRental.StackallocThreshold ? stackalloc byte[length] : new MemoryRental(length)) { - stream.ReadBlock(bytesBuffer.Span); + stream.ReadExactly(bytesBuffer.Span); charCount = encoding.GetChars(bytesBuffer.Span, charBuffer.Span); } @@ -414,7 +414,7 @@ public static MemoryOwner ReadString(this Stream stream, int length, Encod else { using var bytesBuffer = length <= MemoryRental.StackallocThreshold ? stackalloc byte[length] : new MemoryRental(length); - stream.ReadBlock(bytesBuffer.Span); + stream.ReadExactly(bytesBuffer.Span); result = encoding.GetChars(bytesBuffer.Span, allocator); } @@ -590,7 +590,7 @@ public static async ValueTask ReadBigIntegerAsync(this Stream stream throw new ArgumentException(ExceptionMessages.BufferTooSmall, nameof(buffer)); buffer = buffer.Slice(0, length); - await stream.ReadBlockAsync(buffer, token).ConfigureAwait(false); + await stream.ReadExactlyAsync(buffer, token).ConfigureAwait(false); return new BigInteger(buffer.Span, isBigEndian: !littleEndian); } @@ -610,7 +610,7 @@ public static async ValueTask ReadBigIntegerAsync(this Stream stream return BigInteger.Zero; using var buffer = MemoryAllocator.Allocate(length, true); - await stream.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await stream.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); return new BigInteger(buffer.Span, isBigEndian: !littleEndian); } @@ -792,7 +792,7 @@ public static async ValueTask> ReadStringAsync(this Stream str else { using var bytesBuffer = MemoryAllocator.Allocate(length, exactSize: true); - await stream.ReadBlockAsync(bytesBuffer.Memory, token).ConfigureAwait(false); + await stream.ReadExactlyAsync(bytesBuffer.Memory, token).ConfigureAwait(false); result = encoding.GetChars(bytesBuffer.Span, allocator); } @@ -842,22 +842,6 @@ public static async ValueTask> ReadStringAsync(this Stream str return await ReadStringAsync(stream, await stream.ReadLengthAsync(lengthFormat, lengthDecodingBuffer.Memory, token).ConfigureAwait(false), encoding, allocator, token).ConfigureAwait(false); } - /// - /// Reads exact number of bytes. - /// - /// The stream to read from. - /// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The end of the stream is reached. - public static void ReadBlock(this Stream stream, Span output) - { - for (int size = output.Length, bytesRead, offset = 0; size > 0; size -= bytesRead, offset += bytesRead) - { - bytesRead = stream.Read(output.Slice(offset, size)); - if (bytesRead == 0) - throw new EndOfStreamException(); - } - } - /// /// Decodes the block of bytes. /// @@ -873,7 +857,7 @@ public static MemoryOwner ReadBlock(this Stream stream, LengthFormat lengt if (length > 0) { result = allocator.Invoke(length, true); - stream.ReadBlock(result.Span); + stream.ReadExactly(result.Span); } else { @@ -894,30 +878,10 @@ public static unsafe T Read(this Stream stream) where T : unmanaged { var result = default(T); - stream.ReadBlock(Span.AsBytes(ref result)); + stream.ReadExactly(Span.AsBytes(ref result)); return result; } - /// - /// Reads exact number of bytes asynchronously. - /// - /// The stream to read from. - /// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The token that can be used to cancel asynchronous operation. - /// The task representing asynchronous execution of this method. - /// The end of the stream is reached. - /// The operation has been canceled. - [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] - public static async ValueTask ReadBlockAsync(this Stream stream, Memory output, CancellationToken token = default) - { - for (int size = output.Length, bytesRead, offset = 0; size > 0; size -= bytesRead, offset += bytesRead) - { - bytesRead = await stream.ReadAsync(output.Slice(offset, size), token).ConfigureAwait(false); - if (bytesRead is 0) - throw new EndOfStreamException(); - } - } - /// /// Decodes the block of bytes asynchronously. /// @@ -937,7 +901,7 @@ public static async ValueTask> ReadBlockAsync(this Stream stre if (length > 0) { result = allocator.Invoke(length, true); - await stream.ReadBlockAsync(result.Memory, token).ConfigureAwait(false); + await stream.ReadExactlyAsync(result.Memory, token).ConfigureAwait(false); } else { @@ -961,7 +925,7 @@ public static async ValueTask> ReadBlockAsync(this Stream stre public static async ValueTask ReadAsync(this Stream stream, Memory buffer, CancellationToken token = default) where T : unmanaged { - await stream.ReadBlockAsync(buffer.Slice(0, Unsafe.SizeOf()), token).ConfigureAwait(false); + await stream.ReadExactlyAsync(buffer.Slice(0, Unsafe.SizeOf()), token).ConfigureAwait(false); return MemoryMarshal.Read(buffer.Span); } @@ -971,7 +935,7 @@ internal static async ValueTask ReadAsync( where TOutput : unmanaged where TConverter : struct, ISupplier { - await stream.ReadBlockAsync(buffer.Slice(0, Unsafe.SizeOf()), token).ConfigureAwait(false); + await stream.ReadExactlyAsync(buffer.Slice(0, Unsafe.SizeOf()), token).ConfigureAwait(false); return converter.Invoke(MemoryMarshal.Read(buffer.Span)); } @@ -989,7 +953,7 @@ public static async ValueTask ReadAsync(this Stream stream, CancellationTo where T : unmanaged { using var buffer = MemoryAllocator.Allocate(Unsafe.SizeOf(), true); - await stream.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await stream.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); return MemoryMarshal.Read(buffer.Span); } @@ -1232,61 +1196,6 @@ public static Stream Combine(this Stream stream, params Stream[] others) /// An object that represents multiple streams as one logical stream. public static Stream Combine(this IEnumerable streams) => new SparseStream(streams); - /// - /// Reads at least the specified number of bytes. - /// - /// The stream to read from. - /// The minimum size to read. - /// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The token that can be used to cancel the operation. - /// The actual number of bytes written to the . This value is equal to or greater than . - /// is greater than the length of . - /// Unexpected end of stream. - /// The operation has been canceled. - [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] - public static async ValueTask ReadAtLeastAsync(this Stream stream, int minimumSize, Memory buffer, CancellationToken token = default) - { - if ((uint)minimumSize > (uint)buffer.Length) - throw new ArgumentOutOfRangeException(nameof(minimumSize)); - - var offset = 0; - - for (int size = minimumSize, bytesRead; size > 0; size -= bytesRead, offset += bytesRead) - { - bytesRead = await stream.ReadAsync(buffer.Slice(offset), token).ConfigureAwait(false); - if (bytesRead is 0) - throw new EndOfStreamException(); - } - - return offset; - } - - /// - /// Reads at least the specified number of bytes. - /// - /// The stream to read from. - /// The minimum size to read. - /// A region of memory. When this method returns, the contents of this region are replaced by the bytes read from the current source. - /// The actual number of bytes written to the . This value is equal to or greater than . - /// is greater than the length of . - /// Unexpected end of stream. - public static int ReadAtLeast(this Stream stream, int minimumSize, Span buffer) - { - if ((uint)minimumSize > (uint)buffer.Length) - throw new ArgumentOutOfRangeException(nameof(minimumSize)); - - var offset = 0; - - for (int size = minimumSize, bytesRead; size > 0; size -= bytesRead, offset += bytesRead) - { - bytesRead = stream.Read(buffer.Slice(offset)); - if (bytesRead is 0) - throw new EndOfStreamException(); - } - - return offset; - } - /// /// Reads the stream sequentially. /// diff --git a/src/DotNext.Tests/IO/FileBufferingWriterTests.cs b/src/DotNext.Tests/IO/FileBufferingWriterTests.cs index 3dc9f0ed9..ec1ef3643 100644 --- a/src/DotNext.Tests/IO/FileBufferingWriterTests.cs +++ b/src/DotNext.Tests/IO/FileBufferingWriterTests.cs @@ -40,7 +40,7 @@ public static void PermanentFile() using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); var actual = new byte[expected.Length]; - fs.ReadBlock(actual); + fs.ReadExactly(actual); Equal(expected, actual); } diff --git a/src/DotNext.Tests/IO/Pipelines/PipeExtensionsTests.cs b/src/DotNext.Tests/IO/Pipelines/PipeExtensionsTests.cs index da03a1886..b44ea89fd 100644 --- a/src/DotNext.Tests/IO/Pipelines/PipeExtensionsTests.cs +++ b/src/DotNext.Tests/IO/Pipelines/PipeExtensionsTests.cs @@ -50,8 +50,8 @@ static async void WriteValueAsync(Memory memory, PipeWriter writer) WriteValueAsync(new byte[] { 1, 5, 8, 9, 10 }, pipe.Writer); var portion1 = new byte[3]; var portion2 = new byte[2]; - await pipe.Reader.ReadBlockAsync(portion1); - await pipe.Reader.ReadBlockAsync(portion2); + await pipe.Reader.ReadExactlyAsync(portion1); + await pipe.Reader.ReadExactlyAsync(portion2); Equal(1, portion1[0]); Equal(5, portion1[1]); Equal(8, portion1[2]); @@ -71,7 +71,7 @@ static async void WriteValueAsync(Memory memory, PipeWriter writer) var pipe = new Pipe(); WriteValueAsync(new byte[] { 1, 5, 8, 9, 10 }, pipe.Writer); Memory result = new byte[124]; - await ThrowsAsync(() => pipe.Reader.ReadBlockAsync(result).AsTask()); + await ThrowsAsync(() => pipe.Reader.ReadExactlyAsync(result).AsTask()); } [Fact] @@ -320,13 +320,13 @@ public static void ReadBlockSynchronously() { var pipe = new Pipe(); pipe.Writer.Write(new byte[] { 10, 20, 30 }); - False(pipe.Reader.TryReadBlock(10L, out var result)); + False(pipe.Reader.TryReadExactly(10L, out var result)); True(result.Buffer.IsEmpty); False(result.IsCanceled); False(result.IsCompleted); pipe.Writer.Complete(); - True(pipe.Reader.TryReadBlock(3L, out result)); + True(pipe.Reader.TryReadExactly(3L, out result)); True(result.IsCompleted); False(result.Buffer.IsEmpty); Equal(3L, result.Buffer.Length); diff --git a/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs b/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs index fed250f82..9df748544 100644 --- a/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs +++ b/src/DotNext.Tests/IO/SequenceBinaryReaderTests.cs @@ -42,7 +42,7 @@ public static async Task CopyToPipe() await reader.CopyToAsync(pipe.Writer); await pipe.Writer.CompleteAsync(); var actual = new byte[expected.Length]; - await pipe.Reader.ReadBlockAsync(actual); + await pipe.Reader.ReadExactlyAsync(actual); Equal(expected, actual); } diff --git a/src/DotNext.Tests/IO/StreamExtensionsTests.cs b/src/DotNext.Tests/IO/StreamExtensionsTests.cs index e568bb6e5..3ab1e9545 100644 --- a/src/DotNext.Tests/IO/StreamExtensionsTests.cs +++ b/src/DotNext.Tests/IO/StreamExtensionsTests.cs @@ -557,9 +557,9 @@ public static void CombineStreams() False(combined.CanSeek); Span buffer = stackalloc byte[6]; - combined.ReadBlock(buffer); + combined.ReadExactly(buffer); - Equal(new byte[] { 1, 2, 3, 4, 5, 6 }, buffer.ToArray()); + Equal([1, 2, 3, 4, 5, 6], buffer.ToArray()); } [Fact] @@ -570,9 +570,9 @@ public static async Task CombineStreamsAsync() await using var combined = new[] { ms1, ms2 }.Combine(); var buffer = new byte[6]; - await combined.ReadBlockAsync(buffer); + await combined.ReadExactlyAsync(buffer); - Equal(new byte[] { 1, 2, 3, 4, 5, 6 }, buffer); + Equal([1, 2, 3, 4, 5, 6], buffer); } [Fact] @@ -631,24 +631,6 @@ public static async Task UnsupportedMethodsOfSparseStream() await ThrowsAsync(async () => await combined.WriteAsync(ReadOnlyMemory.Empty)); } - [Fact] - public static void ReadAtLeastBytes() - { - using var ms = new MemoryStream(new byte[] { 1, 2, 3, 4 }); - Span buffer = stackalloc byte[4]; - Equal(4, ms.ReadAtLeast(3, buffer)); - Equal(ms.ToArray(), buffer.ToArray()); - } - - [Fact] - public static async Task ReadAtLeastBytesAsync() - { - using var ms = new MemoryStream(new byte[] { 1, 2, 3, 4 }); - var buffer = new byte[4]; - Equal(4, await ms.ReadAtLeastAsync(3, buffer)); - Equal(ms.ToArray(), buffer.ToArray()); - } - [Fact] public static async Task ReadEmptyStream() { diff --git a/src/DotNext.Tests/IO/StreamSourceTests.cs b/src/DotNext.Tests/IO/StreamSourceTests.cs index 901466583..6c4d184fa 100644 --- a/src/DotNext.Tests/IO/StreamSourceTests.cs +++ b/src/DotNext.Tests/IO/StreamSourceTests.cs @@ -182,7 +182,7 @@ public static void ReadBlockFromSparseMemory(ReadOnlySequence sequence) buffer.Write(in sequence); using var src = buffer.AsStream(true); Span dest = new byte[data.Length]; - src.ReadBlock(dest); + src.ReadExactly(dest); Equal(data, dest.ToArray()); } @@ -224,7 +224,7 @@ public static async Task ReadBlockFromSparseMemoryAsync(ReadOnlySequence s Equal(src.Length, sequence.Length); Equal(0L, src.Position); Memory dest = new byte[data.Length]; - await src.ReadBlockAsync(dest); + await src.ReadExactlyAsync(dest); Equal(src.Length, src.Position); Equal(data, dest.ToArray()); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs index e38f4be10..0d4c19e1e 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/AppendEntriesMessage.cs @@ -83,7 +83,7 @@ private protected ValueTask SkipAsync() // fast path - attempt to consume metadata synchronously private bool TryConsume() { - if (!reader.TryReadBlock(LogEntryMetadata.Size, out var result) || result.IsCanceled) + if (!reader.TryReadExactly(LogEntryMetadata.Size, out var result) || result.IsCanceled) return false; metadata = new(result.Buffer, out var metadataEnd); @@ -98,7 +98,7 @@ private async ValueTask ConsumeSlowAsync() if (metadataBuffer.IsEmpty) metadataBuffer = new byte[LogEntryMetadata.Size]; - await reader.ReadBlockAsync(metadataBuffer).ConfigureAwait(false); + await reader.ReadExactlyAsync(metadataBuffer).ConfigureAwait(false); metadata = new(metadataBuffer); consumed = false; } @@ -265,7 +265,7 @@ internal AppendEntriesMessage(HttpRequest request, out Func, Cancel : this(request.Headers, out var entriesCount) { entries = CreateReader(request, entriesCount); - configurationReader = request.BodyReader.ReadBlockAsync; + configurationReader = request.BodyReader.ReadExactlyAsync; } private static ILogEntryProducer CreateReader(HttpRequest request, long count) diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs index c7c29621c..c9dd1fa1d 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Consensus/Raft/Http/SynchronizeMessage.cs @@ -48,7 +48,7 @@ internal SynchronizeMessage(HttpRequest request) await using (stream.ConfigureAwait(false)) { using var buffer = MemoryAllocator.Allocate(sizeof(long), exactSize: true); - await stream.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await stream.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); return ReadInt64LittleEndian(buffer.Span); } } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs index a699860d2..a64c5978d 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs @@ -19,7 +19,7 @@ private async Task ProcessDisconnectAsync(HttpRequest request, HttpResponse resp EndPoint sender; bool isAlive; - if (request.BodyReader.TryReadBlock(payloadLength, out var result)) + if (request.BodyReader.TryReadExactly(payloadLength, out var result)) { (sender, isAlive) = DeserializeDisconnectRequest(result.Buffer, out var position); request.BodyReader.AdvanceTo(position); @@ -27,7 +27,7 @@ private async Task ProcessDisconnectAsync(HttpRequest request, HttpResponse resp else { using var buffer = allocator.Invoke(payloadLength, true); - await request.BodyReader.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); (sender, isAlive) = DeserializeDisconnectRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs index 02dfcf2c3..de39edd49 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs @@ -18,7 +18,7 @@ private async Task ProcessForwardJoinAsync(HttpRequest request, HttpResponse res EndPoint sender, joinedPeer; int timeToLive; - if (request.BodyReader.TryReadBlock(payloadLength, out var result)) + if (request.BodyReader.TryReadExactly(payloadLength, out var result)) { // fast path, no need to allocate temp buffer (sender, joinedPeer, timeToLive) = DeserializeForwardJoinRequest(result.Buffer, out var position); @@ -28,7 +28,7 @@ private async Task ProcessForwardJoinAsync(HttpRequest request, HttpResponse res { // slow path, allocate temp buffer using var buffer = allocator.Invoke(payloadLength, true); - await request.BodyReader.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); (sender, joinedPeer, timeToLive) = DeserializeForwardJoinRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs index 358daeb1f..450c4fa5d 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs @@ -16,7 +16,7 @@ internal partial class HttpPeerController private async Task ProcessJoinAsync(HttpRequest request, HttpResponse response, int payloadLength, CancellationToken token) { EndPoint joinedPeer; - if (request.BodyReader.TryReadBlock(payloadLength, out var result)) + if (request.BodyReader.TryReadExactly(payloadLength, out var result)) { // fast path, no need to allocate temp buffer joinedPeer = DeserializeJoinRequest(result.Buffer, out var position); @@ -26,7 +26,7 @@ private async Task ProcessJoinAsync(HttpRequest request, HttpResponse response, { // slow path, copy to temp buffer using var buffer = allocator.Invoke(payloadLength, true); - await request.BodyReader.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); joinedPeer = DeserializeJoinRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs index 6cde1dd38..ef3ddecb0 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs @@ -19,7 +19,7 @@ private async Task ProcessNeighborAsync(HttpRequest request, HttpResponse respon EndPoint sender; bool highPriority; - if (request.BodyReader.TryReadBlock(payloadLength, out var result)) + if (request.BodyReader.TryReadExactly(payloadLength, out var result)) { // fast path, no need to allocate temp buffer (sender, highPriority) = DeserializeNeighborRequest(result.Buffer, out var position); @@ -29,7 +29,7 @@ private async Task ProcessNeighborAsync(HttpRequest request, HttpResponse respon { // slow path, allocate temp buffer using var buffer = allocator.Invoke(payloadLength, true); - await request.BodyReader.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); (sender, highPriority) = DeserializeNeighborRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs index 9dec68652..c0256042a 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs @@ -18,7 +18,7 @@ private async Task ProcessShuffleReplyAsync(HttpRequest request, HttpResponse re { IReadOnlySet peers; - if (request.BodyReader.TryReadBlock(payloadLength, out var result)) + if (request.BodyReader.TryReadExactly(payloadLength, out var result)) { peers = DeserializeShuffleReply(result.Buffer, out var position); request.BodyReader.AdvanceTo(position); @@ -26,7 +26,7 @@ private async Task ProcessShuffleReplyAsync(HttpRequest request, HttpResponse re else { using var buffer = allocator.Invoke(payloadLength, true); - await request.BodyReader.ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); + await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); peers = DeserializeShuffleReply(buffer.Memory); } @@ -94,7 +94,7 @@ private async Task ProcessShuffleRequestAsync(HttpRequest request, HttpResponse IReadOnlySet peers; int timeToLive; - if (request.BodyReader.TryReadBlock(payloadLength, out var result)) + if (request.BodyReader.TryReadExactly(payloadLength, out var result)) { (sender, origin, timeToLive, peers) = DeserializeShuffleRequest(result.Buffer, out var position); request.BodyReader.AdvanceTo(position); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpProtocolStream.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpProtocolStream.cs index 9cd04fc8f..2a475d306 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpProtocolStream.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpProtocolStream.cs @@ -38,13 +38,13 @@ public override long Position private protected override int ReadFromTransport(Span buffer) => BaseStream.Read(buffer); - private protected override int ReadFromTransport(int count, Span buffer) => BaseStream.ReadAtLeast(count, buffer); + private protected override int ReadFromTransport(int count, Span buffer) => BaseStream.ReadAtLeast(buffer, count); private protected override ValueTask ReadFromTransportAsync(Memory buffer, CancellationToken token) => BaseStream.ReadAsync(buffer, token); private protected override ValueTask ReadFromTransportAsync(int minimumSize, Memory buffer, CancellationToken token) - => BaseStream.ReadAtLeastAsync(minimumSize, buffer, token); + => BaseStream.ReadAtLeastAsync(buffer, minimumSize, cancellationToken: token); private protected override void WriteToTransport(ReadOnlySpan buffer) => BaseStream.Write(buffer); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.cs index de39c54d3..99b416363 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.cs @@ -140,7 +140,7 @@ private async ValueTask AppendEntriesAsync(ProtocolStream protocol, Cancellation var configuration = entries.Configuration.Content; if (!configuration.IsEmpty) { - await protocol.ReadBlockAsync(configuration, token).ConfigureAwait(false); + await protocol.ReadExactlyAsync(configuration, token).ConfigureAwait(false); } protocol.ResetReadState(); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Configuration.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Configuration.cs index 92dcf65ea..a7d18e58c 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Configuration.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ServerExchange.Configuration.cs @@ -9,7 +9,7 @@ private async ValueTask BeginReceiveConfiguration(ReadOnlyMemory inp var count = ConfigurationExchange.ParseAnnouncement(input.Span, out var fingerprint, out var length); input = input.Slice(count); var result = await Writer.WriteAsync(input, token).ConfigureAwait(false); - task = server.ProposeConfigurationAsync(Reader.ReadBlockAsync, length, fingerprint, token).AsTask(); + task = server.ProposeConfigurationAsync(Reader.ReadExactlyAsync, length, fingerprint, token).AsTask(); if (result.IsCompleted || completed) { await Writer.CompleteAsync().ConfigureAwait(false); From 16a5d1d55dec0f8d111167fc44e804a0d31b829a Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 18:55:51 +0200 Subject: [PATCH 075/155] Fixed naming convention --- src/DotNext.IO/IO/DecodingTextReader.cs | 4 ++-- src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs | 2 +- src/DotNext.IO/IO/FileBufferingWriter.cs | 6 +++--- src/DotNext.IO/IO/FileReader.Binary.cs | 2 +- src/DotNext.IO/IO/FileReader.cs | 2 +- src/DotNext.IO/IO/FileWriter.cs | 2 +- src/DotNext.IO/IO/IDataTransferObject.cs | 2 +- src/DotNext.IO/IO/MemoryTransferObject.cs | 2 +- src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs | 4 ++-- src/DotNext.IO/IO/SequenceReader.cs | 4 ++-- src/DotNext.IO/IO/StreamExtensions.Writers.cs | 2 +- src/DotNext.IO/IO/StreamExtensions.cs | 10 +++++----- src/DotNext.IO/IO/TextStreamExtensions.cs | 2 +- .../Maintenance/ApplicationMaintenanceInterfaceHost.cs | 2 +- src/DotNext.Tests/Buffers/MemoryOwnerTests.cs | 2 +- src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs | 2 +- src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs | 2 +- src/DotNext/Buffers/BufferWriterSlim.cs | 4 ++-- src/DotNext/Buffers/IBinaryFormattable.cs | 2 +- src/DotNext/Buffers/MemoryAllocator.cs | 2 +- src/DotNext/Buffers/PooledBufferWriter.cs | 2 +- .../Buffers/PoolingInterpolatedStringHandler.cs | 2 +- src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs | 4 ++-- src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs | 2 +- src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs | 2 +- src/DotNext/Text/EncodingExtensions.cs | 4 ++-- .../HyParView/Http/HttpPeerController.Disconnect.cs | 2 +- .../HyParView/Http/HttpPeerController.ForwardJoin.cs | 2 +- .../HyParView/Http/HttpPeerController.Join.cs | 2 +- .../HyParView/Http/HttpPeerController.Neighbor.cs | 2 +- .../HyParView/Http/HttpPeerController.Shuffle.cs | 2 +- .../Net/Cluster/Consensus/Raft/BinaryLogEntry.cs | 2 +- .../PersistentClusterConfigurationStorage.cs | 2 +- .../Consensus/Raft/PersistentState.NodeState.cs | 2 +- .../Consensus/Raft/PersistentState.Partition.cs | 2 +- .../Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs | 2 +- .../Consensus/Raft/RaftLogEntryBufferingOptions.cs | 2 +- .../Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs | 2 +- .../ConnectionOriented/ProtocolStream.cs | 6 +++--- .../ConnectionOriented/Server.LogEntryProducer.cs | 2 +- 40 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/DotNext.IO/IO/DecodingTextReader.cs b/src/DotNext.IO/IO/DecodingTextReader.cs index 58809eee0..22ac4e49e 100644 --- a/src/DotNext.IO/IO/DecodingTextReader.cs +++ b/src/DotNext.IO/IO/DecodingTextReader.cs @@ -25,7 +25,7 @@ internal DecodingTextReader(ReadOnlySequence sequence, Encoding encoding, decoder = encoding.GetDecoder(); this.sequence = sequence; this.allocator = allocator; - buffer = allocator.Invoke(bufferSize, false); + buffer = allocator.Allocate(bufferSize, false); } private Span Buffer => buffer.Span; @@ -140,7 +140,7 @@ public override int Read(Span buffer) private string ReadToEnd(int bufferSize, bool bufferNotEmpty) { - using var output = allocator.Invoke(bufferSize, false); + using var output = allocator.Allocate(bufferSize, false); var writer = new SpanWriter(output.Span); if (bufferNotEmpty) { diff --git a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs index cbb486931..d0cc13cae 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs @@ -121,7 +121,7 @@ internal ReadOnlySequenceSource(FileBufferingWriter writer, int segmentLength) var buffer = writer.buffer; tail = buffer.Memory.Slice(0, writer.position); handle = writer.fileBackend.SafeFileHandle; - this.buffer = writer.allocator.Invoke(segmentLength, true); + this.buffer = writer.allocator.Allocate(segmentLength, true); this.segmentLength = segmentLength; session = writer.EnterReadMode(this); diff --git a/src/DotNext.IO/IO/FileBufferingWriter.cs b/src/DotNext.IO/IO/FileBufferingWriter.cs index ce63ae661..dd2d0787f 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.cs @@ -205,7 +205,7 @@ public FileBufferingWriter(in Options options) var memoryThreshold = options.MemoryThreshold; if (options.InitialCapacity > 0) { - buffer = allocator.Invoke(options.InitialCapacity, exactSize: false); + buffer = allocator.Allocate(options.InitialCapacity, exactSize: false); if (buffer.Length > memoryThreshold) memoryThreshold = buffer.Length < Array.MaxLength ? buffer.Length + 1 : Array.MaxLength; } @@ -589,7 +589,7 @@ public async Task CopyToAsync(TConsumer consumer, int bufferSize, Can if (fileBackend is not null) { - using var buffer = allocator.Invoke(bufferSize, exactSize: false); + using var buffer = allocator.Allocate(bufferSize, exactSize: false); int count; for (long offset = 0L; (count = await RandomAccess.ReadAsync(fileBackend.SafeFileHandle, buffer.Memory, offset, token).ConfigureAwait(false)) > 0; offset += count) await consumer.Invoke(buffer.Memory.Slice(0, count), token).ConfigureAwait(false); @@ -616,7 +616,7 @@ public void CopyTo(TConsumer consumer, int bufferSize, CancellationTo { if (fileBackend is not null) { - using var buffer = allocator.Invoke(bufferSize, exactSize: false); + using var buffer = allocator.Allocate(bufferSize, exactSize: false); int count; for (long offset = 0L; (count = RandomAccess.Read(fileBackend.SafeFileHandle, buffer.Span, offset)) > 0; offset += count, token.ThrowIfCancellationRequested()) consumer.Invoke(buffer.Span.Slice(0, count)); diff --git a/src/DotNext.IO/IO/FileReader.Binary.cs b/src/DotNext.IO/IO/FileReader.Binary.cs index 326f54803..b8463773f 100644 --- a/src/DotNext.IO/IO/FileReader.Binary.cs +++ b/src/DotNext.IO/IO/FileReader.Binary.cs @@ -141,7 +141,7 @@ public async ValueTask> ReadAsync(LengthFormat lengthFormat, M MemoryOwner result; if (length > 0) { - result = allocator.Invoke(length, true); + result = allocator.Allocate(length, true); await ReadBlockAsync(result.Memory, token).ConfigureAwait(false); } else diff --git a/src/DotNext.IO/IO/FileReader.cs b/src/DotNext.IO/IO/FileReader.cs index 16c81ad98..bb367cd9b 100644 --- a/src/DotNext.IO/IO/FileReader.cs +++ b/src/DotNext.IO/IO/FileReader.cs @@ -49,7 +49,7 @@ public FileReader(SafeFileHandle handle, long fileOffset = 0L, int bufferSize = if (bufferSize <= 16) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - buffer = allocator.Invoke(bufferSize, exactSize: false); + buffer = allocator.Allocate(bufferSize, exactSize: false); this.handle = handle; this.fileOffset = fileOffset; } diff --git a/src/DotNext.IO/IO/FileWriter.cs b/src/DotNext.IO/IO/FileWriter.cs index 12cd2acf7..d68bded37 100644 --- a/src/DotNext.IO/IO/FileWriter.cs +++ b/src/DotNext.IO/IO/FileWriter.cs @@ -49,7 +49,7 @@ public FileWriter(SafeFileHandle handle, long fileOffset = 0L, int bufferSize = if (bufferSize <= 16) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - buffer = allocator.Invoke(bufferSize, exactSize: false); + buffer = allocator.Allocate(bufferSize, exactSize: false); this.handle = handle; this.fileOffset = fileOffset; } diff --git a/src/DotNext.IO/IO/IDataTransferObject.cs b/src/DotNext.IO/IO/IDataTransferObject.cs index 5a9df5cd3..047a435ac 100644 --- a/src/DotNext.IO/IO/IDataTransferObject.cs +++ b/src/DotNext.IO/IO/IDataTransferObject.cs @@ -114,7 +114,7 @@ protected static async ValueTask TransformAsync TransformAsync(Stream input, TTransformation transformation, bool resetStream, MemoryAllocator? allocator, CancellationToken token) where TTransformation : notnull, ITransformation { - var buffer = allocator.Invoke(DefaultBufferSize, false); + var buffer = allocator.Allocate(DefaultBufferSize, false); try { return await transformation.TransformAsync(new AsyncStreamBinaryAccessor(input, buffer.Memory), token).ConfigureAwait(false); diff --git a/src/DotNext.IO/IO/MemoryTransferObject.cs b/src/DotNext.IO/IO/MemoryTransferObject.cs index d6a42425a..5a433a385 100644 --- a/src/DotNext.IO/IO/MemoryTransferObject.cs +++ b/src/DotNext.IO/IO/MemoryTransferObject.cs @@ -15,7 +15,7 @@ public class MemoryTransferObject : Disposable, IDataTransferObject /// The length, in bytes, of the content. /// The memory allocator. public MemoryTransferObject(int length, MemoryAllocator? allocator = null) - => owner = length > 0 ? allocator.Invoke(length, true) : default; + => owner = length > 0 ? allocator.Allocate(length, true) : default; /// /// Transforms this object to serialized form. diff --git a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs index 1bb412638..3ad59bc51 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs @@ -230,7 +230,7 @@ public static async ValueTask> ReadStringAsync(this PipeReader } else { - result = allocator.Invoke(length, exactSize: true); + result = allocator.Allocate(length, exactSize: true); length = await ReadAsync>>(reader, new(context, new ArrayBuffer(result)), token).ConfigureAwait(false); result.TryResize(length); } @@ -570,7 +570,7 @@ public static async ValueTask> ReadBlockAsync(this PipeReader MemoryOwner result; if (length > 0) { - result = allocator.Invoke(length, true); + result = allocator.Allocate(length, true); await ReadExactlyAsync(reader, result.Memory, token).ConfigureAwait(false); } else diff --git a/src/DotNext.IO/IO/SequenceReader.cs b/src/DotNext.IO/IO/SequenceReader.cs index 52632226b..68f71f411 100644 --- a/src/DotNext.IO/IO/SequenceReader.cs +++ b/src/DotNext.IO/IO/SequenceReader.cs @@ -172,7 +172,7 @@ public MemoryOwner Read(LengthFormat lengthFormat, MemoryAllocator? MemoryOwner result; if (length > 0) { - result = allocator.Invoke(length, exactSize: true); + result = allocator.Allocate(length, exactSize: true); Read(result.Span); } else @@ -352,7 +352,7 @@ public unsafe MemoryOwner ReadString(int length, in DecodingContext contex } else { - result = allocator.Invoke(length, exactSize: true); + result = allocator.Allocate(length, exactSize: true); length = Read>>(new(in context, new ArrayBuffer(result))); result.TryResize(length); } diff --git a/src/DotNext.IO/IO/StreamExtensions.Writers.cs b/src/DotNext.IO/IO/StreamExtensions.Writers.cs index 194db12d5..89458cbb9 100644 --- a/src/DotNext.IO/IO/StreamExtensions.Writers.cs +++ b/src/DotNext.IO/IO/StreamExtensions.Writers.cs @@ -297,7 +297,7 @@ public static async ValueTask WriteBigIntegerAsync(this Stream stream, BigIntege public static async ValueTask WriteBigIntegerAsync(this Stream stream, BigInteger value, bool littleEndian, MemoryAllocator? allocator = null, LengthFormat? lengthFormat = null, CancellationToken token = default) { var bytesCount = value.GetByteCount(); - using var buffer = allocator.Invoke(Math.Max(1, bytesCount), false); + using var buffer = allocator.Allocate(Math.Max(1, bytesCount), false); if (lengthFormat.HasValue) await stream.WriteLengthAsync(bytesCount, lengthFormat.GetValueOrDefault(), buffer.Memory, token).ConfigureAwait(false); diff --git a/src/DotNext.IO/IO/StreamExtensions.cs b/src/DotNext.IO/IO/StreamExtensions.cs index ee1c62624..a09576a39 100644 --- a/src/DotNext.IO/IO/StreamExtensions.cs +++ b/src/DotNext.IO/IO/StreamExtensions.cs @@ -187,7 +187,7 @@ public static MemoryOwner ReadString(this Stream stream, int length, in De } else { - result = allocator.Invoke(length, exactSize: true); + result = allocator.Allocate(length, exactSize: true); length = ReadString(stream, result.Span, in context, buffer); result.TryResize(length); } @@ -700,7 +700,7 @@ public static async ValueTask> ReadStringAsync(this Stream str } else { - result = allocator.Invoke(length, exactSize: true); + result = allocator.Allocate(length, exactSize: true); length = await ReadStringAsync(stream, result.Memory, context, buffer, token).ConfigureAwait(false); result.TryResize(length); } @@ -856,7 +856,7 @@ public static MemoryOwner ReadBlock(this Stream stream, LengthFormat lengt MemoryOwner result; if (length > 0) { - result = allocator.Invoke(length, true); + result = allocator.Allocate(length, true); stream.ReadExactly(result.Span); } else @@ -900,7 +900,7 @@ public static async ValueTask> ReadBlockAsync(this Stream stre MemoryOwner result; if (length > 0) { - result = allocator.Invoke(length, true); + result = allocator.Allocate(length, true); await stream.ReadExactlyAsync(result.Memory, token).ConfigureAwait(false); } else @@ -1213,7 +1213,7 @@ public static async IAsyncEnumerable> ReadAllAsync(this Str if (bufferSize < 1) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - using var bufferOwner = allocator.Invoke(bufferSize, exactSize: false); + using var bufferOwner = allocator.Allocate(bufferSize, exactSize: false); var buffer = bufferOwner.Memory; for (int count; (count = await stream.ReadAsync(buffer, token).ConfigureAwait(false)) > 0;) diff --git a/src/DotNext.IO/IO/TextStreamExtensions.cs b/src/DotNext.IO/IO/TextStreamExtensions.cs index ad18de742..173080cde 100644 --- a/src/DotNext.IO/IO/TextStreamExtensions.cs +++ b/src/DotNext.IO/IO/TextStreamExtensions.cs @@ -206,7 +206,7 @@ public static async IAsyncEnumerable> ReadAllAsync(this Tex if (bufferSize < 1) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - using var bufferOwner = allocator.Invoke(bufferSize, exactSize: false); + using var bufferOwner = allocator.Allocate(bufferSize, exactSize: false); var buffer = bufferOwner.Memory; for (int count; (count = await reader.ReadAsync(buffer, token).ConfigureAwait(false)) > 0;) diff --git a/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs b/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs index 2bbf9e923..fa1e6b89c 100644 --- a/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs +++ b/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs @@ -175,7 +175,7 @@ private async void ProcessRequestAsync(Socket clientSocket, CancellationToken to { int commandLength; - using (var buffer = ByteBufferAllocator.Invoke(bufferSize, exactSize: false)) + using (var buffer = ByteBufferAllocator.Allocate(bufferSize, exactSize: false)) { commandLength = await ReadRequestAsync(clientSocket, encoding, decoder, buffer.Memory, inputBuffer, token).ConfigureAwait(false); } diff --git a/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs b/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs index 6fdaa3260..c1395f3c5 100644 --- a/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs +++ b/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs @@ -76,7 +76,7 @@ public static IEnumerable GetArrayAllocators() [MemberData(nameof(GetArrayAllocators))] public static void ArrayAllocation(MemoryAllocator allocator) { - using var owner = allocator.Invoke(4, false); + using var owner = allocator.Allocate(4, false); Equal(4, owner.Length); } diff --git a/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs b/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs index e5beea791..04053b31a 100644 --- a/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs +++ b/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs @@ -49,7 +49,7 @@ public static unsafe void ArrayInteropTest() [Fact] public static unsafe void UnmanagedMemoryAllocation() { - using var owner = UnmanagedMemoryAllocator.GetAllocator(false).Invoke(3, false); + using var owner = UnmanagedMemoryAllocator.GetAllocator(false).Allocate(3, false); Span array = owner.Span; array[0] = 10; array[1] = 20; diff --git a/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs b/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs index b3aa515a2..ab43b8b44 100644 --- a/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs +++ b/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs @@ -29,7 +29,7 @@ public static void Resize(this ref MemoryOwner owner, int newLength, bool { if (!owner.TryResize(newLength)) { - var newBuffer = allocator.Invoke(newLength, exactSize); + var newBuffer = allocator.Allocate(newLength, exactSize); owner.Span.CopyTo(newBuffer.Span); owner.Dispose(); owner = newBuffer; diff --git a/src/DotNext/Buffers/BufferWriterSlim.cs b/src/DotNext/Buffers/BufferWriterSlim.cs index 42f332fe5..85ac4ea51 100644 --- a/src/DotNext/Buffers/BufferWriterSlim.cs +++ b/src/DotNext/Buffers/BufferWriterSlim.cs @@ -60,7 +60,7 @@ public BufferWriterSlim(int initialCapacity, MemoryAllocator? allocator = nul initialBuffer = default; this.allocator = allocator; - extraBuffer = initialCapacity is 0 ? default : allocator.Invoke(initialCapacity, exactSize: false); + extraBuffer = initialCapacity is 0 ? default : allocator.Allocate(initialCapacity, exactSize: false); position = 0; } @@ -133,7 +133,7 @@ internal Span InternalGetSpan(int sizeHint) // need to copy initial buffer if (IGrowableBuffer.GetBufferSize(sizeHint, initialBuffer.Length, position, out newSize)) { - extraBuffer = allocator.Invoke(newSize, exactSize: false); + extraBuffer = allocator.Allocate(newSize, exactSize: false); initialBuffer.CopyTo(result = extraBuffer.Span); } else diff --git a/src/DotNext/Buffers/IBinaryFormattable.cs b/src/DotNext/Buffers/IBinaryFormattable.cs index 27cc350e2..a6aafbf3a 100644 --- a/src/DotNext/Buffers/IBinaryFormattable.cs +++ b/src/DotNext/Buffers/IBinaryFormattable.cs @@ -55,7 +55,7 @@ public static bool TryParse(ReadOnlySpan input, [NotNullWhen(true)] out TS /// The buffer containing formatted value. public static MemoryOwner Format(TSelf value, MemoryAllocator? allocator = null) { - var result = allocator.Invoke(TSelf.Size, true); + var result = allocator.Allocate(TSelf.Size, true); var writer = new SpanWriter(result.Span); value.Format(ref writer); return result; diff --git a/src/DotNext/Buffers/MemoryAllocator.cs b/src/DotNext/Buffers/MemoryAllocator.cs index f8159f779..0b11b48f0 100644 --- a/src/DotNext/Buffers/MemoryAllocator.cs +++ b/src/DotNext/Buffers/MemoryAllocator.cs @@ -66,7 +66,7 @@ public static MemoryAllocator ToAllocator(this Func> /// The type of the items in the memory pool. /// The allocated memory. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Invoke(this MemoryAllocator? allocator, int length, [ConstantExpected] bool exactSize) + public static MemoryOwner Allocate(this MemoryAllocator? allocator, int length, [ConstantExpected] bool exactSize) { MemoryOwner result; if (allocator is null) diff --git a/src/DotNext/Buffers/PooledBufferWriter.cs b/src/DotNext/Buffers/PooledBufferWriter.cs index ceba669e3..9b8886d50 100644 --- a/src/DotNext/Buffers/PooledBufferWriter.cs +++ b/src/DotNext/Buffers/PooledBufferWriter.cs @@ -29,7 +29,7 @@ public override int Capacity case < 0: throw new ArgumentOutOfRangeException(nameof(value)); case > 0: - buffer = allocator.Invoke(value, exactSize: false); + buffer = allocator.Allocate(value, exactSize: false); break; } } diff --git a/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs b/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs index cfec2e671..67849b480 100644 --- a/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs +++ b/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs @@ -35,7 +35,7 @@ public PoolingInterpolatedStringHandler(int literalLength, int formattedCount, M var length = (charsPerPlaceholder * formattedCount) + literalLength; buffer = (uint)length <= (uint)Array.MaxLength - ? allocator.Invoke(length, exactSize: false) + ? allocator.Allocate(length, exactSize: false) : throw new InsufficientMemoryException(); this.allocator = allocator; diff --git a/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs b/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs index 2066f00b0..d2678cf38 100644 --- a/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs +++ b/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs @@ -62,7 +62,7 @@ private sealed class PooledMemoryChunk : MemoryChunk internal PooledMemoryChunk(MemoryAllocator? allocator, int length, MemoryChunk? previous = null) : base(previous) - => owner = allocator.Invoke(length, exactSize: false); + => owner = allocator.Allocate(length, exactSize: false); /// /// Indicates that the chunk has no occupied elements in the memory. @@ -97,7 +97,7 @@ internal void Advance(int count) internal void Realloc(MemoryAllocator? allocator, int length) { owner.Dispose(); - owner = allocator.Invoke(length, exactSize: false); + owner = allocator.Allocate(length, exactSize: false); } protected override void Dispose(bool disposing) diff --git a/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs b/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs index ef099ba73..a775f49db 100644 --- a/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs +++ b/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs @@ -256,7 +256,7 @@ public static async IAsyncEnumerable> EncodeToCharsAsync(IA if (encoder.HasBufferedData) { - using (buffer = allocator.Invoke(MaxBufferedDataSize, exactSize: false)) + using (buffer = allocator.Allocate(MaxBufferedDataSize, exactSize: false)) { var count = encoder.Flush(buffer.Span); yield return buffer.Memory.Slice(0, count); diff --git a/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs b/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs index cbc0d4109..de3662a66 100644 --- a/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs +++ b/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs @@ -223,7 +223,7 @@ public static async IAsyncEnumerable> EncodeToUtf8Async(IAs if (encoder.HasBufferedData) { - using (buffer = allocator.Invoke(MaxBufferedDataSize, exactSize: false)) + using (buffer = allocator.Allocate(MaxBufferedDataSize, exactSize: false)) { var count = encoder.Flush(buffer.Span); yield return buffer.Memory.Slice(0, count); diff --git a/src/DotNext/Text/EncodingExtensions.cs b/src/DotNext/Text/EncodingExtensions.cs index 24d084a88..275ee281c 100644 --- a/src/DotNext/Text/EncodingExtensions.cs +++ b/src/DotNext/Text/EncodingExtensions.cs @@ -38,7 +38,7 @@ public static MemoryOwner GetBytes(this Encoding encoding, ReadOnlySpan GetChars(this Encoding encoding, ReadOnlySpan MemoryOwner IBinaryLogEntry.ToBuffer(MemoryAllocator allocator) { - var buffer = allocator.Invoke(T.Size, exactSize: true); + var buffer = allocator.Allocate(T.Size, exactSize: true); var writer = new SpanWriter(buffer.Span); Content.Format(ref writer); return buffer; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs index d7aa74110..b59c8c2e6 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs @@ -80,7 +80,7 @@ async ValueTask IDataTransferObject.WriteToAsync(TWriter writer, Cancel { // this method should be safe for concurrent invocations var handle = fs.SafeFileHandle; - using var buffer = allocator.Invoke(bufferSize, exactSize: false); + using var buffer = allocator.Allocate(bufferSize, exactSize: false); for (int offset = PayloadOffset, count; (count = await RandomAccess.ReadAsync(handle, buffer.Memory, offset, token).ConfigureAwait(false)) > 0; offset += count) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs index 6f0b990af..3b06db003 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs @@ -58,7 +58,7 @@ private NodeState(string fileName, MemoryAllocator allocator, bool integri { Debug.Assert(Capacity >= LastVoteOffset + sizeof(long)); - buffer = allocator.Invoke(Capacity, true); + buffer = allocator.Allocate(Capacity, true); if (File.Exists(fileName)) { handle = File.OpenHandle(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.SequentialScan); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs index ae914b509..15cc5b524 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs @@ -42,7 +42,7 @@ internal Partition(DirectoryInfo location, int bufferSize, int recordsPerPartiti PartitionNumber = partitionNumber; // allocate metadata segment - metadata = manager.BufferAllocator.Invoke(fileOffset, true); + metadata = manager.BufferAllocator.Allocate(fileOffset, true); metadataFlushStartAddress = int.MaxValue; entryCache = manager.AllocLogEntryCache(recordsPerPartition); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs index be76d2294..65e8ec826 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs @@ -270,7 +270,7 @@ ValueTask ILocalMember.ResignAsync(CancellationToken token) [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] // hot path, avoid allocations async ValueTask ILocalMember.ProposeConfigurationAsync(Func, CancellationToken, ValueTask> configurationReader, long configurationLength, long fingerprint, CancellationToken token) { - var buffer = allocator.Invoke(int.CreateSaturating(configurationLength), exactSize: true); + var buffer = allocator.Allocate(int.CreateSaturating(configurationLength), exactSize: true); await configurationReader(buffer.Memory, token).ConfigureAwait(false); cachedConfig.Clear(); cachedConfig.Update(buffer, fingerprint); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs index e7e856ef7..885b17ec0 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs @@ -55,7 +55,7 @@ public MemoryAllocator? MemoryAllocator internal string GetRandomFileName() => Path.Combine(TempPath, Path.GetRandomFileName()); - internal MemoryOwner RentBuffer() => MemoryAllocator.Invoke(BufferSize, false); + internal MemoryOwner RentBuffer() => MemoryAllocator.Allocate(BufferSize, false); internal FileBufferingWriter CreateBufferingWriter() { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs index 61583df56..1ec70643a 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs @@ -24,7 +24,7 @@ internal ConnectionContext(TcpStream transport, TcpProtocolStream protocol, int this.transport = transport; this.protocol = protocol; - buffer = allocator.Invoke(bufferSize, exactSize: false); + buffer = allocator.Allocate(bufferSize, exactSize: false); } internal int CloseTimeout diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs index b2e95e5a5..274d4bb1b 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs @@ -23,7 +23,7 @@ private protected ProtocolStream(MemoryAllocator allocator, int transmissi { Debug.Assert(transmissionBlockSize > 0); - buffer = allocator.Invoke(transmissionBlockSize, exactSize: false); + buffer = allocator.Allocate(transmissionBlockSize, exactSize: false); this.allocator = allocator; } @@ -55,7 +55,7 @@ private protected virtual void WriteToTransport(ReadOnlySpan buffer) private protected virtual int ReadFromTransport(Span buffer) { int result; - var localBuffer = allocator.Invoke(buffer.Length, exactSize: true); + var localBuffer = allocator.Allocate(buffer.Length, exactSize: true); var timeoutTracker = new CancellationTokenSource(ReadTimeout); var task = ReadFromTransportAsync(localBuffer.Memory, timeoutTracker.Token).AsTask(); try @@ -83,7 +83,7 @@ private protected virtual int ReadFromTransport(Span buffer) private protected virtual int ReadFromTransport(int minimumSize, Span buffer) { int result; - var localBuffer = allocator.Invoke(buffer.Length, exactSize: true); + var localBuffer = allocator.Allocate(buffer.Length, exactSize: true); var timeoutTracker = new CancellationTokenSource(ReadTimeout); var task = ReadFromTransportAsync(minimumSize, localBuffer.Memory, timeoutTracker.Token).AsTask(); try diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs index 981e561ea..89efb8bc9 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs @@ -99,7 +99,7 @@ ValueTask IDataTransferObject.WriteToAsync(TWriter writer, Cancellation async ValueTask IDataTransferObject.TransformAsync(TTransformation transformation, CancellationToken token) { // we don't need large buffer. It is used for encoding some special data types, such as strings - using var buffer = allocator.Invoke(length: 512, exactSize: false); + using var buffer = allocator.Allocate(length: 512, exactSize: false); var result = await IDataTransferObject.TransformAsync(stream, transformation, resetStream: false, buffer.Memory, token).ConfigureAwait(false); consumed = true; return result; From b9e87e7e1d327f2203c7d9a54a01bb2d93f6061a Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 22:34:17 +0200 Subject: [PATCH 076/155] Split Alloc/Invoke on two methods: allocation of exact size and at least size to reduce branching on hot path --- src/DotNext.IO/IO/DataTransferObject.cs | 2 +- src/DotNext.IO/IO/DecodingTextReader.cs | 4 +- .../IO/FileBufferingWriter.Sequence.cs | 2 +- src/DotNext.IO/IO/FileBufferingWriter.cs | 11 ++-- src/DotNext.IO/IO/FileReader.Binary.cs | 16 ++--- src/DotNext.IO/IO/FileReader.cs | 13 ++-- src/DotNext.IO/IO/FileWriter.Binary.cs | 6 +- src/DotNext.IO/IO/FileWriter.cs | 2 +- src/DotNext.IO/IO/IAsyncBinaryReader.cs | 12 ++-- src/DotNext.IO/IO/IAsyncBinaryWriter.cs | 6 +- src/DotNext.IO/IO/IDataTransferObject.cs | 6 +- src/DotNext.IO/IO/MemoryTransferObject.cs | 2 +- .../IO/Pipelines/PipeExtensions.Readers.cs | 6 +- src/DotNext.IO/IO/SequenceReader.cs | 4 +- src/DotNext.IO/IO/StreamExtensions.Writers.cs | 10 ++-- src/DotNext.IO/IO/StreamExtensions.cs | 38 ++++++------ src/DotNext.IO/IO/TextStreamExtensions.cs | 5 +- .../Runtime/Serialization/Serializable.cs | 2 +- src/DotNext.IO/Text/Json/JsonSerializable.cs | 2 +- .../ApplicationMaintenanceInterfaceHost.cs | 2 +- src/DotNext.Tests/Buffers/MemoryOwnerTests.cs | 6 +- .../Buffers/UnmanagedMemoryPoolTests.cs | 2 +- .../TransportServices/TransportTestSuite.cs | 2 +- .../Buffers/BufferHelpers.MemoryOwner.cs | 8 +-- src/DotNext/Buffers/BufferWriterSlim.cs | 9 ++- src/DotNext/Buffers/IBinaryFormattable.cs | 2 +- src/DotNext/Buffers/MemoryAllocator.cs | 59 +++++++++++++------ src/DotNext/Buffers/PooledBufferWriter.cs | 4 +- .../PoolingInterpolatedStringHandler.cs | 4 +- .../Buffers/SparseBufferWriter.MemoryChunk.cs | 4 +- .../Buffers/Text/Base64Encoder.Unicode.cs | 2 +- .../Buffers/Text/Base64Encoder.Utf8.cs | 2 +- src/DotNext/Text/EncodingExtensions.cs | 4 +- .../Consensus/Raft/Http/SynchronizeMessage.cs | 2 +- .../Http/HttpPeerController.Disconnect.cs | 2 +- .../Http/HttpPeerController.ForwardJoin.cs | 2 +- .../HyParView/Http/HttpPeerController.Join.cs | 2 +- .../Http/HttpPeerController.Neighbor.cs | 2 +- .../Http/HttpPeerController.Shuffle.cs | 2 +- .../Cluster/Consensus/Raft/BinaryLogEntry.cs | 2 +- .../PersistentClusterConfigurationStorage.cs | 2 +- .../Raft/PersistentState.LogEntry.cs | 4 +- .../Raft/PersistentState.NodeState.cs | 2 +- .../Raft/PersistentState.Partition.cs | 2 +- .../Consensus/Raft/RaftCluster.DefaultImpl.cs | 2 +- .../Raft/RaftLogEntryBufferingOptions.cs | 2 +- .../Cluster/Consensus/Raft/Tcp/TcpClient.cs | 2 +- .../ConnectionOriented/ProtocolStream.cs | 6 +- .../Server.LogEntryProducer.cs | 2 +- 49 files changed, 154 insertions(+), 143 deletions(-) diff --git a/src/DotNext.IO/IO/DataTransferObject.cs b/src/DotNext.IO/IO/DataTransferObject.cs index 681c5a810..9ae3f8441 100644 --- a/src/DotNext.IO/IO/DataTransferObject.cs +++ b/src/DotNext.IO/IO/DataTransferObject.cs @@ -78,7 +78,7 @@ public static ValueTask WriteToAsync(this TObject dto, Stream output, i static async ValueTask WriteToStreamAsync(TObject dto, Stream output, int bufferSize, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(bufferSize, exactSize: false); + using var buffer = MemoryAllocator.AllocateAtLeast(bufferSize); await WriteToAsync(dto, output, buffer.Memory, token).ConfigureAwait(false); } } diff --git a/src/DotNext.IO/IO/DecodingTextReader.cs b/src/DotNext.IO/IO/DecodingTextReader.cs index 22ac4e49e..035e0463e 100644 --- a/src/DotNext.IO/IO/DecodingTextReader.cs +++ b/src/DotNext.IO/IO/DecodingTextReader.cs @@ -25,7 +25,7 @@ internal DecodingTextReader(ReadOnlySequence sequence, Encoding encoding, decoder = encoding.GetDecoder(); this.sequence = sequence; this.allocator = allocator; - buffer = allocator.Allocate(bufferSize, false); + buffer = allocator.AllocateAtLeast(bufferSize); } private Span Buffer => buffer.Span; @@ -140,7 +140,7 @@ public override int Read(Span buffer) private string ReadToEnd(int bufferSize, bool bufferNotEmpty) { - using var output = allocator.Allocate(bufferSize, false); + using var output = allocator.AllocateAtLeast(bufferSize); var writer = new SpanWriter(output.Span); if (bufferNotEmpty) { diff --git a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs index d0cc13cae..2930ea9d8 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs @@ -121,7 +121,7 @@ internal ReadOnlySequenceSource(FileBufferingWriter writer, int segmentLength) var buffer = writer.buffer; tail = buffer.Memory.Slice(0, writer.position); handle = writer.fileBackend.SafeFileHandle; - this.buffer = writer.allocator.Allocate(segmentLength, true); + this.buffer = writer.allocator.AllocateExactly(segmentLength); this.segmentLength = segmentLength; session = writer.EnterReadMode(this); diff --git a/src/DotNext.IO/IO/FileBufferingWriter.cs b/src/DotNext.IO/IO/FileBufferingWriter.cs index dd2d0787f..4fe707333 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.cs @@ -205,7 +205,7 @@ public FileBufferingWriter(in Options options) var memoryThreshold = options.MemoryThreshold; if (options.InitialCapacity > 0) { - buffer = allocator.Allocate(options.InitialCapacity, exactSize: false); + buffer = allocator.AllocateAtLeast(options.InitialCapacity); if (buffer.Length > memoryThreshold) memoryThreshold = buffer.Length < Array.MaxLength ? buffer.Length + 1 : Array.MaxLength; } @@ -341,7 +341,7 @@ private MemoryEvaluationResult PrepareMemory(int size, out Memory output) if ((uint)bufLen > (uint)newSize && (uint)bufLen <= (uint)memoryThreshold) newSize = bufLen; - buffer.Resize(newSize, exactSize: false, allocator: allocator); + buffer.Resize(newSize, allocator); AllocationMeter.Record(buffer.Length, measurementTags); } @@ -584,12 +584,11 @@ public override void SetLength(long value) public async Task CopyToAsync(TConsumer consumer, int bufferSize, CancellationToken token) where TConsumer : notnull, ISupplier, CancellationToken, ValueTask> { - if (bufferSize <= 0) - throw new ArgumentOutOfRangeException(nameof(bufferSize)); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize); if (fileBackend is not null) { - using var buffer = allocator.Allocate(bufferSize, exactSize: false); + using var buffer = allocator.AllocateAtLeast(bufferSize); int count; for (long offset = 0L; (count = await RandomAccess.ReadAsync(fileBackend.SafeFileHandle, buffer.Memory, offset, token).ConfigureAwait(false)) > 0; offset += count) await consumer.Invoke(buffer.Memory.Slice(0, count), token).ConfigureAwait(false); @@ -616,7 +615,7 @@ public void CopyTo(TConsumer consumer, int bufferSize, CancellationTo { if (fileBackend is not null) { - using var buffer = allocator.Allocate(bufferSize, exactSize: false); + using var buffer = allocator.AllocateAtLeast(bufferSize); int count; for (long offset = 0L; (count = RandomAccess.Read(fileBackend.SafeFileHandle, buffer.Span, offset)) > 0; offset += count, token.ThrowIfCancellationRequested()) consumer.Invoke(buffer.Span.Slice(0, count)); diff --git a/src/DotNext.IO/IO/FileReader.Binary.cs b/src/DotNext.IO/IO/FileReader.Binary.cs index b8463773f..888503f54 100644 --- a/src/DotNext.IO/IO/FileReader.Binary.cs +++ b/src/DotNext.IO/IO/FileReader.Binary.cs @@ -72,7 +72,7 @@ public ValueTask ReadAsync(CancellationToken token = default) private async ValueTask ReadSlowAsync(CancellationToken token) where T : unmanaged { - using var buffer = MemoryAllocator.Allocate(Unsafe.SizeOf(), exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(Unsafe.SizeOf()); await ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); return MemoryMarshal.Read(buffer.Span); } @@ -141,7 +141,7 @@ public async ValueTask> ReadAsync(LengthFormat lengthFormat, M MemoryOwner result; if (length > 0) { - result = allocator.Allocate(length, true); + result = allocator.AllocateExactly(length); await ReadBlockAsync(result.Memory, token).ConfigureAwait(false); } else @@ -194,7 +194,7 @@ public async ValueTask ReadStringAsync(LengthFormat lengthFormat, Decodi if (this.length < length) throw new EndOfStreamException(); - using var result = MemoryAllocator.Allocate(length, true); + using var result = MemoryAllocator.AllocateExactly(length); length = await ReadStringAsync(result.Memory, context, token).ConfigureAwait(false); return new string(result.Span.Slice(0, length)); } @@ -216,7 +216,7 @@ public async ValueTask ReadStringAsync(int length, DecodingContext conte if (this.length < length) throw new EndOfStreamException(); - using var result = MemoryAllocator.Allocate(length, true); + using var result = MemoryAllocator.AllocateExactly(length); length = await ReadStringAsync(result.Memory, context, token).ConfigureAwait(false); return new string(result.Span.Slice(0, length)); } @@ -243,12 +243,12 @@ public async ValueTask ParseAsync(Parser parser, LengthFormat lengthFor var length = ReadLength(lengthFormat); if (length <= 0) - return parser(ReadOnlySpan.Empty, provider); + return parser([], provider); if (this.length < length) throw new EndOfStreamException(); - using var result = MemoryAllocator.Allocate(length, true); + using var result = MemoryAllocator.AllocateExactly(length); length = await ReadStringAsync(result.Memory, context, token).ConfigureAwait(false); return parser(result.Span.Slice(0, length), provider); } @@ -308,7 +308,7 @@ private T ParseFast() private async ValueTask ParseSlowAsync(CancellationToken token) where T : notnull, IBinaryFormattable { - using var buffer = MemoryAllocator.Allocate(T.Size, exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(T.Size); await ReadBlockAsync(buffer.Memory, token).ConfigureAwait(false); return IBinaryFormattable.Parse(buffer.Span); } @@ -334,7 +334,7 @@ public async ValueTask ReadBigIntegerAsync(LengthFormat lengthFormat if (this.length < length) throw new EndOfStreamException(); - using var block = MemoryAllocator.Allocate(length, exactSize: true); + using var block = MemoryAllocator.AllocateExactly(length); await ReadBlockAsync(block.Memory, token).ConfigureAwait(false); return new BigInteger(block.Span, isBigEndian: !littleEndian); } diff --git a/src/DotNext.IO/IO/FileReader.cs b/src/DotNext.IO/IO/FileReader.cs index bb367cd9b..46fb05436 100644 --- a/src/DotNext.IO/IO/FileReader.cs +++ b/src/DotNext.IO/IO/FileReader.cs @@ -42,14 +42,10 @@ public partial class FileReader : Disposable public FileReader(SafeFileHandle handle, long fileOffset = 0L, int bufferSize = 4096, MemoryAllocator? allocator = null) { ArgumentNullException.ThrowIfNull(handle); + ArgumentOutOfRangeException.ThrowIfNegative(fileOffset); + ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(bufferSize, 16); - if (fileOffset < 0L) - throw new ArgumentOutOfRangeException(nameof(fileOffset)); - - if (bufferSize <= 16) - throw new ArgumentOutOfRangeException(nameof(bufferSize)); - - buffer = allocator.Allocate(bufferSize, exactSize: false); + buffer = allocator.AllocateAtLeast(bufferSize); this.handle = handle; this.fileOffset = fileOffset; } @@ -64,8 +60,7 @@ public long FilePosition get => fileOffset; set { - if (value < 0L) - throw new ArgumentOutOfRangeException(nameof(value)); + ArgumentOutOfRangeException.ThrowIfNegative(value); if (HasBufferedData) throw new InvalidOperationException(); diff --git a/src/DotNext.IO/IO/FileWriter.Binary.cs b/src/DotNext.IO/IO/FileWriter.Binary.cs index 0dddf9e44..1ee4b71eb 100644 --- a/src/DotNext.IO/IO/FileWriter.Binary.cs +++ b/src/DotNext.IO/IO/FileWriter.Binary.cs @@ -201,7 +201,7 @@ public async ValueTask WriteBigIntegerAsync(BigInteger value, bool littleEndian, else { Debug.Assert(bufferOffset is 0); - using var buffer = MemoryAllocator.Allocate(bytesCount, exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(bytesCount); value.TryWriteBytes(buffer.Span, out bytesWritten, isBigEndian: !littleEndian); await RandomAccess.WriteAsync(handle, buffer.Memory, fileOffset, token).ConfigureAwait(false); fileOffset += bytesCount; @@ -229,7 +229,7 @@ public async ValueTask WriteFormattableAsync(T value, LengthFormat lengthForm for (var charBufferSize = initialCharBufferSize; ; charBufferSize = charBufferSize <= maxBufferSize ? charBufferSize * 2 : throw new InsufficientMemoryException()) { - using var charBuffer = MemoryAllocator.Allocate(charBufferSize, false); + using var charBuffer = MemoryAllocator.AllocateAtLeast(charBufferSize); if (value.TryFormat(charBuffer.Span, out var charsWritten, format, provider)) { @@ -262,7 +262,7 @@ public async ValueTask WriteFormattableAsync(T value, CancellationToken token } else { - using var buffer = MemoryAllocator.Allocate(T.Size, true); + using var buffer = MemoryAllocator.AllocateExactly(T.Size); IBinaryFormattable.Format(value, buffer.Span); await RandomAccess.WriteAsync(handle, buffer.Memory, fileOffset, token).ConfigureAwait(false); fileOffset += T.Size; diff --git a/src/DotNext.IO/IO/FileWriter.cs b/src/DotNext.IO/IO/FileWriter.cs index d68bded37..b0905213c 100644 --- a/src/DotNext.IO/IO/FileWriter.cs +++ b/src/DotNext.IO/IO/FileWriter.cs @@ -49,7 +49,7 @@ public FileWriter(SafeFileHandle handle, long fileOffset = 0L, int bufferSize = if (bufferSize <= 16) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - buffer = allocator.Allocate(bufferSize, exactSize: false); + buffer = allocator.AllocateAtLeast(bufferSize); this.handle = handle; this.fileOffset = fileOffset; } diff --git a/src/DotNext.IO/IO/IAsyncBinaryReader.cs b/src/DotNext.IO/IO/IAsyncBinaryReader.cs index 17233df8a..f45ccb6d5 100644 --- a/src/DotNext.IO/IO/IAsyncBinaryReader.cs +++ b/src/DotNext.IO/IO/IAsyncBinaryReader.cs @@ -34,7 +34,7 @@ public interface IAsyncBinaryReader async ValueTask ReadAsync(CancellationToken token = default) where T : unmanaged { - using var buffer = MemoryAllocator.Allocate(Unsafe.SizeOf(), true); + using var buffer = MemoryAllocator.AllocateExactly(Unsafe.SizeOf()); await ReadAsync(buffer.Memory, token).ConfigureAwait(false); return MemoryMarshal.Read(buffer.Span); } @@ -112,7 +112,7 @@ async ValueTask ParseAsync(Parser parser, LengthFormat lengthFormat, De async ValueTask ParseAsync(CancellationToken token = default) where T : notnull, IBinaryFormattable { - using var buffer = MemoryAllocator.Allocate(T.Size, true); + using var buffer = MemoryAllocator.AllocateExactly(T.Size); await ReadAsync(buffer.Memory, token).ConfigureAwait(false); return IBinaryFormattable.Parse(buffer.Span); } @@ -137,7 +137,7 @@ ValueTask ReadBigIntegerAsync(int length, bool littleEndian, Cancell async ValueTask ReadAsync() { - using var buffer = MemoryAllocator.Allocate(length, exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(length); await this.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return new(buffer.Span, isBigEndian: !littleEndian); } @@ -191,7 +191,7 @@ async ValueTask SkipAsync() const int tempBufferSize = 4096; // fixed-size buffer to avoid OOM - using var buffer = MemoryAllocator.Allocate((int)Math.Min(tempBufferSize, length), exactSize: false); + using var buffer = MemoryAllocator.AllocateAtLeast((int)Math.Min(tempBufferSize, length)); for (var bytesToRead = buffer.Length; length > 0L; length -= bytesToRead) { bytesToRead = (int)Math.Min(bytesToRead, length); @@ -232,7 +232,7 @@ ValueTask ReadStringAsync(int length, DecodingContext context, Cancellat async ValueTask ReadAsync() { - using var buffer = MemoryAllocator.Allocate(length, exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(length); await this.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return context.Encoding.GetString(buffer.Span); } @@ -259,7 +259,7 @@ ValueTask> ReadStringAsync(int length, DecodingContext context async ValueTask> ReadAsync() { - using var buffer = MemoryAllocator.Allocate(length, exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(length); await this.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return context.Encoding.GetChars(buffer.Span, allocator); } diff --git a/src/DotNext.IO/IO/IAsyncBinaryWriter.cs b/src/DotNext.IO/IO/IAsyncBinaryWriter.cs index 56e476155..de7ffb491 100644 --- a/src/DotNext.IO/IO/IAsyncBinaryWriter.cs +++ b/src/DotNext.IO/IO/IAsyncBinaryWriter.cs @@ -26,7 +26,7 @@ public interface IAsyncBinaryWriter : ISupplier, Cancellati async ValueTask WriteAsync(T value, CancellationToken token = default) where T : unmanaged { - using var buffer = MemoryAllocator.Allocate(Unsafe.SizeOf(), true); + using var buffer = MemoryAllocator.AllocateExactly(Unsafe.SizeOf()); Span.AsReadOnlyBytes(value).CopyTo(buffer.Span); await WriteAsync(buffer.Memory, null, token).ConfigureAwait(false); } @@ -92,7 +92,7 @@ async ValueTask WriteBigIntegerAsync(BigInteger value, bool littleEndian, Length } else { - using var buffer = MemoryAllocator.Allocate(bytesCount, true); + using var buffer = MemoryAllocator.AllocateExactly(bytesCount); if (!value.TryWriteBytes(buffer.Span, out bytesCount, isBigEndian: !littleEndian)) throw new InternalBufferOverflowException(); await WriteAsync(buffer.Memory, lengthFormat, token).ConfigureAwait(false); @@ -194,7 +194,7 @@ async ValueTask WriteFormattableAsync(T value, CancellationToken token = defa async Task CopyFromAsync(Stream input, CancellationToken token = default) { const int defaultBufferSize = 512; - using var buffer = MemoryAllocator.Allocate(defaultBufferSize, false); + using var buffer = MemoryAllocator.AllocateAtLeast(defaultBufferSize); for (int count; (count = await input.ReadAsync(buffer.Memory, token).ConfigureAwait(false)) > 0;) await WriteAsync(buffer.Memory.Slice(0, count), null, token).ConfigureAwait(false); } diff --git a/src/DotNext.IO/IO/IDataTransferObject.cs b/src/DotNext.IO/IO/IDataTransferObject.cs index 047a435ac..bb67782ea 100644 --- a/src/DotNext.IO/IO/IDataTransferObject.cs +++ b/src/DotNext.IO/IO/IDataTransferObject.cs @@ -114,7 +114,7 @@ protected static async ValueTask TransformAsync TransformAsync(Stream input, TTransformation transformation, bool resetStream, MemoryAllocator? allocator, CancellationToken token) where TTransformation : notnull, ITransformation { - var buffer = allocator.Allocate(DefaultBufferSize, false); + var buffer = allocator.AllocateAtLeast(DefaultBufferSize); try { return await transformation.TransformAsync(new AsyncStreamBinaryAccessor(input, buffer.Memory), token).ConfigureAwait(false); @@ -174,7 +174,7 @@ private async ValueTask GetUnknownObjectDataAsync(DefaultBufferSize, exactSize: false); + using var buffer = MemoryAllocator.AllocateAtLeast(DefaultBufferSize); // serialize await WriteToAsync(new AsyncStreamBinaryAccessor(output, buffer.Memory), token).ConfigureAwait(false); @@ -207,7 +207,7 @@ private async ValueTask GetLargeObjectDataAsync(DefaultBufferSize, false); + using var buffer = MemoryAllocator.AllocateAtLeast(DefaultBufferSize); // serialize await WriteToAsync(new AsyncStreamBinaryAccessor(fs, buffer.Memory), token).ConfigureAwait(false); diff --git a/src/DotNext.IO/IO/MemoryTransferObject.cs b/src/DotNext.IO/IO/MemoryTransferObject.cs index 5a433a385..155e816a9 100644 --- a/src/DotNext.IO/IO/MemoryTransferObject.cs +++ b/src/DotNext.IO/IO/MemoryTransferObject.cs @@ -15,7 +15,7 @@ public class MemoryTransferObject : Disposable, IDataTransferObject /// The length, in bytes, of the content. /// The memory allocator. public MemoryTransferObject(int length, MemoryAllocator? allocator = null) - => owner = length > 0 ? allocator.Allocate(length, true) : default; + => owner = length > 0 ? allocator.AllocateExactly(length) : default; /// /// Transforms this object to serialized form. diff --git a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs index 3ad59bc51..ab25c5024 100644 --- a/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs +++ b/src/DotNext.IO/IO/Pipelines/PipeExtensions.Readers.cs @@ -141,7 +141,7 @@ public static ValueTask ParseAsync(this PipeReader reader, CancellationTok [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] static async ValueTask ParseSlowAsync(PipeReader reader, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(T.Size, true); + using var buffer = MemoryAllocator.AllocateExactly(T.Size); await ReadExactlyAsync(reader, buffer.Memory, token).ConfigureAwait(false); return IBinaryFormattable.Parse(buffer.Span); } @@ -230,7 +230,7 @@ public static async ValueTask> ReadStringAsync(this PipeReader } else { - result = allocator.Allocate(length, exactSize: true); + result = allocator.AllocateExactly(length); length = await ReadAsync>>(reader, new(context, new ArrayBuffer(result)), token).ConfigureAwait(false); result.TryResize(length); } @@ -570,7 +570,7 @@ public static async ValueTask> ReadBlockAsync(this PipeReader MemoryOwner result; if (length > 0) { - result = allocator.Allocate(length, true); + result = allocator.AllocateExactly(length); await ReadExactlyAsync(reader, result.Memory, token).ConfigureAwait(false); } else diff --git a/src/DotNext.IO/IO/SequenceReader.cs b/src/DotNext.IO/IO/SequenceReader.cs index 68f71f411..58a9f81cf 100644 --- a/src/DotNext.IO/IO/SequenceReader.cs +++ b/src/DotNext.IO/IO/SequenceReader.cs @@ -172,7 +172,7 @@ public MemoryOwner Read(LengthFormat lengthFormat, MemoryAllocator? MemoryOwner result; if (length > 0) { - result = allocator.Allocate(length, exactSize: true); + result = allocator.AllocateExactly(length); Read(result.Span); } else @@ -352,7 +352,7 @@ public unsafe MemoryOwner ReadString(int length, in DecodingContext contex } else { - result = allocator.Allocate(length, exactSize: true); + result = allocator.AllocateExactly(length); length = Read>>(new(in context, new ArrayBuffer(result))); result.TryResize(length); } diff --git a/src/DotNext.IO/IO/StreamExtensions.Writers.cs b/src/DotNext.IO/IO/StreamExtensions.Writers.cs index 89458cbb9..4e4b3d63c 100644 --- a/src/DotNext.IO/IO/StreamExtensions.Writers.cs +++ b/src/DotNext.IO/IO/StreamExtensions.Writers.cs @@ -297,7 +297,7 @@ public static async ValueTask WriteBigIntegerAsync(this Stream stream, BigIntege public static async ValueTask WriteBigIntegerAsync(this Stream stream, BigInteger value, bool littleEndian, MemoryAllocator? allocator = null, LengthFormat? lengthFormat = null, CancellationToken token = default) { var bytesCount = value.GetByteCount(); - using var buffer = allocator.Allocate(Math.Max(1, bytesCount), false); + using var buffer = allocator.AllocateAtLeast(Math.Max(1, bytesCount)); if (lengthFormat.HasValue) await stream.WriteLengthAsync(bytesCount, lengthFormat.GetValueOrDefault(), buffer.Memory, token).ConfigureAwait(false); @@ -391,7 +391,7 @@ private static ValueTask WriteLengthAsync(this Stream stream, int length, Length public static async ValueTask WriteStringAsync(this Stream stream, ReadOnlyMemory value, Encoding encoding, LengthFormat? lengthFormat = null, CancellationToken token = default) { var bytesCount = encoding.GetByteCount(value.Span); - using var buffer = MemoryAllocator.Allocate(bytesCount, true); + using var buffer = MemoryAllocator.AllocateExactly(bytesCount); await stream.WriteLengthAsync(bytesCount, lengthFormat, buffer.Memory, token).ConfigureAwait(false); if (bytesCount == 0) return; @@ -420,7 +420,7 @@ public static async ValueTask WriteFormattableAsync(this Stream stream, T val { for (var charBufferSize = InitialCharBufferSize; ; charBufferSize = charBufferSize <= MaxBufferSize ? charBufferSize * 2 : throw new InsufficientMemoryException()) { - using var owner = MemoryAllocator.Allocate(charBufferSize, false); + using var owner = MemoryAllocator.AllocateAtLeast(charBufferSize); if (value.TryFormat(owner.Span, out var charsWritten, format, provider)) { @@ -449,7 +449,7 @@ public static async ValueTask WriteFormattableAsync(this Stream stream, T val public static async ValueTask WriteFormattableAsync(this Stream stream, T value, LengthFormat lengthFormat, EncodingContext context, string? format = null, IFormatProvider? provider = null, CancellationToken token = default) where T : notnull, ISpanFormattable { - using var owner = MemoryAllocator.Allocate(DefaultBufferSize, false); + using var owner = MemoryAllocator.AllocateAtLeast(DefaultBufferSize); await WriteFormattableAsync(stream, value, lengthFormat, context, owner.Memory, format, provider, token).ConfigureAwait(false); } @@ -560,7 +560,7 @@ public static ValueTask WriteAsync(this Stream stream, T value, Memory public static async ValueTask WriteAsync(this Stream stream, T value, CancellationToken token = default) where T : unmanaged { - using var buffer = MemoryAllocator.Allocate(Unsafe.SizeOf(), false); + using var buffer = MemoryAllocator.AllocateAtLeast(Unsafe.SizeOf()); await WriteAsync(stream, value, buffer.Memory, token).ConfigureAwait(false); } diff --git a/src/DotNext.IO/IO/StreamExtensions.cs b/src/DotNext.IO/IO/StreamExtensions.cs index a09576a39..632665dcd 100644 --- a/src/DotNext.IO/IO/StreamExtensions.cs +++ b/src/DotNext.IO/IO/StreamExtensions.cs @@ -187,7 +187,7 @@ public static MemoryOwner ReadString(this Stream stream, int length, in De } else { - result = allocator.Allocate(length, exactSize: true); + result = allocator.AllocateExactly(length); length = ReadString(stream, result.Span, in context, buffer); result.TryResize(length); } @@ -476,7 +476,7 @@ public static async ValueTask ParseAsync(this Stream stream, Parser par if (length == 0) throw new EndOfStreamException(); - using var result = MemoryAllocator.Allocate(length, true); + using var result = MemoryAllocator.AllocateExactly(length); length = await ReadStringAsync(stream, result.Memory, context, buffer, token).ConfigureAwait(false); return parser(result.Memory.Slice(0, length).Span, provider); } @@ -500,14 +500,14 @@ public static async ValueTask ParseAsync(this Stream stream, Parser par { int length; MemoryOwner buffer; - using (buffer = MemoryAllocator.Allocate(BufferSizeForLength, false)) + using (buffer = MemoryAllocator.AllocateAtLeast(BufferSizeForLength)) length = await ReadLengthAsync(stream, lengthFormat, buffer.Memory, token).ConfigureAwait(false); - if (length == 0) + if (length is 0) throw new EndOfStreamException(); - using var result = MemoryAllocator.Allocate(length, true); - using (buffer = MemoryAllocator.Allocate(length, false)) + using var result = MemoryAllocator.AllocateExactly(length); + using (buffer = MemoryAllocator.AllocateAtLeast(length)) { length = await ReadStringAsync(stream, result.Memory, context, buffer.Memory, token).ConfigureAwait(false); return parser(result.Memory.Slice(0, length).Span, provider); @@ -548,7 +548,7 @@ public static async ValueTask ParseAsync(this Stream stream, Memory public static async ValueTask ParseAsync(this Stream stream, CancellationToken token = default) where T : notnull, IBinaryFormattable { - using var buffer = MemoryAllocator.Allocate(T.Size, true); + using var buffer = MemoryAllocator.AllocateExactly(T.Size); return await ParseAsync(stream, buffer.Memory, token).ConfigureAwait(false); } @@ -609,7 +609,7 @@ public static async ValueTask ReadBigIntegerAsync(this Stream stream if (length == 0) return BigInteger.Zero; - using var buffer = MemoryAllocator.Allocate(length, true); + using var buffer = MemoryAllocator.AllocateExactly(length); await stream.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); return new BigInteger(buffer.Span, isBigEndian: !littleEndian); } @@ -643,7 +643,7 @@ public static async ValueTask ReadBigIntegerAsync(this Stream stream /// is invalid. public static async ValueTask ReadBigIntegerAsync(this Stream stream, LengthFormat lengthFormat, bool littleEndian, CancellationToken token = default) { - using var lengthDecodingBuffer = MemoryAllocator.Allocate(BufferSizeForLength, false); + using var lengthDecodingBuffer = MemoryAllocator.AllocateAtLeast(BufferSizeForLength); return await ReadBigIntegerAsync(stream, await stream.ReadLengthAsync(lengthFormat, lengthDecodingBuffer.Memory, token).ConfigureAwait(false), littleEndian, token).ConfigureAwait(false); } @@ -700,7 +700,7 @@ public static async ValueTask> ReadStringAsync(this Stream str } else { - result = allocator.Allocate(length, exactSize: true); + result = allocator.AllocateExactly(length); length = await ReadStringAsync(stream, result.Memory, context, buffer, token).ConfigureAwait(false); result.TryResize(length); } @@ -791,7 +791,7 @@ public static async ValueTask> ReadStringAsync(this Stream str } else { - using var bytesBuffer = MemoryAllocator.Allocate(length, exactSize: true); + using var bytesBuffer = MemoryAllocator.AllocateExactly(length); await stream.ReadExactlyAsync(bytesBuffer.Memory, token).ConfigureAwait(false); result = encoding.GetChars(bytesBuffer.Span, allocator); } @@ -816,7 +816,7 @@ public static async ValueTask> ReadStringAsync(this Stream str /// is invalid. public static async ValueTask ReadStringAsync(this Stream stream, LengthFormat lengthFormat, Encoding encoding, CancellationToken token = default) { - using var lengthDecodingBuffer = MemoryAllocator.Allocate(BufferSizeForLength, exactSize: false); + using var lengthDecodingBuffer = MemoryAllocator.AllocateAtLeast(BufferSizeForLength); return await ReadStringAsync(stream, await stream.ReadLengthAsync(lengthFormat, lengthDecodingBuffer.Memory, token).ConfigureAwait(false), encoding, token).ConfigureAwait(false); } @@ -838,7 +838,7 @@ public static async ValueTask ReadStringAsync(this Stream stream, Length /// is invalid. public static async ValueTask> ReadStringAsync(this Stream stream, LengthFormat lengthFormat, Encoding encoding, MemoryAllocator? allocator, CancellationToken token = default) { - using var lengthDecodingBuffer = MemoryAllocator.Allocate(BufferSizeForLength, exactSize: false); + using var lengthDecodingBuffer = MemoryAllocator.AllocateAtLeast(BufferSizeForLength); return await ReadStringAsync(stream, await stream.ReadLengthAsync(lengthFormat, lengthDecodingBuffer.Memory, token).ConfigureAwait(false), encoding, allocator, token).ConfigureAwait(false); } @@ -856,7 +856,7 @@ public static MemoryOwner ReadBlock(this Stream stream, LengthFormat lengt MemoryOwner result; if (length > 0) { - result = allocator.Allocate(length, true); + result = allocator.AllocateExactly(length); stream.ReadExactly(result.Span); } else @@ -900,7 +900,7 @@ public static async ValueTask> ReadBlockAsync(this Stream stre MemoryOwner result; if (length > 0) { - result = allocator.Allocate(length, true); + result = allocator.AllocateExactly(length); await stream.ReadExactlyAsync(result.Memory, token).ConfigureAwait(false); } else @@ -952,7 +952,7 @@ internal static async ValueTask ReadAsync( public static async ValueTask ReadAsync(this Stream stream, CancellationToken token = default) where T : unmanaged { - using var buffer = MemoryAllocator.Allocate(Unsafe.SizeOf(), true); + using var buffer = MemoryAllocator.AllocateExactly(Unsafe.SizeOf()); await stream.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); return MemoryMarshal.Read(buffer.Span); } @@ -1084,7 +1084,7 @@ public static async Task CopyToAsync(this Stream stream, ReadOnlySpanActio if (bufferSize <= 0) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - using var owner = MemoryAllocator.Allocate(bufferSize, false); + using var owner = MemoryAllocator.AllocateAtLeast(bufferSize); await CopyToAsync(stream, reader, arg, owner.Memory, token).ConfigureAwait(false); } @@ -1120,7 +1120,7 @@ public static async Task CopyToAsync(this Stream stream, Func(bufferSize, false); + using var owner = MemoryAllocator.AllocateAtLeast(bufferSize); await CopyToAsync(stream, reader, arg, owner.Memory, token).ConfigureAwait(false); } @@ -1213,7 +1213,7 @@ public static async IAsyncEnumerable> ReadAllAsync(this Str if (bufferSize < 1) throw new ArgumentOutOfRangeException(nameof(bufferSize)); - using var bufferOwner = allocator.Allocate(bufferSize, exactSize: false); + using var bufferOwner = allocator.AllocateAtLeast(bufferSize); var buffer = bufferOwner.Memory; for (int count; (count = await stream.ReadAsync(buffer, token).ConfigureAwait(false)) > 0;) diff --git a/src/DotNext.IO/IO/TextStreamExtensions.cs b/src/DotNext.IO/IO/TextStreamExtensions.cs index 173080cde..b0c1f5740 100644 --- a/src/DotNext.IO/IO/TextStreamExtensions.cs +++ b/src/DotNext.IO/IO/TextStreamExtensions.cs @@ -203,10 +203,9 @@ public static void WriteLine(this TextWriter writer, MemoryAllocator? allo /// is less than 1. public static async IAsyncEnumerable> ReadAllAsync(this TextReader reader, int bufferSize, MemoryAllocator? allocator = null, [EnumeratorCancellation] CancellationToken token = default) { - if (bufferSize < 1) - throw new ArgumentOutOfRangeException(nameof(bufferSize)); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(bufferSize); - using var bufferOwner = allocator.Allocate(bufferSize, exactSize: false); + using var bufferOwner = allocator.AllocateAtLeast(bufferSize); var buffer = bufferOwner.Memory; for (int count; (count = await reader.ReadAsync(buffer, token).ConfigureAwait(false)) > 0;) diff --git a/src/DotNext.IO/Runtime/Serialization/Serializable.cs b/src/DotNext.IO/Runtime/Serialization/Serializable.cs index c26b5d3bf..ea008fa20 100644 --- a/src/DotNext.IO/Runtime/Serialization/Serializable.cs +++ b/src/DotNext.IO/Runtime/Serialization/Serializable.cs @@ -38,7 +38,7 @@ public static ValueTask ReadFromAsync(this Stream input, Memor public static async ValueTask ReadFromAsync(this Stream input, int bufferSize = 128, CancellationToken token = default) where TObject : notnull, ISerializable { - using var owner = MemoryAllocator.Allocate(bufferSize, false); + using var owner = MemoryAllocator.AllocateAtLeast(bufferSize); return await ReadFromAsync(input, owner.Memory, token).ConfigureAwait(false); } diff --git a/src/DotNext.IO/Text/Json/JsonSerializable.cs b/src/DotNext.IO/Text/Json/JsonSerializable.cs index f02d319bd..1c10691f3 100644 --- a/src/DotNext.IO/Text/Json/JsonSerializable.cs +++ b/src/DotNext.IO/Text/Json/JsonSerializable.cs @@ -110,7 +110,7 @@ public static ValueTask> ReadFromAsync(TReader read [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] static async ValueTask> DeserializeBufferedAsync(TReader reader, int bufferSize, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(bufferSize, exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(bufferSize); await reader.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return Deserialize(new(buffer.Memory)); } diff --git a/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs b/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs index fa1e6b89c..9ea149a3f 100644 --- a/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs +++ b/src/DotNext.MaintenanceServices/Maintenance/ApplicationMaintenanceInterfaceHost.cs @@ -175,7 +175,7 @@ private async void ProcessRequestAsync(Socket clientSocket, CancellationToken to { int commandLength; - using (var buffer = ByteBufferAllocator.Allocate(bufferSize, exactSize: false)) + using (var buffer = ByteBufferAllocator.AllocateAtLeast(bufferSize)) { commandLength = await ReadRequestAsync(clientSocket, encoding, decoder, buffer.Memory, inputBuffer, token).ConfigureAwait(false); } diff --git a/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs b/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs index c1395f3c5..aab0971f6 100644 --- a/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs +++ b/src/DotNext.Tests/Buffers/MemoryOwnerTests.cs @@ -76,7 +76,7 @@ public static IEnumerable GetArrayAllocators() [MemberData(nameof(GetArrayAllocators))] public static void ArrayAllocation(MemoryAllocator allocator) { - using var owner = allocator.Allocate(4, false); + using var owner = allocator(4); Equal(4, owner.Length); } @@ -119,10 +119,10 @@ public static void ResizeBuffer() var allocator = MemoryAllocator.GetArrayAllocator(); var buffer = default(MemoryOwner); - buffer.Resize(10, false, allocator); + buffer.Resize(10, allocator); Equal(10, buffer.Length); - buffer.Resize(3, false, allocator); + buffer.Resize(3, allocator); Equal(3, buffer.Length); True(buffer.TryResize(10)); diff --git a/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs b/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs index 04053b31a..dc53ac44e 100644 --- a/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs +++ b/src/DotNext.Tests/Buffers/UnmanagedMemoryPoolTests.cs @@ -49,7 +49,7 @@ public static unsafe void ArrayInteropTest() [Fact] public static unsafe void UnmanagedMemoryAllocation() { - using var owner = UnmanagedMemoryAllocator.GetAllocator(false).Allocate(3, false); + using var owner = UnmanagedMemoryAllocator.GetAllocator(false).AllocateAtLeast(3); Span array = owner.Span; array[0] = 10; array[1] = 20; diff --git a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs index c5afe30f4..caca6870d 100644 --- a/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs +++ b/src/DotNext.Tests/Net/Cluster/Consensus/Raft/TransportServices/TransportTestSuite.cs @@ -80,7 +80,7 @@ internal LocalMember(bool smallAmountOfMetadata = false) async ValueTask ILocalMember.ProposeConfigurationAsync(Func, CancellationToken, ValueTask> configurationReader, long configurationLength, long fingerprint, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(int.CreateSaturating(configurationLength), exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(int.CreateSaturating(configurationLength)); await configurationReader(buffer.Memory, token).ConfigureAwait(false); Equal(42L, fingerprint); ReceivedConfiguration = buffer.Memory.ToArray(); diff --git a/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs b/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs index ab43b8b44..48be7ba83 100644 --- a/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs +++ b/src/DotNext/Buffers/BufferHelpers.MemoryOwner.cs @@ -19,17 +19,13 @@ public static partial class BufferHelpers /// The type of the elements in the buffer. /// The buffer owner to resize. /// A new length of the buffer. - /// - /// to ask allocator to allocate exactly ; - /// to allocate at least . - /// /// The allocator to be called if the requested length is larger than the requested length. /// is less than zero. - public static void Resize(this ref MemoryOwner owner, int newLength, bool exactSize = true, MemoryAllocator? allocator = null) + public static void Resize(this ref MemoryOwner owner, int newLength, MemoryAllocator? allocator = null) { if (!owner.TryResize(newLength)) { - var newBuffer = allocator.Allocate(newLength, exactSize); + var newBuffer = allocator.AllocateAtLeast(newLength); owner.Span.CopyTo(newBuffer.Span); owner.Dispose(); owner = newBuffer; diff --git a/src/DotNext/Buffers/BufferWriterSlim.cs b/src/DotNext/Buffers/BufferWriterSlim.cs index 85ac4ea51..d9d10d2d9 100644 --- a/src/DotNext/Buffers/BufferWriterSlim.cs +++ b/src/DotNext/Buffers/BufferWriterSlim.cs @@ -55,12 +55,11 @@ public BufferWriterSlim(Span buffer, MemoryAllocator? allocator = null) /// is less than zero. public BufferWriterSlim(int initialCapacity, MemoryAllocator? allocator = null) { - if (initialCapacity < 0) - throw new ArgumentOutOfRangeException(nameof(initialCapacity)); + ArgumentOutOfRangeException.ThrowIfNegative(initialCapacity); initialBuffer = default; this.allocator = allocator; - extraBuffer = initialCapacity is 0 ? default : allocator.Allocate(initialCapacity, exactSize: false); + extraBuffer = initialCapacity is 0 ? default : allocator.AllocateAtLeast(initialCapacity); position = 0; } @@ -133,7 +132,7 @@ internal Span InternalGetSpan(int sizeHint) // need to copy initial buffer if (IGrowableBuffer.GetBufferSize(sizeHint, initialBuffer.Length, position, out newSize)) { - extraBuffer = allocator.Allocate(newSize, exactSize: false); + extraBuffer = allocator.AllocateAtLeast(newSize); initialBuffer.CopyTo(result = extraBuffer.Span); } else @@ -145,7 +144,7 @@ internal Span InternalGetSpan(int sizeHint) { // no need to copy initial buffer if (IGrowableBuffer.GetBufferSize(sizeHint, extraBuffer.Length, position, out newSize)) - extraBuffer.Resize(newSize, exactSize: false, allocator); + extraBuffer.Resize(newSize, allocator); result = extraBuffer.Span; } diff --git a/src/DotNext/Buffers/IBinaryFormattable.cs b/src/DotNext/Buffers/IBinaryFormattable.cs index a6aafbf3a..f8549d73c 100644 --- a/src/DotNext/Buffers/IBinaryFormattable.cs +++ b/src/DotNext/Buffers/IBinaryFormattable.cs @@ -55,7 +55,7 @@ public static bool TryParse(ReadOnlySpan input, [NotNullWhen(true)] out TS /// The buffer containing formatted value. public static MemoryOwner Format(TSelf value, MemoryAllocator? allocator = null) { - var result = allocator.Allocate(TSelf.Size, true); + var result = allocator.AllocateExactly(TSelf.Size); var writer = new SpanWriter(result.Span); value.Format(ref writer); return result; diff --git a/src/DotNext/Buffers/MemoryAllocator.cs b/src/DotNext/Buffers/MemoryAllocator.cs index 0b11b48f0..fb9a48bb2 100644 --- a/src/DotNext/Buffers/MemoryAllocator.cs +++ b/src/DotNext/Buffers/MemoryAllocator.cs @@ -55,31 +55,48 @@ public static MemoryAllocator ToAllocator(this Func> => provider.Allocate; /// - /// Allocates memory. + /// Allocates memory of at least size. /// /// The memory allocator. /// The number of items in the rented memory. - /// - /// to ask allocator to allocate exactly ; - /// to allocate at least . - /// /// The type of the items in the memory pool. /// The allocated memory. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Allocate(this MemoryAllocator? allocator, int length, [ConstantExpected] bool exactSize) + public static MemoryOwner AllocateAtLeast(this MemoryAllocator? allocator, int length) { MemoryOwner result; if (allocator is null) { - result = Allocate(length, exactSize); + result = AllocateAtLeast(length); } else { result = allocator(length); - if (exactSize) - result.Truncate(length); - else - result.Expand(); + result.Expand(); + } + + return result; + } + + /// + /// Allocates memory of size. + /// + /// The memory allocator. + /// The number of items in the rented memory. + /// The type of the items in the memory pool. + /// The allocated memory. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MemoryOwner AllocateExactly(this MemoryAllocator? allocator, int length) + { + MemoryOwner result; + if (allocator is null) + { + result = AllocateAtLeast(length); + } + else + { + result = allocator(length); + result.Truncate(length); } return result; @@ -113,16 +130,22 @@ static MemoryOwner AllocateArray(int length) } /// - /// Rents a block of memory from pool. + /// Rents a block of memory of at least + /// size from pool. /// /// The type of the items in the memory pool. /// The number of items in the rented memory. - /// - /// to ask allocator to allocate exactly ; - /// to allocate at least . - /// /// The allocated memory. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryOwner Allocate(int length, [ConstantExpected] bool exactSize) - => new(ArrayPool.Shared, length, exactSize); + public static MemoryOwner AllocateAtLeast(int length) + => new(ArrayPool.Shared, length, exactSize: false); + + /// + /// Rents a block of memory of the specified size from pool. + /// + /// The type of the items in the memory pool. + /// The number of items in the rented memory. + /// The allocated memory. + public static MemoryOwner AllocateExactly(int length) + => new(ArrayPool.Shared, length, exactSize: true); } \ No newline at end of file diff --git a/src/DotNext/Buffers/PooledBufferWriter.cs b/src/DotNext/Buffers/PooledBufferWriter.cs index 9b8886d50..f3758149d 100644 --- a/src/DotNext/Buffers/PooledBufferWriter.cs +++ b/src/DotNext/Buffers/PooledBufferWriter.cs @@ -29,7 +29,7 @@ public override int Capacity case < 0: throw new ArgumentOutOfRangeException(nameof(value)); case > 0: - buffer = allocator.Allocate(value, exactSize: false); + buffer = allocator.AllocateAtLeast(value); break; } } @@ -111,7 +111,7 @@ public override MemoryOwner DetachBuffer() /// private protected override void Resize(int newSize) { - buffer.Resize(newSize, false, allocator); + buffer.Resize(newSize, allocator); PooledBufferWriter.AllocationMeter.Record(buffer.Length, measurementTags); } diff --git a/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs b/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs index 67849b480..ebe4fd51d 100644 --- a/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs +++ b/src/DotNext/Buffers/PoolingInterpolatedStringHandler.cs @@ -35,7 +35,7 @@ public PoolingInterpolatedStringHandler(int literalLength, int formattedCount, M var length = (charsPerPlaceholder * formattedCount) + literalLength; buffer = (uint)length <= (uint)Array.MaxLength - ? allocator.Allocate(length, exactSize: false) + ? allocator.AllocateAtLeast(length) : throw new InsufficientMemoryException(); this.allocator = allocator; @@ -107,7 +107,7 @@ internal MemoryOwner DetachBuffer() private Span GetSpan(int sizeHint) { if (IGrowableBuffer.GetBufferSize(sizeHint, buffer.Length, count, out sizeHint)) - buffer.Resize(sizeHint, exactSize: false, allocator); + buffer.Resize(sizeHint, allocator); return buffer.Span.Slice(count); } diff --git a/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs b/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs index d2678cf38..55e2be903 100644 --- a/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs +++ b/src/DotNext/Buffers/SparseBufferWriter.MemoryChunk.cs @@ -62,7 +62,7 @@ private sealed class PooledMemoryChunk : MemoryChunk internal PooledMemoryChunk(MemoryAllocator? allocator, int length, MemoryChunk? previous = null) : base(previous) - => owner = allocator.Allocate(length, exactSize: false); + => owner = allocator.AllocateAtLeast(length); /// /// Indicates that the chunk has no occupied elements in the memory. @@ -97,7 +97,7 @@ internal void Advance(int count) internal void Realloc(MemoryAllocator? allocator, int length) { owner.Dispose(); - owner = allocator.Allocate(length, exactSize: false); + owner = allocator.AllocateAtLeast(length); } protected override void Dispose(bool disposing) diff --git a/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs b/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs index a775f49db..5124f53a2 100644 --- a/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs +++ b/src/DotNext/Buffers/Text/Base64Encoder.Unicode.cs @@ -256,7 +256,7 @@ public static async IAsyncEnumerable> EncodeToCharsAsync(IA if (encoder.HasBufferedData) { - using (buffer = allocator.Allocate(MaxBufferedDataSize, exactSize: false)) + using (buffer = allocator.AllocateAtLeast(MaxBufferedDataSize)) { var count = encoder.Flush(buffer.Span); yield return buffer.Memory.Slice(0, count); diff --git a/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs b/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs index de3662a66..8243cad00 100644 --- a/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs +++ b/src/DotNext/Buffers/Text/Base64Encoder.Utf8.cs @@ -223,7 +223,7 @@ public static async IAsyncEnumerable> EncodeToUtf8Async(IAs if (encoder.HasBufferedData) { - using (buffer = allocator.Allocate(MaxBufferedDataSize, exactSize: false)) + using (buffer = allocator.AllocateAtLeast(MaxBufferedDataSize)) { var count = encoder.Flush(buffer.Span); yield return buffer.Memory.Slice(0, count); diff --git a/src/DotNext/Text/EncodingExtensions.cs b/src/DotNext/Text/EncodingExtensions.cs index 275ee281c..dfa73e64e 100644 --- a/src/DotNext/Text/EncodingExtensions.cs +++ b/src/DotNext/Text/EncodingExtensions.cs @@ -38,7 +38,7 @@ public static MemoryOwner GetBytes(this Encoding encoding, ReadOnlySpan GetChars(this Encoding encoding, ReadOnlySpan(sizeof(long), exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(sizeof(long)); await stream.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); return ReadInt64LittleEndian(buffer.Span); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs index 42a69e456..66276f138 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Disconnect.cs @@ -26,7 +26,7 @@ private async Task ProcessDisconnectAsync(HttpRequest request, HttpResponse resp } else { - using var buffer = allocator.Allocate(payloadLength, true); + using var buffer = allocator.AllocateExactly(payloadLength); await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); (sender, isAlive) = DeserializeDisconnectRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs index a600a1c1a..fc94e0760 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.ForwardJoin.cs @@ -27,7 +27,7 @@ private async Task ProcessForwardJoinAsync(HttpRequest request, HttpResponse res else { // slow path, allocate temp buffer - using var buffer = allocator.Allocate(payloadLength, true); + using var buffer = allocator.AllocateExactly(payloadLength); await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); (sender, joinedPeer, timeToLive) = DeserializeForwardJoinRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs index 5fbee7459..e1edbc69b 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Join.cs @@ -25,7 +25,7 @@ private async Task ProcessJoinAsync(HttpRequest request, HttpResponse response, else { // slow path, copy to temp buffer - using var buffer = allocator.Allocate(payloadLength, true); + using var buffer = allocator.AllocateExactly(payloadLength); await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); joinedPeer = DeserializeJoinRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs index 1a4ff2e0d..b772245dd 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Neighbor.cs @@ -28,7 +28,7 @@ private async Task ProcessNeighborAsync(HttpRequest request, HttpResponse respon else { // slow path, allocate temp buffer - using var buffer = allocator.Allocate(payloadLength, true); + using var buffer = allocator.AllocateExactly(payloadLength); await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); (sender, highPriority) = DeserializeNeighborRequest(buffer.Memory); } diff --git a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs index fd6212a97..633f8e2bc 100644 --- a/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs +++ b/src/cluster/DotNext.AspNetCore.Cluster/Net/Cluster/Discovery/HyParView/Http/HttpPeerController.Shuffle.cs @@ -25,7 +25,7 @@ private async Task ProcessShuffleReplyAsync(HttpRequest request, HttpResponse re } else { - using var buffer = allocator.Allocate(payloadLength, true); + using var buffer = allocator.AllocateExactly(payloadLength); await request.BodyReader.ReadExactlyAsync(buffer.Memory, token).ConfigureAwait(false); peers = DeserializeShuffleReply(buffer.Memory); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs index ae6cc782e..381f1b662 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/BinaryLogEntry.cs @@ -61,7 +61,7 @@ public int? CommandId /// MemoryOwner IBinaryLogEntry.ToBuffer(MemoryAllocator allocator) { - var buffer = allocator.Allocate(T.Size, exactSize: true); + var buffer = allocator.AllocateExactly(T.Size); var writer = new SpanWriter(buffer.Span); Content.Format(ref writer); return buffer; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs index b59c8c2e6..db80a5a53 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Membership/PersistentClusterConfigurationStorage.cs @@ -80,7 +80,7 @@ async ValueTask IDataTransferObject.WriteToAsync(TWriter writer, Cancel { // this method should be safe for concurrent invocations var handle = fs.SafeFileHandle; - using var buffer = allocator.Allocate(bufferSize, exactSize: false); + using var buffer = allocator.AllocateAtLeast(bufferSize); for (int offset = PayloadOffset, count; (count = await RandomAccess.ReadAsync(handle, buffer.Memory, offset, token).ConfigureAwait(false)) > 0; offset += count) { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs index 6a92be299..96eff31db 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.LogEntry.cs @@ -271,7 +271,7 @@ public IAsyncBinaryReader GetReader() [RequiresUnreferencedCode("JSON deserialization may be incompatible with IL trimming")] static async ValueTask DeserializeSlowAsync(IAsyncBinaryReader reader, LogEntryMetadata metadata, Func? typeLoader, JsonSerializerOptions? options, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(int.CreateSaturating(metadata.Length), true); + using var buffer = MemoryAllocator.AllocateExactly(int.CreateSaturating(metadata.Length)); await reader.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return JsonLogEntry.Deserialize(IAsyncBinaryReader.Create(buffer.Memory), typeLoader, options); } @@ -325,7 +325,7 @@ public IAsyncBinaryReader GetReader() static async ValueTask DeserializeSlowAsync(IAsyncBinaryReader reader, LogEntryMetadata metadata, Func typeLoader, JsonSerializerContext context, CancellationToken token) { - using var buffer = MemoryAllocator.Allocate(int.CreateSaturating(metadata.Length), exactSize: true); + using var buffer = MemoryAllocator.AllocateExactly(int.CreateSaturating(metadata.Length)); await reader.ReadAsync(buffer.Memory, token).ConfigureAwait(false); return JsonLogEntry.Deserialize(IAsyncBinaryReader.Create(buffer.Memory), typeLoader, context); } diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs index 3b06db003..dee0bb665 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.NodeState.cs @@ -58,7 +58,7 @@ private NodeState(string fileName, MemoryAllocator allocator, bool integri { Debug.Assert(Capacity >= LastVoteOffset + sizeof(long)); - buffer = allocator.Allocate(Capacity, true); + buffer = allocator.AllocateExactly(Capacity); if (File.Exists(fileName)) { handle = File.OpenHandle(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, FileOptions.SequentialScan); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs index 15cc5b524..f728e2a4f 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.Partition.cs @@ -42,7 +42,7 @@ internal Partition(DirectoryInfo location, int bufferSize, int recordsPerPartiti PartitionNumber = partitionNumber; // allocate metadata segment - metadata = manager.BufferAllocator.Allocate(fileOffset, true); + metadata = manager.BufferAllocator.AllocateExactly(fileOffset); metadataFlushStartAddress = int.MaxValue; entryCache = manager.AllocLogEntryCache(recordsPerPartition); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs index 65e8ec826..b978016b2 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.DefaultImpl.cs @@ -270,7 +270,7 @@ ValueTask ILocalMember.ResignAsync(CancellationToken token) [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder))] // hot path, avoid allocations async ValueTask ILocalMember.ProposeConfigurationAsync(Func, CancellationToken, ValueTask> configurationReader, long configurationLength, long fingerprint, CancellationToken token) { - var buffer = allocator.Allocate(int.CreateSaturating(configurationLength), exactSize: true); + var buffer = allocator.AllocateExactly(int.CreateSaturating(configurationLength)); await configurationReader(buffer.Memory, token).ConfigureAwait(false); cachedConfig.Clear(); cachedConfig.Update(buffer, fingerprint); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs index 885b17ec0..f789ffc80 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftLogEntryBufferingOptions.cs @@ -55,7 +55,7 @@ public MemoryAllocator? MemoryAllocator internal string GetRandomFileName() => Path.Combine(TempPath, Path.GetRandomFileName()); - internal MemoryOwner RentBuffer() => MemoryAllocator.Allocate(BufferSize, false); + internal MemoryOwner RentBuffer() => MemoryAllocator.AllocateAtLeast(BufferSize); internal FileBufferingWriter CreateBufferingWriter() { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs index 1ec70643a..108dce269 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/Tcp/TcpClient.cs @@ -24,7 +24,7 @@ internal ConnectionContext(TcpStream transport, TcpProtocolStream protocol, int this.transport = transport; this.protocol = protocol; - buffer = allocator.Allocate(bufferSize, exactSize: false); + buffer = allocator.AllocateAtLeast(bufferSize); } internal int CloseTimeout diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs index 274d4bb1b..76f0c7c55 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/ProtocolStream.cs @@ -23,7 +23,7 @@ private protected ProtocolStream(MemoryAllocator allocator, int transmissi { Debug.Assert(transmissionBlockSize > 0); - buffer = allocator.Allocate(transmissionBlockSize, exactSize: false); + buffer = allocator.AllocateAtLeast(transmissionBlockSize); this.allocator = allocator; } @@ -55,7 +55,7 @@ private protected virtual void WriteToTransport(ReadOnlySpan buffer) private protected virtual int ReadFromTransport(Span buffer) { int result; - var localBuffer = allocator.Allocate(buffer.Length, exactSize: true); + var localBuffer = allocator.AllocateExactly(buffer.Length); var timeoutTracker = new CancellationTokenSource(ReadTimeout); var task = ReadFromTransportAsync(localBuffer.Memory, timeoutTracker.Token).AsTask(); try @@ -83,7 +83,7 @@ private protected virtual int ReadFromTransport(Span buffer) private protected virtual int ReadFromTransport(int minimumSize, Span buffer) { int result; - var localBuffer = allocator.Allocate(buffer.Length, exactSize: true); + var localBuffer = allocator.AllocateExactly(buffer.Length); var timeoutTracker = new CancellationTokenSource(ReadTimeout); var task = ReadFromTransportAsync(minimumSize, localBuffer.Memory, timeoutTracker.Token).AsTask(); try diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs index 89efb8bc9..c9a06e8ac 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Server.LogEntryProducer.cs @@ -99,7 +99,7 @@ ValueTask IDataTransferObject.WriteToAsync(TWriter writer, Cancellation async ValueTask IDataTransferObject.TransformAsync(TTransformation transformation, CancellationToken token) { // we don't need large buffer. It is used for encoding some special data types, such as strings - using var buffer = allocator.Allocate(length: 512, exactSize: false); + using var buffer = allocator.AllocateAtLeast(length: 512); var result = await IDataTransferObject.TransformAsync(stream, transformation, resetStream: false, buffer.Memory, token).ConfigureAwait(false); consumed = true; return result; From ed6eb4e4b80b22dfab675943b531859f75f995b2 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 22:46:51 +0200 Subject: [PATCH 077/155] Replace ThrowIfDisposed with existing helper from .NET --- .../IO/FileBufferingWriter.Sequence.cs | 2 +- src/DotNext.IO/IO/FileBufferingWriter.cs | 18 +++-------- src/DotNext.IO/IO/FileReader.cs | 8 ++--- src/DotNext.IO/IO/FileWriter.cs | 4 +-- .../ReadOnlySequenceAccessor.cs | 4 +-- .../Threading/AsyncAutoResetEvent.cs | 4 +-- .../Threading/AsyncBarrier.cs | 6 ++-- .../Threading/AsyncCountdownEvent.cs | 10 +++--- .../Threading/AsyncCounter.cs | 8 ++--- .../Threading/AsyncExchanger.cs | 6 ++-- .../Threading/AsyncExclusiveLock.cs | 4 +-- .../Threading/AsyncManualResetEvent.cs | 4 +-- .../Threading/AsyncReaderWriterLock.cs | 10 +++--- .../Threading/AsyncSharedLock.cs | 6 ++-- .../Threading/AsyncTrigger.cs | 2 +- .../Threading/QueuedSynchronizer.cs | 8 ++--- src/DotNext.Unsafe/Buffers/UnmanagedMemory.cs | 17 +++------- src/DotNext/Buffers/BufferWriter.cs | 4 +-- .../Buffers/PooledArrayBufferWriter.cs | 32 +++++++++---------- src/DotNext/Buffers/PooledBufferWriter.cs | 8 ++--- .../Buffers/SparseBufferWriter.Reader.cs | 12 +++---- .../Buffers/SparseBufferWriter.Writer.cs | 4 +-- src/DotNext/Buffers/SparseBufferWriter.cs | 19 ++++++----- src/DotNext/Disposable.cs | 14 -------- .../Consensus/Raft/DiskBasedStateMachine.cs | 2 +- .../Consensus/Raft/MemoryBasedStateMachine.cs | 6 ++-- .../Cluster/Consensus/Raft/PersistentState.cs | 4 +-- .../Net/Cluster/Consensus/Raft/RaftCluster.cs | 6 ++-- .../ConnectionOriented/Client.cs | 2 +- .../Datagram/ExchangePeer.cs | 2 +- 30 files changed, 102 insertions(+), 134 deletions(-) diff --git a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs index 2930ea9d8..4303168b0 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.Sequence.cs @@ -170,7 +170,7 @@ public ReadOnlySequence Sequence { get { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LazySegment? first = null, last = null; BuildSegments(ref first, ref last); diff --git a/src/DotNext.IO/IO/FileBufferingWriter.cs b/src/DotNext.IO/IO/FileBufferingWriter.cs index 4fe707333..c5da9419a 100644 --- a/src/DotNext.IO/IO/FileBufferingWriter.cs +++ b/src/DotNext.IO/IO/FileBufferingWriter.cs @@ -62,19 +62,9 @@ internal void SetLength(int value) length = value; } - private void ThrowIfDisposed() - { - if (ptr is null) - Throw(); - - [DoesNotReturn] - [StackTraceHidden] - void Throw() => throw new ObjectDisposedException(GetType().Name); - } - public override Span GetSpan() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(ptr is null, this); return new(ptr, length); } @@ -82,11 +72,11 @@ public override Span GetSpan() public override MemoryHandle Pin(int elementIndex) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(ptr is null, this); return new(Unsafe.Add(ptr, elementIndex)); } - public override void Unpin() => ThrowIfDisposed(); + public override void Unpin() => ObjectDisposedException.ThrowIf(ptr is null, this); protected override void Dispose(bool disposing) { @@ -96,7 +86,7 @@ protected override void Dispose(bool disposing) session = default; } - if (ptr != null) + if (ptr is not null) NativeMemory.Free(ptr); ptr = null; diff --git a/src/DotNext.IO/IO/FileReader.cs b/src/DotNext.IO/IO/FileReader.cs index 46fb05436..040ca831b 100644 --- a/src/DotNext.IO/IO/FileReader.cs +++ b/src/DotNext.IO/IO/FileReader.cs @@ -141,7 +141,7 @@ public void Consume(int bytes) [AsyncMethodBuilder(typeof(PoolingAsyncValueTaskMethodBuilder<>))] public async ValueTask ReadAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var buffer = this.buffer.Memory; @@ -173,7 +173,7 @@ public async ValueTask ReadAsync(CancellationToken token = default) /// Internal buffer has no free space. public bool Read() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var buffer = this.buffer.Span; @@ -249,7 +249,7 @@ private async ValueTask ReadBufferedAsync(Memory output, Cancellation /// The reader has been disposed. public int Read(Span output) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); int count; @@ -287,7 +287,7 @@ public int Read(Span output) /// is less than zero. public void Skip(long bytes) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (bytes < 0L) throw new ArgumentOutOfRangeException(nameof(bytes)); diff --git a/src/DotNext.IO/IO/FileWriter.cs b/src/DotNext.IO/IO/FileWriter.cs index b0905213c..140f0b9fd 100644 --- a/src/DotNext.IO/IO/FileWriter.cs +++ b/src/DotNext.IO/IO/FileWriter.cs @@ -168,7 +168,7 @@ public ValueTask WriteAsync(CancellationToken token = default) /// The writer has been disposed. public void Write() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (HasBufferedData) FlushCore(); @@ -242,7 +242,7 @@ public ValueTask WriteAsync(ReadOnlyMemory input, CancellationToken token /// The object has been disposed. public void Write(ReadOnlySpan input) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (input.Length <= FreeCapacity) { diff --git a/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs b/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs index c13ad02a5..77682a6d4 100644 --- a/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs +++ b/src/DotNext.IO/IO/MemoryMappedFiles/ReadOnlySequenceAccessor.cs @@ -185,7 +185,7 @@ public ReadOnlySequence Sequence { get { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (totalLength == 0) return ReadOnlySequence.Empty; @@ -196,7 +196,7 @@ public ReadOnlySequence Sequence private unsafe Span GetSpan(in Segment window) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (current != window) { diff --git a/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs b/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs index 9313af9e2..aa0a4e2c9 100644 --- a/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs +++ b/src/DotNext.Threading/Threading/AsyncAutoResetEvent.cs @@ -81,7 +81,7 @@ private void OnCompleted(DefaultWaitNode node) /// The current instance has already been disposed. public bool Reset() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = TryAcquire(ref manager); @@ -97,7 +97,7 @@ public bool Reset() /// The current instance has already been disposed. public bool Set() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var suspendedCaller = default(ManualResetCompletionSource); bool result; diff --git a/src/DotNext.Threading/Threading/AsyncBarrier.cs b/src/DotNext.Threading/Threading/AsyncBarrier.cs index 5fd8d91d0..7b2299384 100644 --- a/src/DotNext.Threading/Threading/AsyncBarrier.cs +++ b/src/DotNext.Threading/Threading/AsyncBarrier.cs @@ -70,7 +70,7 @@ public AsyncBarrier(long participantCount) /// The current instance has already been disposed. public long AddParticipants(long participantCount) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); switch (participantCount) { @@ -132,7 +132,7 @@ public void RemoveParticipants(long participantCount) /// fails. public async ValueTask SignalAndWaitAsync(TimeSpan timeout, CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (ParticipantCount is 0L) throw new InvalidOperationException(); @@ -163,7 +163,7 @@ public async ValueTask SignalAndWaitAsync(TimeSpan timeout, CancellationTo /// fails. public async ValueTask SignalAndWaitAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (ParticipantCount is 0L) throw new InvalidOperationException(); diff --git a/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs b/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs index a4c53a03f..0903afe7e 100644 --- a/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs +++ b/src/DotNext.Threading/Threading/AsyncCountdownEvent.cs @@ -141,7 +141,7 @@ private bool TryAddCountCore(long signalCount) /// is less than zero. public bool TryAddCount(long signalCount) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return signalCount switch { @@ -158,7 +158,7 @@ public bool TryAddCount(long signalCount) /// The current instance has already been disposed. public bool TryAddCount() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return TryAddCountCore(1L); } @@ -193,7 +193,7 @@ public void AddCount(long signalCount) /// The current instance has already been disposed. public bool Reset() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); bool result; LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) @@ -222,7 +222,7 @@ public bool Reset(long count) if (count < 0L) throw new ArgumentOutOfRangeException(nameof(count)); - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); bool result; LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) @@ -370,7 +370,7 @@ public bool Signal(long signalCount = 1L) if (signalCount < 1L) throw new ArgumentOutOfRangeException(nameof(signalCount)); - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); bool result; LinkedValueTaskCompletionSource? suspendedCallers; diff --git a/src/DotNext.Threading/Threading/AsyncCounter.cs b/src/DotNext.Threading/Threading/AsyncCounter.cs index 111a78507..be0743c56 100644 --- a/src/DotNext.Threading/Threading/AsyncCounter.cs +++ b/src/DotNext.Threading/Threading/AsyncCounter.cs @@ -102,7 +102,7 @@ private void OnCompleted(DefaultWaitNode node) /// bool IAsyncEvent.Reset() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = manager.TryReset(); @@ -118,7 +118,7 @@ bool IAsyncEvent.Reset() /// This object is disposed. public void Increment() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); IncrementCore(1L); } @@ -131,7 +131,7 @@ public void Increment() /// Counter overflow detected. public void Increment(long delta) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); switch (delta) { @@ -205,7 +205,7 @@ public ValueTask WaitAsync(CancellationToken token = default) /// if the counter decremented successfully; if this counter is already zero. public bool TryDecrement() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = TryAcquire(ref manager); diff --git a/src/DotNext.Threading/Threading/AsyncExchanger.cs b/src/DotNext.Threading/Threading/AsyncExchanger.cs index 516ddef5f..983771a76 100644 --- a/src/DotNext.Threading/Threading/AsyncExchanger.cs +++ b/src/DotNext.Threading/Threading/AsyncExchanger.cs @@ -203,7 +203,7 @@ public bool TryExchange(ref T value) lock (SyncRoot) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (IsDisposing) { @@ -240,7 +240,7 @@ public void Terminate(Exception? exception = null) { lock (SyncRoot) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (termination is not null) throw new InvalidOperationException(); @@ -259,7 +259,7 @@ public bool IsTerminated { get { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return termination is not null; } } diff --git a/src/DotNext.Threading/Threading/AsyncExclusiveLock.cs b/src/DotNext.Threading/Threading/AsyncExclusiveLock.cs index 765a469a0..b5c1b938d 100644 --- a/src/DotNext.Threading/Threading/AsyncExclusiveLock.cs +++ b/src/DotNext.Threading/Threading/AsyncExclusiveLock.cs @@ -85,7 +85,7 @@ private void OnCompleted(DefaultWaitNode node) /// This object has been disposed. public bool TryAcquire() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = TryAcquire(ref manager); Monitor.Exit(SyncRoot); @@ -218,7 +218,7 @@ public ValueTask StealAsync(object? reason = null, CancellationToken token = def /// This object has been disposed. public void Release() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); ManualResetCompletionSource? suspendedCaller; lock (SyncRoot) diff --git a/src/DotNext.Threading/Threading/AsyncManualResetEvent.cs b/src/DotNext.Threading/Threading/AsyncManualResetEvent.cs index eb67f4ad8..a526dc2db 100644 --- a/src/DotNext.Threading/Threading/AsyncManualResetEvent.cs +++ b/src/DotNext.Threading/Threading/AsyncManualResetEvent.cs @@ -104,7 +104,7 @@ private void OnCompleted(DefaultWaitNode node) /// The current instance has already been disposed. public bool Set(bool autoReset) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); bool result; LinkedValueTaskCompletionSource? suspendedCallers; @@ -126,7 +126,7 @@ public bool Set(bool autoReset) /// The current instance has already been disposed. public bool Reset() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = manager.TryReset(); diff --git a/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs b/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs index ea54ef9cb..184b92691 100644 --- a/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs +++ b/src/DotNext.Threading/Threading/AsyncReaderWriterLock.cs @@ -254,7 +254,7 @@ private void OnCompleted(WaitNode node) /// This object has been disposed. public LockStamp TryOptimisticRead() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); // Ordering of version and lock state must be respected: // Write lock acquisition changes the state to Acquired and then increments the version. @@ -324,7 +324,7 @@ public ValueTask EnterReadLockAsync(CancellationToken token = default) /// This object has been disposed. public bool TryEnterWriteLock(in LockStamp stamp) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = stamp.IsValid(in state) && TryAcquire(ref GetLockManager()); @@ -389,7 +389,7 @@ public ValueTask EnterWriteLockAsync(TimeSpan timeout, CancellationToken token = private bool TryEnter() where TLockManager : struct, ILockManager { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = TryAcquire(ref GetLockManager()); @@ -547,7 +547,7 @@ public ValueTask StealWriteLockAsync(object? reason = null, CancellationToken to /// This object has been disposed. public void Release() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) @@ -576,7 +576,7 @@ public void Release() /// This object has been disposed. public void DowngradeFromWriteLock() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) diff --git a/src/DotNext.Threading/Threading/AsyncSharedLock.cs b/src/DotNext.Threading/Threading/AsyncSharedLock.cs index b4feea2b7..0297ac0b8 100644 --- a/src/DotNext.Threading/Threading/AsyncSharedLock.cs +++ b/src/DotNext.Threading/Threading/AsyncSharedLock.cs @@ -150,7 +150,7 @@ private ref TLockManager GetLockManager() private bool TryAcquire() where TManager : struct, ILockManager { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); Monitor.Enter(SyncRoot); var result = TryAcquire(ref GetLockManager()); @@ -272,7 +272,7 @@ public ValueTask AcquireAsync(bool strongLock, CancellationToken token = default /// This object has been disposed. public void Downgrade() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) @@ -294,7 +294,7 @@ public void Downgrade() /// This object has been disposed. public void Release() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) diff --git a/src/DotNext.Threading/Threading/AsyncTrigger.cs b/src/DotNext.Threading/Threading/AsyncTrigger.cs index a19e63153..eff453ad0 100644 --- a/src/DotNext.Threading/Threading/AsyncTrigger.cs +++ b/src/DotNext.Threading/Threading/AsyncTrigger.cs @@ -83,7 +83,7 @@ private void OnCompleted(DefaultWaitNode node) /// This trigger has been disposed. public bool Signal(bool resumeAll = false) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; bool result; diff --git a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs index 80c24ac44..8cbd72a80 100644 --- a/src/DotNext.Threading/Threading/QueuedSynchronizer.cs +++ b/src/DotNext.Threading/Threading/QueuedSynchronizer.cs @@ -318,7 +318,7 @@ public void CancelSuspendedCallers(CancellationToken token) if (!token.IsCancellationRequested) throw new ArgumentOutOfRangeException(nameof(token)); - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) @@ -732,7 +732,7 @@ private void OnCompleted(WaitNode node) /// This object has been disposed. protected void Release() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) @@ -757,7 +757,7 @@ protected void Release() /// This object has been disposed. protected void Release(TContext context) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); LinkedValueTaskCompletionSource? suspendedCallers; lock (SyncRoot) @@ -784,7 +784,7 @@ protected void Release(TContext context) /// This object has been disposed. protected bool TryAcquire(TContext context) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); lock (SyncRoot) { return TryAcquireCore(context); diff --git a/src/DotNext.Unsafe/Buffers/UnmanagedMemory.cs b/src/DotNext.Unsafe/Buffers/UnmanagedMemory.cs index 06dcd0095..da7d83b2e 100644 --- a/src/DotNext.Unsafe/Buffers/UnmanagedMemory.cs +++ b/src/DotNext.Unsafe/Buffers/UnmanagedMemory.cs @@ -36,11 +36,8 @@ private protected IntPtr Address { get { - return address is not null ? new(address) : Throw(); - - [DoesNotReturn] - [StackTraceHidden] - IntPtr Throw() => throw new ObjectDisposedException(GetType().Name); + ObjectDisposedException.ThrowIf(address is null, this); + return new(address); } } @@ -50,11 +47,8 @@ private protected IntPtr Address internal void Reallocate(int length) { - if (length <= 0) - throw new ArgumentOutOfRangeException(nameof(length)); - - if (address is null) - throw new ObjectDisposedException(GetType().Name); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(length); + ObjectDisposedException.ThrowIf(address is null, this); Length = length; var size = (nuint)SizeOf(length); @@ -66,8 +60,7 @@ public sealed override Span GetSpan() public sealed override MemoryHandle Pin(int elementIndex = 0) { - if (address is null) - throw new ObjectDisposedException(GetType().Name); + ObjectDisposedException.ThrowIf(address is null, this); return new(Unsafe.Add(address, elementIndex)); } diff --git a/src/DotNext/Buffers/BufferWriter.cs b/src/DotNext/Buffers/BufferWriter.cs index 2a9e77c37..58d2def20 100644 --- a/src/DotNext/Buffers/BufferWriter.cs +++ b/src/DotNext/Buffers/BufferWriter.cs @@ -207,7 +207,7 @@ static int CopyFromCollection(ICollection input, Span output) /// This writer has been disposed. public void Advance(int count) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (count < 0) ThrowCountOutOfRangeException(); @@ -231,7 +231,7 @@ static void ThrowInvalidOperationException() /// is less than zero or greater than . public void Rewind(int count) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if ((uint)count > (uint)position) ThrowCountOutOfRangeException(); diff --git a/src/DotNext/Buffers/PooledArrayBufferWriter.cs b/src/DotNext/Buffers/PooledArrayBufferWriter.cs index 7ba83cd6e..c102148a5 100644 --- a/src/DotNext/Buffers/PooledArrayBufferWriter.cs +++ b/src/DotNext/Buffers/PooledArrayBufferWriter.cs @@ -48,7 +48,7 @@ void ICollection.CopyTo(T[] array, int arrayIndex) { get { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return ref buffer[index]; } } @@ -56,14 +56,14 @@ void ICollection.CopyTo(T[] array, int arrayIndex) /// int IList.IndexOf(T item) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return Array.IndexOf(buffer, item, 0, position); } /// bool ICollection.Contains(T item) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return Array.IndexOf(buffer, item, 0, position) >= 0; } @@ -78,14 +78,14 @@ private void RemoveAt(int index) if (--position is 0) { ReturnBuffer(); - buffer = Array.Empty(); + buffer = []; } } /// void IList.RemoveAt(int index) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if ((uint)index >= (uint)position) throw new ArgumentOutOfRangeException(nameof(index)); @@ -95,7 +95,7 @@ void IList.RemoveAt(int index) /// bool ICollection.Remove(T item) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var index = Array.IndexOf(buffer, item, 0, position); if (index < 0) return false; @@ -117,7 +117,7 @@ void IList.Insert(int index, T item) /// This writer has been disposed. public void Insert(int index, ReadOnlySpan items) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, (uint)position, nameof(index)); if (items.IsEmpty) @@ -158,7 +158,7 @@ public void Insert(int index, ReadOnlySpan items) /// This writer has been disposed. public void Overwrite(int index, ReadOnlySpan items) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); ArgumentOutOfRangeException.ThrowIfGreaterThan((uint)index, (uint)position, nameof(index)); if (buffer.GetLength() is 0U) @@ -220,7 +220,7 @@ public override ReadOnlyMemory WrittenMemory { get { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return new(buffer, 0, position); } } @@ -233,7 +233,7 @@ public ArraySegment WrittenArray { get { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return new(buffer, 0, position); } } @@ -251,7 +251,7 @@ public ArraySegment WrittenArray /// This writer has been disposed. public override void Clear(bool reuseBuffer = false) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (buffer.GetLength() is 0) { @@ -273,7 +273,7 @@ public override void Clear(bool reuseBuffer = false) /// public override MemoryOwner DetachBuffer() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); MemoryOwner result; if (position > 0) { @@ -294,7 +294,7 @@ private T[] GetRawArray(int sizeHint) if (sizeHint < 0) throw new ArgumentOutOfRangeException(nameof(sizeHint)); - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); CheckAndResizeBuffer(sizeHint); return buffer; } @@ -338,7 +338,7 @@ public ArraySegment GetArray(int sizeHint = 0) /// public override void AddAll(ICollection items) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var count = items.Count; if (count <= 0) @@ -357,7 +357,7 @@ public override void AddAll(ICollection items) /// This writer has been disposed. public void RemoveLast(int count) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); ArgumentOutOfRangeException.ThrowIfNegative(count); if (buffer.GetLength() is 0) @@ -390,7 +390,7 @@ public void RemoveLast(int count) /// This writer has been disposed. public void RemoveFirst(int count) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); ArgumentOutOfRangeException.ThrowIfNegative(count); if (buffer.GetLength() is 0) diff --git a/src/DotNext/Buffers/PooledBufferWriter.cs b/src/DotNext/Buffers/PooledBufferWriter.cs index f3758149d..5f7e8423a 100644 --- a/src/DotNext/Buffers/PooledBufferWriter.cs +++ b/src/DotNext/Buffers/PooledBufferWriter.cs @@ -37,7 +37,7 @@ public override int Capacity private Memory GetWrittenMemory() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return buffer.Memory.Slice(0, position); } @@ -57,7 +57,7 @@ private Memory GetWrittenMemory() /// This writer has been disposed. public override void Clear(bool reuseBuffer = false) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (!reuseBuffer) { @@ -74,7 +74,7 @@ public override void Clear(bool reuseBuffer = false) private ref readonly MemoryOwner GetBuffer(int sizeHint) { ArgumentOutOfRangeException.ThrowIfNegative(sizeHint); - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); CheckAndResizeBuffer(sizeHint); return ref buffer; @@ -91,7 +91,7 @@ public override Span GetSpan(int sizeHint = 0) /// public override MemoryOwner DetachBuffer() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); MemoryOwner result; if (position > 0) { diff --git a/src/DotNext/Buffers/SparseBufferWriter.Reader.cs b/src/DotNext/Buffers/SparseBufferWriter.Reader.cs index 97e76cd83..c2b7d76a2 100644 --- a/src/DotNext/Buffers/SparseBufferWriter.Reader.cs +++ b/src/DotNext/Buffers/SparseBufferWriter.Reader.cs @@ -74,7 +74,7 @@ public bool MoveNext() /// is negative or greater than the number of available elements starting from . public SequencePosition GetPosition(long offset, SequencePosition origin = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (offset is 0L) return origin; @@ -140,7 +140,7 @@ private void NormalizePosition(scoped ref SequencePosition position) /// The buffer has been disposed. public ReadOnlySequence Read(ref SequencePosition position, long count) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (count < 0L) throw new ArgumentOutOfRangeException(nameof(count)); @@ -175,7 +175,7 @@ public ReadOnlySequence Read(ref SequencePosition position, long count) /// The buffer has been disposed. public int CopyTo(scoped Span output, scoped ref SequencePosition position) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); NormalizePosition(ref position); var result = 0; @@ -198,7 +198,7 @@ public int CopyTo(scoped Span output, scoped ref SequencePosition position) public void CopyTo(TConsumer consumer, SequencePosition start) where TConsumer : notnull, IReadOnlySpanConsumer { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); NormalizePosition(ref start); for (long count = length; Read(ref start, ref count) is { IsEmpty: false } block;) @@ -221,7 +221,7 @@ public void CopyTo(TConsumer consumer, SequencePosition start) public long CopyTo(TConsumer consumer, scoped ref SequencePosition position, long count) where TConsumer : notnull, IReadOnlySpanConsumer { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (count < 0L) throw new ArgumentOutOfRangeException(nameof(count)); @@ -244,7 +244,7 @@ public long CopyTo(TConsumer consumer, scoped ref SequencePosition po /// The buffer has been disposed. public Enumerator GetEnumerator() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return new Enumerator(first); } diff --git a/src/DotNext/Buffers/SparseBufferWriter.Writer.cs b/src/DotNext/Buffers/SparseBufferWriter.Writer.cs index 6433206e8..ba205437e 100644 --- a/src/DotNext/Buffers/SparseBufferWriter.Writer.cs +++ b/src/DotNext/Buffers/SparseBufferWriter.Writer.cs @@ -23,7 +23,7 @@ private unsafe Memory GetMemory() private Memory GetMemory(int sizeHint) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); switch (sizeHint) { @@ -66,7 +66,7 @@ Span IBufferWriter.GetSpan(int sizeHint) /// void IBufferWriter.Advance(int count) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (last is not PooledMemoryChunk chunk) throw new InvalidOperationException(); chunk.Advance(count); diff --git a/src/DotNext/Buffers/SparseBufferWriter.cs b/src/DotNext/Buffers/SparseBufferWriter.cs index d25d3781d..0b73d158e 100644 --- a/src/DotNext/Buffers/SparseBufferWriter.cs +++ b/src/DotNext/Buffers/SparseBufferWriter.cs @@ -38,8 +38,7 @@ public partial class SparseBufferWriter : Disposable, IGrowableBuffer, ISu /// is less than or equal to zero. public SparseBufferWriter(int chunkSize, SparseBufferGrowth growth = SparseBufferGrowth.None, MemoryAllocator? allocator = null) { - if (chunkSize <= 0) - throw new ArgumentOutOfRangeException(nameof(chunkSize)); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(chunkSize); this.chunkSize = chunkSize; this.allocator = allocator; @@ -89,7 +88,7 @@ public long WrittenCount { get { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); return length; } } @@ -107,7 +106,7 @@ public long WrittenCount /// The builder has been disposed. public bool TryGetWrittenContent(out ReadOnlyMemory segment) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (IsSingleSegment) { @@ -144,7 +143,7 @@ private long FragmentedBytes /// The builder has been disposed. public unsafe void Write(ReadOnlySpan input) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (last is null) first = last = new PooledMemoryChunk(allocator, chunkSize); @@ -168,7 +167,7 @@ public unsafe void Write(ReadOnlySpan input) /// The builder has been disposed. public void Write(ReadOnlyMemory input, bool copyMemory = true) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (input.IsEmpty) goto exit; @@ -222,7 +221,7 @@ public void Write(in ReadOnlySequence sequence, bool copyMemory = true) public void CopyTo(TConsumer consumer) where TConsumer : notnull, IReadOnlySpanConsumer { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); for (MemoryChunk? current = first; current is not null; current = current.Next) { consumer.Invoke(current.WrittenMemory.Span); @@ -232,7 +231,7 @@ public void CopyTo(TConsumer consumer) /// async ValueTask IGrowableBuffer.CopyToAsync(TConsumer consumer, CancellationToken token) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); for (MemoryChunk? current = first; current is not null; current = current.Next) { await consumer.Invoke(current.WrittenMemory, token).ConfigureAwait(false); @@ -257,7 +256,7 @@ public void CopyTo(ReadOnlySpanAction writer, TArg arg) /// The builder has been disposed. public int CopyTo(Span output) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var total = 0; for (MemoryChunk? current = first; current is not null && !output.IsEmpty; current = current.Next) { @@ -276,7 +275,7 @@ public int CopyTo(Span output) /// The builder has been disposed. public void Clear() { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); ReleaseChunks(); length = 0L; } diff --git a/src/DotNext/Disposable.cs b/src/DotNext/Disposable.cs index c102d5e28..5d4b742eb 100644 --- a/src/DotNext/Disposable.cs +++ b/src/DotNext/Disposable.cs @@ -39,20 +39,6 @@ public abstract class Disposable : IDisposable private string ObjectName => GetType().Name; - /// - /// Throws exception if this object is disposed. - /// - /// Object is disposed. - protected void ThrowIfDisposed() - { - if (IsDisposed) - Throw(); - - [DoesNotReturn] - [StackTraceHidden] - void Throw() => throw new ObjectDisposedException(ObjectName); - } - /// /// Gets a task representing exception. /// diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.cs index 675ca406c..f1b975152 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/DiskBasedStateMachine.cs @@ -207,7 +207,7 @@ private protected sealed override async ValueTask AppendAndCommitAsyncThe operation has been cancelled. public override async Task InitializeAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); await syncRoot.AcquireAsync(LockType.ExclusiveLock, token).ConfigureAwait(false); var session = sessionManager.Take(); try diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs index c11831557..15924763e 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/MemoryBasedStateMachine.cs @@ -714,7 +714,7 @@ private ValueTask ApplyAsync(int sessionId, CancellationToken token) /// The task representing asynchronous state of the method. public async Task ReplayAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); await syncRoot.AcquireAsync(LockType.ExclusiveLock, token).ConfigureAwait(false); var session = sessionManager.Take(); try @@ -761,7 +761,7 @@ public async Task ReplayAsync(CancellationToken token = default) /// public override async Task InitializeAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); await base.InitializeAsync(token).ConfigureAwait(false); @@ -772,7 +772,7 @@ public override async Task InitializeAsync(CancellationToken token = default) /// protected sealed override async Task ClearAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); await syncRoot.AcquireAsync(LockType.ExclusiveLock, token).ConfigureAwait(false); try { diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs index e6b1c6985..ad6ceb860 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/PersistentState.cs @@ -692,7 +692,7 @@ public ValueTask AppendAsync(TEntry entry, bool addToCache, Cancel public async ValueTask AppendAsync(ILogEntryProducer entries, CancellationToken token = default) where TEntry : notnull, IRaftLogEntry { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (entries.RemainingCount == 0L) throw new ArgumentException(ExceptionMessages.EntrySetIsEmpty); await syncRoot.AcquireAsync(LockType.WriteLock, token).ConfigureAwait(false); @@ -725,7 +725,7 @@ public async ValueTask AppendAsync(ILogEntryProducer entri /// represents index of the committed entry. public async ValueTask DropAsync(long startIndex, bool reuseSpace = false, CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var count = 0L; if (startIndex > state.LastIndex) goto exit; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs index 46e9b5229..d3c123046 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/RaftCluster.cs @@ -341,7 +341,7 @@ public virtual async Task StartAsync(CancellationToken token) /// public async ValueTask RevertToNormalModeAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); if (TryGetLocalMember() is not null && state is StandbyState { Resumable: true } standbyState) { @@ -379,7 +379,7 @@ public async ValueTask RevertToNormalModeAsync(CancellationToken token = d /// public async ValueTask EnableStandbyModeAsync(CancellationToken token = default) { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); RaftState currentState; if ((currentState = state) is FollowerState or CandidateState) @@ -1159,7 +1159,7 @@ public ValueTask ForceReplicationAsync(CancellationToken token = default) public async ValueTask ReplicateAsync(TEntry entry, CancellationToken token) where TEntry : notnull, IRaftLogEntry { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var leaderState = LeaderStateOrException; var tokenSource = token.LinkTo(leaderState.LeadershipToken); diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs index 571a2d08b..20f63f621 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/ConnectionOriented/Client.cs @@ -62,7 +62,7 @@ internal TimeSpan ConnectTimeout private async Task RequestAsync(TExchange exchange, CancellationToken token) where TExchange : notnull, IClientExchange { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); var timeStamp = new Timestamp(); var lockTaken = false; diff --git a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs index 646e37efd..a4bcc6f1e 100644 --- a/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs +++ b/src/cluster/DotNext.Net.Cluster/Net/Cluster/Consensus/Raft/TransportServices/Datagram/ExchangePeer.cs @@ -34,7 +34,7 @@ internal PipeOptions PipeConfig private async Task SendAsync(TExchange exchange, CancellationToken token) where TExchange : class, IClientExchange> { - ThrowIfDisposed(); + ObjectDisposedException.ThrowIf(IsDisposed, this); exchange.Sender = localMember.Id; var timeoutSource = CancellationTokenSource.CreateLinkedTokenSource(token); timeoutSource.CancelAfter(RequestTimeout); From 0aae201d14297477b9d7943e8cce04abd0f5dacb Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 22:56:34 +0200 Subject: [PATCH 078/155] Migration to primary ctor --- src/DotNext/IConsumer1.cs | 41 ++++++-------- src/DotNext/ISupplier0.cs | 55 +++++++------------ src/DotNext/ISupplier1.cs | 55 +++++++------------ src/DotNext/ISupplier2.cs | 55 +++++++------------ src/DotNext/Result.cs | 1 - src/DotNext/UserDataSlot.cs | 9 +-- src/DotNext/UserDataStorage.BackingStorage.cs | 2 +- 7 files changed, 79 insertions(+), 139 deletions(-) diff --git a/src/DotNext/IConsumer1.cs b/src/DotNext/IConsumer1.cs index 975304121..9c289bad6 100644 --- a/src/DotNext/IConsumer1.cs +++ b/src/DotNext/IConsumer1.cs @@ -32,19 +32,16 @@ public interface IConsumer : IFunctional> /// Represents typed function pointer implementing . /// /// The type of the consumer argument. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// is zero. [CLSCompliant(false)] [StructLayout(LayoutKind.Auto)] -public readonly unsafe struct Consumer : IConsumer +public readonly unsafe struct Consumer(delegate* ptr) : IConsumer { - private readonly delegate* ptr; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// is zero. - public Consumer(delegate* ptr) - => this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); /// /// Gets a value indicating that this function pointer is zero. @@ -88,24 +85,18 @@ public Consumer(delegate* ptr) /// /// The type describing closure. /// The type of the consumer argument. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// The context to be passed to the function pointer. +/// is zero. [CLSCompliant(false)] [StructLayout(LayoutKind.Auto)] -public readonly unsafe struct ConsumerClosure : IConsumer +public readonly unsafe struct ConsumerClosure(delegate* ptr, TContext context) : IConsumer { - private readonly delegate* ptr; - private readonly TContext context; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// The context to be passed to the function pointer. - /// is zero. - public ConsumerClosure(delegate* ptr, TContext context) - { - this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); - this.context = context; - } + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly TContext context = context; /// /// Gets a value indicating that this function pointer is zero. diff --git a/src/DotNext/ISupplier0.cs b/src/DotNext/ISupplier0.cs index 49ba4ec47..10e4f8772 100644 --- a/src/DotNext/ISupplier0.cs +++ b/src/DotNext/ISupplier0.cs @@ -36,19 +36,16 @@ public interface ISupplier : IFunctional> /// Represents typed function pointer implementing . /// /// The type of the result. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// is zero. [StructLayout(LayoutKind.Auto)] [CLSCompliant(false)] -public readonly unsafe struct Supplier : ISupplier +public readonly unsafe struct Supplier(delegate* ptr) : ISupplier { - private readonly delegate* ptr; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// is zero. - public Supplier(delegate* ptr) - => this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); /// /// Gets a value indicating that this function pointer is zero. @@ -106,16 +103,12 @@ public Supplier(delegate* ptr) /// Represents constant value supplier. /// /// The type of the value to supply. -public readonly struct ValueSupplier : ISupplier +/// +/// Creates a new wrapper for the value. +/// +/// The value to wrap. +public readonly struct ValueSupplier(T value) : ISupplier { - private readonly T value; - - /// - /// Creates a new wrapper for the value. - /// - /// The value to wrap. - public ValueSupplier(T value) => this.value = value; - /// T ISupplier.Invoke() => value; @@ -139,24 +132,18 @@ public Supplier(delegate* ptr) /// /// The type describing closure. /// The type of the result. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// The context to be passed to the function pointer. +/// is zero. [StructLayout(LayoutKind.Auto)] [CLSCompliant(false)] -public readonly unsafe struct SupplierClosure : ISupplier +public readonly unsafe struct SupplierClosure(delegate* ptr, TContext context) : ISupplier { - private readonly delegate* ptr; - private readonly TContext context; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// The context to be passed to the function pointer. - /// is zero. - public SupplierClosure(delegate* ptr, TContext context) - { - this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); - this.context = context; - } + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly TContext context = context; /// /// Gets a value indicating that this function pointer is zero. diff --git a/src/DotNext/ISupplier1.cs b/src/DotNext/ISupplier1.cs index 100d5fb3d..383af34a5 100644 --- a/src/DotNext/ISupplier1.cs +++ b/src/DotNext/ISupplier1.cs @@ -35,19 +35,16 @@ public interface ISupplier : IFunctional> /// /// The type of the argument. /// The type of the result. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// is zero. [StructLayout(LayoutKind.Auto)] [CLSCompliant(false)] -public readonly unsafe struct Supplier : ISupplier +public readonly unsafe struct Supplier(delegate* ptr) : ISupplier { - private readonly delegate* ptr; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// is zero. - public Supplier(delegate* ptr) - => this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); /// /// Gets a value indicating that this function pointer is zero. @@ -92,24 +89,18 @@ public Supplier(delegate* ptr) /// The type describing closure. /// The type of the argument. /// The type of the result. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// The context to be passed to the function pointer. +/// is zero. [StructLayout(LayoutKind.Auto)] [CLSCompliant(false)] -public readonly unsafe struct SupplierClosure : ISupplier +public readonly unsafe struct SupplierClosure(delegate* ptr, TContext context) : ISupplier { - private readonly delegate* ptr; - private readonly TContext context; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// The context to be passed to the function pointer. - /// is zero. - public SupplierClosure(delegate* ptr, TContext context) - { - this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); - this.context = context; - } + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly TContext context = context; /// /// Gets a value indicating that this function pointer is zero. @@ -164,13 +155,8 @@ public static implicit operator DelegatingSupplier(Func } [StructLayout(LayoutKind.Auto)] -internal readonly struct DelegatingPredicate : ISupplier, IFunctional> +internal readonly struct DelegatingPredicate(Predicate predicate) : ISupplier, IFunctional> { - private readonly Predicate predicate; - - internal DelegatingPredicate(Predicate predicate) - => this.predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); - /// bool ISupplier.Invoke(T arg) => predicate(arg); @@ -185,12 +171,9 @@ public static implicit operator DelegatingPredicate(Predicate predicate) } [StructLayout(LayoutKind.Auto)] -internal readonly struct DelegatingConverter : ISupplier, IFunctional> +internal readonly struct DelegatingConverter(Converter converter) : ISupplier, IFunctional> { - private readonly Converter converter; - - internal DelegatingConverter(Converter converter) - => this.converter = converter ?? throw new ArgumentNullException(nameof(converter)); + private readonly Converter converter = converter ?? throw new ArgumentNullException(nameof(converter)); /// TOutput ISupplier.Invoke(TInput arg) => converter(arg); diff --git a/src/DotNext/ISupplier2.cs b/src/DotNext/ISupplier2.cs index 9085931fc..b1aa290ad 100644 --- a/src/DotNext/ISupplier2.cs +++ b/src/DotNext/ISupplier2.cs @@ -38,19 +38,16 @@ public interface ISupplier : IFunctionalThe type of the first argument. /// The type of the second argument. /// The type of the result. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// is zero. [StructLayout(LayoutKind.Auto)] [CLSCompliant(false)] -public readonly unsafe struct Supplier : ISupplier +public readonly unsafe struct Supplier(delegate* ptr) : ISupplier { - private readonly delegate* ptr; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// is zero. - public Supplier(delegate* ptr) - => this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); /// /// Gets a value indicating that this function pointer is zero. @@ -98,24 +95,18 @@ public static explicit operator Func(Supplier /// The type of the first argument. /// The type of the second argument. /// The type of the result. +/// +/// Wraps the function pointer. +/// +/// The function pointer. +/// The context to be passed to the function pointer. +/// is zero. [StructLayout(LayoutKind.Auto)] [CLSCompliant(false)] -public readonly unsafe struct SupplierClosure : ISupplier +public readonly unsafe struct SupplierClosure(TContext context, delegate* ptr) : ISupplier { - private readonly delegate* ptr; - private readonly TContext context; - - /// - /// Wraps the function pointer. - /// - /// The function pointer. - /// The context to be passed to the function pointer. - /// is zero. - public SupplierClosure(TContext context, delegate* ptr) - { - this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); - this.context = context; - } + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly TContext context = context; /// /// Gets a value indicating that this function pointer is zero. @@ -170,12 +161,9 @@ public DelegatingSupplier(Func func) } [StructLayout(LayoutKind.Auto)] -internal readonly struct DelegatingComparer : IComparer, ISupplier, IFunctional> +internal readonly struct DelegatingComparer(Comparison comparison) : IComparer, ISupplier, IFunctional> { - private readonly Comparison comparison; - - internal DelegatingComparer(Comparison comparison) - => this.comparison = comparison ?? throw new ArgumentNullException(nameof(comparison)); + private readonly Comparison comparison = comparison ?? throw new ArgumentNullException(nameof(comparison)); /// int ISupplier.Invoke(T? x, T? y) => comparison(x, y); @@ -193,12 +181,9 @@ internal DelegatingComparer(Comparison comparison) } [StructLayout(LayoutKind.Auto)] -internal readonly unsafe struct ComparerWrapper : IComparer, ISupplier +internal readonly unsafe struct ComparerWrapper(delegate* ptr) : IComparer, ISupplier { - private readonly delegate* ptr; - - internal ComparerWrapper(delegate* ptr) - => this.ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); + private readonly delegate* ptr = ptr is not null ? ptr : throw new ArgumentNullException(nameof(ptr)); int ISupplier.Invoke(T? x, T? y) => ptr(x, y); diff --git a/src/DotNext/Result.cs b/src/DotNext/Result.cs index 971119bf4..b11e150a7 100644 --- a/src/DotNext/Result.cs +++ b/src/DotNext/Result.cs @@ -3,7 +3,6 @@ using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; -using System.Text.Json; using System.Text.Json.Serialization; namespace DotNext; diff --git a/src/DotNext/UserDataSlot.cs b/src/DotNext/UserDataSlot.cs index bb2e0a46b..92a623ef1 100644 --- a/src/DotNext/UserDataSlot.cs +++ b/src/DotNext/UserDataSlot.cs @@ -21,17 +21,12 @@ internal static string ToString(int typeIndex, int valueIndex) /// /// The type of the value stored in user data slot. [StructLayout(LayoutKind.Auto)] -public readonly record struct UserDataSlot : IEquatable> +public readonly record struct UserDataSlot() : IEquatable> { private static volatile int valueIndexCounter; internal static readonly int TypeIndex = UserDataSlot.Allocate(); - private readonly int valueIndex; - - /// - /// Allocates a new data slot. - /// - public UserDataSlot() => valueIndex = Interlocked.Increment(ref valueIndexCounter); + private readonly int valueIndex = Interlocked.Increment(ref valueIndexCounter); internal int ValueIndex => valueIndex - 1; diff --git a/src/DotNext/UserDataStorage.BackingStorage.cs b/src/DotNext/UserDataStorage.BackingStorage.cs index 05b8c4f2d..91a8f3c87 100644 --- a/src/DotNext/UserDataStorage.BackingStorage.cs +++ b/src/DotNext/UserDataStorage.BackingStorage.cs @@ -164,7 +164,7 @@ private BackingStorage(bool isEmpty) { if (isEmpty) { - tables = Array.Empty(); + tables = []; } else { From b72b311cc3576ff865d4dcef7553e46aa9b58ea2 Mon Sep 17 00:00:00 2001 From: sakno Date: Sat, 25 Nov 2023 23:08:32 +0200 Subject: [PATCH 079/155] Migration to collection expressions --- src/.editorconfig | 1 + src/DotNext.IO/IO/DataTransferObject.cs | 2 +- .../Authentication/IAuthenticationHandler.cs | 2 +- .../Authorization/AuthorizationMiddleware.cs | 2 +- .../CommandLineMaintenanceInterfaceHost.cs | 2 +- .../InterpolationExpression.Handler.cs | 2 +- .../Linq/Expressions/MetaExpression.cs | 4 ++-- .../Linq/Expressions/MutationExpression.cs | 2 +- .../Buffers/ChunkSequenceTests.cs | 12 ++++++------ .../Threading/QueuedSynchronizer.cs | 2 +- src/DotNext/Buffers/PooledArrayBufferWriter.cs | 2 +- src/DotNext/Collections/Generic/Collection.cs | 18 ++++++++++++------ src/DotNext/Collections/Generic/List.cs | 4 ++-- .../Specialized/ConcurrentTypeMap.cs | 4 ++-- src/DotNext/Collections/Specialized/TypeMap.cs | 4 ++-- src/DotNext/Reflection/EnumType.cs | 2 +- src/DotNext/Text/EncodingWithoutPreamble.cs | 2 +- .../Consensus/Raft/ConsensusOnlyState.cs | 2 +- .../Consensus/Raft/LeaderState.Context.cs | 4 ++-- .../Membership/ClusterConfigurationStorage.cs | 2 +- .../Consensus/Raft/PersistentState.Internal.cs | 2 +- .../Cluster/Consensus/Raft/PersistentState.cs | 4 ++-- 22 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/.editorconfig b/src/.editorconfig index 888a818ff..77bc63f29 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -127,6 +127,7 @@ dotnet_diagnostic.SA1642.severity = none dotnet_diagnostic.SA1643.severity = none dotnet_diagnostic.SA1649.severity = none dotnet_diagnostic.SX1101.severity = warning +dotnet_diagnostic.SA1010.severity = none # CA1040: Avoid empty interfaces dotnet_diagnostic.CA1040.severity = error diff --git a/src/DotNext.IO/IO/DataTransferObject.cs b/src/DotNext.IO/IO/DataTransferObject.cs index 9ae3f8441..4f85cf23e 100644 --- a/src/DotNext.IO/IO/DataTransferObject.cs +++ b/src/DotNext.IO/IO/DataTransferObject.cs @@ -216,7 +216,7 @@ static async ValueTask BufferizeAsync(TObject dto, MemoryAllocator { var buffer = CreateBuffer(dto.Length, allocator); if (buffer is null) - return Array.Empty(); + return []; try { diff --git a/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/IAuthenticationHandler.cs b/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/IAuthenticationHandler.cs index e909a4e4b..a259964f4 100644 --- a/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/IAuthenticationHandler.cs +++ b/src/DotNext.MaintenanceServices/Maintenance/CommandLine/Authentication/IAuthenticationHandler.cs @@ -22,5 +22,5 @@ public interface IAuthenticationHandler /// Gets global options that can be used to authenticate the command. /// /// A collection of global options. - IEnumerable