├── .gitignore
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.props
├── FSharpExtensions.sln
├── LICENSE
├── README.md
├── benchmarks
├── Benchmarks.CSharp
│ ├── Benchmarks.CSharp.csproj
│ ├── Program.cs
│ ├── ScanfBenchmark.cs
│ └── TryCatchBenchmark.cs
├── Benchmarks.FSharp
│ ├── ActionSeq.fs
│ ├── BenchmarkConfig.fs
│ ├── BenchmarkRunner.fs
│ ├── Benchmarks.FSharp.fsproj
│ ├── CollectionsBuildersAllocations.fs
│ ├── ComparisonOperatorBenchmark.fs
│ ├── CustomBuildersVsLibraryBuilders.fs
│ ├── EResultAllocationsBenchmark.fs
│ ├── FSharpOptimizer.fs
│ ├── FSharpOptimizerWithExperimentalPipe.fs
│ ├── GSeq.fs
│ ├── GenericEqualityBenchmark.fs
│ ├── GenericEqualityObjectArrayBenchmark.fs
│ ├── GenericTaskBuilder.fs
│ ├── HttpVerbParsing.fs
│ ├── IEquatableEqualityOperatorBenchmark.fs
│ ├── MathematicalOperatorsBenchmarks.fs
│ ├── NodeCode.fs
│ ├── Program.fs
│ ├── PushStream.fs
│ ├── ScanfBenchmark.fs
│ ├── TaskBuildersBenchmarks.fs
│ └── VectorsBase64.fs
└── Benchmarks.Lib
│ ├── Benchmarks.Lib.csproj
│ └── HttpVerbsWithLength.cs
├── global.json
├── src
├── En3Tho.Extensions.DependencyInjection.XUnit
│ ├── En3Tho.Extensions.DependencyInjection.XUnit.csproj
│ └── IServiceCollectionExtensions.cs
├── En3Tho.Extensions.DependencyInjection
│ ├── AddAsOpenGeneric.cs
│ ├── AddFromConfiguration.cs
│ ├── AddHttpClient.cs
│ ├── AddOrFail.cs
│ ├── AddOrReplace.cs
│ ├── AddOrReplaceOrFail.cs
│ ├── AddScopedAsSelfAnd.cs
│ ├── AddScopedFunc.cs
│ ├── AddSingletonAsSelfAnd.cs
│ ├── AddSingletonFunc.cs
│ ├── AddTransientFunc.cs
│ ├── ChangeLifetime.cs
│ ├── Configuration.cs
│ ├── Decoration.cs
│ ├── En3Tho.Extensions.DependencyInjection.csproj
│ ├── IServiceProviderExtensions.cs
│ ├── IServiceScopeExtensions.cs
│ ├── TryAddScopedAsSelfAnd.cs
│ ├── TryAddScopedAsSelfAndOrFail.cs
│ ├── TryAddScopedFunc.cs
│ ├── TryAddScopedOrFailFunc.cs
│ ├── TryAddSingletonAsSelfAnd.cs
│ ├── TryAddSingletonAsSelfAndOrFail.cs
│ ├── TryAddSingletonFunc.cs
│ ├── TryAddSingletonOrFailFunc.cs
│ ├── TryAddTransientFunc.cs
│ ├── TryAddTransientOrFailFunc.cs
│ ├── Update.cs
│ └── Utility.cs
├── En3Tho.FSharp.ComputationExpressions
│ ├── ArrayPoolBasedBuilders.fs
│ ├── CodeBuilder.fs
│ ├── CollectionBuilderExtensions.fs
│ ├── En3Tho.FSharp.ComputationExpressions.fsproj
│ ├── GSeqExtensions.fs
│ ├── GenericCollectionCodeExtensions.fs
│ ├── GenericTaskBuilder
│ │ ├── Builder
│ │ │ ├── GenericTaskBuilderBase.fs
│ │ │ ├── GenericTaskBuilderCore.fs
│ │ │ ├── GenericTaskBuilderExtensions.fs
│ │ │ ├── GenericTaskBuilderExtensions2.fs
│ │ │ └── ResumableCodeHelpers.fs
│ │ ├── Data
│ │ │ ├── SeqStateMachineData.fs
│ │ │ ├── StateMachineData.fs
│ │ │ ├── StateMachineDataWithState.fs
│ │ │ ├── UnitStateMachineData.fs
│ │ │ └── UnitStateMachineDataWithState.fs
│ │ ├── GenericTaskBuilderInterfaces.fs
│ │ ├── GenericTaskBuilderIntrinsics.fs
│ │ └── Tasks
│ │ │ ├── ActivityTasks.fs
│ │ │ ├── AsyncIteratorMethodBuilderWrapper.fs
│ │ │ ├── CancellableTasks.fs
│ │ │ ├── ExceptionResultTasks.fs
│ │ │ ├── ExceptionTask.fs
│ │ │ ├── LazyTask.fs
│ │ │ ├── MethodBuilderBehaviors.fs
│ │ │ ├── RepeatableTask.fs
│ │ │ ├── ResultTasks.fs
│ │ │ ├── SemaphoreSlimTask.fs
│ │ │ ├── SynchronizationContextTask.fs
│ │ │ ├── TaskMethodBuilderWrappers.fs
│ │ │ ├── TaskSeq.fs
│ │ │ ├── Tasks.fs
│ │ │ ├── ValueOptionTasks.fs
│ │ │ └── ValueTaskMethodBuilderWrappers.fs
│ ├── GenericUnitBuilderBase.fs
│ ├── HttpBuilder
│ │ ├── Content.fs
│ │ ├── Extensions.fs
│ │ ├── HeadersBuilders.fs
│ │ ├── Serializers.fs
│ │ └── Stages.fs
│ ├── OptionBuilder.fs
│ ├── ResultBuilder.fs
│ ├── Tasks.fs
│ ├── TryExprBuilder.fs
│ └── ValueTaskExtensions.fs
├── En3Tho.FSharp.Extensions.Logging
│ ├── En3Tho.FSharp.Extensions.Logging.fsproj
│ └── ILoggerExtensions.fs
├── En3Tho.FSharp.Extensions
│ ├── ActivePatterns.fs
│ ├── Disposables.fs
│ ├── En3Tho.FSharp.Extensions.fsproj
│ ├── Experimental.fs
│ ├── Extensions.fs
│ ├── GSeq.fs
│ ├── GSeqEnumerators.fs
│ ├── InterfaceShortcuts.fs
│ ├── Modules.fs
│ ├── NumericsAndVectors.fs
│ ├── Pointers.fs
│ ├── Scanf.fs
│ ├── Span.fs
│ ├── Stackalloc.fs
│ ├── String.fs
│ └── TopLevel.fs
├── En3Tho.FSharp.Validation
│ ├── AsyncMultiValidators.fs
│ ├── BasicTypes.fs
│ ├── CommonValidatedTypes.fs
│ ├── En3Tho.FSharp.Validation.fsproj
│ ├── Extensions.fs
│ ├── JsonConverter.fs
│ └── MultiValidators.fs
└── En3Tho.FSharp.XUnitExtensions
│ ├── En3Tho.FSharp.XUnitExtensions.fsproj
│ └── Extensions.fs
├── tests
├── En3Tho.Extensions.DependencyInjection.Tests
│ ├── AddFuncTests.fs
│ ├── AddSingletonAsSelfAndInjectionTests.fs
│ ├── DecorationTests.fs
│ ├── En3Tho.Extensions.DependencyInjection.Tests.fsproj
│ ├── Module2DependencyInjectionTest.fs
│ ├── ModuleDependencyInjectionTest.fs
│ ├── RootNamespaceDependencyInjectionTest.fs
│ ├── ServiceCollectionExtensionsTests.fs
│ └── UpdateTests.fs
├── En3Tho.FSharp.ComputationExpressions.Tests
│ ├── ArrayPoolBasedBuilderTests.fs
│ ├── CancellableTaskTests.fs
│ ├── CodeBuilderTests.fs
│ ├── ComputationExpressionBuildTests.fs
│ ├── En3Tho.FSharp.ComputationExpressions.Tests.fsproj
│ ├── GenericTaskBuilderTests.fs
│ ├── HttpBuilderTests.fs
│ ├── LazyTaskTests.fs
│ ├── OptionsAndResults.fs
│ ├── RepeatableTaskTests.fs
│ ├── StringBuilderBuilderTests.fs
│ ├── TaskAndCollectionsOverloadingTests.fs
│ ├── TaskOptionAndResult.fs
│ ├── TaskResultOverloadingTests.fs
│ ├── Tasks.fs
│ └── TryExprBuilderTests.fs
├── En3Tho.FSharp.Extensions.Tests
│ ├── AsyncTests.fs
│ ├── Byrefs.fs
│ ├── DeferTests.fs
│ ├── En3Tho.FSharp.Extensions.Tests.fsproj
│ ├── FunkyPipesTests.fs
│ ├── GSeq.fs
│ ├── IEquatableOperatorTests.fs
│ ├── ScanfTest.fs
│ └── Vectors.fs
└── En3Tho.FSharp.Validation.Tests
│ ├── En3Tho.FSharp.Validation.Tests.fsproj
│ ├── JsonSerialization.fs
│ └── Validation.fs
└── tools
├── CentralPackageManagementMigrationTool
├── CentralPackageManagementMigrationTool.fsproj
├── DotnetInfo.fs
├── MigrationTool.fs
├── Program.fs
├── Properties
│ └── launchSettings.json
├── PropertiesAndExtensions.fs
└── XmlNodeBuilder.fs
└── ProjectUtilities
├── ActivePatternsCodeGen.fs
├── AddFunc.fs
├── Code.fs
├── ILoggerExtensionsCodeGen.fs
├── IntrinsicsCodeGen.fs
├── Program.fs
├── ProjectUtilities.fsproj
├── ServiceProviderCodeGen.fs
├── TryAddAsSelfAnd.fs
└── WebApplicationsExtensionsCodeGen.fs
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | true
6 |
7 |
8 |
9 | enable
10 | enable
11 | default
12 |
13 |
14 |
15 | false
16 | $(OtherFlags) --test:GraphBasedChecking --test:ParallelOptimization --test:ParallelIlxGen
17 |
18 | 9,
19 | 0760,
20 | 96,
21 | 3535,
22 | 42,
23 | 77,
24 | 3391,
25 | 3513,
26 | 1204
27 |
28 | preview
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Igor Bagdamyan
5 | Copyright (c) 2024 Igor Bagdamyan
6 | https://github.com/En3Tho/FSharpExtensions
7 | https://github.com/En3Tho/FSharpExtensions/blob/main/LICENSE
8 | https://github.com/En3Tho/FSharpExtensions
9 | $(NugetVersion)
10 | $(NugetVersion)
11 | $(NugetVersion)
12 | true
13 | snupkg
14 | true
15 | true
16 | true
17 |
18 |
19 |
20 |
21 | all
22 | runtime; build; native; contentfiles; analyzers; buildtransitive
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | runtime; build; native; contentfiles; analyzers; buildtransitive
31 | all
32 |
33 |
34 | runtime; build; native; contentfiles; analyzers; buildtransitive
35 | all
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Directory.Packages.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | all
13 | runtime; build; native; contentfiles; analyzers; buildtransitive
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Igor Bagdamyan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FSharpExtensions
2 | A set of extensions, computation expressions and other stuff I'm using
3 |
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.CSharp/Benchmarks.CSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.CSharp/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 |
3 | using BenchmarkDotNet.Running;
4 | using Benchmarks.CSharp;
5 |
6 | BenchmarkRunner.Run();
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.CSharp/ScanfBenchmark.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using BenchmarkDotNet.Attributes;
3 | using BenchmarkDotNet.Jobs;
4 | using En3Tho.FSharp.Extensions;
5 |
6 | namespace Benchmarks.CSharp
7 | {
8 | [SimpleJob(RuntimeMoniker.Net60)]
9 | [MemoryDiagnoser]
10 | [DisassemblyDiagnoser]
11 | public class ScanfBenchmark
12 | {
13 | [Benchmark]
14 | public bool PrimitivesOnlyPreallocated()
15 | {
16 | return Scanf.scanf(Benchmarks.ScanfBenchmark.Assets.primitivesOnlyFmt, "123456 123456.123456");
17 | }
18 |
19 | // [Benchmark]
20 | // public bool PrimitivesOnlyPreallocatedSpan()
21 | // {
22 | // return Scanf.scanfSpan(Benchmarks.FSharp.ScanfBenchmark.Assets.primitivesOnlyFmt, "123456 123456.123456".AsSpan());
23 | // }
24 | }
25 | }
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.CSharp/TryCatchBenchmark.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 |
3 | namespace Benchmarks.CSharp;
4 |
5 | [MemoryDiagnoser]
6 | [DisassemblyDiagnoser]
7 | public class TryCatchBenchmark
8 | {
9 | private Action? Action;
10 |
11 | [GlobalSetup]
12 | public void GlobalSetup()
13 | {
14 | Action = null;
15 | }
16 |
17 | [Benchmark]
18 | public void RunTryCatch()
19 | {
20 | try
21 | {
22 | Action?.Invoke(this);
23 | }
24 | catch
25 | {
26 | }
27 | }
28 |
29 | [Benchmark]
30 | public void RunCheckThenTryCatch()
31 | {
32 | if (Action is {} action)
33 | {
34 | try
35 | {
36 | action(this);
37 | }
38 | catch
39 | {
40 | }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/BenchmarkRunner.fs:
--------------------------------------------------------------------------------
1 | []
2 | module Benchmarks.BenchmarkRunner
3 |
4 | open System.Runtime.InteropServices
5 | open BenchmarkDotNet.Configs
6 | open BenchmarkDotNet.Running
7 |
8 | type BenchmarkRunner with
9 | static member Run<'T1, 'T2>([] config: IConfig) =
10 | BenchmarkRunner.Run([|
11 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config)
12 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config)
13 | |])
14 |
15 | static member Run<'T1, 'T2, 'T3>([] config: IConfig) =
16 | BenchmarkRunner.Run([|
17 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config)
18 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config)
19 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config)
20 | |])
21 |
22 | static member Run<'T1, 'T2, 'T3, 'T4>([] config: IConfig) =
23 | BenchmarkRunner.Run([|
24 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config)
25 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config)
26 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config)
27 | BenchmarkConverter.TypeToBenchmarks(typeof<'T4>, config)
28 | |])
29 |
30 | static member Run<'T1, 'T2, 'T3, 'T4, 'T5>([] config: IConfig) =
31 | BenchmarkRunner.Run([|
32 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config)
33 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config)
34 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config)
35 | BenchmarkConverter.TypeToBenchmarks(typeof<'T4>, config)
36 | BenchmarkConverter.TypeToBenchmarks(typeof<'T5>, config)
37 | |])
38 |
39 | static member Run<'T1, 'T2, 'T3, 'T4, 'T5, 'T6>([] config: IConfig) =
40 | BenchmarkRunner.Run([|
41 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config)
42 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config)
43 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config)
44 | BenchmarkConverter.TypeToBenchmarks(typeof<'T4>, config)
45 | BenchmarkConverter.TypeToBenchmarks(typeof<'T5>, config)
46 | BenchmarkConverter.TypeToBenchmarks(typeof<'T6>, config)
47 | |])
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/Benchmarks.FSharp.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | preview
6 | Benchmarks
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/CollectionsBuildersAllocations.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.CollectionsBuildersAllocations
2 |
3 | open BenchmarkDotNet.Attributes
4 | open BenchmarkDotNet.Jobs
5 | open En3Tho.FSharp.ComputationExpressions
6 | open En3Tho.FSharp.ComputationExpressions.ICollectionBuilder
7 |
8 | [<
9 | MemoryDiagnoser;
10 | DisassemblyDiagnoser(filters = [||]);
11 | SimpleJob(RuntimeMoniker.Net60)
12 | >]
13 | type Benchmark() =
14 |
15 | member val Count = 10 with get, set
16 |
17 | member val Items = [ for i = 0 to 10 do i ] :> seq<_>
18 |
19 | []
20 | member this.RunResizeArrayBuilder() = ResizeArray() {
21 | 1
22 | 2
23 | 3
24 | for i in this.Items do
25 | i
26 | let mutable i = this.Count
27 | while i > 0 do
28 | i
29 | i <- i - 1
30 | try
31 | i
32 | with e ->
33 | i
34 | try
35 | i
36 | finally
37 | ()
38 | }
39 |
40 | []
41 | member this.RunResizeArray() =
42 | let rsz = ResizeArray()
43 |
44 | rsz.Add(1)
45 | rsz.Add(2)
46 | rsz.Add(3)
47 |
48 | for i in this.Items do
49 | rsz.Add(i)
50 | let mutable i = this.Count
51 | while i > 0 do
52 | rsz.Add(i)
53 | i <- i - 1
54 | try
55 | rsz.Add(i)
56 | with e ->
57 | rsz.Add(i)
58 | try
59 | rsz.Add(i)
60 | finally
61 | ()
62 | rsz
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/ComparisonOperatorBenchmark.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.ComparisonOperatorBenchmark
2 |
3 | open System
4 | open BenchmarkDotNet.Attributes
5 |
6 | // TODO: benchmark more operators to find allocating ones
7 |
8 | type [] CustomRecordType = {
9 | V1: string
10 | V2: int
11 | V3: int64
12 | }
13 |
14 | let inline compareReferences<'a when 'a: not struct> (left: 'a) (right: 'a) =
15 | match box left, box right with
16 | | null, null -> 0
17 | | _, null -> -1
18 | | null, _ -> 1
19 | | _ -> 0
20 |
21 | let inline compare<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right)
22 |
23 | let inline compareArrays<'a when 'a :> IComparable<'a>> (left: 'a[]) (right: 'a[]) =
24 | match compareReferences left right with
25 | | 0 ->
26 | match left.Length.CompareTo(right.Length) with
27 | | 0 ->
28 | let rec go index =
29 | if uint index < uint left.Length then
30 | match left[index].CompareTo(right[index]) with
31 | | 0 ->
32 | go (index + 1)
33 | | result ->
34 | result
35 | else
36 | 0
37 | go 0
38 | | result ->
39 | result
40 | | result ->
41 | result
42 |
43 | let inline (<.) left right = compare left right = -1
44 | let inline (<..) left right = compareArrays left right = -1
45 | let inline (<=.) left right = compare left right <= 0
46 | let inline (<=..) left right = compareArrays left right <= 0
47 |
48 |
49 | let inline (>.) left right = compare left right = 1
50 | let inline (>=.) left right = compare left right >= 0
51 |
52 | []
53 | type Benchmark() =
54 |
55 | let value1 = {
56 | V1 = "test"
57 | V2 = 10
58 | V3 = 10
59 | }
60 |
61 | let value2 = {
62 | V1 = "test"
63 | V2 = 10
64 | V3 = 11
65 | }
66 |
67 | let values1 = [| value1; value1; value1; value1; value1; |]
68 | let values2 = [| value1; value1; value1; value1; value2; |]
69 |
70 | []
71 | member _.CompareCustomTypesUsingCustomOperator() = value1 <. value2
72 |
73 | []
74 | member _.CompareCustomTypesUsingNativeOperator() = value1 < value2
75 |
76 | []
77 | member _.CompareCustomArrayTypesUsingCustomOperator() = values1 <.. values2
78 |
79 | []
80 | member _.CompareCustomArrayTypesUsingNativeOperator() = value1 < value2
81 |
82 | []
83 | member _.CompareTimeSpanUsingCustomOperator() = TimeSpan() <. TimeSpan()
84 |
85 | []
86 | member _.CompareTimeSpanUsingNativeOperator() = TimeSpan() < TimeSpan()
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/CustomBuildersVsLibraryBuilders.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.CustomBuildersVsLibraryBuilders
2 |
3 | open BenchmarkDotNet.Attributes
4 | open En3Tho.FSharp.ComputationExpressions
5 | open En3Tho.FSharp.ComputationExpressions.ArrayPoolBasedBuilders
6 |
7 | [<
8 | MemoryDiagnoser;
9 | Config(typeof<``Net 6, Pgo``>)
10 | >]
11 | type Benchmark() =
12 |
13 | []
14 | member val Count = 100 with get, set
15 |
16 | []
17 | member this.LibraryArray() = [|
18 | 1
19 | 2
20 | 3
21 | for i = 0 to this.Count do
22 | i
23 | let mutable i = this.Count
24 | while i > 0 do
25 | i
26 | i <- i - 1
27 | |]
28 |
29 | []
30 | member this.LibraryList() = [
31 | 1
32 | 2
33 | 3
34 | for i = 0 to this.Count do
35 | i
36 | let mutable i = this.Count
37 | while i > 0 do
38 | i
39 | i <- i - 1
40 | ]
41 |
42 | []
43 | member this.CustomArray() = arr {
44 | 1
45 | 2
46 | 3
47 | for i = 0 to this.Count do
48 | i
49 | let mutable i = this.Count
50 | while i > 0 do
51 | i
52 | i <- i - 1
53 | }
54 |
55 | []
56 | member this.CustomResizeArray() = rsz {
57 | 1
58 | 2
59 | 3
60 | for i = 0 to this.Count do
61 | i
62 | let mutable i = this.Count
63 | while i > 0 do
64 | i
65 | i <- i - 1
66 | }
67 |
68 | []
69 | member this.CustomImmutableArray() = block {
70 | 1
71 | 2
72 | 3
73 | for i = 0 to this.Count do
74 | i
75 | let mutable i = this.Count
76 | while i > 0 do
77 | i
78 | i <- i - 1
79 | }
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/EResultAllocationsBenchmark.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.EResultAllocationsBenchmark
2 |
3 | open System
4 | open BenchmarkDotNet.Attributes
5 | open BenchmarkDotNet.Jobs
6 | open En3Tho.FSharp.ComputationExpressions.ResultBuilder
7 |
8 |
9 | [<
10 | MemoryDiagnoser;
11 | DisassemblyDiagnoser(filters = [||]);
12 | SimpleJob(RuntimeMoniker.Net60)
13 | >]
14 | type Benchmark() =
15 |
16 | let exnInstance = Exception()
17 |
18 | []
19 | member _.EResultManual() =
20 | match Error exnInstance, Error exnInstance with
21 | | Ok q, Ok w -> Ok (q + w)
22 | | Error exn, _
23 | | _, Error exn -> Error exn
24 |
25 | []
26 | member _.EResultSimple() = eresult {
27 | let! q = Error exnInstance
28 | let! w = Error exnInstance
29 | return q + w
30 | }
31 |
32 | []
33 | member _.EResultMultiManual2() =
34 | let q = Error exnInstance
35 | let w = Error exnInstance
36 | match q, w with
37 | | Ok q, Ok w ->
38 | Ok (q + w)
39 | | Error exn1, Error exn2 ->
40 | Error (AggregateException(exn1, exn2))
41 | | Error exn, _
42 | | _, Error exn ->
43 | Error (AggregateException(exn))
44 |
45 | []
46 | member _.EResultMulti2() = eresult {
47 | let! q = Error exnInstance
48 | and! w = Error exnInstance
49 | return q + w
50 | }
51 |
52 | []
53 | member _.EResultMultiManual3() =
54 | let q = Error exnInstance
55 | let w = Error exnInstance
56 | let e = Error exnInstance
57 | match q, w, e with
58 | | Ok q, Ok w, Ok e -> Ok (q + w + e)
59 | | _ ->
60 | [
61 | match q with Error exn -> exn | _ -> ()
62 | match w with Error exn -> exn | _ -> ()
63 | match e with Error exn -> exn | _ -> ()
64 | ] |> AggregateException :> exn |> Error
65 |
66 | []
67 | member _.EResultMulti3() = eresult {
68 | let! q = Error exnInstance
69 | and! w = Error exnInstance
70 | and! e = Error exnInstance
71 | return q + w + e
72 | }
73 |
74 | []
75 | member _.EResultMulti4() = eresult {
76 | let! q = Error exnInstance
77 | and! w = Error exnInstance
78 | and! e = Error exnInstance
79 | and! r = Error exnInstance
80 | return q + w + e + r
81 | }
82 |
83 | []
84 | member _.EResultMulti5() = eresult {
85 | let! q = Error exnInstance
86 | and! w = Error exnInstance
87 | and! e = Error exnInstance
88 | and! r = Error exnInstance
89 | and! t = Error exnInstance
90 | return q + w + e + r + t
91 | }
92 |
93 | []
94 | member _.EResultMulti6() = eresult {
95 | let! q = Error exnInstance
96 | and! w = Error exnInstance
97 | and! e = Error exnInstance
98 | and! r = Error exnInstance
99 | and! t = Error exnInstance
100 | and! y = Error exnInstance
101 | return q + w + e + r + t + y
102 | }
103 |
104 | []
105 | member _.EResultMulti7() = eresult {
106 | let! q = Error exnInstance
107 | and! w = Error exnInstance
108 | and! e = Error exnInstance
109 | and! r = Error exnInstance
110 | and! t = Error exnInstance
111 | and! y = Error exnInstance
112 | and! u = Error exnInstance
113 | return q + w + e + r + t + y + u
114 | }
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/GSeq.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.GSeq
2 |
3 | open BenchmarkDotNet.Jobs
4 | open En3Tho.FSharp.Extensions
5 | open BenchmarkDotNet.Attributes
6 | open System.Linq
7 |
8 | []
9 | //[)>]
10 | []
11 | type Benchmark() =
12 |
13 | []
14 | member val Count = 0 with get, set
15 |
16 | member val Array = [||] with get, set
17 |
18 | []
19 | member this.GlobalSetup() =
20 | this.Array <- Array.init this.Count id
21 |
22 | []
23 | member this.IEnumerableFilterMapSkipFold() =
24 | this.Array
25 | .Where(fun x -> x % 2 <> 0)
26 | .Skip(5)
27 | .Select(fun x -> x + 15)
28 | .Aggregate(0, (fun x y -> x + y))
29 |
30 | []
31 | member this.SeqFilterMapSkipFold() =
32 | this.Array
33 | |> Seq.filter ^ fun x -> x % 2 <> 0
34 | |> Seq.skip 5
35 | |> Seq.map ^ fun x -> x + 15
36 | |> Seq.fold (fun x y -> x + y) 0
37 |
38 | []
39 | member this.GSeqFilterMapSkipFold() =
40 | this.Array
41 | |> GSeq.ofArray
42 | |> GSeq.filter ^ fun x -> x % 2 <> 0
43 | |> GSeq.skip 5
44 | |> GSeq.map ^ fun x -> x + 15
45 | |> GSeq.fold 0 ^ fun x y -> x + y
46 |
47 | []
48 | member this.ActionSeqFilterMapSkipFold() =
49 | let action =
50 | (ActionSeq.fold 0 ^ fun x y -> x + y // termination stage, not iteration
51 | |> ActionSeq.map ^ fun x -> x + 15
52 | |> ActionSeq.skip 5
53 | |> ActionSeq.filter ^ fun x -> x % 2 <> 0
54 | |> ActionSeq.fromArray)
55 |
56 | action.Invoke(this.Array)
57 | action.next.next.next.next.Result
58 |
59 | []
60 | member this.For() =
61 | let mutable skip = 0
62 | let mutable result = 0
63 | let array = this.Array
64 | for i = 0 to array.Length - 1 do
65 | let x = array[i]
66 | if x % 2 <> 0 then
67 | if skip < 5 then
68 | skip <- skip + 1
69 | else
70 | let x = x + 15
71 | result <- result + x
72 | result
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/GenericEqualityBenchmark.fs:
--------------------------------------------------------------------------------
1 |
2 | module Benchmarks.GenericEqualityBenchmark
3 |
4 | open System
5 | open BenchmarkDotNet.Attributes
6 |
7 | type WootRecord = {
8 | Value1: string[]
9 | Value2: string[]
10 | Value3: string[]
11 | }
12 |
13 | let makeStringArray n = Array.init n (ignore >> Guid.NewGuid >> string)
14 |
15 | let makeWootRecord() : WootRecord = {
16 | Value1 = makeStringArray 5
17 | Value2 = makeStringArray 10
18 | Value3 = makeStringArray 5
19 | }
20 |
21 | let customStringArrayEquality (array1: string[]) (array2: string[]) =
22 | let mutable result = array1.Length = array2.Length
23 | let mutable i = 0
24 | while result && i < array1.Length do
25 | result <- array1[i].Equals(array2[i])
26 | i <- i + 1
27 | result
28 |
29 | let customIEquatableArrayEquality<'a when 'a :> IEquatable<'a>> (array1: 'a[]) (array2: 'a[]) =
30 | let mutable result = array1.Length = array2.Length
31 | let mutable i = 0
32 | while result && i < array1.Length do
33 | result <- array1[i].Equals(array2[i])
34 | i <- i + 1
35 | result
36 |
37 | let customWootRecordEquality (woot1: WootRecord) (woot2: WootRecord) =
38 | customStringArrayEquality woot1.Value1 woot2.Value1
39 | && customStringArrayEquality woot1.Value2 woot2.Value2
40 | && customStringArrayEquality woot1.Value3 woot2.Value3
41 |
42 | let customWootRecordIEquatableEquality (woot1: WootRecord) (woot2: WootRecord) =
43 | customIEquatableArrayEquality woot1.Value1 woot2.Value1
44 | && customIEquatableArrayEquality woot1.Value2 woot2.Value2
45 | && customIEquatableArrayEquality woot1.Value3 woot2.Value3
46 |
47 | []
48 | type Benchmark() =
49 |
50 | let woot1 = makeWootRecord()
51 | let woot2 = makeWootRecord()
52 | let woot1Copy = { { woot1 with Value1 = woot2.Value1 } with Value1 = woot1.Value1 }
53 |
54 | let longStringArray = makeStringArray 1000
55 | let longStringArrayCopy =
56 | let result = Array.copy longStringArray
57 | result[result.Length - 1] <- Guid.NewGuid().ToString()
58 | result
59 |
60 | []
61 | member x.TestAutoGeneratedEquality() =
62 | woot1 = woot2
63 |
64 | []
65 | member x.TestManualEquality() =
66 | customWootRecordEquality woot1 woot2
67 |
68 | []
69 | member x.TestIEquatableEquality() =
70 | customWootRecordIEquatableEquality woot1 woot2
71 |
72 | []
73 | member x.TestAutoGeneratedEqualityCopy() =
74 | woot1 = woot1Copy
75 |
76 | []
77 | member x.TestManualEqualityCopy() =
78 | customWootRecordEquality woot1 woot1Copy
79 |
80 | []
81 | member x.TestIEquatableEqualityCopy() =
82 | customWootRecordIEquatableEquality woot1 woot1Copy
83 |
84 | // arrays only
85 |
86 | []
87 | member x.TestAutoGeneratedArrayEquality() =
88 | woot1.Value1 = woot2.Value1
89 |
90 | []
91 | member x.TestManualArrayEquality() =
92 | customStringArrayEquality woot1.Value1 woot2.Value1
93 |
94 | []
95 | member x.TestIEquatableArrayEquality() =
96 | customIEquatableArrayEquality woot1.Value1 woot2.Value1
97 |
98 | []
99 | member x.TestAutoGeneratedArrayEqualityCopy() =
100 | woot1.Value1 = woot1Copy.Value1
101 |
102 | []
103 | member x.TestManualArrayEqualityCopy() =
104 | customStringArrayEquality woot1.Value1 woot1Copy.Value1
105 |
106 | []
107 | member x.TestIEquatableArrayEqualityCopy() =
108 | customIEquatableArrayEquality woot1.Value1 woot1Copy.Value1
109 |
110 | []
111 | member x.TestLongArrayAutoGenerated() =
112 | longStringArray = longStringArrayCopy
113 |
114 | []
115 | member x.TestLongArrayManual() =
116 | customStringArrayEquality longStringArray longStringArrayCopy
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/GenericEqualityObjectArrayBenchmark.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.GenericEqualityObjectArrayBenchmark
2 |
3 | open System
4 | open BenchmarkDotNet.Attributes
5 |
6 | module FastEquality =
7 | type T = T with
8 | static member inline ($) (_, structEquatable: ^a when ^a :> IEquatable<^a> and ^a: struct) = fun (value: ^a) -> structEquatable.Equals(value)
9 | static member inline ($) (_, refEquatable: 'a when 'a: not struct) = fun (value: 'b when 'b: not struct) -> refEquatable.Equals(value)
10 |
11 | let inline (====) a b = (T $ a) b
12 |
13 | open FastEquality
14 |
15 | type [] AutoGeneratedNested = {
16 | Value1: string
17 | Value2: int
18 | }
19 |
20 | type [] AutoGenerated = {
21 | Value1: string
22 | Value2: int
23 | Value3: double
24 | Value4: AutoGeneratedNested
25 | }
26 |
27 | let makeAutoGeneratedNested value : AutoGeneratedNested = {
28 | Value1 = "Bla"
29 | Value2 = value
30 | }
31 |
32 | let makeAutoGenerated value : AutoGenerated = {
33 | Value1 = "Bla"
34 | Value2 = value
35 | Value3 = 10.
36 | Value4 = makeAutoGeneratedNested value
37 | }
38 |
39 | let inline manualEquality (value1: AutoGenerated) (value2: AutoGenerated) =
40 | value1.Value1.Equals(value2.Value1)
41 | && value1.Value2.Equals(value2.Value2)
42 | && value1.Value3.Equals(value2.Value3)
43 | && value1.Value4.Value1.Equals(value2.Value4.Value1)
44 | && value1.Value4.Value2.Equals(value2.Value4.Value2)
45 |
46 | let inline slowIEquatableEquality (value1: #IEquatable<'a>) (value2: #IEquatable<'a>) = value1.Equals(value2)
47 | let inline fastIEquatableEquality<'a when 'a :> IEquatable<'a>> (value1: 'a) (value2: 'a) = value1.Equals(value2)
48 |
49 | let inline inrefIEquatableEquality<'a when 'a :> IEquatable<'a>> (value1: 'a inref) (value2: 'a inref) = value1.Equals(value2)
50 | let inline byrefIEquatableEquality<'a when 'a :> IEquatable<'a>> (value1: 'a byref) (value2: 'a byref) = value1.Equals(value2)
51 |
52 | let defaultValue = makeAutoGenerated 100500
53 | let defaultValue2 = makeAutoGenerated 100500
54 | let array1 = Array.init 1000 makeAutoGenerated
55 | let array2 = Array.init 1000 makeAutoGenerated
56 |
57 | let list1 = List.init 1000 makeAutoGenerated
58 | let list2 = List.init 1000 makeAutoGenerated
59 |
60 | []
61 | type Benchmark() =
62 |
63 | []
64 | member x.TestAutoGeneratedEquality() =
65 | Array.forall2 (fun first second -> first = second) array1 array2
66 |
67 | []
68 | member x.TestAutoGeneratedEqualityForArrayDirectly() =
69 | array1 = array2
70 |
71 | []
72 | member x.TestAutoGeneratedEqualityUsingIEquatable() =
73 | Array.forall2 (fun first second -> fastIEquatableEquality first second) array1 array2
74 |
75 | []
76 | member x.TestAutoGeneratedEqualityUsingSequenceEquals() =
77 | array1.AsSpan().SequenceEqual(array2)
78 |
79 | []
80 | member x.TestAutoGeneratedEqualityUsingAnotherFasterOperator() =
81 | Array.forall2 (fun first second -> first ==== second) array1 array2
82 |
83 | []
84 | member x.TestAutoGeneratedEqualityWithLists() =
85 | List.forall2 (fun first second -> first = second) list1 list2
86 |
87 | []
88 | member x.TestAutoGeneratedEqualityWithListsDirectly() =
89 | list1 = list2
90 |
91 | []
92 | member x.TestAutoGeneratedEqualityUsingIEquatableWithLists() =
93 | List.forall2 (fun first second -> fastIEquatableEquality first second) list1 list2
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/GenericTaskBuilder.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.GenericTaskBuilder
2 |
3 | open System.Collections.Generic
4 | open System.Threading.Tasks
5 | open BenchmarkDotNet.Attributes
6 | open En3Tho.FSharp.ComputationExpressions.Tasks
7 | open En3Tho.FSharp.Extensions
8 |
9 | let task2 = En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.Native.TaskBuilder()
10 |
11 | type MapAsyncEnumerableEnumerator<'a, 'b>(source1: IAsyncEnumerable<'a>, map: 'a -> 'b) =
12 | let enumerator = source1.GetAsyncEnumerator()
13 |
14 | interface IAsyncEnumerator<'b> with
15 | member this.Current = enumerator.Current |> map
16 | member this.DisposeAsync() = enumerator.DisposeAsync()
17 | member this.MoveNextAsync() = enumerator.MoveNextAsync()
18 |
19 | module AsyncEnumerable =
20 | let map map source = { new IAsyncEnumerable<'b> with member _.GetAsyncEnumerator(_) = MapAsyncEnumerableEnumerator<'a, 'b>(source, map) }
21 |
22 | let init v = taskSeq {
23 | for i in 1 .. v do
24 | do! Task.Yield()
25 | yield i
26 | }
27 |
28 | []
29 | []
30 | type Benchmark() =
31 |
32 | []
33 | member _.WaitTask() = task {
34 | do! Task.Yield()
35 | }
36 |
37 | []
38 | member _.WaitTask2() = task2 {
39 | do! Task.Yield()
40 | }
41 |
42 | []
43 | [)>]
44 | type AsyncSeqBenchmark() =
45 |
46 | []
47 | member _.Direct() = task2 {
48 | for v in AsyncEnumerable.init 10 do
49 | string v |> ignore
50 | }
51 |
52 | []
53 | member _.WihtoutGseqMap() = task2 {
54 | let values =
55 | AsyncEnumerable.init 10
56 | |> AsyncEnumerable.map string
57 |
58 | for _ in values do ()
59 | }
60 |
61 | []
62 | member _.WithGSeqMap() = task2 { // slower and allocates more for single map case. TODO: more?
63 | let values =
64 | AsyncEnumerable.init 10
65 | |> GSeq.withAsyncSeq (GSeq.map string)
66 |
67 | for _ in values do ()
68 | }
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/HttpVerbParsing.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.HttpVerbParsing
2 |
3 | open System
4 | open System.Runtime.CompilerServices
5 | open System.Text
6 | open BenchmarkDotNet.Attributes
7 | open Benchmarks.Lib
8 |
9 | module HttpChecker =
10 |
11 | let [] C = 67uy
12 |
13 | let [] P = 80uy
14 | let [] U = 85uy
15 | let [] A = 65uy
16 |
17 | let [] G = 71uy
18 | let [] H = 72uy
19 |
20 | let [] D = 68uy
21 | let [] O = 79uy
22 | let [] T = 84uy
23 |
24 | let [] NewLine = 10uy
25 |
26 | let connect = Encoding.UTF8.GetBytes("CONNECT")
27 |
28 | let post = Encoding.UTF8.GetBytes("POST")
29 | let put = Encoding.UTF8.GetBytes("PUT")
30 | let patch = Encoding.UTF8.GetBytes("PATCH")
31 |
32 | let get = Encoding.UTF8.GetBytes("GET")
33 | let head = Encoding.UTF8.GetBytes("HEAD")
34 |
35 | let delete = Encoding.UTF8.GetBytes("DELETE")
36 | let options = Encoding.UTF8.GetBytes("OPTIONS")
37 | let trace = Encoding.UTF8.GetBytes("TRACE")
38 |
39 | let getHttpVerbLength (span: ReadOnlySpan) =
40 | let mutable bytes = null
41 | match span[0] with
42 | | C -> bytes <- connect
43 | | G -> bytes <- get
44 | | P ->
45 | match span[1] with
46 | | O -> bytes <- post
47 | | U -> bytes <- put
48 | | A -> bytes <- patch
49 | | _ -> ()
50 | | D -> bytes <- delete
51 | | H -> bytes <- head
52 | | O -> bytes <- options
53 | | T -> bytes <- trace
54 | | _ -> ()
55 | if not (Object.ReferenceEquals(bytes, null)) && span.StartsWith(bytes) then
56 | bytes.Length
57 | else
58 | 0
59 |
60 | []
61 | []
62 | type Benchmark() =
63 |
64 | member val Bytes = null with get, set
65 |
66 | []
67 | member this.GlobalSetup() =
68 | this.Bytes <- Encoding.UTF8.GetBytes("TRACE blabla")
69 |
70 | []
71 | member this.GetHttpVerbLengthLoop() =
72 | let span = this.Bytes.AsSpan()
73 | let value = Unsafe.As(&Unsafe.AsRef(&span[0]))
74 | HttpVerbsWithLength.GetHttpVerbLengthWithLoop(value)
75 |
76 | []
77 | member this.GetHttpVerbLengthIfElse() =
78 | let span = this.Bytes.AsSpan()
79 | let value = Unsafe.As(&Unsafe.AsRef(&span[0]))
80 | HttpVerbsAvx2.GetHttpVerbLengthIfElse(value)
81 |
82 | []
83 | member this.GetHttpVerbLengthAvx2() =
84 | let span = this.Bytes.AsSpan()
85 | let value = Unsafe.As(&Unsafe.AsRef(&span[0]))
86 | HttpVerbsAvx2.GetHttpVerbLengthAvx2(value)
87 |
88 | []
89 | member this.GetHttpVerbLengthAvx2_2() =
90 | let span = this.Bytes.AsSpan()
91 | let value = Unsafe.As(&Unsafe.AsRef(&span[0]))
92 | HttpVerbsAvx2.GetHttpVerbLengthAvx2_2(value)
93 |
94 | []
95 | member this.GetHttpVerbLengthAvx2_3() =
96 | let span = this.Bytes.AsSpan()
97 | let value = Unsafe.As(&Unsafe.AsRef(&span[0]))
98 | HttpVerbsAvx2.GetHttpVerbLengthAvx2_3(value)
99 |
100 | []
101 | member this.GetHttpVerbLengthArrays() =
102 | HttpChecker.getHttpVerbLength this.Bytes
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/IEquatableEqualityOperatorBenchmark.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.IEquatableEqualityOperatorBenchmark
2 |
3 | open BenchmarkDotNet.Attributes
4 | open En3Tho.FSharp.Extensions
5 | open En3Tho.FSharp.ComputationExpressions.ICollectionBuilder
6 |
7 | type [] CustomRecordType = {
8 | V1: string
9 | V2: int
10 | V3: int64
11 | }
12 |
13 | []
14 | type Benchmark() =
15 |
16 | let value1 = {
17 | V1 = "test"
18 | V2 = 10
19 | V3 = 10
20 | }
21 |
22 | let value2 = {
23 | V1 = "test"
24 | V2 = 10
25 | V3 = 11
26 | }
27 |
28 | let mkSeq1 count =
29 | Seq.init count (fun _ -> value1)
30 |
31 | let mkSeq2 count =
32 | let left = Seq.init (count - 1) (fun _ -> value1)
33 | let right = seq { value2 }
34 | Seq.append left right
35 |
36 | let array1 = mkSeq1 100 |> Seq.toArray
37 | let array2 = mkSeq2 100 |> Seq.toArray
38 | let list1 = mkSeq1 100 |> Seq.toList
39 | let list2 = mkSeq2 100 |> Seq.toList
40 | let sequence1 = array1 :> seq<_>
41 | let sequence2 = array2 :> seq<_>
42 | let resizeArray1 = ResizeArray() { yield! mkSeq1 100 }
43 | let resizeArray2 = ResizeArray() { yield! mkSeq2 100 }
44 |
45 | []
46 | member _.TestValues() = value1 == value2
47 |
48 | []
49 | member _.TestArrays() = array1 === array2
50 |
51 | []
52 | member _.TestLists() = list1 === list2
53 |
54 | []
55 | member _.TestSequences() = sequence1 === sequence2
56 |
57 | []
58 | member _.TestResizeArrays() = resizeArray1 === resizeArray2
59 |
60 | []
61 | member _.TestValuesUsingDefaultEqualityOperator() = value1 = value2
62 |
63 | []
64 | member _.TestArraysUsingDefaultEqualityOperator() = array1 = array2
65 |
66 | []
67 | member _.TestListsUsingDefaultEqualityOperator() = list1 = list2
68 |
69 | []
70 | member _.TestSequencesUsingDefaultEqualityOperator() = sequence1 = sequence2
71 |
72 | []
73 | member _.TestResizeArraysUsingDefaultEqualityOperator() = resizeArray1 = resizeArray2
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/MathematicalOperatorsBenchmarks.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.MathematicalOperatorsBenchmarks
2 |
3 | open System
4 | open En3Tho.FSharp.Extensions
5 | open BenchmarkDotNet.Attributes
6 |
7 | []
8 | type Benchmark() =
9 |
10 | []
11 | member val IntValue1 = 0L with get, set
12 | member val TimeSpanValue1 = TimeSpan() with get, set
13 | member val DateTimeValue1 = DateTime() with get, set
14 |
15 | member val IntValue2 = 0L with get, set
16 | member val TimeSpanValue2 = TimeSpan() with get, set
17 | member val DateTimeValue2 = DateTime() with get, set
18 |
19 | member this.GlobalSetup() =
20 | this.TimeSpanValue1 <- TimeSpan.FromMinutes(this.IntValue1.f64)
21 | this.DateTimeValue1 <- DateTime(this.IntValue1)
22 |
23 | this.IntValue2 <- this.IntValue1
24 | this.TimeSpanValue2 <- TimeSpan.FromMinutes(this.IntValue1.f64)
25 | this.DateTimeValue2 <- DateTime(this.IntValue1)
26 |
27 | []
28 | member this.TestInt() = this.IntValue1 - this.IntValue2
29 |
30 | []
31 | member this.TimeSpan() = this.TimeSpanValue1 - this.TimeSpanValue2
32 |
33 | []
34 | member this.DateTime() = this.DateTimeValue1 - this.DateTimeValue2
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/NodeCode.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.NodeCode
2 |
3 | open System.Diagnostics
4 | open Benchmarks
5 |
6 | []
7 | type NodeCode<'T> = Node of Async<'T>
8 |
9 | []
10 | type NodeCodeBuilder() =
11 |
12 | static let zero = Node(async.Zero())
13 |
14 | []
15 | member _.Zero() : NodeCode = zero
16 |
17 | []
18 | member _.Delay(f: unit -> NodeCode<'T>) =
19 | Node(async.Delay(fun () -> match f() with Node(p) -> p))
20 |
21 | []
22 | member _.Return(value) = Node(async.Return(value))
23 |
24 | []
25 | member _.ReturnFrom(computation: NodeCode<_>) = computation
26 |
27 | []
28 | member _.Bind(Node(p): NodeCode<'a>, binder: 'a -> NodeCode<'b>) : NodeCode<'b> =
29 | Node(async.Bind(p, fun x -> match binder x with Node p -> p))
30 |
31 | []
32 | member _.TryWith(Node(p): NodeCode<'T>, binder: exn -> NodeCode<'T>) : NodeCode<'T> =
33 | Node(async.TryWith(p, fun ex -> match binder ex with Node p -> p))
34 |
35 | []
36 | member _.TryFinally(Node(p): NodeCode<'T>, binder: unit -> unit) : NodeCode<'T> =
37 | Node(async.TryFinally(p, binder))
38 |
39 | []
40 | member _.For(xs: 'T seq, binder: 'T -> NodeCode) : NodeCode =
41 | Node(async.For(xs, fun x -> match binder x with Node p -> p))
42 |
43 | []
44 | member _.Combine(Node(p1): NodeCode, Node(p2): NodeCode<'T>) : NodeCode<'T> =
45 | Node(async.Combine(p1, p2))
46 |
47 | let node = NodeCodeBuilder()
48 |
49 | open BenchmarkDotNet.Attributes
50 |
51 | []
52 | []
53 | type ReturnBenchmark() =
54 | let value = 1
55 |
56 | let directReturned = node.Return(value)
57 | let ceReturned = node { return value }
58 |
59 | []
60 | member _.DirectReturn() =
61 | node.Return(value)
62 |
63 | []
64 | member _.CompExprReturn() =
65 | node { return value }
66 |
67 | []
68 | member _.AwaitDirectReturned() =
69 | match directReturned with
70 | | Node computation ->
71 | Async.RunSynchronously(computation) |> ignore
72 |
73 | []
74 | member _.AwaitCEReturned() =
75 | match ceReturned with
76 | | Node computation ->
77 | Async.RunSynchronously(computation) |> ignore
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/Program.fs:
--------------------------------------------------------------------------------
1 | open System
2 | open System.Runtime.InteropServices
3 | open BenchmarkDotNet.Configs
4 | open BenchmarkDotNet.Running
5 | open Benchmarks
6 | open Benchmarks.FSharp
7 |
8 | BenchmarkRunner.Run<
9 | // GenericEqualityBenchmark.Benchmark,
10 | // NodeCode.ReturnBenchmark,
11 | // FSharpOptimizer.Benchmark,
12 | // FSharpOptimizerWithExperimentalPipe.Benchmark,
13 | // HttpVerbParsing.Benchmark,
14 | GSeq.Benchmark
15 | >() |> ignore
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/ScanfBenchmark.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.ScanfBenchmark
2 |
3 | open System
4 | open BenchmarkDotNet.Attributes
5 | open BenchmarkDotNet.Jobs
6 | open En3Tho.FSharp.Extensions
7 | open En3Tho.FSharp.Extensions.Scanf
8 |
9 | module Assets = // for CSharp version of bench
10 |
11 | let value1 = ref "0"
12 | let value2 = ref 0
13 | let value3 = ref 0.
14 | let value4 = ref 0.f
15 | let value5 = ref '0'
16 | let value6 = ref false
17 | let value7 = ref 0u
18 |
19 | let manySmallValuesText = $"{value1.Value} {value2.Value} {value3.Value} {value4.Value} {value5.Value} {value6.Value} {value7.Value}"
20 | let manySmallValuesFmt : Printf.StringFormat<_> = $"{value1} {value2} {value3} {value4} {value5} {value6} {value7}"
21 |
22 | let realisticFmt : Printf.StringFormat<_> = $"/authorize {value1} {value2}"
23 |
24 | let cmd = ref ""
25 | let userName = ref ""
26 | let userCode = ref 0
27 | let realisticCommandFmt : Printf.StringFormat<_> = $"/{cmd} {userName} {userCode}"
28 |
29 | let intRef = ref 0
30 | let floatRef = ref 0.
31 | let primitivesOnlyFmt : Printf.StringFormat<_> = $"{intRef} {floatRef}"
32 |
33 | open Assets
34 |
35 | [<
36 | MemoryDiagnoser;
37 | DisassemblyDiagnoser(filters = [||]);
38 | //SimpleJob(RuntimeMoniker.Net60)
39 | Config(typeof<``Net 6, Pgo``>)
40 | >]
41 | type Benchmark() =
42 |
43 | []
44 | member _.ManySmallValues() =
45 | let value1 = ref "0"
46 | let value2 = ref 0
47 | let value3 = ref 0.
48 | let value4 = ref 0.f
49 | let value5 = ref '0'
50 | let value6 = ref false
51 | let value7 = ref 0u
52 |
53 | let text = $"{value1.Value} {value2.Value} {value3.Value} {value4.Value} {value5.Value} {value6.Value} {value7.Value}"
54 | scanf $"{value1} {value2} {value3} {value4} {value5} {value6} {value7}" text
55 |
56 | []
57 | member _.ManySmallValuesTextPreallocated() =
58 | scanf $"{value1} {value2} {value3} {value4} {value5} {value6} {value7}" manySmallValuesText
59 |
60 | []
61 | member _.ManySmallValuesFullyPreallocated() =
62 | scanf manySmallValuesFmt manySmallValuesText
63 |
64 | []
65 | member _.Realistic() =
66 | let value1 = ref "0"
67 | let value2 = ref 0
68 | scanf $"/authorize {value1} {value2}" "/authorize myText 123"
69 |
70 | []
71 | member _.RealisticPreallocated() =
72 | scanf realisticFmt "/authorize myText 123"
73 |
74 | []
75 | member _.RealisticCommand() =
76 | scanf $"/{cmd} {userName} {userCode}" "/authorize myText 123"
77 |
78 | []
79 | member _.RealisticCommandPreallocated() =
80 | scanf realisticCommandFmt "/authorize myText 123"
81 |
82 | // []
83 | // member _.RealisticCommandPreallocatedSpan() =
84 | // scanfSpan realisticCommandFmt ("/authorize myText 123".AsSpan())
85 |
86 | []
87 | member _.PrimitivesOnlyPreallocated() =
88 | scanf primitivesOnlyFmt "123456 123456.123456"
89 |
90 | // []
91 | // member _.PrimitivesOnlyPreallocatedSpan() =
92 | // scanfSpan primitivesOnlyFmt ("123456 123456.123456".AsSpan())
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/TaskBuildersBenchmarks.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.TaskBuildersBenchmarks
2 |
3 | open System.Threading.Tasks
4 | open BenchmarkDotNet.Attributes
5 | open BenchmarkDotNet.Jobs
6 | open En3Tho.FSharp.ComputationExpressions.Tasks
7 |
8 | [<
9 | MemoryDiagnoser;
10 | DisassemblyDiagnoser(filters = [||]);
11 | SimpleJob(RuntimeMoniker.Net60)
12 | >]
13 | type Benchmark() =
14 | let returnSomeValueAsTask x = Task.FromResult(x)
15 | let returnSomeValueAsValueTask x = ValueTask.FromResult(x)
16 | let returnSomeValueAsAsync x = async.Return(x)
17 |
18 | let runVTaskTaskAndAsyncAsTask() = task {
19 | let! x = returnSomeValueAsValueTask 5
20 | let! y = returnSomeValueAsTask 10
21 | let! z = returnSomeValueAsAsync 15
22 | return x + y
23 | }
24 |
25 | let runVTaskOnlyAsTask() = task {
26 | let! x = returnSomeValueAsValueTask 5
27 | let! y = returnSomeValueAsValueTask 10
28 | return x + y
29 | }
30 |
31 | let runVTaskOnlyAsVTask() = vtask {
32 | let! x = returnSomeValueAsValueTask 5
33 | let! y = returnSomeValueAsValueTask 10
34 | return x + y
35 | }
36 |
37 | let runVTaskAndTaskAsTask() = task {
38 | let! x = returnSomeValueAsValueTask 5
39 | let! y = returnSomeValueAsValueTask 10
40 | let! z = returnSomeValueAsTask 15
41 | return x + y + z
42 | }
43 |
44 | let runVTaskAndTaskAsVTask() = vtask {
45 | let! x = returnSomeValueAsValueTask 5
46 | let! y = returnSomeValueAsValueTask 10
47 | let! z = returnSomeValueAsTask 15
48 | return x + y + z
49 | }
50 |
51 | let runVTaskAndTaskAsUnitTask() = utask {
52 | let! x = returnSomeValueAsValueTask 5
53 | let! y = returnSomeValueAsValueTask 10
54 | let! z = returnSomeValueAsTask 15
55 | return ()
56 | }
57 |
58 | let runVTaskAndTaskAsUnitVTask() = uvtask {
59 | let! x = returnSomeValueAsValueTask 5
60 | let! y = returnSomeValueAsValueTask 10
61 | let! z = returnSomeValueAsTask 15
62 | return ()
63 | }
64 |
65 | []
66 | member _.RunVTaskOnlyAsTask() = runVTaskOnlyAsTask().Result
67 |
68 | []
69 | member _.RunVTaskOnlyAsVTask() = runVTaskOnlyAsVTask().Result
70 |
71 | []
72 | member _.RunVTaskAndTaskAsTask() = runVTaskAndTaskAsTask().Result
73 |
74 | []
75 | member _.RunVTaskAndTaskAsUnitTask() = runVTaskAndTaskAsUnitTask().GetAwaiter().GetResult()
76 |
77 | []
78 | member _.RunVTaskAndTaskAsUnitVTask() = runVTaskAndTaskAsUnitVTask().GetAwaiter().GetResult()
79 |
80 | []
81 | member _.RunVTaskTaskAndAsyncAsTask() = runVTaskTaskAndAsyncAsTask().Result
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.FSharp/VectorsBase64.fs:
--------------------------------------------------------------------------------
1 | module Benchmarks.VectorsBase64
2 |
3 | open System
4 | open System.Runtime.CompilerServices
5 | open System.Runtime.InteropServices
6 | open BenchmarkDotNet.Attributes
7 | open En3Tho.FSharp.Extensions
8 |
9 | []
10 | []
11 | type Benchmark() =
12 |
13 | member val Bytes = null with get, set
14 |
15 | []
16 | member this.GlobalSetup() =
17 | this.Bytes <- Array.init 24 (fun _ -> 0xFFuy)
18 |
19 | member _.BytesToBase64VectorLookup1(source: ReadOnlySpan, destination: Span) =
20 | // Convert from
21 | // [ 00000000_66666666_55555555_44444444_00000000_33333333_22222222_11111111 ]
22 | // To
23 | // [ 00666666_00665555_00555544_00444444_00333333_00332222_00222211_00111111 ]
24 |
25 | // [ 00000000_33333333_22222222_11111111 ]
26 | // [ 00333333_00332222_00222211_00111111 ]
27 |
28 |
29 | // [ 00000000_00000000_00000000_00111111 ] // and 0x3F
30 | // [ 00000000_00000000_00000000_00111111 ] // shift 0
31 |
32 | // [ 00000000_00000000_00002222_11000000 ] // and 0xFC0
33 | // [ 00000000_00000000_00222211_00000000 ] // shift 2
34 |
35 | // [ 00000000_00000033_22220000_00000000 ] // and 0x3F000
36 | // [ 00000000_00332222_00000000_00000000 ] // shift 4
37 |
38 | // [ 00000000_33333300_00000000_00000000 ] // and 0xFC0000
39 | // [ 00333333_00000000_00000000_00000000 ] // shift 6
40 |
41 | // ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
42 | let lookup0_31 = v256.Create(0x48_47_46_45_44_43_42_41UL, 0x50_4F_4E_4D_4C_4B_4A_49UL, 0x58_57_56_55_54_53_52_51UL, 0x66_65_64_63_62_61_5A_59UL).u8
43 | let lookup32_63 = v256.Create(0x6E_6D_6C_6B_6A_69_68_67UL, 0x76_75_74_73_72_71_70_6FUL, 0x33_32_31_30_7A_79_78_77UL, 0x2F_2B_39_38_37_36_35_34UL).u8
44 | let valuesU64 = MemoryMarshal.Cast<_, u64>(source)
45 |
46 | let mutable i = 0
47 | while i + 2 < valuesU64.Length do
48 | let v1 = valuesU64[i]
49 | let v2 = valuesU64[i + 1]
50 | let v3 = valuesU64[i + 2]
51 |
52 | let vec = v256.Create(v1, v2, v3, 0UL)
53 |
54 | // [ 00000000_66666666_55555555_44444444_00000000_33333333_22222222_11111111 ]
55 | let shuffled = v256.Shuffle(vec.u8, v256.Create(0xF0_05_04_03_F0_02_01_00UL, 0xF0_0B_0A_09_F0_08_07_06UL, 0xF0_11_10_0F_F0_0E_0D_0CUL, 0xF0_17_16_15_F0_14_13_12UL).u8).u32
56 |
57 | // [ 00666666_00665555_00555544_00444444_00333333_00332222_00222211_00111111 ]
58 | let vec6Bits =
59 | (shuffled &&& v256.Create(0x3Fu))
60 | ||| ((shuffled &&& v256.Create(0xFC0u)) <<< 2)
61 | ||| ((shuffled &&& v256.Create(0x3F000u)) <<< 4)
62 | ||| ((shuffled &&& v256.Create(0xFC0000u)) <<< 6)
63 |
64 | let mask32_63 = v256.GreaterThan(vec6Bits.u8, v256.Create(31uy))
65 | let mask0_31 = v256.Equals(mask32_63, v256.Zero)
66 |
67 | let byteChars0_31 = v256.Shuffle(lookup0_31, vec6Bits.u8 &&& mask0_31)
68 | let byteChars32_63 = v256.Shuffle(lookup32_63, vec6Bits.u8 &&& v256.Create(0x1Fuy) &&& mask32_63)
69 | let byteChars = (byteChars0_31 &&& mask0_31) ||| (byteChars32_63 &&& mask32_63)
70 |
71 | let chars1 = v256.Shuffle(byteChars, v256.Create(0xF0_03_F0_02_F0_01_F0_00UL, 0xF0_07_F0_06_F0_05_F0_04UL, 0xF0_0B_F0_0A_F0_09_F0_08UL, 0xF0_0F_F0_0E_F0_0D_F0_0CUL).u8)
72 | let chars2 = v256.Shuffle(byteChars, v256.Create(0xF0_13_F0_12_F0_11_F0_10UL, 0xF0_17_F0_16_F0_15_F0_14UL, 0xF0_1B_F0_1A_F0_19_F0_18UL, 0xF0_1F_F0_1E_F0_1D_F0_1CUL).u8)
73 |
74 | v256.StoreUnsafe(chars1.u16, &Unsafe.As<_, u16>(&destination[i * 32]))
75 | v256.StoreUnsafe(chars2.u16, &Unsafe.As<_, u16>(&destination[i * 32 + 16]))
76 |
77 | i <- i + 3
78 |
79 | []
80 | member this.ConvertToBase64() =
81 | Convert.ToBase64String(this.Bytes) |> ignore
82 |
83 | []
84 | member this.ConvertToBase64Naive() =
85 | let converted = Array.zeroCreate 32
86 | this.BytesToBase64VectorLookup1(this.Bytes, converted.AsSpan())
--------------------------------------------------------------------------------
/benchmarks/Benchmarks.Lib/Benchmarks.Lib.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.0",
4 | "rollForward": "latestMinor",
5 | "allowPrerelease": false
6 | }
7 | }
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection.XUnit/En3Tho.Extensions.DependencyInjection.XUnit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 8.0.1
5 | Small set of extensions based on XUnit.DependencyInjection library
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection.XUnit/IServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Hosting;
3 |
4 | namespace En3Tho.Extensions.DependencyInjection.XUnit;
5 |
6 | public interface IXunitStartup
7 | {
8 | IHostBuilder? CreateHostBuilder() => null;
9 | void ConfigureHost(IHostBuilder hostBuilder) { }
10 | void ConfigureServices(IServiceCollection serviceCollection, HostBuilderContext hostBuilderContext) { }
11 | }
12 |
13 | public static class IServiceCollectionExtensions
14 | {
15 | public static IServiceCollection AddMockSingleton(this IServiceCollection collection)
16 | where TService : class
17 | {
18 | collection.AddSingleton>()
19 | .AddSingleton(services => services.GetRequiredService>().Object);
20 | return collection;
21 | }
22 |
23 | public static IServiceCollection AddMockScoped(this IServiceCollection collection)
24 | where TService : class
25 | {
26 | collection.AddScoped>()
27 | .AddScoped(services => services.GetRequiredService>().Object);
28 | return collection;
29 | }
30 | }
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection/AddFromConfiguration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Configuration;
2 | using Microsoft.Extensions.DependencyInjection;
3 | using Microsoft.Extensions.DependencyInjection.Extensions;
4 |
5 | namespace En3Tho.Extensions.DependencyInjection;
6 |
7 | public static partial class IServiceCollectionExtensions
8 | {
9 | public static IServiceCollection TryAddSingletonFromConfiguration(this IServiceCollection collection, string name)
10 | where TService : class
11 | {
12 | collection.TryAddSingleton(services =>
13 | services.GetRequiredService()
14 | .GetSection(name)
15 | .Get()!);
16 | return collection;
17 | }
18 |
19 | public static IServiceCollection TryAddSingletonFromConfiguration(this IServiceCollection collection)
20 | where TService : class
21 | {
22 | var name = typeof(TService).Name;
23 | return collection.TryAddSingletonFromConfiguration(name);
24 | }
25 |
26 | public static IServiceCollection TryAddSingletonFromConfigurationOrFail(this IServiceCollection collection, string name)
27 | where TService : class
28 | {
29 | return collection.TryAddSingletonOrFail(services =>
30 | services.GetRequiredService()
31 | .GetSection(name)
32 | .Get()!);
33 | }
34 |
35 | public static IServiceCollection TryAddSingletonFromConfigurationOrFail(this IServiceCollection collection)
36 | where TService : class
37 | {
38 | var name = typeof(TService).Name;
39 | return collection.TryAddSingletonFromConfigurationOrFail(name);
40 | }
41 | }
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection/AddHttpClient.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace En3Tho.Extensions.DependencyInjection;
4 |
5 | public static partial class IServiceCollectionExtensions
6 | {
7 | public static IServiceCollection AddHttpClient(this IServiceCollection collection, Uri uri)
8 | where TClient : class
9 | {
10 | collection.AddHttpClient(client => client.BaseAddress = uri);
11 | return collection;
12 | }
13 | }
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection/ChangeLifetime.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace En3Tho.Extensions.DependencyInjection;
4 |
5 | public static partial class IServiceCollectionExtensions
6 | {
7 | private static bool TryChangeLifetimeInternal(this IServiceCollection collection, ServiceLifetime newLifetime)
8 | {
9 | var changed = false;
10 |
11 | for (var i = 0; i < collection.Count; i++)
12 | {
13 | var descriptor = collection[i];
14 | if (descriptor.ServiceType.Equals(typeof(TService))
15 | && descriptor.TryChangeLifetime(newLifetime, out var newDescriptor))
16 | {
17 | collection[i] = newDescriptor;
18 | changed = true;
19 | }
20 | }
21 |
22 | return changed;
23 | }
24 |
25 | public static IServiceCollection TryChangeLifetime(this IServiceCollection collection,
26 | ServiceLifetime newLifetime)
27 | {
28 | collection.TryChangeLifetimeInternal(newLifetime);
29 | return collection;
30 | }
31 |
32 | public static IServiceCollection TryChangeLifetimeOrFail(this IServiceCollection collection,
33 | ServiceLifetime newLifetime)
34 | {
35 | if (collection.TryChangeLifetimeInternal(newLifetime))
36 | return collection;
37 |
38 | throw new InvalidOperationException("Service type is either not found in collection or is registered as single object instance");
39 | }
40 | }
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection/Configuration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace En3Tho.Extensions.DependencyInjection;
4 |
5 | public static partial class IServiceCollectionExtensions
6 | {
7 | public static IServiceCollection ConfigureByName(this IServiceCollection services) where TOptions : class
8 | => services.Configure(typeof(TOptions).Name, _ => {});
9 |
10 | public static IServiceCollection ConfigureByName(this IServiceCollection services, Action configure) where TOptions : class
11 | => services.Configure(typeof(TOptions).Name, configure);
12 | }
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection/En3Tho.Extensions.DependencyInjection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 8.0.1
5 | Extensions for IServiceCollection
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/En3Tho.Extensions.DependencyInjection/Update.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace En3Tho.Extensions.DependencyInjection;
4 |
5 | public static partial class IServiceCollectionExtensions
6 | {
7 | public static IServiceCollection Update(this IServiceCollection collection, Func configureFactory)
8 | where TService : class
9 | {
10 | if (!collection.TryFindServiceDescriptor(out var possibleDescriptor))
11 | throw new InvalidOperationException(
12 | $"Service {typeof(TService).FullName} was not found among registered services");
13 |
14 | var (oldDescriptor, oldDescriptorIndex) = possibleDescriptor;
15 |
16 | ServiceDescriptor newDescriptor;
17 |
18 | switch (oldDescriptor)
19 | {
20 | // case of Add(x => instance)
21 | case { ImplementationFactory: { } factory }:
22 | var factoryFromFactory = (IServiceProvider serviceProvider) =>
23 | {
24 | var oldImplementationInstance = factory(serviceProvider);
25 | return configureFactory((TService)oldImplementationInstance);
26 | };
27 |
28 | newDescriptor = new(typeof(TService), factoryFromFactory, oldDescriptor.Lifetime);
29 | break;
30 |
31 | // case of Add(instance)
32 | case { ImplementationInstance: { } instance }:
33 | var factoryFromInstance = (IServiceProvider serviceProvider) => configureFactory((TService)instance);
34 | newDescriptor = new(typeof(TService), factoryFromInstance, oldDescriptor.Lifetime);
35 | break;
36 |
37 | // case of Add()
38 | default:
39 | var implementationFactory = ActivatorUtilities.CreateFactory(typeof(TService), Array.Empty());
40 | var factoryFromServices = (IServiceProvider serviceProvider) =>
41 | {
42 | var oldImplementationInstance = implementationFactory(serviceProvider, Array.Empty