Compare commits

...

73 Commits

Author SHA1 Message Date
dependabot[bot] 61e8fdae14 Bump microsoft/setup-msbuild from 1.0.1 to 1.3.1
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1.0.1 to 1.3.1.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/main/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1.0.1...v1.3.1)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 10:11:00 +00:00
dependabot[bot] a25f2e5acb Bump FluentAssertions from 6.7.0 to 6.9.0 (#41)
Bumps [FluentAssertions](https://github.com/fluentassertions/fluentassertions) from 6.7.0 to 6.9.0.
- [Release notes](https://github.com/fluentassertions/fluentassertions/releases)
- [Changelog](https://github.com/fluentassertions/fluentassertions/blob/develop/AcceptApiChanges.ps1)
- [Commits](https://github.com/fluentassertions/fluentassertions/compare/6.7.0...6.9.0)

---
updated-dependencies:
- dependency-name: FluentAssertions
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-25 21:37:09 +01:00
dependabot[bot] 8e64309c2e Bump actions/setup-dotnet from 2 to 3 (#32)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 2 to 3.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-11 12:16:48 +02:00
dependabot[bot] 24c3e37b5e Bump Microsoft.NET.Test.Sdk from 17.3.1 to 17.3.2 (#31)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.1 to 17.3.2.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.3.1...v17.3.2)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-27 11:44:21 +02:00
amacocian f6435b24ab Increment dependency injection version (#29) 2022-09-12 19:28:10 +02:00
amacocian 9118c864bf Make httpclient use a factory to create handlers (#28) 2022-09-12 17:22:55 +00:00
amacocian fb84036215 Bump slim to 1.9.2 (#27)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>

Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2022-09-12 09:59:05 +00:00
amacocian f6fd9ec616 Update Slim (#26) 2022-09-09 15:05:48 +00:00
dependabot[bot] 6a10fe91e3 Bump actions/checkout from 2 to 3 (#22)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 14:20:27 +00:00
dependabot[bot] e890b21bb6 Bump actions/setup-dotnet from 1 to 2 (#21)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 1 to 2.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v1...v2)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 14:12:59 +00:00
dependabot[bot] e36e6605ff Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.3.1 (#23)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.2.0 to 17.3.1.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Commits](https://github.com/microsoft/vstest/compare/v17.2.0...v17.3.1)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 16:06:51 +02:00
dependabot[bot] 5e8e772d36 Bump Moq from 4.18.1 to 4.18.2 (#24)
Bumps [Moq](https://github.com/moq/moq4) from 4.18.1 to 4.18.2.
- [Release notes](https://github.com/moq/moq4/releases)
- [Changelog](https://github.com/moq/moq4/blob/main/CHANGELOG.md)
- [Commits](https://github.com/moq/moq4/compare/v4.18.1...v4.18.2)

---
updated-dependencies:
- dependency-name: Moq
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-06 15:58:34 +02:00
amacocian e6a06915cd Improve IHttpClient factories (#20)
Codestyle fixes
Setup CODEOWNERS
Setup dependabot
2022-09-06 15:49:07 +02:00
amacocian 3c741ab969 Make AesEncrypter disposable (#19) 2022-06-07 12:49:39 +02:00
amacocian a46fde652d Expose Aes object in AesEncrypter constructor (#18) 2022-06-07 12:26:08 +02:00
amacocian ecc65a4302 Update dependencies for NetStandard.DependencyInjection (#17) 2022-06-02 11:35:08 +02:00
amacocian 7222528eed Update dependencies (#16)
* Update dependencies
Implement AsyncLazy
Fix namespaces
Fix CD pipeline

* Fix code issues
Fix Int64BitStruct test
2022-06-02 10:42:34 +02:00
amacocian 41b1b48f98 Remove null check for flow id in scoped loggers (#15) 2022-01-23 21:13:37 +01:00
amacocian 721b444dda Scoped logger (#14)
Logging extensions
2022-01-16 15:14:23 +01:00
amacocian 1c7d3a2be2 Adjust project descriptions (#12)
* Adjust package description
2022-01-02 16:54:15 +02:00
amacocian 0a30872781 Introduce .netcore extensions (#11)
* Introduce .netcore extensions
Implement ArgumentNullException for .net6 and c#10

* Update pipelines to install .netcore 6

* Update test projects to .net 6.0
2022-01-02 00:10:49 +02:00
Alexandru Macocian 7eed1ca466 Unify namespace naming 2021-11-15 22:21:46 +01:00
Alexandru Macocian b6cc64fd51 Update dependencies 2021-11-15 22:08:24 +01:00
Alexandru Macocian c8bfaffb42 Fix namespaces 2021-11-15 22:04:18 +01:00
Alexandru Macocian bf163251dd Password hashing implementation 2021-11-15 21:52:39 +01:00
Alexandru Macocian c0b5734b71 Add extensions to simplify CVLoggerFactory usage 2021-11-01 16:18:10 +01:00
Alexandru Macocian 2944d9887e Update dependencies 2021-10-02 07:45:42 +02:00
amacocian 57925151fa Update dependencies (#10)
Fix code style
2021-09-16 13:56:05 +02:00
amacocian d20e5bd308 Fix CV extension (#9) 2021-08-18 16:34:54 +02:00
amacocian c9d1ba8773 Merge pull request #8 from AlexMacocian/alexmacocian/extend-cryptorngprovider
Extend CryptoRngProvider api
2021-07-15 16:33:28 +03:00
Alexandru Macocian ce3cac9fa2 Extend CryptoRngProvider api 2021-07-15 16:32:40 +03:00
amacocian a2d23ed716 Merge pull request #7 from AlexMacocian/alexmacocian/rngprovider-securestring
Implementation of SecureString and CryptoRngProvider
2021-07-15 16:19:32 +03:00
Alexandru Macocian 7660893087 Remove UTs expecting exception 2021-07-15 16:15:17 +03:00
Alexandru Macocian 68ec8f983d Implementation of SecureString and CryptoRngProvider 2021-07-15 16:11:05 +03:00
amacocian 1711dd2ec2 Merge pull request #6 from AlexMacocian/alexmacocian/serialization-extensions
Serialize and Deserialize extensions
2021-07-15 12:46:48 +03:00
Alexandru Macocian 3b67e45e64 Bump version 2021-07-15 12:41:47 +03:00
Alexandru Macocian 22fa8eccf7 Serialize and Deserialize extensions 2021-07-15 12:41:18 +03:00
amacocian 9609b2912c Merge pull request #5 from AlexMacocian/alexmacocian/security-package
SystemExtensions Security
2021-07-14 12:22:53 +03:00
Alexandru Macocian a2e99cb263 SystemExtensions Security 2021-07-14 12:17:31 +03:00
amacocian f9088a5aef Merge pull request #4 from AlexMacocian/alexmacocian/move-logger-resolver
Move logger resolver to System.Logging
2021-07-10 13:06:35 +03:00
Alexandru Macocian d67ee95916 Fix UTs 2021-07-10 13:02:54 +03:00
Alexandru Macocian 8bfb8f1757 Move logger resolver to System.Logging
Nit fixes
2021-07-10 13:01:47 +03:00
amacocian a6572a1d15 Merge pull request #3 from AlexMacocian/alexmacocian/move-di-config-namespace
Move configuration namespace to system
2021-07-10 11:29:20 +03:00
Alexandru Macocian 646b6dc253 Move configuration namespace to system 2021-07-10 11:24:37 +03:00
amacocian 0b2a76143b Merge pull request #2 from AlexMacocian/alexmacocian/di-extensions
Dependency Injection extensions
2021-07-10 11:15:48 +03:00
Alexandru Macocian 019d7e240d Stop CD pipeline from running on PR 2021-07-10 11:04:31 +03:00
Alexandru Macocian 7a764a5929 Dependency Injection extensions 2021-07-10 10:59:27 +03:00
amacocian 5aaa0d318a Merge pull request #1 from AlexMacocian/alexmacocian/add-pipelines
Added CI&CD pipelines
2021-06-02 13:04:46 +02:00
Alexandru Macocian 5767ccfd70 Fix misnaming in CI pipeline
Disable CD pipeline for PRs
2021-06-02 13:02:01 +02:00
Alexandru Macocian 16a9208e07 Added CI&CD pipelines
Changed test project to .net5
Changed ThreadPool to use CancellationTokens
2021-06-02 12:58:04 +02:00
Alexandru Macocian 3038661f6d Mockable http client implementation. 2021-05-13 13:55:17 +02:00
Alexandru Macocian 035e58248e Updated to 1.1.4 2021-04-23 10:52:13 +02:00
Alexandru Macocian 0d418ac9bf Merge branch 'master' of https://github.com/AlexMacocian/SystemExtensions 2021-04-23 10:49:50 +02:00
Alexandru Macocian 0288b385a0 String IsNullOrWhiteSpace and IsNullOrEmpty extensions. 2021-04-23 10:49:41 +02:00
Alexandru Macocian 5d442580c3 Add linq extension Do 2021-04-03 14:44:17 +02:00
Alexandru Macocian ee341af2c3 Read until extension. 2021-04-01 14:43:18 +02:00
Alexandru Macocian 8d1355ecbc Include repository 2021-04-01 12:49:44 +02:00
Alexandru Macocian c04b6ba906 Include author name 2021-04-01 12:49:04 +02:00
Alexandru Macocian 9a7125fbb0 Added various extensions.
Added coding style helpers.
Unit tests.
2021-04-01 12:38:26 +02:00
Alexandru Macocian 66b766326b Use license file from repo 2021-03-26 12:00:53 +01:00
Alexandru Macocian c8a98a0494 Merge 2021-03-26 11:59:12 +01:00
Alexandru Macocian f39e8cf89e Port to netstandard.
Nit fixes.
Fixed e2e tests.
Added MIT license.
2021-03-26 11:58:07 +01:00
Alexandru Macocian 0b5e93f9ea Changed package id 2020-07-27 11:21:22 +02:00
Alexandru Macocian 8fd22b3644 Restructured project to new project style
Targeted netstandard 2.0
Built nuget package
2020-07-27 11:18:56 +02:00
amacocian f6069bd3aa Bit field structures. 2019-12-09 10:44:16 +01:00
amacocian 89159e6cba Merge branch 'master' of https://github.com/AlexMacocian/SystemExtensions 2019-09-16 08:41:45 +03:00
amacocian a608b1ac95 changed proj details 2019-09-16 08:41:33 +03:00
amacocian 2ebd821bbd Cleaned code and upgraded framework to 4.7.2. 2019-07-19 11:10:34 +03:00
amacocian f0a5d509ea Implemented another way to get current process filename 2019-07-15 10:47:09 +03:00
amacocian 9003157a0f Changed elevateprocess way of getting the filename 2019-07-15 10:42:45 +03:00
amacocian 47c1e67f38 Implemented basic application functionality and security. 2019-07-15 10:36:50 +03:00
amacocian 5cb4b3a512 Removed a mistake in code 2019-07-11 09:49:00 +03:00
amacocian e8c9e682ed Marked class as sealed 2019-07-11 09:46:22 +03:00
133 changed files with 10021 additions and 3991 deletions
+129
View File
@@ -0,0 +1,129 @@
[*.{cs,vb}]
# IDE0003: Remove qualification
dotnet_diagnostic.IDE0003.severity = error
dotnet_style_operator_placement_when_wrapping = beginning_of_line
tab_width = 4
indent_size = 4
end_of_line = crlf
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:error
dotnet_style_prefer_auto_properties = true:error
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
dotnet_style_namespace_match_folder = true:suggestion
[*.cs]
csharp_indent_labels = one_less_than_current
csharp_space_around_binary_operators = before_and_after
csharp_using_directive_placement = outside_namespace:error
csharp_prefer_simple_using_statement = true:error
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = file_scoped:error
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_throw_expression = true:suggestion
csharp_style_prefer_null_check_over_type_check = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_prefer_local_over_anonymous_function = true:error
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
csharp_style_prefer_tuple_swap = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_deconstructed_variable_declaration = true:error
csharp_style_unused_value_assignment_preference = discard_variable:error
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
csharp_prefer_static_local_function = true:suggestion
csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false:suggestion
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
csharp_style_conditional_delegate_call = true:suggestion
csharp_style_prefer_parameter_null_checking = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
csharp_style_prefer_pattern_matching = true:silent
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_prefer_not_pattern = true:suggestion
csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
[*.{cs,vb}]
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_readonly_field = true:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
dotnet_style_allow_multiple_blank_lines_experimental = true:silent
dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion
dotnet_code_quality_unused_parameters = all:error
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:suggestion
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
dotnet_style_qualification_for_field = true:error
dotnet_style_qualification_for_property = true:error
dotnet_style_qualification_for_method = true:error
dotnet_style_qualification_for_event = true:error
+1
View File
@@ -0,0 +1 @@
* amacocian@yahoo.com
+16
View File
@@ -0,0 +1,16 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "github-actions" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "daily"
+71
View File
@@ -0,0 +1,71 @@
name: SystemExtensions CD Pipeline
on:
push:
branches:
- master
jobs:
build:
environment: Default
strategy:
matrix:
targetplatform: [x64]
runs-on: windows-latest
env:
Configuration: Release
Solution_Path: SystemExtensions.sln
Test_Project_Path: SystemExtensions.Tests\SystemExtensions.NetStandard.Tests.csproj
DI_Test_Project_Path: SystemExtensions.DependencyInjection.Tests\SystemExtensions.NetStandard.DependencyInjection.Tests.csproj
Source_Project_Path: SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj
Core_Project_Path: SystemExtensions.NetCore\SystemExtensions.NetCore.csproj
Actions_Allow_Unsecure_Commands: true
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.3.1
- name: Restore project
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
env:
RuntimeIdentifier: win-${{ matrix.targetplatform }}
- name: Build SystemExtensions.NetStandard project
run: dotnet build SystemExtensions.NetStandard -c $env:Configuration
- name: Build SystemExtensions.NetCore project
run: dotnet build SystemExtensions.NetCore -c $env:Configuration
- name: Build SystemExtensions.NetStandard.DependencyInjection project
run: dotnet build SystemExtensions.NetStandard.DependencyInjection -c $env:Configuration
- name: Build SystemExtensions.NetStandard.Security project
run: dotnet build SystemExtensions.NetStandard.Security -c $env:Configuration
- name: Package SystemExtensions.NetStandard
run: dotnet pack -c Release -o . SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj
- name: Package SystemExtensions.NetCore
run: dotnet pack -c Release -o . SystemExtensions.NetCore\SystemExtensions.NetCore.csproj
- name: Package SystemExtensions.NetStandard.DependencyInjection
run: dotnet pack -c Release -o . SystemExtensions.NetStandard.DependencyInjection\SystemExtensions.NetStandard.DependencyInjection.csproj
- name: Package SystemExtensions.NetStandard.Security
run: dotnet pack -c Release -o . SystemExtensions.NetStandard.Security\SystemExtensions.NetStandard.Security.csproj
- name: Publish
run: dotnet nuget push *.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
+56
View File
@@ -0,0 +1,56 @@
name: SystemExtensions CI Pipeline
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
strategy:
matrix:
targetplatform: [x64]
runs-on: windows-latest
env:
Solution_Path: SystemExtensions.sln
Test_Project_Path: SystemExtensions.Tests\SystemExtensions.NetStandard.Tests.csproj
DI_Test_Project_Path: SystemExtensions.DependencyInjection.Tests\SystemExtensions.NetStandard.DependencyInjection.Tests.csproj
Sec_Test_Project_Path: SystemExtensions.NetStandard.Security.Tests\SystemExtensions.NetStandard.Security.Tests.csproj
Source_Project_Path: SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj
Actions_Allow_Unsecure_Commands: true
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v3
with:
dotnet-version: '6.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.3.1
- name: Execute Unit Tests
run: dotnet test $env:Test_Project_Path
- name: Execute DI Unit Tests
run: dotnet test $env:DI_Test_Project_Path
- name: Execute Security Unit Tests
run: dotnet test $env:Sec_Test_Project_Path
- name: Restore Project
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier
env:
Configuration: Debug
RuntimeIdentifier: win-${{ matrix.targetplatform }}
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2019 Macocian Alexandru Victor
Copyright (c) 2021 Macocian Alexandru Victor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,25 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Configuration;
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
[TestClass]
public class DefaultOptionsManagerTests
{
private readonly DefaultOptionsManager optionsManager = new();
[TestMethod]
public void GetOptions_ReturnsDefault()
{
var options = this.optionsManager.GetOptions<string>();
options.Should().BeNull();
}
[TestMethod]
public void UpdateOptions_Succeeds()
{
this.optionsManager.UpdateOptions(string.Empty);
}
}
@@ -0,0 +1,5 @@
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
public sealed class DummyOptions
{
}
@@ -0,0 +1,68 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Configuration;
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
[TestClass]
public class LiveOptionsResolverTests
{
private readonly LiveOptionsResolver liveOptionsResolver = new();
private readonly Mock<Slim.IServiceProvider> serviceProviderMock = new();
private readonly Mock<IOptionsManager> optionsManagerMock = new();
[TestMethod]
public void CanResolve_ILiveOptions_ReturnsTrue()
{
var type = typeof(ILiveOptions<string>);
var canResolve = this.liveOptionsResolver.CanResolve(type);
canResolve.Should().BeTrue();
}
[TestMethod]
public void CanResolve_AnythingElse_ReturnsFalse()
{
var types = new Type[] { typeof(object), typeof(string), typeof(LiveOptionsResolverTests), typeof(int) };
foreach (var type in types)
{
var canResolve = this.liveOptionsResolver.CanResolve(type);
canResolve.Should().BeFalse();
}
}
[TestMethod]
public void Resolve_ILiveOptions_ReturnsILiveOptions()
{
this.SetupServiceProvider();
var liveOptions = this.liveOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(ILiveOptions<string>));
liveOptions.Should().BeAssignableTo<ILiveOptions<string>>();
}
[TestMethod]
public void Resolve_AnythingElse_Throws()
{
this.SetupServiceProvider();
Action action = new(() =>
{
this.liveOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string));
});
action.Should().Throw<Exception>();
}
private void SetupServiceProvider()
{
this.serviceProviderMock
.Setup(u => u.GetService<IOptionsManager>())
.Returns(this.optionsManagerMock.Object);
}
}
@@ -0,0 +1,68 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Configuration;
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
[TestClass]
public class LiveUpdateableOptionsResolverTests
{
private readonly LiveUpdateableOptionsResolver liveUpdateableOptionsResolver = new();
private readonly Mock<Slim.IServiceProvider> serviceProviderMock = new();
private readonly Mock<IOptionsManager> optionsManagerMock = new();
[TestMethod]
public void CanResolve_ILiveUpdateableOptions_ReturnsTrue()
{
var type = typeof(ILiveUpdateableOptions<string>);
var canResolve = this.liveUpdateableOptionsResolver.CanResolve(type);
canResolve.Should().BeTrue();
}
[TestMethod]
public void CanResolve_AnythingElse_ReturnsFalse()
{
var types = new Type[] { typeof(object), typeof(string), typeof(LiveOptionsResolverTests), typeof(int) };
foreach (var type in types)
{
var canResolve = this.liveUpdateableOptionsResolver.CanResolve(type);
canResolve.Should().BeFalse();
}
}
[TestMethod]
public void Resolve_ILiveUpdateableOptions_ReturnsILiveUpdateableOptions()
{
this.SetupServiceProvider();
var liveOptions = this.liveUpdateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(ILiveUpdateableOptions<string>));
liveOptions.Should().BeAssignableTo<ILiveUpdateableOptions<string>>();
}
[TestMethod]
public void Resolve_AnythingElse_Throws()
{
this.SetupServiceProvider();
Action action = new(() =>
{
this.liveUpdateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string));
});
action.Should().Throw<Exception>();
}
private void SetupServiceProvider()
{
this.serviceProviderMock
.Setup(u => u.GetService<IOptionsManager>())
.Returns(this.optionsManagerMock.Object);
}
}
@@ -0,0 +1,43 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Configuration;
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
[TestClass]
public class LiveUpdateableOptionsWrapperTests
{
private LiveUpdateableOptionsWrapper<string> optionsWrapper;
private readonly Mock<IOptionsManager> optionsManagerMock = new();
[TestInitialize]
public void TestInitialize()
{
this.optionsWrapper = new LiveUpdateableOptionsWrapper<string>(this.optionsManagerMock.Object);
}
[TestMethod]
public void GetValue_ReturnsValue()
{
this.optionsManagerMock
.Setup(u => u.GetOptions<string>())
.Returns("hello");
var value = this.optionsWrapper.Value;
value.Should().Be("hello");
}
[TestMethod]
public void UpdateOption_CallsOptionsManager()
{
this.optionsManagerMock
.Setup(u => u.UpdateOptions<string>(It.IsAny<string>()))
.Verifiable();
this.optionsWrapper.UpdateOption();
this.optionsManagerMock.Verify();
}
}
@@ -0,0 +1,70 @@
using FluentAssertions;
using Microsoft.Extensions.Options;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Configuration;
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
[TestClass]
public class OptionsResolverTests
{
private readonly OptionsResolver optionsResolver = new();
private readonly Mock<Slim.IServiceProvider> serviceProviderMock = new();
public Mock<IOptionsManager> OptionsManagerMock { get; } = new();
[TestMethod]
public void CanResolve_ILiveOptions_ReturnsTrue()
{
var type = typeof(IOptions<string>);
var canResolve = this.optionsResolver.CanResolve(type);
canResolve.Should().BeTrue();
}
[TestMethod]
public void CanResolve_AnythingElse_ReturnsFalse()
{
var types = new Type[] { typeof(object), typeof(string), typeof(OptionsResolverTests), typeof(int) };
foreach (var type in types)
{
var canResolve = this.optionsResolver.CanResolve(type);
canResolve.Should().BeFalse();
}
}
[TestMethod]
public void Resolve_IOptions_ReturnsIOptions()
{
this.SetupServiceProvider();
var liveOptions = this.optionsResolver.Resolve(this.serviceProviderMock.Object, typeof(IOptions<string>));
liveOptions.Should().BeAssignableTo<IOptions<string>>();
}
[TestMethod]
public void Resolve_AnythingElse_Throws()
{
this.SetupServiceProvider();
Action action = new(() =>
{
this.optionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string));
});
action.Should().Throw<Exception>();
}
private void SetupServiceProvider()
{
this.serviceProviderMock
.Setup(u => u.GetService<IOptionsManager>())
.Returns(this.OptionsManagerMock.Object);
}
}
@@ -0,0 +1,68 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Configuration;
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
[TestClass]
public class UpdateableOptionsResolverTests
{
private readonly UpdateableOptionsResolver updateableOptionsResolver = new();
private readonly Mock<Slim.IServiceProvider> serviceProviderMock = new();
private readonly Mock<IOptionsManager> optionsManagerMock = new();
[TestMethod]
public void CanResolve_ILiveOptions_ReturnsTrue()
{
var type = typeof(IUpdateableOptions<string>);
var canResolve = this.updateableOptionsResolver.CanResolve(type);
canResolve.Should().BeTrue();
}
[TestMethod]
public void CanResolve_AnythingElse_ReturnsFalse()
{
var types = new Type[] { typeof(object), typeof(string), typeof(UpdateableOptionsResolverTests), typeof(int) };
foreach (var type in types)
{
var canResolve = this.updateableOptionsResolver.CanResolve(type);
canResolve.Should().BeFalse();
}
}
[TestMethod]
public void Resolve_IUpdateableOptions_ReturnsIUpdateableOptions()
{
this.SetupServiceProvider();
var liveOptions = this.updateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(IUpdateableOptions<string>));
liveOptions.Should().BeAssignableTo<IUpdateableOptions<string>>();
}
[TestMethod]
public void Resolve_AnythingElse_Throws()
{
this.SetupServiceProvider();
Action action = new(() =>
{
this.updateableOptionsResolver.Resolve(this.serviceProviderMock.Object, typeof(string));
});
action.Should().Throw<Exception>();
}
private void SetupServiceProvider()
{
this.serviceProviderMock
.Setup(u => u.GetService<IOptionsManager>())
.Returns(this.optionsManagerMock.Object);
}
}
@@ -0,0 +1,47 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Configuration;
using System.Extensions.Configuration;
namespace SystemExtensions.DependencyInjection.Tests.Configuration;
[TestClass]
public class UpdateableOptionsWrapperTests
{
private const string Value = "hello";
private UpdateableOptionsWrapper<string> optionsWrapper;
private readonly Mock<IOptionsManager> optionsManagerMock = new();
[TestInitialize]
public void TestInitialize()
{
this.optionsWrapper = new UpdateableOptionsWrapper<string>(this.optionsManagerMock.Object, Value);
}
[TestMethod]
public void GetValue_ReturnsValue()
{
this.optionsManagerMock
.Setup(u => u.GetOptions<string>())
.Throws<Exception>();
var value = this.optionsWrapper.Value;
value.Should().Be(Value);
}
[TestMethod]
public void UpdateOption_CallsOptionsManager()
{
this.optionsManagerMock
.Setup(u => u.UpdateOptions<string>(It.IsAny<string>()))
.Verifiable();
this.optionsWrapper.UpdateOption();
this.optionsManagerMock.Verify();
}
}
@@ -0,0 +1,198 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Slim;
using System;
using System.DependencyInjection.Http;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using SystemExtensions.NetStandard.DependencyInjection.Tests.Http.Models;
namespace SystemExtensions.NetStandard.DependencyInjection.Tests.Http;
[TestClass]
public sealed class HttpClientBuilderTests
{
private const string SomeHeader = "SomeHeader";
private const string SomeValue = "SomeValue";
private readonly HttpClientBuilder<object> httpClientBuilder;
private readonly Mock<IServiceProducer> serviceProducerMock = new();
private readonly Uri baseAddress = new("http://contoso.co");
public HttpClientBuilderTests()
{
this.httpClientBuilder = new HttpClientBuilder<object>(this.serviceProducerMock.Object);
}
[TestMethod]
public void Constructor_NullServiceProducer_Throws()
{
var action = () => new HttpClientBuilder<object>(null);
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WithMessageHandler_NullHandler_Throws()
{
var action = () => this.httpClientBuilder.WithMessageHandler(null);
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WithBaseAddress_NullBaseAddress_Throws()
{
var action = () => this.httpClientBuilder.WithBaseAddress(null);
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WithDefaultRequestHeadersSetup_NullSetup_Throws()
{
var action = () => this.httpClientBuilder.WithDefaultRequestHeadersSetup(null);
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void Build_ReturnsServiceProducer()
{
var producer = this.httpClientBuilder.Build();
producer.Should().Be(this.serviceProducerMock.Object);
}
[TestMethod]
public void Build_RegistersWithServiceProducer()
{
this.serviceProducerMock.Setup(u => u.RegisterScoped(It.IsAny<Func<Slim.IServiceProvider, IHttpClient<object>>>(), false));
this.httpClientBuilder.Build();
this.serviceProducerMock.Verify();
}
[TestMethod]
public void Build_CreatesExpectedClient()
{
this.serviceProducerMock.Setup(u => u.RegisterScoped(It.IsAny<Func<Slim.IServiceProvider, IHttpClient<object>>>(), false))
.Callback<Func<Slim.IServiceProvider, IHttpClient<object>>, bool>((factory, _) =>
{
var client = factory(new Mock<Slim.IServiceProvider>().Object);
client.BaseAddress.Should().BeNull();
client.DefaultRequestHeaders.Should().BeEmpty();
client.MaxResponseContentBufferSize.Should().Be(2147483647L);
client.Timeout.Should().Be(TimeSpan.FromSeconds(100));
});
this.httpClientBuilder.Build();
}
[TestMethod]
public void Build_WithBaseAddress_ReturnsClientWithBaseAddress()
{
this.serviceProducerMock.Setup(u => u.RegisterScoped(It.IsAny<Func<Slim.IServiceProvider, IHttpClient<object>>>(), false))
.Callback<Func<Slim.IServiceProvider, IHttpClient<object>>, bool>((factory, _) =>
{
var client = factory(new Mock<Slim.IServiceProvider>().Object);
client.BaseAddress.Should().Be(this.baseAddress);
});
this.httpClientBuilder.WithBaseAddress(this.baseAddress)
.Build();
}
[TestMethod]
public void Build_WithDefaultRequestHeaders_CallsFactory()
{
this.serviceProducerMock.Setup(u => u.RegisterScoped(It.IsAny<Func<Slim.IServiceProvider, IHttpClient<object>>>(), false))
.Callback<Func<Slim.IServiceProvider, IHttpClient<object>>, bool>((factory, _) =>
{
var client = factory(new Mock<Slim.IServiceProvider>().Object);
client.DefaultRequestHeaders.TryGetValues(SomeHeader, out var values);
values.Should().HaveCount(1);
values.FirstOrDefault().Should().Be(SomeValue);
});
this.httpClientBuilder.WithDefaultRequestHeadersSetup((headers) =>
{
headers.TryAddWithoutValidation(SomeHeader, SomeValue);
}).Build();
}
[TestMethod]
public void Build_WithMaxResponseBufferSize_ReturnsClientWithMaxResponseBufferSize()
{
this.serviceProducerMock.Setup(u => u.RegisterScoped(It.IsAny<Func<Slim.IServiceProvider, IHttpClient<object>>>(), false))
.Callback<Func<Slim.IServiceProvider, IHttpClient<object>>, bool>((factory, _) =>
{
var client = factory(new Mock<Slim.IServiceProvider>().Object);
client.MaxResponseContentBufferSize.Should().Be(100);
});
this.httpClientBuilder.WithMaxResponseBufferSize(100)
.Build();
}
[TestMethod]
public void Build_WithTimeout_ReturnsClientWithTimeout()
{
this.serviceProducerMock.Setup(u => u.RegisterScoped(It.IsAny<Func<Slim.IServiceProvider, IHttpClient<object>>>(), false))
.Callback<Func<Slim.IServiceProvider, IHttpClient<object>>, bool>((factory, _) =>
{
var client = factory(new Mock<Slim.IServiceProvider>().Object);
client.Timeout.Should().Be(TimeSpan.FromSeconds(5));
});
this.httpClientBuilder.WithTimeout(TimeSpan.FromSeconds(5))
.Build();
}
[TestMethod]
public void Build_WithMessageHandler_CallsMessageHandler()
{
var handlerMock = new HttpMessageHandlerMock();
this.serviceProducerMock.Setup(u => u.RegisterScoped(It.IsAny<Func<Slim.IServiceProvider, IHttpClient<object>>>(), false))
.Callback<Func<Slim.IServiceProvider, IHttpClient<object>>, bool>(async (factory, _) =>
{
var client = factory(new Mock<Slim.IServiceProvider>().Object);
await client.GetAsync(this.baseAddress);
handlerMock.Called.Should().BeTrue();
});
this.httpClientBuilder.WithMessageHandler(sp => handlerMock)
.Build();
}
[TestMethod]
public async Task HttpClientBuilder_RegistersClientCorrectly_IServiceProviderReturnsExpected()
{
var container = new ServiceManager();
var messageHandler = new HttpMessageHandlerMock();
new HttpClientBuilder<object>(container)
.WithBaseAddress(this.baseAddress)
.WithDefaultRequestHeadersSetup(header => header.TryAddWithoutValidation(SomeHeader, SomeValue))
.WithMaxResponseBufferSize(5)
.WithMessageHandler(sp => messageHandler)
.WithTimeout(TimeSpan.FromSeconds(5))
.Build();
var client = container.GetService<IHttpClient<object>>();
await client.GetAsync("");
client.Should().NotBeNull();
client.BaseAddress.Should().Be(this.baseAddress);
client.DefaultRequestHeaders.Should().HaveCount(1);
client.DefaultRequestHeaders.First().Key.Should().Be(SomeHeader);
client.DefaultRequestHeaders.First().Value.Should().HaveCount(1);
client.DefaultRequestHeaders.First().Value.First().Should().Be(SomeValue);
client.MaxResponseContentBufferSize.Should().Be(5);
client.Timeout.Should().Be(TimeSpan.FromSeconds(5));
messageHandler.Called.Should().BeTrue();
}
}
@@ -0,0 +1,68 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Http;
using System.Net.Http;
namespace SystemExtensions.DependencyInjection.Tests.Http;
[TestClass]
public class HttpClientResolverTests
{
private readonly HttpClientResolver httpClientResolver = new();
private readonly Mock<Slim.IServiceProvider> serviceProviderMock = new();
[TestMethod]
public void CanResolve_IHttpClient_ReturnsTrue()
{
var type = typeof(IHttpClient<>);
var canResolve = this.httpClientResolver.CanResolve(type);
canResolve.Should().BeTrue();
}
[TestMethod]
public void CanResolve_AnythingElse_ReturnsFalse()
{
var types = new Type[] { typeof(HttpClient), typeof(object), typeof(string), typeof(HttpClientResolverTests), typeof(int) };
foreach (var type in types)
{
var canResolve = this.httpClientResolver.CanResolve(type);
canResolve.Should().BeFalse();
}
}
[TestMethod]
public void Resolve_TypedClient_ReturnsIHttpClient()
{
var client = this.httpClientResolver.Resolve(this.serviceProviderMock.Object, typeof(IHttpClient<string>));
client.Should().BeAssignableTo<IHttpClient<string>>();
}
[TestMethod]
public void Resolve_NonGenericType_Throws()
{
Action action = new(() =>
{
this.httpClientResolver.Resolve(this.serviceProviderMock.Object, typeof(IHttpClient<>));
});
action.Should().Throw<Exception>();
}
[TestMethod]
public void Resolve_RandomType_Throws()
{
Action action = new(() =>
{
this.httpClientResolver.Resolve(this.serviceProviderMock.Object, typeof(string));
});
action.Should().Throw<Exception>();
}
}
@@ -0,0 +1,16 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace SystemExtensions.NetStandard.DependencyInjection.Tests.Http.Models;
public sealed class HttpMessageHandlerMock : HttpMessageHandler
{
public bool Called { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
this.Called = true;
return Task.FromResult(new HttpResponseMessage());
}
}
@@ -0,0 +1,42 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Logging;
namespace SystemExtensions.DependencyInjection.Tests.Logging;
[TestClass]
public class CVLoggerProviderTests
{
private readonly Mock<ILogsWriter> logsWriterMock = new();
private CVLoggerProvider cVLoggerProvider;
[TestInitialize]
public void TestInitialize()
{
this.cVLoggerProvider = new CVLoggerProvider(this.logsWriterMock.Object);
}
[TestMethod]
public void CreateLogger_CreatesNewLogger()
{
var logger = this.cVLoggerProvider.CreateLogger(string.Empty);
logger.Should().NotBeNull();
}
[TestMethod]
public void LogEntry_CallsLogWriter()
{
this.cVLoggerProvider.LogEntry(new Log());
this.logsWriterMock.Verify();
}
private void SetupLogsWriter()
{
this.logsWriterMock
.Setup(u => u.WriteLog(It.IsAny<Log>()))
.Verifiable();
}
}
@@ -0,0 +1,75 @@
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Logging;
namespace SystemExtensions.DependencyInjection.Tests.Logging;
[TestClass]
public class CVLoggerTests
{
private readonly Mock<ICVLoggerProvider> cvLoggerProviderMock = new();
private CVLogger cVLogger;
[TestInitialize]
public void TestInitialize()
{
this.cVLogger = new CVLogger(string.Empty, this.cvLoggerProviderMock.Object);
}
[TestMethod]
public void BeginScope_ReturnsNull()
{
var scope = this.cVLogger.BeginScope(string.Empty);
scope.Should().BeNull();
}
[TestMethod]
[DataRow(LogLevel.Debug)]
[DataRow(LogLevel.Trace)]
[DataRow(LogLevel.Information)]
[DataRow(LogLevel.Warning)]
[DataRow(LogLevel.Error)]
[DataRow(LogLevel.Critical)]
public void IsEnabled_OnAllLogLevels_ReturnsTrue(LogLevel logLevel)
{
var enabled = this.cVLogger.IsEnabled(logLevel);
enabled.Should().BeTrue();
}
[TestMethod]
public void Log_CallsFormatter()
{
var called = false;
Func<string, Exception, string> messageFormatter = new((state, exception) =>
{
called = true;
return string.Empty;
});
this.cVLogger.Log(LogLevel.Debug, new EventId(), "Some message", new Exception(), messageFormatter);
called.Should().BeTrue();
}
[TestMethod]
public void Log_CallsLogsProvider()
{
this.SetupLoggerProvider();
this.cVLogger.Log(LogLevel.Debug, new EventId(), "Some message", new Exception(), new Func<string, Exception, string>((s, e) => string.Empty));
this.cvLoggerProviderMock.Verify();
}
private void SetupLoggerProvider()
{
this.cvLoggerProviderMock
.Setup(u => u.LogEntry(It.IsAny<Log>()))
.Verifiable();
}
}
@@ -0,0 +1,29 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Logging;
namespace SystemExtensions.DependencyInjection.Tests.Logging;
[TestClass]
public class DebugLogsWriterTests
{
private readonly DebugLogsWriter logsWriter = new();
[TestMethod]
public void WriteLog_Succeeds()
{
this.logsWriter.WriteLog(new Log());
}
[TestMethod]
public void WriteNullLog_Throws()
{
Action action = new(() =>
{
this.logsWriter.WriteLog(null);
});
action.Should().Throw<Exception>();
}
}
@@ -0,0 +1,107 @@
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
using System.Logging;
namespace SystemExtensions.DependencyInjection.Tests.Logging;
[TestClass]
public class LoggerResolverTests
{
private readonly Mock<Slim.IServiceProvider> serviceProviderMock = new();
private readonly Mock<ILoggerFactory> loggerFactoryMock = new();
private readonly Mock<ILogger> loggerMock = new();
private readonly LoggerResolver loggerResolver = new();
[TestMethod]
public void CanResolve_ILogger_ReturnsTrue()
{
var type = typeof(ILogger);
var canResolve = this.loggerResolver.CanResolve(type);
canResolve.Should().BeTrue();
}
[TestMethod]
public void CanResolve_GenericILogger_ReturnsTrue()
{
var type = typeof(ILogger<string>);
var canResolve = this.loggerResolver.CanResolve(type);
canResolve.Should().BeTrue();
}
[TestMethod]
public void CanResolve_AnythingElse_ReturnsFalse()
{
var types = new Type[] { typeof(CVLogger), typeof(object), typeof(string), typeof(LoggerResolverTests), typeof(int) };
foreach(var type in types)
{
var canResolve = this.loggerResolver.CanResolve(type);
canResolve.Should().BeFalse();
}
}
[TestMethod]
public void Resolve_ILogger_ReturnsILogger()
{
this.SetupIServiceProvider();
var logger = this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(ILogger));
logger.Should().BeAssignableTo<ILogger>();
}
[TestMethod]
public void Resolve_TypedILogger_ReturnsTypedILogger()
{
this.SetupIServiceProvider();
var logger = this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(ILogger<string>));
logger.Should().BeAssignableTo<ILogger<string>>();
}
[TestMethod]
public void Resolve_RandomType_Throws()
{
this.SetupIServiceProvider();
Action action = new(() =>
{
this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(string));
});
action.Should().Throw<Exception>();
}
[TestMethod]
public void Resolve_NonTypedGeneric_Throws()
{
this.SetupIServiceProvider();
Action action = new(() =>
{
this.loggerResolver.Resolve(this.serviceProviderMock.Object, typeof(ILogger<>));
});
action.Should().Throw<Exception>();
}
private void SetupIServiceProvider()
{
this.serviceProviderMock
.Setup(u => u.GetService<ILoggerFactory>())
.Returns(this.loggerFactoryMock.Object);
this.loggerFactoryMock
.Setup(u => u.CreateLogger(It.IsAny<string>()))
.Returns(this.loggerMock.Object);
}
}
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SystemExtensions.NetStandard.DependencyInjection\SystemExtensions.NetStandard.DependencyInjection.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,22 @@
using System.Runtime.CompilerServices;
namespace System.Core.Extensions;
public static class ObjectExtensions
{
public static T ThrowIfNull<T>([ValidatedNotNull] this T obj, [CallerArgumentExpression("obj")] string? paramName = null)
where T : class
{
if (obj is null)
{
throw new ArgumentNullException(paramName);
}
return obj;
}
[AttributeUsage(AttributeTargets.Parameter)]
sealed class ValidatedNotNullAttribute : Attribute
{
}
}
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Nullable>enable</Nullable>
<RootNamespace>System</RootNamespace>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Version>1.0.1</Version>
<Authors>Alexandru Macocian</Authors>
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
<Description>Extensions for the System namespace</Description>
</PropertyGroup>
<ItemGroup>
<None Include="../LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,13 @@
namespace System.Configuration;
public sealed class DefaultOptionsManager : IOptionsManager
{
public T GetOptions<T>() where T : class
{
return default;
}
public void UpdateOptions<T>(T value) where T : class
{
}
}
@@ -0,0 +1,8 @@
using Microsoft.Extensions.Options;
namespace System.Configuration;
public interface ILiveOptions<T> : IOptions<T>
where T : class
{
}
@@ -0,0 +1,6 @@
namespace System.Configuration;
public interface ILiveUpdateableOptions<T> : ILiveOptions<T>, IUpdateableOptions<T>
where T : class
{
}
@@ -0,0 +1,9 @@
namespace System.Configuration;
public interface IOptionsManager
{
T GetOptions<T>()
where T : class;
void UpdateOptions<T>(T value)
where T : class;
}
@@ -0,0 +1,12 @@
using Microsoft.Extensions.Options;
namespace System.Configuration;
public interface IUpdateableOptions<out T> : IOptions<T>
where T : class
{
/// <summary>
/// Updates the configuration with the current value stored in <see cref="IOptions{TOptions}.Value"/>.
/// </summary>
void UpdateOption();
}
@@ -0,0 +1,26 @@
using Slim.Resolvers;
namespace System.Configuration;
public sealed class LiveOptionsResolver : IDependencyResolver
{
private static readonly Type optionsType = typeof(LiveUpdateableOptionsWrapper<>);
public bool CanResolve(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILiveOptions<>))
{
return true;
}
return false;
}
public object Resolve(Slim.IServiceProvider serviceProvider, Type type)
{
var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments());
var configurationManager = serviceProvider.GetService<IOptionsManager>();
return Activator.CreateInstance(typedOptionsType, configurationManager);
}
}
@@ -0,0 +1,25 @@
using Slim.Resolvers;
namespace System.Configuration;
public sealed class LiveUpdateableOptionsResolver : IDependencyResolver
{
private static readonly Type optionsType = typeof(LiveUpdateableOptionsWrapper<>);
public bool CanResolve(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILiveUpdateableOptions<>))
{
return true;
}
return false;
}
public object Resolve(Slim.IServiceProvider serviceProvider, Type type)
{
var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments());
var configurationManager = serviceProvider.GetService<IOptionsManager>();
return Activator.CreateInstance(typedOptionsType, configurationManager);
}
}
@@ -0,0 +1,30 @@
using System.Extensions;
namespace System.Configuration;
public sealed class LiveUpdateableOptionsWrapper<T> : ILiveUpdateableOptions<T>
where T : class
{
private readonly IOptionsManager configurationManager;
private T value;
public T Value
{
get
{
this.value = this.configurationManager.GetOptions<T>();
return this.value;
}
}
public LiveUpdateableOptionsWrapper(IOptionsManager configurationManager)
{
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
}
public void UpdateOption()
{
this.configurationManager.UpdateOptions<T>(this.value);
}
}
@@ -0,0 +1,32 @@
using Microsoft.Extensions.Options;
using Slim.Resolvers;
namespace System.Configuration;
public sealed class OptionsResolver : IDependencyResolver
{
private static readonly Type optionsType = typeof(OptionsWrapper<>);
private static readonly Type configurationType = typeof(IOptionsManager);
public bool CanResolve(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IOptions<>))
{
return true;
}
return false;
}
public object Resolve(Slim.IServiceProvider serviceProvider, Type type)
{
var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments());
var configurationManager = serviceProvider.GetService<IOptionsManager>();
var optionsValue = configurationType
.GetMethod(nameof(IOptionsManager.GetOptions))
.MakeGenericMethod(type.GetGenericArguments())
.Invoke(configurationManager, Array.Empty<object>());
return Activator.CreateInstance(typedOptionsType, optionsValue);
}
}
@@ -0,0 +1,32 @@
using Slim.Resolvers;
using System.Extensions.Configuration;
namespace System.Configuration;
public sealed class UpdateableOptionsResolver : IDependencyResolver
{
private static readonly Type optionsType = typeof(UpdateableOptionsWrapper<>);
private static readonly Type configurationType = typeof(IOptionsManager);
public bool CanResolve(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IUpdateableOptions<>))
{
return true;
}
return false;
}
public object Resolve(Slim.IServiceProvider serviceProvider, Type type)
{
var typedOptionsType = optionsType.MakeGenericType(type.GetGenericArguments());
var configurationManager = serviceProvider.GetService<IOptionsManager>();
var optionsValue = configurationType
.GetMethod(nameof(IOptionsManager.GetOptions))
.MakeGenericMethod(type.GetGenericArguments())
.Invoke(configurationManager, Array.Empty<object>());
return Activator.CreateInstance(typedOptionsType, configurationManager, optionsValue);
}
}
@@ -0,0 +1,22 @@
using System.Configuration;
namespace System.Extensions.Configuration;
public sealed class UpdateableOptionsWrapper<T> : IUpdateableOptions<T>
where T : class
{
private readonly IOptionsManager configurationManager;
public T Value { get; }
public UpdateableOptionsWrapper(IOptionsManager configurationManager, T options)
{
this.configurationManager = configurationManager.ThrowIfNull(nameof(configurationManager));
this.Value = options;
}
public void UpdateOption()
{
this.configurationManager.UpdateOptions(this.Value);
}
}
@@ -0,0 +1,189 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Slim;
using System.Configuration;
using System.DependencyInjection.Http;
using System.Http;
using System.Logging;
using System.Net.Http;
namespace System.Extensions;
public static class ServiceManagerExtensions
{
/// <summary>
/// Registers a <see cref="IOptionsManager"/> with the default <see cref="DefaultOptionsManager"/>.
/// This call also registers the resolver that resolves <see cref="IUpdateableOptions{T}"/> and <see cref="IOptions{T}"/>.
/// </summary>
/// <param name="serviceManager"><see cref="IServiceManager"/>.</param>
/// <returns>Provided <see cref="IServiceManager"/>.</returns>
public static IServiceProducer RegisterOptionsManager(this IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterSingleton<IOptionsManager, DefaultOptionsManager>();
serviceProducer.RegisterOptionsResolver();
return serviceProducer;
}
/// <summary>
/// Registers a <see cref="IOptionsManager"/>.
/// This call also registers the resolver that resolves <see cref="IUpdateableOptions{T}"/> and <see cref="IOptions{T}"/>.
/// </summary>
/// <typeparam name="T">Implementation of <see cref="IOptionsManager"/>.</typeparam>
/// <param name="serviceManager"><see cref="IServiceManager"/>.</param>
/// <returns>Provided <see cref="IServiceManager"/>.</returns>
public static IServiceProducer RegisterOptionsManager<T>(this IServiceProducer serviceProducer)
where T : IOptionsManager
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterSingleton<IOptionsManager, T>();
serviceProducer.RegisterOptionsResolver();
return serviceProducer;
}
/// <summary>
/// Registers resolvers for <see cref="IOptions{TOptions}"/> and <see cref="IUpdateableOptions{T}"/>.
/// Depends on a <see cref="IOptionsManager"/> in order to properly resolve options.
/// </summary>
/// <param name="serviceManager"><see cref="IServiceManager"/>.</param>
/// <returns><see cref="IServiceManager"/>.</returns>
public static IServiceProducer RegisterOptionsResolver(this IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterResolver(new OptionsResolver());
serviceProducer.RegisterResolver(new UpdateableOptionsResolver());
serviceProducer.RegisterResolver(new LiveOptionsResolver());
serviceProducer.RegisterResolver(new LiveUpdateableOptionsResolver());
return serviceProducer;
}
/// <summary>
/// Registers a <see cref="ILogsWriter"/> with the default <see cref="CVLoggerProvider"/>.
/// </summary>
/// <typeparam name="TLogsWriter">Implementation of <see cref="ILogsWriter"/>.</typeparam>
/// <param name="serviceManager"><see cref="IServiceProducer"/>.</param>
/// <returns>Provided <see cref="IServiceProducer"/>.</returns>
public static IServiceProducer RegisterLogWriter<TLogsWriter>(this IServiceProducer serviceManager)
where TLogsWriter : ILogsWriter
{
serviceManager.ThrowIfNull(nameof(serviceManager));
serviceManager.RegisterSingleton<ILogsWriter, TLogsWriter>();
serviceManager.RegisterScoped<ILoggerFactory, LoggerFactory>(sp =>
{
var factory = new LoggerFactory();
factory.AddProvider(new CVLoggerProvider(sp.GetService<ILogsWriter>()));
return factory;
});
return serviceManager;
}
/// <summary>
/// Registers a <see cref="ILogsWriter"/> with the default <see cref="CVLoggerProvider"/>.
/// </summary>
/// <typeparam name="TILogsWriter">Interface of <see cref="ILogsWriter"/>.</typeparam>
/// <typeparam name="TLogsWriter">Implementation of <see cref="ILogsWriter"/>.</typeparam>
/// <param name="serviceManager"><see cref="IServiceProducer"/>.</param>
/// <returns>Provided <see cref="IServiceProducer"/>.</returns>
public static IServiceProducer RegisterLogWriter<TILogsWriter, TLogsWriter>(this IServiceProducer serviceManager)
where TLogsWriter : TILogsWriter
where TILogsWriter : class, ILogsWriter
{
serviceManager.ThrowIfNull(nameof(serviceManager));
serviceManager.RegisterSingleton<TILogsWriter, TLogsWriter>();
serviceManager.RegisterScoped<ILoggerFactory, LoggerFactory>(sp =>
{
var factory = new LoggerFactory();
factory.AddProvider(new CVLoggerProvider(sp.GetService<TILogsWriter>()));
return factory;
});
return serviceManager;
}
/// <summary>
/// Register a <see cref="ILoggerFactory"/> with a <see cref="CVLoggerProvider"/>.
/// </summary>
/// <param name="serviceProducer"></param>
/// <returns></returns>
public static IServiceProducer RegisterCVLoggerFactory(this IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterScoped<ILoggerFactory, LoggerFactory>(sp =>
{
LoggerFactory loggerFactory = new();
loggerFactory.AddProvider(new CVLoggerProvider(sp.GetService<ILogsWriter>()));
return loggerFactory;
});
return serviceProducer;
}
/// <summary>
/// Registers a <see cref="ILoggerFactory"/>.
/// </summary>
/// <param name="serviceProducer"><see cref="IServiceProducer"/>.</param>
/// <param name="loggerFactory">Factory that produces a <see cref="ILoggerFactory"/>.</param>
/// <returns></returns>
public static IServiceProducer RegisterLoggerFactory(this IServiceProducer serviceProducer, Func<Slim.IServiceProvider, ILoggerFactory> loggerFactory)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterSingleton<ILoggerFactory, ILoggerFactory>(loggerFactory);
return serviceProducer;
}
/// <summary>
/// Registers a <see cref="ILoggerFactory"/> with a <see cref="ILogsWriter"/> that writes to <see cref="Diagnostics.Debug"/>.
/// </summary>
/// <param name="serviceProducer"><see cref="IServiceProducer"/>.</param>
/// <returns></returns>
public static IServiceProducer RegisterDebugLoggerFactory(this IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterLogWriter<ILogsWriter, DebugLogsWriter>();
return serviceProducer;
}
/// <summary>
/// Register a scoped <see cref="IHttpClient{TScope}"/> to be used by the DI engine.
/// </summary>
/// <typeparam name="T">Type of the scoped <see cref="IHttpClient{TScope}"/>.</typeparam>
/// <param name="serviceProducer"><see cref="IServiceProducer"/>.</param>
/// <returns><see cref="HttpClientBuilder{T}"/> to build the http client.</returns>
public static HttpClientBuilder<T> RegisterHttpClient<T>(this IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
return new HttpClientBuilder<T>(serviceProducer);
}
[Obsolete($"{nameof(RegisterHttpFactory)} is obsolete. Please use {nameof(RegisterHttpClient)} for each service that requires an instance of {nameof(IHttpClient<object>)}.")]
public static IServiceProducer RegisterHttpFactory(this IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterResolver(new HttpClientResolver());
return serviceProducer;
}
[Obsolete($"{nameof(RegisterHttpFactory)} is obsolete. Please use {nameof(RegisterHttpClient)} for each service that requires an instance of {nameof(IHttpClient<object>)}.")]
public static IServiceProducer RegisterHttpFactory(this IServiceProducer serviceProducer, Func<Slim.IServiceProvider, Type, HttpMessageHandler> handlerFactory)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
serviceProducer.RegisterResolver(
new HttpClientResolver()
.WithHttpMessageHandlerFactory(handlerFactory));
return serviceProducer;
}
}
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("SystemExtensions.NetStandard.DependencyInjection.Tests")]
@@ -0,0 +1,85 @@
using Slim;
using System.Extensions;
using System.Net.Http;
using System.Net.Http.Headers;
namespace System.DependencyInjection.Http;
public sealed class HttpClientBuilder<T>
{
private readonly IServiceProducer serviceProducer;
private Uri baseAddress;
private Func<IServiceProvider, HttpMessageHandler> httpMessageHandlerFactory;
private bool disposeMessageHandler;
private Action<HttpRequestHeaders> defaultRequestHeadersSetup;
private long maxResponseBufferSize = 2147483647L; //2GB default HttpClient value [https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.maxresponsecontentbuffersize?view=net-6.0]
private TimeSpan timeout = TimeSpan.FromSeconds(100); //100 seconds default HttpClient value [https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.timeout?view=net-6.0]
internal HttpClientBuilder(IServiceProducer serviceProducer)
{
serviceProducer.ThrowIfNull(nameof(serviceProducer));
this.serviceProducer = serviceProducer;
}
public HttpClientBuilder<T> WithMessageHandler(Func<IServiceProvider, HttpMessageHandler> httpMessageHandlerFactory)
{
httpMessageHandlerFactory.ThrowIfNull(nameof(httpMessageHandlerFactory));
this.httpMessageHandlerFactory = httpMessageHandlerFactory;
return this;
}
public HttpClientBuilder<T> WithDisposeMessageHandler(bool disposeMessageHandler)
{
this.disposeMessageHandler = disposeMessageHandler;
return this;
}
public HttpClientBuilder<T> WithBaseAddress(Uri baseAddress)
{
baseAddress.ThrowIfNull(nameof(baseAddress));
this.baseAddress = baseAddress;
return this;
}
public HttpClientBuilder<T> WithDefaultRequestHeadersSetup(Action<HttpRequestHeaders> setup)
{
setup.ThrowIfNull(nameof(setup));
this.defaultRequestHeadersSetup = setup;
return this;
}
public HttpClientBuilder<T> WithMaxResponseBufferSize(long responseBufferSize)
{
this.maxResponseBufferSize = responseBufferSize;
return this;
}
public HttpClientBuilder<T> WithTimeout(TimeSpan timeout)
{
this.timeout = timeout;
return this;
}
public IServiceProducer Build()
{
this.serviceProducer.RegisterScoped<IHttpClient<T>>(sp =>
{
var client = this.httpMessageHandlerFactory is not null ?
new HttpClient<T>(this.httpMessageHandlerFactory(sp), this.disposeMessageHandler) :
new HttpClient<T>();
client.BaseAddress = this.baseAddress;
this.defaultRequestHeadersSetup?.Invoke(client.DefaultRequestHeaders);
client.MaxResponseContentBufferSize = this.maxResponseBufferSize;
client.Timeout = this.timeout;
return client;
});
return this.serviceProducer;
}
}
@@ -0,0 +1,47 @@
using Slim.Resolvers;
using System.Linq;
using System.Net.Http;
namespace System.Http;
[Obsolete($"Please use {nameof(Extensions.ServiceManagerExtensions.RegisterHttpClient)} for each use case of {nameof(IHttpClient<object>)}")]
public sealed class HttpClientResolver : IDependencyResolver
{
private static readonly Type clientType = typeof(HttpClient<>);
/// <summary>
/// Factory method. <see cref="Type"/> parameter of the factory is the scope of <see cref="IHttpClient{TScope}"/>.
/// </summary>
public Func<Slim.IServiceProvider, Type, HttpMessageHandler> HttpMessageHandlerFactory { get; set; }
/// <summary>
/// Sets the <see cref="HttpMessageHandlerFactory"/>.
/// </summary>
/// <param name="factory">Factory method. <see cref="Type"/> parameter of the factory is the scope of <see cref="IHttpClient{TScope}"/>.</param>
/// <returns></returns>
public HttpClientResolver WithHttpMessageHandlerFactory(Func<Slim.IServiceProvider, Type, HttpMessageHandler> factory)
{
this.HttpMessageHandlerFactory = factory;
return this;
}
public bool CanResolve(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IHttpClient<>))
{
return true;
}
return false;
}
public object Resolve(Slim.IServiceProvider serviceProvider, Type type)
{
var typedClientType = clientType.MakeGenericType(type.GetGenericArguments());
var handler = this.HttpMessageHandlerFactory?.Invoke(serviceProvider, type.GetGenericArguments().First());
var httpClient = handler is null ?
Activator.CreateInstance(typedClientType) :
Activator.CreateInstance(typedClientType, new object[] { handler });
return httpClient;
}
}
@@ -0,0 +1,43 @@
using Microsoft.Extensions.Logging;
using System.Extensions;
namespace System.Logging;
public sealed class CVLogger : ILogger
{
private readonly string category;
private readonly ICVLoggerProvider cvLoggerProvider;
public CVLogger(string category, ICVLoggerProvider cvLoggerProvider)
{
this.category = category;
this.cvLoggerProvider = cvLoggerProvider.ThrowIfNull(nameof(cvLoggerProvider));
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
var message = formatter(state, exception);
var log = new Log
{
Exception = exception,
LogLevel = logLevel,
EventId = eventId.Name,
Message = message,
Category = category,
LogTime = DateTime.Now
};
this.cvLoggerProvider.LogEntry(log);
}
}
@@ -0,0 +1,38 @@
using Microsoft.CorrelationVector;
using Microsoft.Extensions.Logging;
using System.Extensions;
namespace System.Logging;
public sealed class CVLoggerProvider : ICVLoggerProvider
{
private readonly ILogsWriter logsManager;
private readonly CorrelationVector correlationVector;
public CVLoggerProvider(ILogsWriter logsWriter)
{
this.logsManager = logsWriter.ThrowIfNull(nameof(logsWriter));
this.correlationVector = new CorrelationVector();
}
public void LogEntry(Log log)
{
if (this.correlationVector is not null)
{
log.CorrelationVector = this.correlationVector.Value.ToString();
this.correlationVector.Increment();
}
this.logsManager.WriteLog(log);
}
public ILogger CreateLogger(string categoryName)
{
return new CVLogger(categoryName, this);
}
public void Dispose()
{
throw new System.NotImplementedException();
}
}
@@ -0,0 +1,11 @@
using System.Diagnostics;
namespace System.Logging;
public sealed class DebugLogsWriter : ILogsWriter
{
public void WriteLog(Log log)
{
Debug.WriteLine($"{log.LogTime} - {log.Category} - {log.EventId} - {log.CorrelationVector} - {log.LogLevel} - {log.Message}{Environment.NewLine}{log.Exception}");
}
}
@@ -0,0 +1,8 @@
using Microsoft.Extensions.Logging;
namespace System.Logging;
public interface ICVLoggerProvider : ILoggerProvider
{
void LogEntry(Log log);
}
@@ -0,0 +1,6 @@
namespace System.Logging;
public interface ILogsWriter
{
void WriteLog(Log log);
}
@@ -0,0 +1,49 @@
using Microsoft.Extensions.Logging;
using Slim.Resolvers;
using System.Linq;
namespace System.Logging;
public sealed class LoggerResolver : IDependencyResolver
{
public bool CanResolve(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILogger<>) ||
type == typeof(ILogger))
{
return true;
}
return false;
}
public object Resolve(Slim.IServiceProvider serviceProvider, Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ILogger<>))
{
return ResolveScopedLogger(serviceProvider, type);
}
else if (type == typeof(ILogger))
{
return ResolveLogger(serviceProvider);
}
else
{
throw new InvalidOperationException($"{nameof(LoggerResolver)} cannot resolve an object of type {type.Name}");
}
}
private static object ResolveScopedLogger(Slim.IServiceProvider serviceProvider, Type type)
{
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
var categoryTypes = type.GetGenericArguments();
var createLoggerMethod = typeof(LoggerFactoryExtensions).GetMethods().Where(m => m.IsGenericMethodDefinition && m.Name == nameof(LoggerFactoryExtensions.CreateLogger)).First();
return createLoggerMethod.MakeGenericMethod(categoryTypes.First()).Invoke(null, new object[] { loggerFactory });
}
private static object ResolveLogger(Slim.IServiceProvider serviceProvider)
{
var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
return loggerFactory.CreateLogger(string.Empty);
}
}
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Logging;
namespace System.Logging;
public sealed record Log
{
public Exception Exception { get; set; }
public DateTime LogTime { get; set; }
public string Category { get; set; }
public string EventId { get; set; }
public LogLevel LogLevel { get; set; }
public string Message { get; set; }
public string CorrelationVector { get; set; }
}
@@ -0,0 +1,8 @@
using System.Net.Http;
namespace System.DependencyInjection.Models;
internal sealed class TypedHttpClientFactory<T>
{
public Func<Slim.IServiceProvider, HttpMessageHandler> Factory { get; set; }
}
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>System.DependencyInjection</RootNamespace>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<LangVersion>latest</LangVersion>
<Version>1.2.3</Version>
<Authors>Alexandru Macocian</Authors>
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
<Description>Extensions for the Slim Dependency Injection engine</Description>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE" Link="LICENSE">
<PackagePath></PackagePath>
<Pack>True</Pack>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="Slim" Version="1.9.2" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.5.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,104 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Encryption;
using System.Text;
using System.Threading.Tasks;
namespace SystemExtensions.NetStandard.Security.Tests;
[TestClass]
public class AesEncrypterTests
{
private ISymmetricEncrypter symmetricEncrypter;
private string keyString;
private byte[] keyBytes;
private byte[] ivBytes;
private string ivString;
private string toEncryptString;
private byte[] toEncryptBytes;
[TestInitialize]
public void TestInitialize()
{
this.symmetricEncrypter = new AesEncrypter();
this.keyBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2 };
this.keyString = Convert.ToBase64String(this.keyBytes);
this.ivBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6 };
this.ivString = Convert.ToBase64String(this.ivBytes);
this.toEncryptBytes = Encoding.UTF8.GetBytes("toEncrypt");
this.toEncryptString = Convert.ToBase64String(this.toEncryptBytes);
}
[TestMethod]
public async Task EncryptDecryptStringWithStringKeyStringIv()
{
var encrypted = await this.symmetricEncrypter.Encrypt(this.keyString, this.ivString, this.toEncryptString).ConfigureAwait(false);
var decrypted = await this.symmetricEncrypter.Decrypt(this.keyString, this.ivString, encrypted).ConfigureAwait(false);
this.toEncryptString.Should().Be(decrypted);
}
[TestMethod]
public async Task EncryptDecryptStringWithByteKeyByteIv()
{
var encrypted = await this.symmetricEncrypter.Encrypt(this.keyBytes, this.ivBytes, this.toEncryptString).ConfigureAwait(false);
var decrypted = await this.symmetricEncrypter.Decrypt(this.keyBytes, this.ivBytes, encrypted).ConfigureAwait(false);
this.toEncryptString.Should().Be(decrypted);
}
[TestMethod]
public async Task EncryptDecryptByteWithStringKeyStringIv()
{
var encrypted = await this.symmetricEncrypter.Encrypt(this.keyString, this.ivString, this.toEncryptBytes).ConfigureAwait(false);
var decrypted = await this.symmetricEncrypter.Decrypt(this.keyString, this.ivString, encrypted).ConfigureAwait(false);
this.toEncryptBytes.Should().BeEquivalentTo(decrypted);
}
[TestMethod]
public async Task EncryptDecryptByteWithByteKeyByteIv()
{
var encrypted = await this.symmetricEncrypter.Encrypt(this.keyBytes, this.ivBytes, this.toEncryptBytes).ConfigureAwait(false);
var decrypted = await this.symmetricEncrypter.Decrypt(this.keyBytes, this.ivBytes, encrypted).ConfigureAwait(false);
this.toEncryptBytes.Should().BeEquivalentTo(decrypted);
}
[TestMethod]
public async Task EncryptDecryptStreamWithStringKeyStringIv()
{
var encrypted = await this.symmetricEncrypter.Encrypt(this.keyString, this.ivString, new MemoryStream(this.toEncryptBytes)).ConfigureAwait(false);
encrypted.Position = 0;
var decrypted = await this.symmetricEncrypter.Decrypt(this.keyString, this.ivString, encrypted).ConfigureAwait(false);
this.toEncryptBytes.Should().BeEquivalentTo(((MemoryStream)decrypted).ToArray());
}
[TestMethod]
public async Task EncryptDecryptStreamWithByteKeyByteIv()
{
var encrypted = await this.symmetricEncrypter.Encrypt(this.keyBytes, this.ivBytes, new MemoryStream(this.toEncryptBytes)).ConfigureAwait(false);
encrypted.Position = 0;
var decrypted = await this.symmetricEncrypter.Decrypt(this.keyBytes, this.ivBytes, encrypted).ConfigureAwait(false);
this.toEncryptBytes.Should().BeEquivalentTo(((MemoryStream)decrypted).ToArray());
}
[TestMethod]
public void GetEncryptionStreamReturnsCryptoStream()
{
var encryptionStream = this.symmetricEncrypter.GetEncryptionStream(this.keyBytes, this.ivBytes, new MemoryStream());
encryptionStream.Should().BeAssignableTo<CryptoStream>();
}
[TestMethod]
public void GetDecryptionStreamReturnsCryptoStream()
{
var encryptionStream = this.symmetricEncrypter.GetDecryptionStream(this.keyBytes, this.ivBytes, new MemoryStream());
encryptionStream.Should().BeAssignableTo<CryptoStream>();
}
}
@@ -0,0 +1,54 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Linq;
using System.Security.Rng;
namespace SystemExtensions.NetStandard.Security.Tests;
[TestClass]
public class CryptoRngProviderTests
{
private CryptoRngProvider cryptoRngProvider;
[TestInitialize]
public void TestInitialize()
{
this.cryptoRngProvider = new CryptoRngProvider();
}
[TestMethod]
public void GetBytes_ShouldSetValues()
{
var bytes = new byte[100];
this.cryptoRngProvider.GetBytes(bytes);
bytes.All(b => b == 0).Should().BeFalse();
}
[TestMethod]
public void GetNonZeroBytes_ShouldSetNonZeroValues()
{
var bytes = new byte[100];
this.cryptoRngProvider.GetNonZeroBytes(bytes);
bytes.All(b => b != 0).Should().BeTrue();
}
[TestMethod]
public void GetBytes_ShouldReturnBytes()
{
var bytes = this.cryptoRngProvider.GetBytes(10);
bytes.Length.Should().Be(10);
}
[TestMethod]
public void GetNonZeroBytes_ShouldReturnBytes()
{
var bytes = this.cryptoRngProvider.GetNonZeroBytes(10);
bytes.Length.Should().Be(10);
}
}
@@ -0,0 +1,260 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Security.Hashing;
using System.Threading.Tasks;
namespace SystemExtensions.NetStandard.Security.Tests;
[TestClass]
public sealed class Rfc2898DeriveBytesPasswordHashingServiceTests
{
private const int TooShortHashLength = 31;
private const int DesiredHashLength = 32;
private const int Iterations = 10000;
private const int TooLittleIterations = 1000;
private readonly Rfc2898DeriveBytesPasswordHashingService rfc2898DeriveBytesPasswordHashingService = new();
private byte[] tooShortSaltBytes;
private string tooShortSaltString;
private byte[] incorrectSaltBytes;
private string incorrectSaltString;
private byte[] saltBytes;
private string saltString;
private byte[] passwordBytes;
private string passwordString;
private byte[] incorrectPasswordBytes;
private string incorrectPasswordString;
[TestInitialize]
public void TestInitialize()
{
this.tooShortSaltBytes = new byte[16];
this.tooShortSaltString = Convert.ToBase64String(this.tooShortSaltBytes);
this.saltBytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 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 };
this.incorrectSaltBytes = new byte[32];
this.incorrectSaltString = Convert.ToBase64String(this.incorrectSaltBytes);
this.saltString = Convert.ToBase64String(this.saltBytes);
this.passwordBytes = new byte[] { 5, 1, 2, 34, 35, 123, 4, 23, 1, 235, 32, 234 };
this.passwordString = Convert.ToBase64String(this.passwordBytes);
this.incorrectPasswordBytes = new byte[] { 14, 123, 23, 4, 2, 1, 23, 25 };
this.incorrectPasswordString = Convert.ToBase64String(this.incorrectPasswordBytes);
}
[TestMethod]
public void PasswordNull_HashBytes_Throws_ArgumentNullException()
{
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(null, this.saltBytes, DesiredHashLength, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void PasswordNull_HashString_Throws_ArgumentNullException()
{
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(null, this.saltString, DesiredHashLength, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void SaltNull_HashBytes_Throws_ArgumentNullException()
{
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, null, DesiredHashLength, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void SaltNull_HashString_Throws_ArgumentNullException()
{
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, null, DesiredHashLength, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void HashLengthTooSmall_HashBytes_Throws_InvalidOperationException()
{
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, TooShortHashLength, Iterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public void HashLengthTooSmall_HashString_Throws_InvalidOperationException()
{
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, TooShortHashLength, Iterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public void TooLittleIterations_HashBytes_Throws_InvalidOperationException()
{
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, TooLittleIterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public void TooLittleIterations_HashString_Throws_InvalidOperationException()
{
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, TooLittleIterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public void TooShortSalt_HashBytes_Throws_InvalidOperationException()
{
var action = new Func<Task<byte[]>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.tooShortSaltBytes, DesiredHashLength, TooLittleIterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public void TooShortSalt_HashString_Throws_InvalidOperationException()
{
var action = new Func<Task<string>>(() => this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.tooShortSaltString, DesiredHashLength, TooLittleIterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public void PasswordNull_VerifyBytes_Throws_ArgumentNullException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(null, this.incorrectPasswordBytes, this.saltBytes, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void PasswordNull_VerifyString_Throws_ArgumentNullException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(null, this.incorrectPasswordString, this.saltString, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void HashNull_VerifyBytes_Throws_ArgumentNullException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordBytes, null, this.saltBytes, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void HashNull_VerifyString_Throws_ArgumentNullException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordString, null, this.saltString, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void SaltNull_VerifyBytes_Throws_ArgumentNullException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordBytes, this.incorrectPasswordBytes, null, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void SaltNull_VerifyString_Throws_ArgumentNullException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordString, this.incorrectPasswordString, null, Iterations));
action.Should().ThrowAsync<ArgumentNullException>();
}
[TestMethod]
public void TooLittleIterations_VerifyBytes_Throws_InvalidOperationException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordBytes, this.incorrectPasswordBytes, this.saltBytes, TooLittleIterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public void TooLittleIterations_VerifyString_Throws_InvalidOperationException()
{
var action = new Func<Task<bool>>(() => this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(this.passwordString, this.incorrectPasswordString, this.saltString, TooLittleIterations));
action.Should().ThrowAsync<InvalidOperationException>();
}
[TestMethod]
public async Task HashBytes_ReturnsHashedBytes()
{
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
hashedBytes.Should().NotBeNull();
hashedBytes.Should().HaveCount(DesiredHashLength);
}
[TestMethod]
public async Task HashString_ReturnsHashedString()
{
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
hashedString.Should().NotBeNull();
var hashedBytes = Convert.FromBase64String(hashedString);
hashedBytes.Should().HaveCount(DesiredHashLength);
}
[TestMethod]
public async Task VerifyBytes_CorrectPassword_ReturnsTrue()
{
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.passwordBytes, this.saltBytes, Iterations);
result.Should().BeTrue();
}
[TestMethod]
public async Task VerifyString_CorrectPassword_ReturnsTrue()
{
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.passwordString, this.saltString, Iterations);
result.Should().BeTrue();
}
[TestMethod]
public async Task VerifyBytes_IncorrectPassword_ReturnsFalse()
{
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.incorrectPasswordBytes, this.saltBytes, Iterations);
result.Should().BeFalse();
}
[TestMethod]
public async Task VerifyString_IncorrectPassword_ReturnsFalse()
{
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.incorrectPasswordString, this.saltString, Iterations);
result.Should().BeFalse();
}
[TestMethod]
public async Task VerifyBytes_IncorrectSalt_ReturnsFalse()
{
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.passwordBytes, this.incorrectSaltBytes, Iterations);
result.Should().BeFalse();
}
[TestMethod]
public async Task VerifyString_IncorrectSalt_ReturnsFalse()
{
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.passwordString, this.incorrectSaltString, Iterations);
result.Should().BeFalse();
}
[TestMethod]
public async Task VerifyBytes_IncorrectIterations_ReturnsFalse()
{
var hashedBytes = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordBytes, this.saltBytes, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedBytes, this.passwordBytes, this.saltBytes, Iterations + 1);
result.Should().BeFalse();
}
[TestMethod]
public async Task VerifyString_IncorrectIterations_ReturnsFalse()
{
var hashedString = await this.rfc2898DeriveBytesPasswordHashingService.Hash(this.passwordString, this.saltString, DesiredHashLength, Iterations);
var result = await this.rfc2898DeriveBytesPasswordHashingService.VerifyPassword(hashedString, this.passwordString, this.saltString, Iterations + 1);
result.Should().BeFalse();
}
}
@@ -0,0 +1,121 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security.Encryption;
namespace SystemExtensions.NetStandard.Security.Tests;
[TestClass]
public class SecureStringTests
{
[TestMethod]
public void SecureString_NewSecureString_ReturnsValue()
{
var str = new SecureString("hello");
str.Value.Should().Be("hello");
}
[TestMethod]
public void SecureStringEmpty_AreEqual()
{
var str = SecureString.Empty;
str.Should().Be(SecureString.Empty);
}
[TestMethod]
public void SecureStringEmpty_Equals_StringEmpty()
{
var ss = SecureString.Empty;
var s = string.Empty;
ss.Should().Be(s);
}
[TestMethod]
public void SecureString_Equals_OtherSecureString()
{
var ss1 = new SecureString("Hello");
var ss2 = new SecureString("Hello");
ss1.Equals(ss2).Should().BeTrue();
}
[TestMethod]
[DataRow("hello", "hello", true)]
[DataRow("hello", "henlo", false)]
public void SecureString_EqualOperator_OtherSecureString(string str1, string str2, bool isEqual)
{
var ss1 = new SecureString(str1);
var ss2 = new SecureString(str2);
(ss1 == ss2).Should().Be(isEqual);
}
[TestMethod]
[DataRow("hello", "hello", false)]
[DataRow("hello", "henlo", true)]
public void SecureString_DifferentOperator_OtherSecureString(string str1, string str2, bool isDifferent)
{
var ss1 = new SecureString(str1);
var ss2 = new SecureString(str2);
(ss1 != ss2).Should().Be(isDifferent);
}
[TestMethod]
[DataRow("hello", "hello", true)]
[DataRow("hello", "henlo", false)]
public void SecureString_EqualOperator_String(string str1, string str2, bool isEqual)
{
var ss1 = new SecureString(str1);
(ss1 == str2).Should().Be(isEqual);
}
[TestMethod]
[DataRow("hello", "hello", false)]
[DataRow("hello", "henlo", true)]
public void SecureString_DifferentOperator_String(string str1, string str2, bool isDifferent)
{
var ss1 = new SecureString(str1);
(ss1 != str2).Should().Be(isDifferent);
}
[TestMethod]
public void SecureString_PlusOperator_SecureString()
{
var ss1 = new SecureString("Hello ");
var ss2 = new SecureString("World");
(ss1 + ss2).Should().Be("Hello World");
}
[TestMethod]
public void SecureString_PlusOperator_String()
{
var ss1 = new SecureString("Hello ");
var ss2 = "World";
(ss1 + ss2).Should().Be("Hello World");
}
[TestMethod]
public void SecureString_PlusOperator_Char()
{
var ss1 = new SecureString("Hello ");
var c = 'W';
(ss1 + c).Should().Be("Hello W");
}
[TestMethod]
public void SecureString_WithOptionalEntropy_Matches()
{
SecureString.AddOptionalEntropy(new byte[] { 10, 20, 25, 34, 56, 12, 10, 81, 200, 155, 123, 144, 123, 192, 122, 1 });
var ss1 = new SecureString("Hello");
var ss2 = new SecureString("Hello");
ss1.Should().Be(ss2);
}
}
@@ -0,0 +1,47 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.IO;
using System.Security.Hashing;
using System.Text;
using System.Threading.Tasks;
namespace SystemExtensions.NetStandard.Security.Tests;
[TestClass]
public sealed class Sha256HashingServiceTests
{
private readonly IHashingService hashingService = new Sha256HashingService();
private string toHashtString;
private byte[] toHashtBytes;
private Stream toHashStream;
[TestInitialize]
public void TestInitialize()
{
this.toHashtBytes = Encoding.UTF8.GetBytes("toEncrypt");
this.toHashtString = Convert.ToBase64String(this.toHashtBytes);
this.toHashStream = new MemoryStream(this.toHashtBytes);
}
[TestMethod]
public async Task HashString()
{
var hash = await this.hashingService.Hash(this.toHashtString).ConfigureAwait(false);
hash.Should().BeOfType<string>();
}
[TestMethod]
public async Task HashBytes()
{
var hash = await this.hashingService.Hash(this.toHashtBytes).ConfigureAwait(false);
hash.Should().BeOfType<byte[]>();
}
[TestMethod]
public async Task HashStream()
{
var hash = await this.hashingService.Hash(this.toHashStream).ConfigureAwait(false);
hash.Should().BeAssignableTo<Stream>();
}
}
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SystemExtensions.NetStandard.Security\SystemExtensions.NetStandard.Security.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,226 @@
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Security.Utilities;
namespace System.Security.Encryption;
public sealed class AesEncrypter : ISymmetricEncrypter, IDisposable
{
private readonly Aes aes;
public AesEncrypter()
{
this.aes = Aes.Create();
}
public AesEncrypter(Aes aes)
{
this.aes = aes;
}
public async Task<string> Decrypt(string key, string iv, string value)
{
using var valueStream = BytesToStream(StringToBytes(value));
using var decryptedStream = await this.DecryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false);
return StreamToString(decryptedStream);
}
public async Task<string> Decrypt(byte[] key, byte[] iv, string value)
{
using var valueStream = BytesToStream(StringToBytes(value));
using var decryptedStream = await this.DecryptInternal(key, iv, valueStream).ConfigureAwait(false);
return StreamToString(decryptedStream);
}
public async Task<byte[]> Decrypt(string key, string iv, byte[] value)
{
using var valueStream = BytesToStream(value);
using var decryptedStream = await this.DecryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false);
return StreamToBytes(decryptedStream);
}
public async Task<byte[]> Decrypt(byte[] key, byte[] iv, byte[] value)
{
using var valueStream = BytesToStream(value);
using var decryptedStream = await this.DecryptInternal(key, iv, valueStream).ConfigureAwait(false);
return StreamToBytes(decryptedStream);
}
public async Task<Stream> Decrypt(string key, string iv, Stream value)
{
return await this.DecryptInternal(StringToBytes(key), StringToBytes(iv), value).ConfigureAwait(false);
}
public async Task<Stream> Decrypt(byte[] key, byte[] iv, Stream value)
{
return await this.DecryptInternal(key, iv, value).ConfigureAwait(false);
}
public async Task<string> Encrypt(string key, string iv, string value)
{
using var valueStream = BytesToStream(StringToBytes(value));
using var encryptedStream = await this.EncryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false);
return StreamToString(encryptedStream);
}
public async Task<string> Encrypt(byte[] key, byte[] iv, string value)
{
using var valueStream = BytesToStream(StringToBytes(value));
using var encryptedStream = await this.EncryptInternal(key, iv, valueStream).ConfigureAwait(false);
return StreamToString(encryptedStream);
}
public async Task<byte[]> Encrypt(string key, string iv, byte[] value)
{
using var valueStream = BytesToStream(value);
using var encryptedStream = await this.EncryptInternal(StringToBytes(key), StringToBytes(iv), valueStream).ConfigureAwait(false);
return StreamToBytes(encryptedStream);
}
public async Task<byte[]> Encrypt(byte[] key, byte[] iv, byte[] value)
{
using var valueStream = BytesToStream(value);
using var encryptedStream = await this.EncryptInternal(key, iv, valueStream).ConfigureAwait(false);
return StreamToBytes(encryptedStream);
}
public async Task<Stream> Encrypt(string key, string iv, Stream value)
{
return value is null
? throw new ArgumentNullException(nameof(value))
: await this.EncryptInternal(StringToBytes(key), StringToBytes(iv), value).ConfigureAwait(false);
}
public async Task<Stream> Encrypt(byte[] key, byte[] iv, Stream value)
{
return value is null
? throw new ArgumentNullException(nameof(value))
: await this.EncryptInternal(key, iv, value).ConfigureAwait(false);
}
public Stream GetEncryptionStream(string key, string iv, Stream outStream)
{
return this.GetEncryptionStreamInternal(StringToBytes(key), StringToBytes(iv), outStream);
}
public Stream GetEncryptionStream(byte[] key, byte[] iv, Stream outStream)
{
return this.GetEncryptionStreamInternal(key, iv, outStream);
}
public Stream GetDecryptionStream(string key, string iv, Stream inStream)
{
return this.GetDecryptionStreamInternal(StringToBytes(key), StringToBytes(iv), inStream);
}
public Stream GetDecryptionStream(byte[] key, byte[] iv, Stream inStream)
{
return this.GetDecryptionStreamInternal(key, iv, inStream);
}
private async Task<Stream> EncryptInternal(byte[] key, byte[] iv, Stream toEncryptStream)
{
if (key.Length < 32)
{
throw new ArgumentException($"{nameof(key)} must be at least 32 bytes");
}
if (iv.Length < 16)
{
throw new ArgumentException($"{nameof(iv)} must be at least 16 bytes");
}
key = key.Take(32).ToArray();
iv = iv.Take(16).ToArray();
var encryptedMemoryStream = new MemoryStream();
using var cryptoStream = this.GetEncryptionStreamInternal(key, iv, encryptedMemoryStream);
await toEncryptStream.CopyToAsync(cryptoStream).ConfigureAwait(false);
if (!cryptoStream.HasFlushedFinalBlock)
{
cryptoStream.FlushFinalBlock();
}
encryptedMemoryStream.Position = 0;
return encryptedMemoryStream;
}
private async Task<Stream> DecryptInternal(byte[] key, byte[] iv, Stream toDecryptStream)
{
if (key.Length < 32)
{
throw new ArgumentException($"{nameof(key)} must be at least 32 bytes");
}
if (iv.Length < 16)
{
throw new ArgumentException($"{nameof(iv)} must be at least 16 bytes");
}
key = key.Take(32).ToArray();
iv = iv.Take(16).ToArray();
var decryptedMemoryStream = new MemoryStream();
using var cryptoStream = this.GetDecryptionStreamInternal(key, iv, toDecryptStream);
await cryptoStream.CopyToAsync(decryptedMemoryStream).ConfigureAwait(false);
if (!cryptoStream.HasFlushedFinalBlock)
{
cryptoStream.FlushFinalBlock();
}
decryptedMemoryStream.Position = 0;
return decryptedMemoryStream;
}
private CryptoStream GetEncryptionStreamInternal(byte[] key, byte[] iv, Stream encryptedStream)
{
var crypto = this.aes.CreateEncryptor(key, iv);
var cryptoStream = new NotClosingCryptoStream(encryptedStream, crypto, CryptoStreamMode.Write);
return cryptoStream;
}
private CryptoStream GetDecryptionStreamInternal(byte[] key, byte[] iv, Stream encryptedStream)
{
var crypto = this.aes.CreateDecryptor(key, iv);
var cryptoStream = new NotClosingCryptoStream(encryptedStream, crypto, CryptoStreamMode.Read);
return cryptoStream;
}
private static byte[] StreamToBytes(Stream value)
{
value.Position = 0;
var buffer = new byte[1024];
using var ms = new MemoryStream();
var read = -1;
do
{
read = value.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, read);
} while (read > 0);
return ms.ToArray();
}
private static string StreamToString(Stream value)
{
return Convert.ToBase64String(StreamToBytes(value));
}
private static byte[] StringToBytes(string value)
{
return Convert.FromBase64String(value);
}
private static Stream BytesToStream(byte[] value)
{
return new MemoryStream(value);
}
public void Dispose()
{
this.aes.Dispose();
}
}
@@ -0,0 +1,24 @@
using System.IO;
using System.Threading.Tasks;
namespace System.Security.Encryption;
public interface ISymmetricEncrypter
{
Stream GetEncryptionStream(string key, string iv, Stream outStream);
Stream GetEncryptionStream(byte[] key, byte[] iv, Stream outStream);
Stream GetDecryptionStream(string key, string iv, Stream inStream);
Stream GetDecryptionStream(byte[] key, byte[] iv, Stream inStream);
Task<string> Encrypt(string key, string iv, string value);
Task<string> Encrypt(byte[] key, byte[] iv, string value);
Task<byte[]> Encrypt(string key, string iv, byte[] value);
Task<byte[]> Encrypt(byte[] key, byte[] iv, byte[] value);
Task<Stream> Encrypt(string key, string iv, Stream value);
Task<Stream> Encrypt(byte[] key, byte[] iv, Stream value);
Task<string> Decrypt(string key, string iv, string value);
Task<string> Decrypt(byte[] key, byte[] iv, string value);
Task<byte[]> Decrypt(string key, string iv, byte[] value);
Task<byte[]> Decrypt(byte[] key, byte[] iv, byte[] value);
Task<Stream> Decrypt(string key, string iv, Stream value);
Task<Stream> Decrypt(byte[] key, byte[] iv, Stream value);
}
@@ -0,0 +1,101 @@
using System.Security.Cryptography;
using System.Text;
namespace System.Security.Encryption;
public sealed class SecureString
{
private static byte[] optionalEntropy;
private byte[] encryptedValue;
public string Value {
get => this.encryptedValue is not null ? Encoding.UTF8.GetString(ProtectedData.Unprotect(this.encryptedValue, optionalEntropy, DataProtectionScope.CurrentUser)) : null;
private set => this.encryptedValue = ProtectedData.Protect(Encoding.UTF8.GetBytes(value), optionalEntropy, DataProtectionScope.CurrentUser);
}
public SecureString(string value)
{
this.Value = value;
}
public override bool Equals(object obj)
{
if (obj is string)
{
return this == (obj as string);
}
else if (obj is SecureString)
{
return this == (obj as SecureString);
}
else
{
return base.Equals(obj);
}
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
public override string ToString()
{
return this.Value;
}
public static readonly SecureString Empty = new(string.Empty);
public static implicit operator string(SecureString ss) => ss is null ? string.Empty : ss.Value;
public static implicit operator SecureString(string s) => new(s);
public static SecureString operator +(SecureString ss1, SecureString ss2)
{
if (ss1 is null)
{
throw new ArgumentNullException(nameof(ss1));
}
if (ss2 is null)
{
throw new ArgumentNullException(nameof(ss2));
}
return new SecureString(ss1.Value + ss2.Value);
}
public static SecureString operator +(SecureString ss1, string s2)
{
if (ss1 is null)
{
throw new ArgumentNullException(nameof(ss1));
}
return new SecureString(ss1.Value + s2);
}
public static SecureString operator +(SecureString ss1, char c)
{
if (ss1 is null)
{
throw new ArgumentNullException(nameof(ss1));
}
return new SecureString(ss1.Value + c);
}
public static bool operator ==(SecureString ss1, SecureString ss2)
{
return ss1?.Value == ss2?.Value;
}
public static bool operator !=(SecureString ss1, SecureString ss2)
{
return !(ss1 == ss2);
}
public static bool operator ==(SecureString ss1, string s2)
{
return ss1?.Value == s2;
}
public static bool operator !=(SecureString ss1, string s2)
{
return !(ss1?.Value == s2);
}
public static void AddOptionalEntropy(byte[] entropy)
{
optionalEntropy = entropy;
}
}
@@ -0,0 +1,11 @@
using System.IO;
using System.Threading.Tasks;
namespace System.Security.Hashing;
public interface IHashingService
{
Task<string> Hash(string raw);
Task<byte[]> Hash(byte[] raw);
Task<Stream> Hash(Stream raw);
}
@@ -0,0 +1,46 @@
using System.Threading.Tasks;
namespace System.Security.Hashing;
public interface IPasswordHashingService
{
/// <summary>
/// Hash provided raw password and return the hashed value.
/// </summary>
/// <param name="raw">Base64 encoded password.</param>
/// <param name="salt">Base64 encoded salt to be used for hashing.</param>
/// <param name="length">Length of the resulting hash.</param>
/// <param name="iterations">Number of hashing iterations to be performed.</param>
/// <remarks>
/// The length of the hash will vary due to the base64 encoding of the resulting hash.
/// </remarks>
/// <returns>Base64 encoded hashed password.</returns>
Task<string> Hash(string raw, string salt, int length, int iterations);
/// <summary>
/// Hash provided raw password and return the hashed value.
/// </summary>
/// <param name="raw">Password to be hashed.</param>
/// <param name="salt">Salt to be used for hashing.</param>
/// <param name="length">Length of the resulting hash.</param>
/// <param name="iterations">Number of hashing iterations to be performed.</param>
/// <returns>Hashed password.</returns>
Task<byte[]> Hash(byte[] raw, byte[] salt, int length, int iterations);
/// <summary>
/// Verify provided password against a hashed password.
/// </summary>
/// <param name="hash">Base64 encoding of the previously hashed password.</param>
/// <param name="password">Base64 encoding of the password to be verified.</param>
/// <param name="salt">Salt used to hash the previous password.</param>
/// <param name="iterations">Number of iterations used to hash the previous password.</param>
/// <returns>Returns true if password matches the previously hashed password. Otherwise returns false.</returns>
Task<bool> VerifyPassword(string hash, string password, string salt, int iterations);
/// <summary>
/// Verify provided password against a hashed password.
/// </summary>
/// <param name="hash">Previously hashed password.</param>
/// <param name="password">Password to be verified.</param>
/// <param name="salt">Salt used to hash the previous password.</param>
/// <param name="iterations">Number of iterations used to hash the previous password.</param>
/// <returns>Returns true if password matches the previously hashed password. Otherwise returns false.</returns>
Task<bool> VerifyPassword(byte[] hash, byte[] password, byte[] salt, int iterations);
}
@@ -0,0 +1,100 @@
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace System.Security.Hashing;
public sealed class Rfc2898DeriveBytesPasswordHashingService : IPasswordHashingService
{
private const int MinimumIterations = 10000;
private const int MinimumHashLength = 32;
public Rfc2898DeriveBytesPasswordHashingService()
{
}
public async Task<string> Hash(string raw, string salt, int length, int iterations)
{
this.ValidateHashArguments(raw, salt, length, iterations);
var rawBytes = Convert.FromBase64String(raw);
var saltBytes = Convert.FromBase64String(salt);
var hashBytes = await this.HashInternal(rawBytes, saltBytes, length, iterations);
return Convert.ToBase64String(hashBytes);
}
public Task<byte[]> Hash(byte[] raw, byte[] salt, int length, int iterations)
{
this.ValidateHashArguments(raw, salt, length, iterations);
return this.HashInternal(raw, salt, length, iterations);
}
public Task<bool> VerifyPassword(string hash, string password, string salt, int iterations)
{
this.ValidateVerifyArguments(hash, password, salt, iterations);
var hashBytes = Convert.FromBase64String(hash);
var saltBytes = Convert.FromBase64String(salt);
var passwordBytes = Convert.FromBase64String(password);
return this.VerifyPasswordInternal(hashBytes, passwordBytes, saltBytes, iterations);
}
public Task<bool> VerifyPassword(byte[] hash, byte[] password, byte[] salt, int iterations)
{
this.ValidateVerifyArguments(hash, password, salt, iterations);
return this.VerifyPasswordInternal(hash, password, salt, iterations);
}
private void ValidateHashArguments(object raw, object salt, int length, int iterations)
{
_ = raw ?? throw new ArgumentNullException(nameof(raw));
_ = salt ?? throw new ArgumentNullException(nameof(salt));
if (iterations < MinimumIterations)
{
throw new InvalidOperationException($"Unable to perform secure hash. Iteration count must be over {MinimumIterations}");
}
if (length < MinimumHashLength)
{
throw new InvalidOperationException($"Unable to perform secure hash. Hash length must be over {MinimumHashLength}");
}
}
private void ValidateVerifyArguments(object hash, object password, object salt, int iterations)
{
_ = hash ?? throw new ArgumentNullException(nameof(hash));
_ = salt ?? throw new ArgumentNullException(nameof(salt));
_ = password ?? throw new ArgumentNullException(nameof(password));
if (iterations < MinimumIterations)
{
throw new InvalidOperationException($"Unable to verify hash. Iteration count must be over {MinimumIterations}");
}
}
private Task<byte[]> HashInternal(byte[] raw, byte[] salt, int length, int iterations)
{
using var pbkdf2 = new Rfc2898DeriveBytes(raw, salt, iterations);
var hash = pbkdf2.GetBytes(length);
return Task.FromResult(hash);
}
private Task<bool> VerifyPasswordInternal(byte[] hash, byte[] password, byte[] salt, int iterations)
{
using var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations);
var hashToVerify = pbkdf2.GetBytes(hash.Length);
var lengthToVerify = Math.Max(hash.Length, hashToVerify.Length);
var match = true;
if (lengthToVerify <= 0)
{
return Task.FromResult(false);
}
for(var i = 0; i < lengthToVerify; i++)
{
if (i < hash.Length && i < hashToVerify.Length)
{
if (hashToVerify[i].Equals(hash[i]) is false)
{
match = false;
}
}
}
return Task.FromResult(match && hash.Length.Equals(hashToVerify.Length));
}
}
@@ -0,0 +1,61 @@
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace System.Security.Hashing;
public sealed class Sha256HashingService : IHashingService
{
public async Task<string> Hash(string raw)
{
using var rawStream = BytesToStream(StringToBytes(raw));
using var hashedStream = await HashInternal(rawStream).ConfigureAwait(false);
return StreamToString(hashedStream);
}
public async Task<byte[]> Hash(byte[] raw)
{
using var rawStream = BytesToStream(raw);
using var hashedStream = await HashInternal(rawStream).ConfigureAwait(false);
return StreamToBytes(hashedStream);
}
public async Task<Stream> Hash(Stream raw)
{
return await HashInternal(raw);
}
private static byte[] StreamToBytes(Stream value)
{
value.Position = 0;
var buffer = new byte[1024];
using var ms = new MemoryStream();
var read = -1;
do
{
read = value.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, read);
} while (read > 0);
return ms.ToArray();
}
private static string StreamToString(Stream value)
{
return Convert.ToBase64String(StreamToBytes(value));
}
private static byte[] StringToBytes(string value)
{
return Convert.FromBase64String(value);
}
private static Stream BytesToStream(byte[] value)
{
return new MemoryStream(value);
}
private static async Task<Stream> HashInternal(Stream raw)
{
if (raw is null)
{
throw new ArgumentNullException(nameof(raw));
}
using var sha256 = SHA256.Create();
return await Task.Run(() => new MemoryStream(sha256.ComputeHash(raw)));
}
}
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Macocian Alexandru Victor
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
@@ -0,0 +1,58 @@
using System.Security.Cryptography;
namespace System.Security.Rng;
public sealed class CryptoRngProvider : ICryptoRngProvider, IDisposable
{
private readonly RNGCryptoServiceProvider rngProvider;
private bool disposedValue;
public CryptoRngProvider()
{
this.rngProvider = new RNGCryptoServiceProvider();
}
public CryptoRngProvider(CspParameters cspParams)
{
this.rngProvider = new RNGCryptoServiceProvider(cspParams);
}
public void GetBytes(byte[] data)
{
this.rngProvider.GetBytes(data);
}
public void GetNonZeroBytes(byte[] data)
{
this.rngProvider.GetNonZeroBytes(data);
}
public byte[] GetBytes(int byteCount)
{
var bytes = new byte[byteCount];
this.GetBytes(bytes);
return bytes;
}
public byte[] GetNonZeroBytes(int byteCount)
{
var bytes = new byte[byteCount];
this.GetNonZeroBytes(bytes);
return bytes;
}
private void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
this.rngProvider.Dispose();
}
this.disposedValue = true;
}
}
public void Dispose()
{
this.Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
@@ -0,0 +1,27 @@
namespace System.Security.Rng;
public interface ICryptoRngProvider
{
/// <summary>
/// Populate provided array of bytes with cryptographically secure random bytes.
/// </summary>
/// <param name="data">Array of bytes to be populated.</param>
public void GetBytes(byte[] data);
/// <summary>
/// Return an array of cryptographically secure random bytes.
/// </summary>
/// <param name="byteCount">Length of the returned array.</param>
/// <returns>Array containing cryptographically secure random values.</returns>
public byte[] GetBytes(int byteCount);
/// <summary>
/// Populate provided array of bytes with cryptographically secure random non-zero bytes.
/// </summary>
/// <param name="data">Array of bytes to be populated.</param>
public void GetNonZeroBytes(byte[] data);
/// <summary>
/// Return an array of cryptographically secure random non-zero bytes.
/// </summary>
/// <param name="byteCount">Length of the returned array.</param>
/// <returns>Array containing cryptographically secure random non-zero values.</returns>
public byte[] GetNonZeroBytes(int byteCount);
}
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RootNamespace>System</RootNamespace>
<Version>1.2.5</Version>
<Authors>Alexandru Macocian</Authors>
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
<Description>Security extensions for the System namespace</Description>
</PropertyGroup>
<ItemGroup>
<None Include="../LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="6.0.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,22 @@
using System.IO;
using System.Security.Cryptography;
namespace System.Security.Utilities;
internal sealed class NotClosingCryptoStream : CryptoStream
{
public NotClosingCryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode)
: base(stream, transform, mode)
{
}
protected override void Dispose(bool disposing)
{
if (!this.HasFlushedFinalBlock)
{
this.FlushFinalBlock();
}
base.Dispose(false);
}
}
@@ -0,0 +1,402 @@
using System.Collections.Generic;
namespace System.Collections.Generic;
/// <summary>
/// AVL tree implementation.
/// Thanks to Karim Oumghar for the implementation example.
/// Read on https://simpledevcode.wordpress.com/2014/09/16/avl-tree-in-c/
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
[Serializable]
public sealed class AVLTree<T> : ICollection<T> where T : IComparable<T>
{
#region Fields
[Serializable]
private class AVLNode<TKey>
{
public TKey Value;
public AVLNode<TKey> Left;
public AVLNode<TKey> Right;
public AVLNode(TKey value)
{
this.Value = value;
}
}
AVLNode<T> root;
private int count = 0;
private readonly bool isReadOnly = false;
#endregion
#region Properties
/// <summary>
/// Count of items currently stored in the tree.
/// </summary>
public int Count
{
get
{
return this.count;
}
}
/// <summary>
/// True if the collection is readonly. False otherwise.
/// </summary>
public bool IsReadOnly => this.isReadOnly;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of an AVLTree collection.
/// </summary>
public AVLTree()
{
}
#endregion
#region Public Methods
/// <summary>
/// Adds the value to the tree.
/// </summary>
/// <param name="value">Value to be added to the tree.</param>
public void Add(T value)
{
this.count++;
var newItem = new AVLNode<T>(value);
if (this.root == null)
{
this.root = newItem;
}
else
{
this.root = this.RecursiveInsertion(this.root, newItem);
}
}
/// <summary>
/// Checks if the key is contained into the tree.
/// </summary>
/// <param name="value">Value to be checked if present in the tree.</param>
/// <returns>True if the value is in the tree.</returns>
public bool Contains(T value)
{
var node = this.Find(value, this.root);
if (node == null)
{
return false;
}
if (node.Value.CompareTo(value) == 0)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// Removes the specified value from the tree.
/// </summary>
/// <param name="value">Value to be deleted.</param>
public bool Remove(T value)
{
this.root = this.Delete(this.root, value);
return true;
}
/// <summary>
/// Clears the tree.
/// </summary>
public void Clear()
{
var queue = new Queue<AVLNode<T>>();
queue.Enqueue(this.root);
while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
if (currentNode.Left != null)
{
queue.Enqueue(currentNode.Left);
currentNode.Left = null;
this.count--;
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
currentNode.Right = null;
this.count--;
}
}
this.root = null;
this.count--;
}
/// <summary>
/// Copies the tree onto the provided array.
/// </summary>
/// <param name="array">Array to store the values in the tree.</param>
/// <param name="arrayIndex">Starting index of the provided array.</param>
public void CopyTo(T[] array, int arrayIndex)
{
var queue = new Queue<AVLNode<T>>();
queue.Enqueue(this.root);
while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
array[arrayIndex++] = currentNode.Value;
if (currentNode.Left != null)
{
queue.Enqueue(currentNode.Left);
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
}
}
}
/// <summary>
/// Enumerator that iterates over the tree.
/// </summary>
/// <returns></returns>
public IEnumerator<T> GetEnumerator()
{
return this.GetEnumerator(this.root);
}
/// <summary>
/// Copies the tree structure into an array.
/// </summary>
/// <returns>Array containing the values contained in the tree.</returns>
public T[] ToArray()
{
var array = new T[this.count];
this.CopyTo(array, 0);
return array;
}
#endregion
#region Private Methods
private AVLNode<T> RecursiveInsertion(AVLNode<T> current, AVLNode<T> n)
{
if (current == null)
{
current = n;
return current;
}
else if (n.Value.CompareTo(current.Value) < 0)
{
current.Left = this.RecursiveInsertion(current.Left, n);
current = this.BalanceTree(current);
}
else if (n.Value.CompareTo(current.Value) > 0)
{
current.Right = this.RecursiveInsertion(current.Right, n);
current = this.BalanceTree(current);
}
return current;
}
private AVLNode<T> BalanceTree(AVLNode<T> current)
{
var b_factor = this.BalanceFactor(current);
if (b_factor > 1)
{
if (this.BalanceFactor(current.Left) > 0)
{
current = this.RotateLL(current);
}
else
{
current = this.RotateLR(current);
}
}
else if (b_factor < -1)
{
if (this.BalanceFactor(current.Right) > 0)
{
current = this.RotateRL(current);
}
else
{
current = this.RotateRR(current);
}
}
return current;
}
private AVLNode<T> Delete(AVLNode<T> current, T target)
{
AVLNode<T> parent;
if (current == null)
{ return null; }
else
{
//left subtree
if (target.CompareTo(current.Value) < 0)
{
current.Left = this.Delete(current.Left, target);
if (this.BalanceFactor(current) == -2)//here
{
if (this.BalanceFactor(current.Right) <= 0)
{
current = this.RotateRR(current);
}
else
{
current = this.RotateRL(current);
}
}
}
//right subtree
else if (target.CompareTo(current.Value) > 0)
{
current.Right = this.Delete(current.Right, target);
if (this.BalanceFactor(current) == 2)
{
if (this.BalanceFactor(current.Left) >= 0)
{
current = this.RotateLL(current);
}
else
{
current = this.RotateLR(current);
}
}
}
//if target is found
else
{
this.count--;
if (current.Right != null)
{
//delete its inorder successor
parent = current.Right;
while (parent.Left != null)
{
parent = parent.Left;
}
current.Value = parent.Value;
current.Right = this.Delete(current.Right, parent.Value);
if (this.BalanceFactor(current) == 2)//rebalancing
{
if (this.BalanceFactor(current.Left) >= 0)
{
current = this.RotateLL(current);
}
else { current = this.RotateLR(current); }
}
}
else
{ //if current.left != null
return current.Left;
}
}
}
return current;
}
private AVLNode<T> Find(T target, AVLNode<T> current)
{
if (current == null)
{
return null;
}
if (target.CompareTo(current.Value) < 0)
{
if (target.CompareTo(current.Value) == 0)
{
return current;
}
else
{
return this.Find(target, current.Left);
}
}
else
{
if (target.CompareTo(current.Value) == 0)
{
return current;
}
else
{
return this.Find(target, current.Right);
}
}
}
private int Max(int l, int r)
{
return l > r ? l : r;
}
private int GetHeight(AVLNode<T> current)
{
var height = 0;
if (current != null)
{
var l = this.GetHeight(current.Left);
var r = this.GetHeight(current.Right);
var m = this.Max(l, r);
height = m + 1;
}
return height;
}
private int BalanceFactor(AVLNode<T> current)
{
var l = this.GetHeight(current.Left);
var r = this.GetHeight(current.Right);
var b_factor = l - r;
return b_factor;
}
private AVLNode<T> RotateRR(AVLNode<T> parent)
{
var pivot = parent.Right;
parent.Right = pivot.Left;
pivot.Left = parent;
return pivot;
}
private AVLNode<T> RotateLL(AVLNode<T> parent)
{
var pivot = parent.Left;
parent.Left = pivot.Right;
pivot.Right = parent;
return pivot;
}
private AVLNode<T> RotateLR(AVLNode<T> parent)
{
var pivot = parent.Left;
parent.Left = this.RotateRR(pivot);
return this.RotateLL(parent);
}
private AVLNode<T> RotateRL(AVLNode<T> parent)
{
var pivot = parent.Right;
parent.Right = this.RotateLL(pivot);
return this.RotateRR(parent);
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
private IEnumerator<T> GetEnumerator(AVLNode<T> rootNode)
{
var queue = new Queue<AVLNode<T>>();
queue.Enqueue(rootNode);
while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
yield return currentNode.Value;
if (currentNode.Left != null)
{
queue.Enqueue(currentNode.Left);
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
}
}
}
#endregion
}
@@ -0,0 +1,219 @@
using System.Linq;
namespace System.Collections.Generic;
/// <summary>
/// Binary heap implementation.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
[Serializable]
public sealed class BinaryHeap<T> : IEnumerable<T> where T : IComparable<T>
{
#region Fields
T[] items;
private int capacity;
private int count;
private readonly int initialCapacity;
#endregion
#region Properties
/// <summary>
/// Minimum value from the heap.
/// </summary>
public T Min
{
get
{
return this.items[1];
}
}
/// <summary>
/// Maximum value from the heap.
/// </summary>
public T Max
{
get
{
return this.items[this.count];
}
}
/// <summary>
/// Capacity of the heap.
/// </summary>
public int Capacity
{
get => this.capacity;
set
{
if (value > this.capacity)
{
Array.Resize(ref this.items, value);
this.capacity = value;
}
}
}
/// <summary>
/// Number of elements in the heap.
/// </summary>
public int Count { get => this.count; }
#endregion
#region Constructors
/// <summary>
/// Constructor for a binary heap data structure.
/// </summary>
public BinaryHeap()
{
this.capacity = 10;
this.initialCapacity = this.capacity;
this.items = new T[this.capacity];
}
/// <summary>
/// Constructor for a binary heap data structure.
/// </summary>
/// <param name="capacity">Initial capacity of the heap. Used for initial alocation of the array.</param>
public BinaryHeap(int capacity)
{
this.capacity = capacity;
this.initialCapacity = capacity;
this.items = new T[capacity];
}
#endregion
#region Public Methods
/// <summary>
/// Adds value to the queue.
/// </summary>
/// <param name="value">Value to be added.</param>
public void Add(T value)
{
if (this.count == this.Capacity - 1)
{
this.Capacity = 2 * this.Capacity;
}
var position = ++this.count;
for (; position > 1 && value.CompareTo(this.items[position / 2]) < 0; position /= 2)
{
this.items[position] = this.items[position / 2];
}
this.items[position] = value;
}
/// <summary>
/// Removes the item at the root. Throws exception if there are no items in the heap.
/// </summary>
/// <returns>Value removed.</returns>
public T Remove()
{
if (this.count == 0)
{
throw new IndexOutOfRangeException("Heap is empty!");
}
var min = this.items[1];
this.items[1] = this.items[this.count--];
this.BubbleDown(1);
return min;
}
/// <summary>
/// Peeks at the item at the root. Throws exception if there are no items in the heap.
/// </summary>
/// <returns></returns>
public T Peek()
{
if (this.count == 0)
{
throw new IndexOutOfRangeException("Heap is empty!");
}
var min = this.items[1];
return min;
}
/// <summary>
/// Return the heap structure as an array
/// </summary>
/// <returns>Array with values sorted as in heap</returns>
public T[] ToArray()
{
var newArray = new T[this.count];
Array.Copy(this.items, 1, newArray, 0, this.count);
return newArray;
}
/// <summary>
/// Determines whether the heap contains specified value
/// </summary>
/// <param name="value">Value to locate in the heap</param>
/// <returns></returns>
public bool Contains(T value)
{
return this.items.Contains(value);
}
/// <summary>
/// Clears the heap
/// </summary>
public void Clear()
{
this.count = 0;
}
/// <summary>
/// Clears the heap.
/// </summary>
/// <param name="completeClear">Specifies if the underlying array should be cleared as well</param>
public void Clear(bool completeClear)
{
this.count = 0;
if (completeClear)
{
this.capacity = this.initialCapacity;
this.items = new T[this.initialCapacity];
}
}
/// <summary>
/// Returns an enumerator that iterates over the heap.
/// </summary>
/// <returns>Enumerator that iterates over the heap.</returns>
public IEnumerator<T> GetEnumerator()
{
for (var i = 0; i < this.count; i++)
{
yield return this.items[i + 1];
}
}
#endregion
#region Private Methods
/// <summary>
/// Bubble the specified element to its position
/// </summary>
/// <param name="index">Index of element to bubble</param>
private void BubbleDown(int index)
{
var temp = this.items[index];
int childIndex;
for (; 2 * index <= this.count; index = childIndex)
{
childIndex = 2 * index;
if (childIndex != this.Count && this.items[childIndex].CompareTo(this.items[childIndex + 1]) > 0)
{
childIndex++;
}
if (temp.CompareTo(this.items[childIndex]) > 0)
{
this.items[index] = this.items[childIndex];
}
else
{
break;
}
}
this.items[index] = temp;
}
/// <summary>
/// Implementation of IEnumerator.
/// </summary>
/// <returns>Enumerator over the array.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
@@ -0,0 +1,579 @@
namespace System.Collections.Generic;
/// <summary>
/// Fibonacci Heap implementation.
/// </summary>
/// <typeparam name="T">Provided type</typeparam>
[Serializable]
public sealed class FibonacciHeap<T> : IEnumerable<T> where T : IComparable<T>
{
#region Fields
private FibonacciNode<T> root;
private int count;
#endregion
#region Properties
/// <summary>
/// Count of values in the heap.
/// </summary>
public int Count
{
get
{
return this.count;
}
}
/// <summary>
/// Minimal value contained in the heap.
/// </summary>
public T Minimum
{
get
{
return this.root.Value;
}
}
#endregion
#region Constructors
/// <summary>
/// Constructor for Fibonacci heap data structure.
/// </summary>
public FibonacciHeap()
{
}
#endregion
#region Public Methods
/// <summary>
/// Adds value to the heap.
/// </summary>
/// <param name="value">Value to be added.</param>
public void Add(T value)
{
var node = new FibonacciNode<T>
{
Value = value,
Marked = false,
Child = null,
Parent = null,
Degree = 0
};
node.Previous = node.Next = node;
this.root = this.Merge(this.root, node);
this.count++;
}
/// <summary>
/// Merge current heap with another heap. The other heap will be disposed at the end of this method.
/// </summary>
/// <param name="otherHeap">The heap to be merged with the current heap.</param>
public void Merge(FibonacciHeap<T> otherHeap)
{
this.root = this.Merge(this.root, otherHeap.root);
otherHeap.root = null;
this.count += otherHeap.count;
}
/// <summary>
/// Remove the minimum value from the heap.
/// </summary>
/// <returns>Minimum value.</returns>
public T Remove()
{
var currentRoot = this.root;
if (currentRoot != null)
{
this.root = this.RemoveMinimum(this.root);
this.count--;
return currentRoot.Value;
}
else
{
throw new IndexOutOfRangeException("Heap is empty!");
}
}
/// <summary>
/// Decrease the old value to a new provided value.
/// </summary>
/// <param name="oldValue">Old value used to find the node to have its key decreased.</param>
/// <param name="value">New value to be assigned to the node.</param>
public void DecreaseKey(T oldValue, T value)
{
var node = this.Find(this.root, oldValue);
this.root = this.DecreaseKey(this.root, node, value);
}
/// <summary>
/// Determines whether the heap contains a specified value.
/// </summary>
/// <param name="value">Value to locate in the heap.</param>
/// <returns></returns>
public bool Contains(T value)
{
return this.Find(this.root, value) != null;
}
/// <summary>
/// Clears the heap.
/// </summary>
public void Clear()
{
this.count = 0;
this.Remove(this.root);
this.root.Next = this.root.Previous = this.root.Parent = this.root.Child = null;
this.root = null;
}
/// <summary>
/// Return the heap structure as an array. Array is not sorted other than the
/// actual structure of the heap.
/// </summary>
/// <returns>Array with values from the heap.</returns>
public T[] ToArray()
{
if (this.count == 0)
{
return null;
}
var array = new T[this.count];
if (this.count == 1)
{
array[0] = this.root.Value;
return array;
}
else
{
var index = 0;
this.RecursiveFillArray(this.root, ref array, ref index);
return array;
}
}
/// <summary>
/// Enumerator that iterates over the heap. Note that the values are not sorted in any way.
/// </summary>
/// <returns>Enumerator that iterates over the heap.</returns>
public IEnumerator<T> GetEnumerator()
{
return this.GetEnumerator(this.root);
}
#endregion
#region Private Methods
/// <summary>
/// Recursively traverse the heap and copy its contents to an array.
/// </summary>
/// <param name="currentNode">Current node.</param>
/// <param name="array">Array to be filled with contents of heap.</param>
/// <param name="index">Index of the next unintialized element in the array.</param>
private void RecursiveFillArray(FibonacciNode<T> currentNode, ref T[] array, ref int index)
{
var oldNode = currentNode;
do
{
array[index] = currentNode.Value;
index++;
if (currentNode.HasChildren())
{
this.RecursiveFillArray(currentNode.Child, ref array, ref index);
}
currentNode = currentNode.Previous;
} while (currentNode != oldNode);
}
/// <summary>
/// Recursively enumerates over the tree.
/// </summary>
/// <param name="currentNode">Current node in the iteration.</param>
private IEnumerator<T> GetEnumerator(FibonacciNode<T> currentNode)
{
var queue = new Queue<FibonacciNode<T>>();
queue.Enqueue(currentNode);
while (queue.Count > 0)
{
currentNode = queue.Dequeue();
var oldNode = currentNode;
do
{
yield return currentNode.Value;
if (currentNode.HasChildren())
{
queue.Enqueue(currentNode.Child);
}
currentNode = currentNode.Previous;
} while (currentNode != oldNode);
}
}
/// <summary>
/// Recursively remove the node and its children from the heap.
/// </summary>
/// <param name="node">Node to be removed.</param>
private void Remove(FibonacciNode<T> node)
{
if (node != null)
{
var current = node;
do
{
this.Remove(current.Child);
if (current.Parent != null)
{
current.Parent.Child = null;
}
current = current.Next;
} while (current != node);
current.Next = current.Previous = current.Child = current.Parent = null;
}
}
/// <summary>
/// Merge two heaps into a larger heap.
/// </summary>
/// <param name="node1">Root of first heap.</param>
/// <param name="node2">Root of second heap.</param>
private FibonacciNode<T> Merge(FibonacciNode<T> node1, FibonacciNode<T> node2)
{
if (node1 == null)
{
return node2;
}
if (node2 == null)
{
return node1;
}
if (node1.Value.CompareTo(node2.Value) > 0)
{
var temp = node1;
node1 = node2;
node2 = temp;
}
var node1Next = node1.Next;
var node2Prev = node2.Previous;
node1.Next = node2;
node2.Previous = node1;
node1Next.Previous = node2Prev;
node2Prev.Next = node1Next;
return node1;
}
/// <summary>
/// Adds child to the parent.
/// </summary>
/// <param name="parent">Parent node to accept child.</param>
/// <param name="child">Child node to be added to the parent.</param>
private void AddChild(FibonacciNode<T> parent, FibonacciNode<T> child)
{
child.Previous = child.Next = child;
child.Parent = parent;
parent.Degree++;
parent.Child = this.Merge(parent.Child, child);
}
/// <summary>
/// Removes the parent of the specified node.
/// </summary>
/// <param name="node">Node to be removed from its parent.</param>
private void RemoveParent(FibonacciNode<T> node)
{
if (node == null)
{
return;
}
var current = node;
do
{
current.Marked = false;
current.Parent = null;
current = current.Next;
} while (current != node);
}
/// <summary>
/// Removes the minimum node from the provided tree.
/// </summary>
/// <param name="node">Root of the provided tree.</param>
/// <returns></returns>
private FibonacciNode<T> RemoveMinimum(FibonacciNode<T> node)
{
this.RemoveParent(node.Child);
if (node.Next == node)
{
node = node.Child;
}
else
{
node.Next.Previous = node.Previous;
node.Previous.Next = node.Next;
node = this.Merge(node.Next, node.Child);
}
if (node == null)
{
return node;
}
var trees = new FibonacciNode<T>[64];
while (true)
{
if (trees[node.Degree] != null)
{
var t = trees[node.Degree];
if (t == node)
{
break;
}
trees[node.Degree] = null;
if (node.Value.CompareTo(t.Value) < 0)
{
t.Previous.Next = t.Next;
t.Next.Previous = t.Previous;
this.AddChild(node, t);
}
else
{
t.Previous.Next = t.Next;
t.Next.Previous = t.Previous;
if (node.Next == node)
{
t.Next = t.Previous = t;
this.AddChild(t, node);
node = t;
}
else
{
node.Previous.Next = t;
node.Next.Previous = t;
t.Next = node.Next;
t.Previous = node.Previous;
this.AddChild(t, node);
node = t;
}
}
continue;
}
else
{
trees[node.Degree] = node;
}
node = node.Next;
}
var min = node;
var start = node;
do
{
if (node.Value.CompareTo(min.Value) < 0)
{
min = node;
}
node = node.Next;
} while (node != start);
return min;
}
/// <summary>
/// Cut node from heap.
/// </summary>
/// <param name="root">Root of heap.</param>
/// <param name="node">Node to be cut.</param>
/// <returns></returns>
private FibonacciNode<T> Cut(FibonacciNode<T> root, FibonacciNode<T> node)
{
if (node.Next == node)
{
node.Parent.Child = null;
}
else
{
node.Next.Previous = node.Previous;
node.Previous.Next = node.Next;
node.Parent.Child = node.Next;
}
node.Next = node.Previous = node;
node.Marked = false;
return this.Merge(root, node);
}
/// <summary>
/// Decrease value of provided node substituting it with the provided value.
/// </summary>
/// <param name="root">Root of the heap.</param>
/// <param name="node">Node to have value decreased.</param>
/// <param name="value">New value of the node. It is only applied if the value is lower than the previous value.</param>
/// <returns></returns>
private FibonacciNode<T> DecreaseKey(FibonacciNode<T> root, FibonacciNode<T> node, T value)
{
if (node.Value.CompareTo(value) < 0)
{
return root;
}
node.Value = value;
if (node.Parent != null)
{
if (node.Value.CompareTo(node.Parent.Value) < 0)
{
root = this.Cut(root, node);
var parent = node.Parent;
node.Parent = null;
while (parent != null && parent.Marked)
{
root = this.Cut(root, parent);
node = parent;
parent = node.Parent;
node.Parent = null;
}
if (parent != null && parent.Parent != null)
{
parent.Marked = true;
}
}
}
else
{
if (node.Value.CompareTo(root.Value) < 0)
{
root = node;
}
}
return root;
}
/// <summary>
/// Find the node that has the specified value in the heap.
/// </summary>
/// <param name="root">Root of the heap.</param>
/// <param name="value">Value to be found.</param>
/// <returns></returns>
private FibonacciNode<T> Find(FibonacciNode<T> root, T value)
{
var node = root;
if (node == null)
{
return null;
}
do
{
if (node.Value.CompareTo(value) == 0)
{
return node;
}
var ret = this.Find(node.Child, value);
if (ret != null)
{
return ret;
}
node = node.Next;
} while (node != root);
return null;
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
[Serializable]
internal sealed class FibonacciNode<T>
{
#region Fields
private FibonacciNode<T> previous;
private FibonacciNode<T> next;
private FibonacciNode<T> child;
private FibonacciNode<T> parent;
private T value;
private int degree;
private bool marked;
#endregion
#region Properties
public FibonacciNode<T> Previous
{
get
{
return this.previous;
}
set
{
this.previous = value;
}
}
public FibonacciNode<T> Next
{
get
{
return this.next;
}
set
{
this.next = value;
}
}
public FibonacciNode<T> Child
{
get
{
return this.child;
}
set
{
this.child = value;
}
}
public FibonacciNode<T> Parent
{
get
{
return this.parent;
}
set
{
this.parent = value;
}
}
public bool Marked
{
get
{
return this.marked;
}
set
{
this.marked = value;
}
}
public T Value
{
get
{
return this.value;
}
set
{
this.value = value;
}
}
public int Degree
{
get
{
return this.degree;
}
set
{
this.degree = value;
}
}
#endregion
#region Public Methods
public bool HasChildren()
{
return this.child != null;
}
public bool HasParent()
{
return this.parent != null;
}
#endregion
}
@@ -0,0 +1,38 @@
namespace System.Collections.Generic;
/// <summary>
/// Interface for queue implementations.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
public interface IQueue<T> : IEnumerable<T>
{
/// <summary>
/// Returns the number of items in the queue.
/// </summary>
int Count { get; }
/// <summary>
/// Inserts item into the queue.
/// </summary>
/// <param name="item">Item to be inserted.</param>
void Enqueue(T item);
/// <summary>
/// Remove the first item in the queue.
/// </summary>
/// <returns>First item in the queue.</returns>
T Dequeue();
/// <summary>
/// Looks up the first item from the queue without removing it.
/// </summary>
/// <returns>First item from the queue.</returns>
T Peek();
/// <summary>
/// Clears the contents of the queue.
/// </summary>
void Clear();
/// <summary>
/// Checks if queue contains an item.
/// </summary>
/// <param name="item">Item to be checked.</param>
/// <returns>True if queue contains provided item. False otherwise.</returns>
bool Contains(T item);
}
@@ -0,0 +1,102 @@
namespace System.Collections.Generic;
/// <summary>
/// Priority Queue data structure. The implementation is based on an array-based implementation of Binary Heap.
/// Exposes some of the functionality of the Binary Heap as a queue.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
[Serializable]
public sealed class PriorityQueue<T> : IQueue<T> where T : IComparable<T>
{
#region Fields
private readonly BinaryHeap<T> binaryHeap;
#endregion
#region Properties
/// <summary>
/// Returns the number of elements stored into the queue.
/// </summary>
public int Count
{
get
{
return this.binaryHeap.Count;
}
}
#endregion
#region Constructors
/// <summary>
/// Constructor for priority queue data structure.
/// </summary>
public PriorityQueue()
{
this.binaryHeap = new BinaryHeap<T>();
}
#endregion
#region Public Methods
/// <summary>
/// Add provided value to the queue.
/// </summary>
/// <param name="value">Value to be added to the queue.</param>
public void Enqueue(T value)
{
this.binaryHeap.Add(value);
}
/// <summary>
/// Pops the queue and removes the highest priority value from the queue.
/// </summary>
/// <returns>Highest priority value from the queue</returns>
public T Dequeue()
{
if (this.Count > 0)
{
return this.binaryHeap.Remove();
}
else
{
throw new IndexOutOfRangeException("Queue is empty!");
}
}
/// <summary>
/// Looks up the highest priority value from the queue. Doesn't alter the queue in any way.
/// </summary>
/// <returns>Highest priority value from the queue.</returns>
public T Peek()
{
return this.binaryHeap.Min;
}
/// <summary>
/// Clears the queue contents, removing any value stored into the queue.
/// </summary>
public void Clear()
{
this.binaryHeap.Clear();
}
/// <summary>
/// Checks if queue contains provided item.
/// </summary>
/// <param name="item">Item to be checked.</param>
/// <returns>True if queue contains the provided item. False otherwise.</returns>
public bool Contains(T item)
{
return this.binaryHeap.Contains(item);
}
/// <summary>
/// Returns an enumerator that iterates over the queue.
/// </summary>
/// <returns>Enumerator that iterates over the queue.</returns>
public IEnumerator<T> GetEnumerator()
{
return this.binaryHeap.GetEnumerator();
}
#endregion
#region Private Methods
/// <summary>
/// Necesarry for the implementatio of IQueue.
/// </summary>
/// <returns></returns>
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
@@ -0,0 +1,248 @@
namespace System.Collections.Generic;
/// <summary>
/// Skip list implementation.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
[Serializable]
public sealed class SkipList<T> : ICollection<T> where T : IComparable<T>
{
#region Fields
[Serializable]
private class NodeSet<TKey>
{
public TKey Key;
public int Level;
public NodeSet<TKey>[] Next;
public NodeSet(TKey key, int level)
{
this.Key = key;
this.Level = level;
this.Next = new NodeSet<TKey>[level + 1];
}
}
private int count;
private readonly Random random;
private readonly NodeSet<T> head;
private readonly NodeSet<T> end;
private readonly int maxLevel = 10;
private int level;
#endregion
#region Properties
/// <summary>
/// Number of elements in the list.
/// </summary>
public int Count { get => this.count; }
/// <summary>
/// Specifies if the collection can be modified.
/// </summary>
public bool IsReadOnly { get; set; }
#endregion
#region Constructors
/// <summary>
/// Creates a new instance of SkipList collection.
/// </summary>
/// <param name="maxLevel">Maximum level of the skip list.</param>
public SkipList(int maxLevel = 10)
{
this.maxLevel = maxLevel;
this.random = new Random();
this.head = new NodeSet<T>(default, maxLevel);
this.end = this.head;
for (var i = 0; i <= maxLevel; i++)
{
this.head.Next[i] = this.end;
}
}
#endregion
#region Public Methods
/// <summary>
/// Adds an item to the collection.
/// </summary>
/// <param name="item">Item to be added.</param>
public void Add(T item)
{
var curNode = this.head;
var newLevel = 0;
while (this.random.Next(0, 2) > 0 && newLevel < this.maxLevel)
{
newLevel++;
}
if (newLevel > this.level)
{
this.level = newLevel;
}
var newNode = new NodeSet<T>(item, newLevel);
for (var i = 0; i <= newLevel; i++)
{
if (i > curNode.Level)
{
curNode = this.head;
}
while (curNode.Next[i] != this.end && curNode.Next[i].Key.CompareTo(item) < 0)
{
curNode = curNode.Next[i];
}
newNode.Next[i] = curNode.Next[i];
curNode.Next[i] = newNode;
}
this.count++;
}
/// <summary>
/// Removes provided item from the collection.
/// </summary>
/// <param name="item">Item to be removed.</param>
/// <returns>True if removal was successful.</returns>
public bool Remove(T item)
{
var removed = false;
var curNode = this.head;
for (var i = 0; i <= this.maxLevel; i++)
{
if (i > curNode.Level)
{
curNode = this.head;
}
while (curNode.Next[i] != this.end && curNode.Next[i].Key.CompareTo(item) < 0)
{
curNode = curNode.Next[i];
}
if (curNode.Next[i].Key.CompareTo(item) == 0) //Item is present on this level
{
curNode.Next[i] = curNode.Next[i].Next[i];
removed = true;
}
else
{
break;
}
}
if (removed)
{
this.count--;
return true;
}
else
{
return false;
}
}
/// <summary>
/// Clears the collection.
/// </summary>
public void Clear()
{
for (var i = 0; i < this.maxLevel; i++)
{
this.head.Next[i] = this.end;
}
this.count = 0;
}
/// <summary>
/// Checks if item is present in the collection.
/// </summary>
/// <param name="item">Item to be checked.</param>
/// <returns>True if item is present in the collection.</returns>
public bool Contains(T item)
{
if (this.Find(item) != null)
{
return true;
}
return false;
}
/// <summary>
/// Copies the skip list contents onto the provided array.
/// </summary>
/// <param name="array">Array to hold the values from the list.</param>
/// <param name="arrayIndex">Index to start insertion in the array.</param>
public void CopyTo(T[] array, int arrayIndex)
{
var node = this.head.Next[0];
while (node != this.end)
{
array[arrayIndex] = node.Key;
arrayIndex++;
node = node.Next[0];
}
}
/// <summary>
/// Copies the elements from the collection into an array.
/// </summary>
/// <returns>Array filled with elements from the collection.</returns>
public T[] ToArray()
{
var array = new T[this.count];
var index = 0;
var curNode = this.head.Next[0];
while (curNode != this.end)
{
array[index] = curNode.Key;
index++;
curNode = curNode.Next[0];
}
return array;
}
/// <summary>
/// Enumerator that iterates over the collection.
/// </summary>
/// <returns></returns>
public IEnumerator<T> GetEnumerator()
{
var curNode = this.head.Next[0];
while (curNode != this.end)
{
yield return curNode.Key;
curNode = curNode.Next[0];
}
}
#endregion
#region Private Methods
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
private NodeSet<T> Find(T key)
{
var curNode = this.head;
for (var i = this.level; i >= 0; i--)
{
while (curNode.Next[i] != this.end)
{
if (curNode.Next[i].Key.CompareTo(key) > 0)
{
break;
}
else if (curNode.Next[i].Key.CompareTo(key) == 0)
{
return curNode.Next[i];
}
curNode = curNode.Next[i];
}
}
curNode = curNode.Next[0];
if (curNode != this.end && curNode.Key.CompareTo(key) == 0)
{
return curNode;
}
return null;
}
#endregion
}
@@ -0,0 +1,287 @@
namespace System.Collections.Generic;
/// <summary>
/// Treap implementation.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
[Serializable]
public sealed class Treap<T> : ICollection<T> where T : IComparable<T>
{
#region Fields
[Serializable]
private class Node<TKey>
{
public TKey Key;
public int Priority;
public Node<TKey> Left, Right;
public Node(TKey key, int priority)
{
this.Key = key;
this.Priority = priority;
this.Left = null;
this.Right = null;
}
}
private readonly Random randomGen;
private Node<T> root;
private int count;
#endregion
#region Properties
/// <summary>
/// Count of values in the treap.
/// </summary>
public int Count
{
get
{
return this.count;
}
}
/// <summary>
/// Not implemented.
/// </summary>
public bool IsReadOnly => false;
#endregion
#region Constructors
/// <summary>
/// Constructor for treap.
/// </summary>
public Treap()
{
this.randomGen = new Random();
}
#endregion
#region Public Methods
/// <summary>
/// Adds value to the treap.
/// </summary>
/// <param name="value">Value to be added.</param>
public void Add(T value)
{
this.root = this.InsertNode(this.root, value);
this.count++;
}
/// <summary>
/// Removes value from treap.
/// </summary>
/// <param name="value">Value to be removed.</param>
public bool Remove(T value)
{
this.root = this.RemoveNode(this.root, value);
this.count--;
return true;
}
/// <summary>
/// Clears the treap.
/// </summary>
public void Clear()
{
this.Clear(this.root);
this.root = null;
this.count = 0;
}
/// <summary>
/// Determines whether the treap contains the specified value.
/// </summary>
/// <param name="value">Value to locate in the treap.</param>
/// <returns></returns>
public bool Contains(T value)
{
return this.Find(this.root, value) != null;
}
/// <summary>
/// Returns the treap structure as an ordered array.
/// </summary>
/// <returns>Ordered array containing the values stored in the treap.</returns>
public T[] ToArray()
{
if (this.root != null)
{
var array = new T[this.count];
var index = 0;
this.ToArray(this.root, ref array, ref index);
return array;
}
else
{
return null;
}
}
/// <summary>
/// Copy the treap into the provided array.
/// </summary>
/// <param name="array">Array to be populated with the values contained in the array.</param>
/// <param name="arrayIndex">Starting index of the array.</param>
public void CopyTo(T[] array, int arrayIndex)
{
this.ToArray(this.root, ref array, ref arrayIndex);
}
/// <summary>
/// Enumerator that iterates over the treap. Note that the values are not guaranteed to be sorted.
/// </summary>
/// <returns>Enumerator that iterates over the treap.</returns>
public IEnumerator<T> GetEnumerator()
{
return this.GetEnumerator(this.root);
}
#endregion
#region Private Methods
private Node<T> InsertNode(Node<T> node, T key)
{
if (node == null)
{
node = new Node<T>(key, this.randomGen.Next(0, 100));
return node;
}
else if (key.CompareTo(node.Key) <= 0)
{
node.Left = this.InsertNode(node.Left, key);
if (node.Left.Priority > node.Priority)
{
node = this.RotateRight(node);
}
}
else
{
node.Right = this.InsertNode(node.Right, key);
if (node.Right.Priority > node.Priority)
{
node = this.RotateLeft(node);
}
}
return node;
}
private Node<T> RemoveNode(Node<T> node, T key)
{
if (node == null)
{
return node;
}
if (key.CompareTo(node.Key) < 0)
{
node.Left = this.RemoveNode(node.Left, key);
}
else if (key.CompareTo(node.Key) > 0)
{
node.Right = this.RemoveNode(node.Right, key);
}
else if (node.Left == null)
{
node = node.Right;
}
else if (node.Right == null)
{
node = node.Left;
}
else if (node.Left.Priority < node.Right.Priority)
{
node = this.RotateLeft(node);
node.Left = this.RemoveNode(node.Left, key);
}
else
{
node = this.RotateRight(node);
node.Right = this.RemoveNode(node.Right, key);
}
return node;
}
private Node<T> RotateRight(Node<T> node)
{
Node<T> temp = node.Left, temp2 = temp.Right;
temp.Right = node;
node.Left = temp2;
return temp;
}
private Node<T> RotateLeft(Node<T> node)
{
Node<T> temp = node.Right, temp2 = temp.Left;
temp.Left = node;
node.Right = temp2;
return temp;
}
private void Clear(Node<T> node)
{
if (node.Left != null)
{
this.Clear(node.Left);
}
if (node.Right != null)
{
this.Clear(node.Right);
}
node.Left = node.Right = null;
}
private Node<T> Find(Node<T> node, T key)
{
if (node == null)
{
return node;
}
else
{
if (node.Key.CompareTo(key) < 0)
{
var found = this.Find(node.Left, key);
if (found == null)
{
found = this.Find(node.Right, key);
}
return found;
}
else if (node.Key.CompareTo(key) > 0)
{
var found = this.Find(node.Right, key);
if (found == null)
{
found = this.Find(node.Left, key);
}
return found;
}
else
{
return node;
}
}
}
private void ToArray(Node<T> node, ref T[] array, ref int index)
{
if (node != null)
{
this.ToArray(node.Left, ref array, ref index);
array[index] = node.Key;
index++;
this.ToArray(node.Right, ref array, ref index);
}
}
private IEnumerator<T> GetEnumerator(Node<T> currentNode)
{
var queue = new Queue<Node<T>>();
queue.Enqueue(currentNode);
while (queue.Count > 0)
{
currentNode = queue.Dequeue();
yield return currentNode.Key;
if (currentNode.Left != null)
{
queue.Enqueue(currentNode.Left);
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
@@ -0,0 +1,24 @@
using System.IO;
namespace System.Extensions;
public static class BytesExtensions
{
public static byte[] ReadAllBytes(this Stream stream)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
var buffer = new byte[256];
using var ms = new MemoryStream();
int read;
while ((read = stream.Read(buffer, 0, 256)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
}
@@ -0,0 +1,49 @@
namespace System.Extensions;
public static class CastExtensions
{
public static int Floor(this double value)
{
return (int)Math.Floor(value);
}
public static int Ceiling(this double value)
{
return (int)Math.Ceiling(value);
}
public static int Round(this double value)
{
return (int)Math.Round(value);
}
public static int Floor(this float value)
{
return (int)Math.Floor(value);
}
public static int Ceiling(this float value)
{
return (int)Math.Ceiling(value);
}
public static int Round(this float value)
{
return (int)Math.Round(value);
}
public static int ToInt(this double value)
{
return (int)value;
}
public static int ToInt(this float value)
{
return (int)value;
}
public static int ToInt(this decimal value)
{
return (int)value;
}
}
@@ -0,0 +1,88 @@
using System.Collections.Generic;
using System.Linq;
namespace System.Extensions;
public static class LinqExtensions
{
public static bool None<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
enumerable.ThrowIfNull(nameof(enumerable));
predicate.ThrowIfNull(nameof(predicate));
return !enumerable.Any(predicate);
}
public static bool None<T>(this IEnumerable<T> enumerable)
{
enumerable.ThrowIfNull(nameof(enumerable));
return !enumerable.Any();
}
public static ICollection<T> ClearAnd<T>(this ICollection<T> collection)
{
if (collection is null)
{
throw new ArgumentNullException(nameof(collection));
}
collection.Clear();
return collection;
}
public static ICollection<T> AddRange<T>(this ICollection<T> collection, IEnumerable<T> values)
{
if (collection is null)
{
throw new ArgumentNullException(nameof(collection));
}
if (values is null)
{
throw new ArgumentNullException(nameof(values));
}
foreach (var item in values)
{
collection.Add(item);
}
return collection;
}
public static int IndexOfWhere<T>(this ICollection<T> collection, Func<T, bool> selector)
{
if (collection is null)
{
throw new ArgumentNullException(nameof(collection));
}
if (selector is null)
{
throw new ArgumentNullException(nameof(selector));
}
var index = 0;
foreach (var item in collection)
{
if (selector(item))
{
return index;
}
index++;
}
return -1;
}
public static IEnumerable<T> Do<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach(var value in enumerable)
{
action(value);
yield return value;
}
}
}
@@ -0,0 +1,12 @@
using Microsoft.Extensions.Logging;
using System.Logging;
namespace System.Extensions;
public static class LoggingExtensions
{
public static ScopedLogger<T> CreateScopedLogger<T>(this ILogger<T> logger, string methodName, string flowIdentifier)
{
return ScopedLogger<T>.Create(logger, methodName, flowIdentifier);
}
}
@@ -0,0 +1,58 @@
using Newtonsoft.Json;
namespace System.Extensions;
public static class ObjectExtensions
{
public static T Deserialize<T>(this string serialized)
where T : class
{
if (serialized.IsNullOrWhiteSpace())
{
throw new ArgumentException("Provided serialized string cannot be null or whitespace", nameof(serialized));
}
return JsonConvert.DeserializeObject<T>(serialized);
}
public static string Serialize<T>(this T obj)
where T : class
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
return JsonConvert.SerializeObject(obj);
}
public static T Cast<T>(this object obj)
{
return (T)obj;
}
public static T As<T>(this object obj) where T : class
{
return obj as T;
}
public static Optional<T> ToOptional<T>(this T obj)
{
return Optional.FromValue(obj);
}
public static T ThrowIfNull<T>([ValidatedNotNull] this T obj, string name) where T : class
{
if (obj is null)
{
throw new ArgumentNullException(name);
}
return obj;
}
[AttributeUsage(AttributeTargets.Parameter)]
sealed class ValidatedNotNullAttribute : Attribute
{
}
}
@@ -0,0 +1,193 @@
namespace System.Extensions;
public static class Optional
{
public static Optional<T> None<T>()
{
return new Optional<T>.None();
}
public static Optional<T> FromValue<T>(T value)
{
if (value == null)
{
return new Optional<T>.None();
}
return new Optional<T>.Some(value);
}
}
public abstract class Optional<T> : IEquatable<Optional<T>>
{
public Optional(T value)
{
this.Value = value;
}
private T Value { get; }
public T ExtractValue()
{
if (this is None)
{
return default;
}
else
{
return this.Value;
}
}
public Optional<T> Do(Action<T> onSome, Action onNone)
{
if (onSome is null)
{
throw new ArgumentNullException(nameof(onSome));
}
if (onNone is null)
{
throw new ArgumentNullException(nameof(onNone));
}
if (this is Some)
{
onSome.Invoke(this.Value);
}
else
{
onNone.Invoke();
}
return this;
}
public Optional<T> DoAny(Action<T> onSome = null, Action onNone = null)
{
if (this is Some)
{
onSome?.Invoke(this.Value);
}
else
{
onNone?.Invoke();
}
return this;
}
public Optional<V> Switch<V>(Func<T, V> onSome, Func<V> onNone)
{
if (onSome is null)
{
throw new ArgumentNullException($"{nameof(onSome)}");
}
if (onNone is null)
{
throw new ArgumentNullException($"{nameof(onNone)}");
}
if (this is Some)
{
return Optional.FromValue(onSome.Invoke(this.Value));
}
else
{
return Optional.FromValue(onNone.Invoke());
}
}
public Optional<V> SwitchAny<V>(Func<T, V> onSome = null, Func<V> onNone = null)
{
if (this is Some)
{
return onSome != null ? Optional.FromValue(onSome.Invoke(this.Value)) : Optional.FromValue(default(V));
}
else
{
return onNone != null ? Optional.FromValue(onNone.Invoke()) : Optional.FromValue(default(V));
}
}
public override bool Equals(object obj)
{
if (obj is Optional<T>)
{
return this.Equals(obj);
}
return base.Equals(obj);
}
public bool Equals(Optional<T> other)
{
if (this is Some && other is Some)
{
return this.As<Some>().Equals(other.As<Some>());
}
return false;
}
public static implicit operator Optional<T>(T value)
{
return Optional.FromValue(value);
}
public override int GetHashCode()
{
return this.Value == null ? base.GetHashCode() : this.Value.GetHashCode();
}
internal class Some : Optional<T>, IEquatable<Some>
{
public Some(T value) : base(value)
{
}
public override bool Equals(object obj)
{
if (obj is Some)
{
return this.Equals(obj.As<Some>());
}
return base.Equals(obj);
}
public bool Equals(Some other)
{
if (other is None)
{
return false;
}
if (other is Some)
{
return this.Value.Equals(other.Value);
}
return false;
}
public static bool operator ==(Some left, Some right)
{
return left?.Equals(right) == true;
}
public static bool operator !=(Some left, Some right)
{
return left?.Equals(right) != true;
}
public override int GetHashCode()
{
return this.Value is object ? this.Value.GetHashCode() : this.As<object>().GetHashCode();
}
}
internal class None : Optional<T>
{
public None() : base(default)
{
}
}
}
@@ -0,0 +1,216 @@
namespace System.Extensions;
public class Result<TSuccess, TFailure>
{
private readonly object value;
public Result(TSuccess successValue)
{
this.value = successValue;
}
public Result(TFailure failureValue)
{
this.value = failureValue;
}
public bool TryExtractSuccess(out TSuccess successValue)
{
if (this.value is TSuccess success)
{
successValue = success;
return true;
}
else
{
successValue = default;
return false;
}
}
public bool TryExtractFailure(out TFailure failureValue)
{
if (this.value is TFailure failure)
{
failureValue = failure;
return true;
}
else
{
failureValue = default;
return false;
}
}
public Result<TSuccess, TFailure> Do(Action onSuccess, Action onFailure)
{
if (onSuccess is null)
{
throw new ArgumentNullException(nameof(onSuccess));
}
if (onFailure is null)
{
throw new ArgumentNullException(nameof(onFailure));
}
if (this.value is TSuccess)
{
onSuccess.Invoke();
}
else if (this.value is TFailure)
{
onFailure.Invoke();
}
return this;
}
public Result<TSuccess, TFailure> DoAny(Action onSuccess = null, Action onFailure = null)
{
if (this.value is TSuccess)
{
onSuccess?.Invoke();
}
else if (this.value is TFailure)
{
onFailure?.Invoke();
}
return this;
}
public Result<TSuccess, TFailure> Do(Action<TSuccess> onSuccess, Action<TFailure> onFailure)
{
if (onSuccess is null)
{
throw new ArgumentNullException(nameof(onSuccess));
}
if (onFailure is null)
{
throw new ArgumentNullException(nameof(onFailure));
}
if (this.value is TSuccess success)
{
onSuccess.Invoke(success);
}
else if (this.value is TFailure failure)
{
onFailure.Invoke(failure);
}
return this;
}
public Result<TSuccess, TFailure> DoAny(Action<TSuccess> onSuccess = null, Action<TFailure> onFailure = null)
{
if (this.value is TSuccess success)
{
onSuccess?.Invoke(success);
}
else if (this.value is TFailure failure)
{
onFailure?.Invoke(failure);
}
return this;
}
public T Switch<T>(Func<TSuccess, T> onSuccess, Func<TFailure, T> onFailure)
{
if (onSuccess is null)
{
throw new ArgumentNullException(nameof(onSuccess));
}
if (onFailure is null)
{
throw new ArgumentNullException(nameof(onFailure));
}
if (this.value is TSuccess success)
{
return onSuccess.Invoke(success);
}
else if (this.value is TFailure failure)
{
return onFailure.Invoke(failure);
}
throw new InvalidOperationException($"{nameof(this.value)} must be of type {typeof(TSuccess)} or {typeof(TFailure)} in order to switch to {typeof(T)}");
}
public T SwitchAny<T>(Func<TSuccess, T> onSuccess = null, Func<TFailure, T> onFailure = null)
{
if (this.value is TSuccess success)
{
return onSuccess is null ? default : onSuccess.Invoke(success);
}
else if (this.value is TFailure failure)
{
return onFailure is null ? default : onFailure.Invoke(failure);
}
throw new InvalidOperationException($"{nameof(this.value)} must be of type {typeof(TSuccess)} or {typeof(TFailure)} in order to switch to {typeof(T)}");
}
public Result<V, K> Switch<V, K>(Func<TSuccess, V> onSuccess, Func<TFailure, K> onFailure)
{
if (onSuccess is null)
{
throw new ArgumentNullException(nameof(onSuccess));
}
if (onFailure is null)
{
throw new ArgumentNullException(nameof(onFailure));
}
if (this.value is TSuccess success)
{
return Result<V, K>.Success(onSuccess.Invoke(success));
}
else if (this.value is TFailure failure)
{
return Result<V, K>.Failure(onFailure.Invoke(failure));
}
throw new InvalidOperationException($"{nameof(this.value)} must be of type {typeof(TSuccess)} or {typeof(TFailure)} in order to switch to Result of type {typeof(V)} or {typeof(K)}");
}
public Result<V, K> SwitchAny<V, K>(Func<TSuccess, V> onSuccess, Func<TFailure, K> onFailure)
{
if (this.value is TSuccess success)
{
return Result<V, K>.Success(onSuccess is null ? default : onSuccess.Invoke(success));
}
else if (this.value is TFailure failure)
{
return Result<V, K>.Failure(onFailure is null ? default : onFailure.Invoke(failure));
}
throw new InvalidOperationException($"{nameof(this.value)} must be of type {typeof(TSuccess)} or {typeof(TFailure)} in order to switch to Result of type {typeof(V)} or {typeof(K)}");
}
public Optional<TSuccess> ToOptional()
{
if (this.value is TSuccess)
{
return Optional.FromValue(this.value.Cast<TSuccess>());
}
return Optional.None<TSuccess>();
}
public static implicit operator Result<TSuccess, TFailure>(TSuccess success)
{
return Success(success);
}
public static implicit operator Result<TSuccess, TFailure>(TFailure failure)
{
return Failure(failure);
}
public static Result<TSuccess, TFailure> Success(TSuccess value)
{
return new Result<TSuccess, TFailure>(value);
}
public static Result<TSuccess, TFailure> Failure(TFailure value)
{
return new Result<TSuccess, TFailure>(value);
}
}
@@ -0,0 +1,84 @@
using System.IO;
using System.Text;
namespace System.Extensions;
public static class StreamExtensions
{
public static void DoWhileReading(this Stream stream, Action<int, byte[]> action, int bufferLength = 256)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}
var buffer = new byte[bufferLength];
var read = stream.Read(buffer, 0, bufferLength);
while(read > 0)
{
action(bufferLength, buffer);
read = stream.Read(buffer, 0, bufferLength);
}
}
public static byte[] ReadBytes(this Stream stream, int count)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
var buffer = new byte[count];
stream.Read(buffer, 0, count);
return buffer;
}
public static Stream Rewind(this Stream stream)
{
if (stream is null)
{
throw new ArgumentNullException(nameof(stream));
}
if (!stream.CanSeek)
{
throw new InvalidOperationException("Stream doesn't support rewinding");
}
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
public static string ReadUntil(this StreamReader sr, string delim)
{
var sb = new StringBuilder();
var found = false;
while (!found && !sr.EndOfStream)
{
for (var i = 0; i < delim.Length; i++)
{
var c = (char)sr.Read();
sb.Append(c);
if (c != delim[i])
{
break;
}
if (i == delim.Length - 1)
{
sb.Remove(sb.Length - delim.Length, delim.Length);
found = true;
}
}
}
return sb.ToString();
}
}
@@ -0,0 +1,26 @@
using System.Text;
namespace System.Extensions;
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string s)
{
return string.IsNullOrEmpty(s);
}
public static bool IsNullOrWhiteSpace(this string s)
{
return string.IsNullOrWhiteSpace(s);
}
public static byte[] GetBytes(this string s)
{
return Encoding.UTF8.GetBytes(s);
}
public static string GetString(this byte[] bytes)
{
return Encoding.UTF8.GetString(bytes);
}
}
@@ -0,0 +1,185 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace System.Extensions;
public static class TaskExtensions
{
/// <summary>
/// Mark task as <see cref="TaskCreationOptions.LongRunning"/>.
/// </summary>
/// <typeparam name="T">Result type.</typeparam>
/// <param name="task">Task.</param>
/// <returns>The started <see cref="Task{TResult}"/></returns>
public static Task<T> LongRunning<T>(this Task<T> task)
{
return Task.Factory.StartNew(async () =>
{
return await task;
}, TaskCreationOptions.LongRunning).Unwrap();
}
/// <summary>
/// Mark task as <see cref="TaskCreationOptions.LongRunning"/>.
/// </summary>
/// <param name="task">Task.</param>
/// <returns>The started <see cref="Task"/></returns>
public static Task LongRunning(this Task task)
{
return Task.Factory.StartNew(async () =>
{
await task;
}, TaskCreationOptions.LongRunning).Unwrap();
}
/// <summary>
/// Execute action periodically and asynchronously.
/// </summary>
/// <param name="onTick">Action to be executed.</param>
/// <param name="dueTime">Time to pass before starting to execute.</param>
/// <param name="interval">Interval between procs.</param>
/// <param name="token">Cancellation token used to cancel the cycle.</param>
/// <returns></returns>
public static async Task RunPeriodicAsync(this Action onTick, TimeSpan dueTime, TimeSpan interval, CancellationToken token)
{
if (dueTime > TimeSpan.Zero)
{
await Task.Delay(dueTime, token).ConfigureAwait(false);
}
while (!token.IsCancellationRequested)
{
onTick?.Invoke();
if (interval > TimeSpan.Zero)
{
await Task.Delay(interval, token).ConfigureAwait(false);
}
}
}
/// <summary>
/// Execute's an async Task<T> method which has a void return value synchronously
/// </summary>
/// <param name="task">Task<T> method to execute</param>
public static void RunSync(this Func<Task> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
synch.Post(async _ =>
{
try
{
await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
/// <summary>
/// Execute's an async Task<T> method which has a T return type synchronously
/// </summary>
/// <typeparam name="T">Return Type</typeparam>
/// <param name="task">Task<T> method to execute</param>
/// <returns></returns>
public static T RunSync<T>(this Func<Task<T>> task)
{
var oldContext = SynchronizationContext.Current;
var synch = new ExclusiveSynchronizationContext();
SynchronizationContext.SetSynchronizationContext(synch);
var ret = default(T);
synch.Post(async _ =>
{
try
{
ret = await task();
}
catch (Exception e)
{
synch.InnerException = e;
throw;
}
finally
{
synch.EndMessageLoop();
}
}, null);
synch.BeginMessageLoop();
SynchronizationContext.SetSynchronizationContext(oldContext);
return ret;
}
private class ExclusiveSynchronizationContext : SynchronizationContext
{
private bool done;
public Exception InnerException { get; set; }
readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
readonly Queue<Tuple<SendOrPostCallback, object>> items =
new Queue<Tuple<SendOrPostCallback, object>>();
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("We cannot send to our same thread");
}
public override void Post(SendOrPostCallback d, object state)
{
lock (this.items)
{
this.items.Enqueue(Tuple.Create(d, state));
}
this.workItemsWaiting.Set();
}
public void EndMessageLoop()
{
this.Post(_ => this.done = true, null);
}
public void BeginMessageLoop()
{
while (!this.done)
{
Tuple<SendOrPostCallback, object> task = null;
lock (this.items)
{
if (this.items.Count > 0)
{
task = this.items.Dequeue();
}
}
if (task != null)
{
task.Item1(task.Item2);
if (this.InnerException != null) // the method threw an exeption
{
throw new AggregateException("AsyncHelpers.Run method threw an exception.", this.InnerException);
}
}
else
{
this.workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy()
{
return this;
}
}
}
@@ -0,0 +1,73 @@
using System.Collections.Generic;
namespace System.Extensions;
public static class Try
{
public static Try<TResult> Action<TResult>(Func<TResult> act)
{
return new Try<TResult>(act);
}
}
public class Try<TResult>
{
private readonly Func<TResult> action;
private readonly List<Func<Exception, TResult>> catchActions = new List<Func<Exception, TResult>>();
internal Try(Func<TResult> action)
{
this.action = action;
}
public static Try<TResult> Action(Func<TResult> act)
{
return new Try<TResult>(act);
}
public Try<TResult> Catch<TException>(Func<TException, TResult> act) where TException : Exception
{
this.catchActions.Add((Func<Exception, TResult>)act);
return this;
}
public TResult Run()
{
try
{
return this.action();
}
catch (Exception ex)
{
foreach (var catchAction in this.catchActions)
{
return catchAction(ex);
}
throw;
}
}
public TResult Finally(Action act)
{
if (act is null)
{
throw new ArgumentNullException(nameof(act));
}
try
{
return this.action();
}
catch (Exception ex)
{
foreach (var catchAction in this.catchActions)
{
return catchAction(ex);
}
throw;
}
finally
{
act();
}
}
}
@@ -0,0 +1,215 @@
using System.IO;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net.Http;
public sealed class HttpClient<Tscope> : IHttpClient<Tscope>, IDisposable
{
private readonly HttpClient httpClient;
private readonly Type scope;
private EventHandler<HttpClientEventMessage> eventEmitted;
public event EventHandler<HttpClientEventMessage> EventEmitted
{
add
{
this.eventEmitted += value;
}
remove
{
this.eventEmitted -= value;
}
}
public Uri BaseAddress { get => this.httpClient.BaseAddress; set => this.httpClient.BaseAddress = value; }
public HttpRequestHeaders DefaultRequestHeaders => this.httpClient.DefaultRequestHeaders;
public long MaxResponseContentBufferSize { get => this.httpClient.MaxResponseContentBufferSize; set => this.httpClient.MaxResponseContentBufferSize = value; }
public TimeSpan Timeout { get => this.httpClient.Timeout; set => this.httpClient.Timeout = value; }
public HttpClient()
{
this.httpClient = new HttpClient();
this.scope = typeof(Tscope);
}
public HttpClient(
HttpMessageHandler handler)
{
this.httpClient = new HttpClient(handler);
this.scope = typeof(Tscope);
}
public HttpClient(
HttpMessageHandler handler,
bool disposeHandler)
{
this.httpClient = new HttpClient(handler, disposeHandler);
this.scope = typeof(Tscope);
}
public void CancelPendingRequests()
{
this.LogInformation(string.Empty, "Canceling request");
this.httpClient.CancelPendingRequests();
}
public Task<HttpResponseMessage> DeleteAsync(string requestUri)
{
this.LogInformation(requestUri, nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri);
}
public Task<HttpResponseMessage> DeleteAsync(string requestUri, CancellationToken cancellationToken)
{
this.LogInformation(requestUri, nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri, cancellationToken);
}
public Task<HttpResponseMessage> DeleteAsync(Uri requestUri)
{
this.LogInformation(requestUri.ToString(), nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri);
}
public Task<HttpResponseMessage> DeleteAsync(Uri requestUri, CancellationToken cancellationToken)
{
this.LogInformation(requestUri.ToString(), nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(string requestUri)
{
this.LogInformation(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri);
}
public Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption)
{
this.LogInformation(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption);
}
public Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
this.LogInformation(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(string requestUri, CancellationToken cancellationToken)
{
this.LogInformation(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri)
{
this.LogInformation(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption)
{
this.LogInformation(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
this.LogInformation(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri, CancellationToken cancellationToken)
{
this.LogInformation(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, cancellationToken);
}
public Task<byte[]> GetByteArrayAsync(string requestUri)
{
this.LogInformation(requestUri, nameof(GetByteArrayAsync));
return this.httpClient.GetByteArrayAsync(requestUri);
}
public Task<byte[]> GetByteArrayAsync(Uri requestUri)
{
this.LogInformation(requestUri.ToString(), nameof(GetByteArrayAsync));
return this.httpClient.GetByteArrayAsync(requestUri);
}
public Task<Stream> GetStreamAsync(string requestUri)
{
this.LogInformation(requestUri, nameof(GetStreamAsync));
return this.httpClient.GetStreamAsync(requestUri);
}
public Task<Stream> GetStreamAsync(Uri requestUri)
{
this.LogInformation(requestUri.ToString(), nameof(GetStreamAsync));
return this.httpClient.GetStreamAsync(requestUri);
}
public Task<string> GetStringAsync(string requestUri)
{
this.LogInformation(requestUri, nameof(GetStringAsync));
return this.httpClient.GetStringAsync(requestUri);
}
public Task<string> GetStringAsync(Uri requestUri)
{
this.LogInformation(requestUri.ToString(), nameof(GetStringAsync));
return this.httpClient.GetStringAsync(requestUri);
}
public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
{
this.LogInformation(requestUri, nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content);
}
public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogInformation(requestUri, nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content)
{
this.LogInformation(requestUri.ToString(), nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content);
}
public Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogInformation(requestUri.ToString(), nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content)
{
this.LogInformation(requestUri, nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content);
}
public Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogInformation(requestUri, nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content)
{
this.LogInformation(requestUri.ToString(), nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content);
}
public Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogInformation(requestUri.ToString(), nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
this.LogInformation(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption)
{
this.LogInformation(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request, completionOption);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
this.LogInformation(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request, completionOption, cancellationToken);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
this.LogInformation(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request, cancellationToken);
}
public void Dispose()
{
this.httpClient.Dispose();
}
private void LogInformation(string url, string method)
{
this.eventEmitted?.Invoke(this, new HttpClientEventMessage(this.scope, method, url));
}
}
@@ -0,0 +1,15 @@
namespace System.Net.Http;
public sealed class HttpClientEventMessage
{
public Type Scope { get; }
public string Method { get; }
public string Url { get; }
internal HttpClientEventMessage(Type scope, string method, string url)
{
this.Scope = scope;
this.Method = method;
this.Url = url;
}
}
@@ -0,0 +1,47 @@
using System.IO;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net.Http;
public interface IHttpClient<TScope>
{
event EventHandler<HttpClientEventMessage> EventEmitted;
Uri BaseAddress { get; set; }
HttpRequestHeaders DefaultRequestHeaders { get; }
long MaxResponseContentBufferSize { get; set; }
TimeSpan Timeout { get; set; }
void CancelPendingRequests();
Task<HttpResponseMessage> DeleteAsync(string requestUri);
Task<HttpResponseMessage> DeleteAsync(string requestUri, CancellationToken cancellationToken);
Task<HttpResponseMessage> DeleteAsync(Uri requestUri);
Task<HttpResponseMessage> DeleteAsync(Uri requestUri, CancellationToken cancellationToken);
Task<HttpResponseMessage> GetAsync(string requestUri);
Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption);
Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken);
Task<HttpResponseMessage> GetAsync(string requestUri, CancellationToken cancellationToken);
Task<HttpResponseMessage> GetAsync(Uri requestUri);
Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption);
Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken);
Task<HttpResponseMessage> GetAsync(Uri requestUri, CancellationToken cancellationToken);
Task<byte[]> GetByteArrayAsync(string requestUri);
Task<byte[]> GetByteArrayAsync(Uri requestUri);
Task<Stream> GetStreamAsync(string requestUri);
Task<Stream> GetStreamAsync(Uri requestUri);
Task<string> GetStringAsync(string requestUri);
Task<string> GetStringAsync(Uri requestUri);
Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content);
Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content);
Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content);
Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content);
Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken);
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
}
@@ -0,0 +1,76 @@
using Microsoft.Extensions.Logging;
using System.Extensions;
namespace System.Logging;
public struct ScopedLogger<T>
{
private readonly ILogger<T> logger;
private readonly string scope;
private readonly string flowId;
private ScopedLogger(ILogger<T> logger, string scope, string flowId)
{
this.logger = logger;
this.scope = scope;
this.flowId = flowId.IsNullOrWhiteSpace() ? string.Empty : $"[{flowId}] ";
}
public ScopedLogger<T> LogInformation(string message, params object[] parameters)
{
this.logger.LogInformation(this.CreateMessage(message), parameters);
return this;
}
public ScopedLogger<T> LogDebug(string message, params object[] parameters)
{
this.logger.LogDebug(this.CreateMessage(message), parameters);
return this;
}
public ScopedLogger<T> LogWarning(string message, params object[] parameters)
{
this.logger.LogWarning(this.CreateMessage(message), parameters);
return this;
}
public ScopedLogger<T> LogError(string message, params object[] parameters)
{
this.logger.LogError(this.CreateMessage(message), parameters);
return this;
}
public ScopedLogger<T> LogCritical(string message, params object[] parameters)
{
this.logger.LogCritical(this.CreateMessage(message), parameters);
return this;
}
public ScopedLogger<T> LogWarning(Exception e, string message, params object[] parameters)
{
this.logger.LogWarning(e, this.CreateMessage(message), parameters);
return this;
}
public ScopedLogger<T> LogError(Exception e, string message, params object[] parameters)
{
this.logger.LogError(e, this.CreateMessage(message), parameters);
return this;
}
public ScopedLogger<T> LogCritical(Exception e, string message, params object[] parameters)
{
this.logger.LogCritical(e, this.CreateMessage(message), parameters);
return this;
}
private string CreateMessage(string message)
{
return $"{this.flowId}{this.scope}: {message}";
}
public static ScopedLogger<T> Create(ILogger<T> logger, string scope, string flowId)
{
return new ScopedLogger<T>(logger, scope.ThrowIfNull(nameof(scope)), flowId);
}
}
@@ -0,0 +1,174 @@
namespace System.Structures.BitStructures;
public struct Int32BitStruct : IEquatable<Int32BitStruct>
{
public Int32BitStruct(uint value) => this.Value = value;
public Int32BitStruct(int value) { unchecked { this.Value = (uint)value; } }
public uint Value { get; set; }
public uint Bit0 { get => this.Value & 0x1; set => this.Value = (this.Value & ~(0x1U) | (0x1 & value)); }
public uint Bit1 { get => this.Value >> 1 & 0x1; set => this.Value = (this.Value & ~(0x1U << 1)) | ((0x1 & value) << 1); }
public uint Bit2 { get => this.Value >> 2 & 0x1; set => this.Value = (this.Value & ~(0x1U << 2)) | ((0x1 & value) << 2); }
public uint Bit3 { get => this.Value >> 3 & 0x1; set => this.Value = (this.Value & ~(0x1U << 3)) | ((0x1 & value) << 3); }
public uint Bit4 { get => this.Value >> 4 & 0x1; set => this.Value = (this.Value & ~(0x1U << 4)) | ((0x1 & value) << 4); }
public uint Bit5 { get => this.Value >> 5 & 0x1; set => this.Value = (this.Value & ~(0x1U << 5)) | ((0x1 & value) << 5); }
public uint Bit6 { get => this.Value >> 6 & 0x1; set => this.Value = (this.Value & ~(0x1U << 6)) | ((0x1 & value) << 6); }
public uint Bit7 { get => this.Value >> 7 & 0x1; set => this.Value = (this.Value & ~(0x1U << 7)) | ((0x1 & value) << 7); }
public uint Bit8 { get => this.Value >> 8 & 0x1; set => this.Value = (this.Value & ~(0x1U << 8)) | ((0x1 & value) << 8); }
public uint Bit9 { get => this.Value >> 9 & 0x1; set => this.Value = (this.Value & ~(0x1U << 9)) | ((0x1 & value) << 9); }
public uint Bit10 { get => this.Value >> 10 & 0x1; set => this.Value = (this.Value & ~(0x1U << 10)) | ((0x1 & value) << 10); }
public uint Bit11 { get => this.Value >> 11 & 0x1; set => this.Value = (this.Value & ~(0x1U << 11)) | ((0x1 & value) << 11); }
public uint Bit12 { get => this.Value >> 12 & 0x1; set => this.Value = (this.Value & ~(0x1U << 12)) | ((0x1 & value) << 12); }
public uint Bit13 { get => this.Value >> 13 & 0x1; set => this.Value = (this.Value & ~(0x1U << 13)) | ((0x1 & value) << 13); }
public uint Bit14 { get => this.Value >> 14 & 0x1; set => this.Value = (this.Value & ~(0x1U << 14)) | ((0x1 & value) << 14); }
public uint Bit15 { get => this.Value >> 15 & 0x1; set => this.Value = (this.Value & ~(0x1U << 15)) | ((0x1 & value) << 15); }
public uint Bit16 { get => this.Value >> 16 & 0x1; set => this.Value = (this.Value & ~(0x1U << 16)) | ((0x1 & value) << 16); }
public uint Bit17 { get => this.Value >> 17 & 0x1; set => this.Value = (this.Value & ~(0x1U << 17)) | ((0x1 & value) << 17); }
public uint Bit18 { get => this.Value >> 18 & 0x1; set => this.Value = (this.Value & ~(0x1U << 18)) | ((0x1 & value) << 18); }
public uint Bit19 { get => this.Value >> 19 & 0x1; set => this.Value = (this.Value & ~(0x1U << 19)) | ((0x1 & value) << 19); }
public uint Bit20 { get => this.Value >> 20 & 0x1; set => this.Value = (this.Value & ~(0x1U << 20)) | ((0x1 & value) << 20); }
public uint Bit21 { get => this.Value >> 21 & 0x1; set => this.Value = (this.Value & ~(0x1U << 21)) | ((0x1 & value) << 21); }
public uint Bit22 { get => this.Value >> 22 & 0x1; set => this.Value = (this.Value & ~(0x1U << 22)) | ((0x1 & value) << 22); }
public uint Bit23 { get => this.Value >> 23 & 0x1; set => this.Value = (this.Value & ~(0x1U << 23)) | ((0x1 & value) << 23); }
public uint Bit24 { get => this.Value >> 24 & 0x1; set => this.Value = (this.Value & ~(0x1U << 24)) | ((0x1 & value) << 24); }
public uint Bit25 { get => this.Value >> 25 & 0x1; set => this.Value = (this.Value & ~(0x1U << 25)) | ((0x1 & value) << 25); }
public uint Bit26 { get => this.Value >> 26 & 0x1; set => this.Value = (this.Value & ~(0x1U << 26)) | ((0x1 & value) << 26); }
public uint Bit27 { get => this.Value >> 27 & 0x1; set => this.Value = (this.Value & ~(0x1U << 27)) | ((0x1 & value) << 27); }
public uint Bit28 { get => this.Value >> 28 & 0x1; set => this.Value = (this.Value & ~(0x1U << 28)) | ((0x1 & value) << 28); }
public uint Bit29 { get => this.Value >> 29 & 0x1; set => this.Value = (this.Value & ~(0x1U << 29)) | ((0x1 & value) << 29); }
public uint Bit30 { get => this.Value >> 30 & 0x1; set => this.Value = (this.Value & ~(0x1U << 30)) | ((0x1 & value) << 30); }
public uint Bit31 { get => this.Value >> 31 & 0x1; set => this.Value = (this.Value & ~(0x1U << 31)) | ((0x1 & value) << 31); }
public static implicit operator Int32BitStruct(uint value) => new Int32BitStruct(value);
public static implicit operator Int32BitStruct(int value) => new Int32BitStruct(value);
public static implicit operator int(Int32BitStruct value) => (int)value.Value;
public static implicit operator uint(Int32BitStruct value) => value.Value;
public static bool operator ==(Int32BitStruct first, Int32BitStruct second)
{
return first.Value == second.Value;
}
public static bool operator !=(Int32BitStruct first, Int32BitStruct second)
{
return first.Value != second.Value;
}
public static bool operator ==(Int32BitStruct first, uint second)
{
return first.Value == second;
}
public static bool operator !=(Int32BitStruct first, uint second)
{
return first.Value != second;
}
public static bool operator ==(Int32BitStruct first, int second)
{
unchecked
{
return first.Value == (uint)second;
}
}
public static bool operator !=(Int32BitStruct first, int second)
{
unchecked
{
return first.Value != (uint)second;
}
}
public static bool operator >=(Int32BitStruct first, Int32BitStruct second)
{
return first.Value >= second.Value;
}
public static bool operator <=(Int32BitStruct first, Int32BitStruct second)
{
return first.Value <= second.Value;
}
public static bool operator >=(Int32BitStruct first, int second)
{
unchecked
{
return first.Value >= (uint)second;
}
}
public static bool operator <=(Int32BitStruct first, int second)
{
unchecked
{
return first.Value <= (uint)second;
}
}
public static bool operator >=(Int32BitStruct first, uint second)
{
return first.Value >= second;
}
public static bool operator <=(Int32BitStruct first, uint second)
{
return first.Value <= second;
}
public static bool operator >(Int32BitStruct first, Int32BitStruct second)
{
return first.Value > second.Value;
}
public static bool operator <(Int32BitStruct first, Int32BitStruct second)
{
return first.Value < second.Value;
}
public static bool operator >(Int32BitStruct first, int second)
{
unchecked
{
return first.Value > (uint)second;
}
}
public static bool operator <(Int32BitStruct first, int second)
{
unchecked
{
return first.Value < (uint)second;
}
}
public static bool operator >(Int32BitStruct first, uint second)
{
return first.Value > second;
}
public static bool operator <(Int32BitStruct first, uint second)
{
return first.Value < second;
}
public bool Equals(Int32BitStruct other)
{
return this.Value == other.Value;
}
public override bool Equals(object obj)
{
if (obj is Int32BitStruct)
{
return this.Equals((Int32BitStruct)obj);
}
else
{
return base.Equals(obj);
}
}
public override int GetHashCode()
{
return (this.Value).GetHashCode();
}
}
@@ -0,0 +1,204 @@
using System;
namespace System.Structures.BitStructures;
public struct Int64BitStruct : IEquatable<Int64BitStruct>
{
public Int64BitStruct(uint low, uint high)
{
this.Value = low + ((ulong)high << 32);
}
public Int64BitStruct(int low, int high)
{
unchecked
{
this.Value = (uint)low + ((ulong)high << 32);
}
}
public Int64BitStruct(long value)
{
unchecked
{
this.Value = (ulong)value;
}
}
public Int64BitStruct(ulong value)
{
this.Value = value;
}
public ulong Value { get; set; }
public uint Low { get => (uint)(this.Value & 0xFFFFFFFF); set => this.Value = (this.Value & ~(0xFFFFFFFF)) | (0xFFFFFFFF & value); }
public uint High { get => (uint)((this.Value >> 32) & 0xFFFFFFFF); set => this.Value = (this.Value & ~(0xFFFFFFFF00000000)) | ((ulong)(0xFFFFFFFF & value) << 32); }
public ulong Bit0 { get => this.Value & 0x1; set => this.Value = (this.Value & ~(0x1UL) | (0x1 & value)); }
public ulong Bit1 { get => this.Value >> 1 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 1)) | ((0x1 & value) << 1); }
public ulong Bit2 { get => this.Value >> 2 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 2)) | ((0x1 & value) << 2); }
public ulong Bit3 { get => this.Value >> 3 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 3)) | ((0x1 & value) << 3); }
public ulong Bit4 { get => this.Value >> 4 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 4)) | ((0x1 & value) << 4); }
public ulong Bit5 { get => this.Value >> 5 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 5)) | ((0x1 & value) << 5); }
public ulong Bit6 { get => this.Value >> 6 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 6)) | ((0x1 & value) << 6); }
public ulong Bit7 { get => this.Value >> 7 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 7)) | ((0x1 & value) << 7); }
public ulong Bit8 { get => this.Value >> 8 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 8)) | ((0x1 & value) << 8); }
public ulong Bit9 { get => this.Value >> 9 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 9)) | ((0x1 & value) << 9); }
public ulong Bit10 { get => this.Value >> 10 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 10)) | ((0x1 & value) << 10); }
public ulong Bit11 { get => this.Value >> 11 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 11)) | ((0x1 & value) << 11); }
public ulong Bit12 { get => this.Value >> 12 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 12)) | ((0x1 & value) << 12); }
public ulong Bit13 { get => this.Value >> 13 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 13)) | ((0x1 & value) << 13); }
public ulong Bit14 { get => this.Value >> 14 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 14)) | ((0x1 & value) << 14); }
public ulong Bit15 { get => this.Value >> 15 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 15)) | ((0x1 & value) << 15); }
public ulong Bit16 { get => this.Value >> 16 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 16)) | ((0x1 & value) << 16); }
public ulong Bit17 { get => this.Value >> 17 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 17)) | ((0x1 & value) << 17); }
public ulong Bit18 { get => this.Value >> 18 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 18)) | ((0x1 & value) << 18); }
public ulong Bit19 { get => this.Value >> 19 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 19)) | ((0x1 & value) << 19); }
public ulong Bit20 { get => this.Value >> 20 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 20)) | ((0x1 & value) << 20); }
public ulong Bit21 { get => this.Value >> 21 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 21)) | ((0x1 & value) << 21); }
public ulong Bit22 { get => this.Value >> 22 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 22)) | ((0x1 & value) << 22); }
public ulong Bit23 { get => this.Value >> 23 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 23)) | ((0x1 & value) << 23); }
public ulong Bit24 { get => this.Value >> 24 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 24)) | ((0x1 & value) << 24); }
public ulong Bit25 { get => this.Value >> 25 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 25)) | ((0x1 & value) << 25); }
public ulong Bit26 { get => this.Value >> 26 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 26)) | ((0x1 & value) << 26); }
public ulong Bit27 { get => this.Value >> 27 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 27)) | ((0x1 & value) << 27); }
public ulong Bit28 { get => this.Value >> 28 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 28)) | ((0x1 & value) << 28); }
public ulong Bit29 { get => this.Value >> 29 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 29)) | ((0x1 & value) << 29); }
public ulong Bit30 { get => this.Value >> 30 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 30)) | ((0x1 & value) << 30); }
public ulong Bit31 { get => this.Value >> 31 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 31)) | ((0x1 & value) << 31); }
public ulong Bit32 { get => this.Value >> 32 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 32)) | ((0x1 & value) << 32); }
public ulong Bit33 { get => this.Value >> 33 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 33)) | ((0x1 & value) << 33); }
public ulong Bit34 { get => this.Value >> 34 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 34)) | ((0x1 & value) << 34); }
public ulong Bit35 { get => this.Value >> 35 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 35)) | ((0x1 & value) << 35); }
public ulong Bit36 { get => this.Value >> 36 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 36)) | ((0x1 & value) << 36); }
public ulong Bit37 { get => this.Value >> 37 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 37)) | ((0x1 & value) << 37); }
public ulong Bit38 { get => this.Value >> 38 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 38)) | ((0x1 & value) << 38); }
public ulong Bit39 { get => this.Value >> 39 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 39)) | ((0x1 & value) << 39); }
public ulong Bit40 { get => this.Value >> 40 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 40)) | ((0x1 & value) << 40); }
public ulong Bit41 { get => this.Value >> 41 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 41)) | ((0x1 & value) << 41); }
public ulong Bit42 { get => this.Value >> 42 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 42)) | ((0x1 & value) << 42); }
public ulong Bit43 { get => this.Value >> 43 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 43)) | ((0x1 & value) << 43); }
public ulong Bit44 { get => this.Value >> 44 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 44)) | ((0x1 & value) << 44); }
public ulong Bit45 { get => this.Value >> 45 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 45)) | ((0x1 & value) << 45); }
public ulong Bit46 { get => this.Value >> 46 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 46)) | ((0x1 & value) << 46); }
public ulong Bit47 { get => this.Value >> 47 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 47)) | ((0x1 & value) << 47); }
public ulong Bit48 { get => this.Value >> 48 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 48)) | ((0x1 & value) << 48); }
public ulong Bit49 { get => this.Value >> 49 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 49)) | ((0x1 & value) << 49); }
public ulong Bit50 { get => this.Value >> 50 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 50)) | ((0x1 & value) << 50); }
public ulong Bit51 { get => this.Value >> 51 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 51)) | ((0x1 & value) << 51); }
public ulong Bit52 { get => this.Value >> 52 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 52)) | ((0x1 & value) << 52); }
public ulong Bit53 { get => this.Value >> 53 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 53)) | ((0x1 & value) << 53); }
public ulong Bit54 { get => this.Value >> 54 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 54)) | ((0x1 & value) << 54); }
public ulong Bit55 { get => this.Value >> 55 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 55)) | ((0x1 & value) << 55); }
public ulong Bit56 { get => this.Value >> 56 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 56)) | ((0x1 & value) << 56); }
public ulong Bit57 { get => this.Value >> 57 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 57)) | ((0x1 & value) << 57); }
public ulong Bit58 { get => this.Value >> 58 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 58)) | ((0x1 & value) << 58); }
public ulong Bit59 { get => this.Value >> 59 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 59)) | ((0x1 & value) << 59); }
public ulong Bit60 { get => this.Value >> 60 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 60)) | ((0x1 & value) << 60); }
public ulong Bit61 { get => this.Value >> 61 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 61)) | ((0x1 & value) << 61); }
public ulong Bit62 { get => this.Value >> 62 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 62)) | ((0x1 & value) << 62); }
public ulong Bit63 { get => this.Value >> 63 & 0x1; set => this.Value = (this.Value & ~(0x1UL << 63)) | ((0x1 & value) << 63); }
public static implicit operator Int64BitStruct(ulong value) => new Int64BitStruct(value);
public static implicit operator Int64BitStruct(long value) => new Int64BitStruct(value);
public static implicit operator long(Int64BitStruct value) => (long)value.Value;
public static implicit operator ulong(Int64BitStruct value) => value.Value;
public static bool operator ==(Int64BitStruct first, Int64BitStruct second)
{
return first.Value == second.Value;
}
public static bool operator !=(Int64BitStruct first, Int64BitStruct second)
{
return first.Value != second.Value;
}
public static bool operator ==(Int64BitStruct first, ulong second)
{
return first.Value == second;
}
public static bool operator !=(Int64BitStruct first, ulong second)
{
return first.Value != second;
}
public static bool operator ==(Int64BitStruct first, long second)
{
unchecked
{
return first.Value == (ulong)second;
}
}
public static bool operator !=(Int64BitStruct first, long second)
{
unchecked
{
return first.Value != (ulong)second;
}
}
public static bool operator >=(Int64BitStruct first, Int64BitStruct second)
{
return first.Value >= second.Value;
}
public static bool operator <=(Int64BitStruct first, Int64BitStruct second)
{
return first.Value <= second.Value;
}
public static bool operator >=(Int64BitStruct first, long second)
{
unchecked
{
return first.Value >= (ulong)second;
}
}
public static bool operator <=(Int64BitStruct first, long second)
{
unchecked
{
return first.Value <= (ulong)second;
}
}
public static bool operator >=(Int64BitStruct first, ulong second)
{
unchecked
{
return first.Value >= second;
}
}
public static bool operator <=(Int64BitStruct first, ulong second)
{
unchecked
{
return first.Value <= second;
}
}
public bool Equals(Int64BitStruct other)
{
return this.Value == other.Value;
}
public override bool Equals(object obj)
{
if (obj is Int64BitStruct)
{
return this.Equals((Int64BitStruct)obj);
}
else
{
return base.Equals(obj);
}
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
}
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RootNamespace>System</RootNamespace>
<Version>1.5</Version>
<Authors>Alexandru Macocian</Authors>
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
<Description>Extensions for the System namespace</Description>
</PropertyGroup>
<ItemGroup>
<None Include="../LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>
@@ -0,0 +1,26 @@
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace System;
/// <summary>
/// Async implementation of <see cref="Lazy{T}"/>.
/// </summary>
/// <remarks>
/// Credits: https://devblogs.microsoft.com/pfxteam/asynclazyt/
/// </remarks>
/// <typeparam name="T">Type of value to be returned.</typeparam>
public sealed class AsyncLazy<T> : Lazy<Task<T>>
{
public TaskAwaiter<T> GetAwaiter() => this.Value.GetAwaiter();
public AsyncLazy(Func<T> valueFactory)
: base(() => Task.Factory.StartNew(valueFactory))
{
}
public AsyncLazy(Func<Task<T>> taskFactory)
: base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap())
{
}
}
@@ -0,0 +1,577 @@
using System.Collections.Generic;
namespace System.Threading;
/// <summary>
/// Threadpool implementation that uses a priority queue as the queue for the tasks to execute.
/// Features both an automated algorithm to calibrate the number of active threads and a Constructor for constructing
/// a threadpool manually, without the auto-managed pool algorithm.
/// </summary>
public class PriorityThreadPool : IDisposable
{
private class QueueEntry : IComparable<QueueEntry>
{
public TaskPriority TaskPriority { get; }
public WaitCallback WaitCallback { get; }
public object Object { get; }
public QueueEntry(TaskPriority priority, WaitCallback callback, object obj)
{
this.TaskPriority = priority;
this.WaitCallback = callback;
this.Object = obj;
}
public int CompareTo(QueueEntry other)
{
int priority1 = 0, priority2 = 0;
switch (this.TaskPriority)
{
case TaskPriority.Highest:
priority1 = 4;
break;
case TaskPriority.AboveNormal:
priority1 = 3;
break;
case TaskPriority.Normal:
priority1 = 2;
break;
case TaskPriority.BelowNormal:
priority1 = 1;
break;
case TaskPriority.Lowest:
priority1 = 0;
break;
}
switch (other.TaskPriority)
{
case TaskPriority.Highest:
priority2 = 4;
break;
case TaskPriority.AboveNormal:
priority2 = 3;
break;
case TaskPriority.Normal:
priority2 = 2;
break;
case TaskPriority.BelowNormal:
priority2 = 1;
break;
case TaskPriority.Lowest:
priority2 = 0;
break;
}
if (priority1 == priority2)
{
return 0;
}
else if (priority1 > priority2)
{
return -1;
}
else
{
return 1;
}
}
}
#region Enum
/// <summary>
/// Enum for priority of task scheduled
/// </summary>
public enum TaskPriority
{
/// <summary>
/// Highest priority of a task.
/// </summary>
Highest,
/// <summary>
/// Priority level above the default level.
/// </summary>
AboveNormal,
/// <summary>
/// Default priority level of all tasks without a specified priority level.
/// </summary>
Normal,
/// <summary>
/// Priority level below the default level.
/// </summary>
BelowNormal,
/// <summary>
/// Lowest priority level.
/// </summary>
Lowest
}
#endregion
#region Fields
/// <summary>
/// List that contains current running threads and their status.
/// </summary>
private volatile List<WorkerThread> threadpool;
private volatile PriorityQueue<QueueEntry> tasks;
private Thread observer;
private CancellationTokenSource observerCancellationTokenSource = new CancellationTokenSource();
private readonly object tasksLock = new object();
private struct WorkerThread
{
public Thread Thread { get; set; }
public bool Running { get; set; }
public bool Working { get; set; }
public CancellationTokenSource CancellationTokenSource { get; set; }
}
private struct Statistics
{
public bool Initialized { get; set; }
public int PerformanceCounter { get; set; }
public DateTime LastUpdate { get; set; }
public double LoopFrequency { get; set; }
}
#endregion
#region Properties
/// <summary>
/// Returns the number of active threads in the threadpool.
/// </summary>
public int NumberOfThreads
{
get
{
return this.threadpool is null ? 0 : this.threadpool.Count;
}
}
/// <summary>
/// Returns true if there are no tasks queued.
/// </summary>
public bool Empty
{
get
{
return this.tasks is null ? true : this.tasks.Count == 0;
}
}
/// <summary>
/// Maximum amount of threads that the pool can utilize
/// </summary>
public int MaxThreads { set; get; }
#endregion
#region Constructors
/// <summary>
/// Constructor that initializes a threadpool using default values. All threads run at the same priority.
/// The maximum number of threads is equal to System.Environment.ProcessorCount
/// </summary>
public PriorityThreadPool()
{
this.threadpool = new List<WorkerThread>();
this.tasks = new PriorityQueue<QueueEntry>();
this.MaxThreads = System.Environment.ProcessorCount;
for (var i = 0; i < this.MaxThreads; i++)
{
this.CreateAndStartWorkerThread();
}
this.observer = new Thread(() => this.ObserverLoop())
{
Name = "ThreadPool ObserverThread",
Priority = ThreadPriority.BelowNormal
};
this.observer.Start();
}
/// <summary>
/// Constructor that initializes a threadpool with a number of threads, all of the same priority.
/// </summary>
/// <param name="maxThreads">Maximum number of threads</param>
public PriorityThreadPool(int maxThreads)
{
this.threadpool = new List<WorkerThread>();
this.tasks = new PriorityQueue<QueueEntry>();
this.MaxThreads = Math.Max(maxThreads, 1);
for (var i = 0; i < maxThreads; i++)
{
this.CreateAndStartWorkerThread();
}
this.observer = new Thread(() => this.ObserverLoop());
this.observer.Name = "ThreadPool ObserverThread";
this.observer.Priority = ThreadPriority.BelowNormal;
this.observer.Start();
}
/// <summary>
/// Constructor for a manually-configured threadpool that runs a specified number of threads.
/// This threadpool with not be automatically managed and will always run the specified amount of threads
/// </summary>
/// <param name="lowest">Number of threads with Lowest priority.</param>
/// <param name="belowNormal">Number of threads with BelowNormal priority.</param>
/// <param name="normal">Number of threads with Normal priority.</param>
/// <param name="aboveNormal">Number of threads with AboveNormal priority.</param>
/// <param name="highest">Number of threads with Highest priority.</param>
public PriorityThreadPool(int lowest, int belowNormal, int normal, int aboveNormal, int highest)
{
this.threadpool = new List<WorkerThread>();
this.tasks = new PriorityQueue<QueueEntry>();
for (var i = 0; i < lowest; i++)
{
this.CreateAndStartWorkerThread(ThreadPriority.Lowest);
}
for (var i = 0; i < belowNormal; i++)
{
this.CreateAndStartWorkerThread(ThreadPriority.BelowNormal);
}
for (var i = 0; i < normal; i++)
{
this.CreateAndStartWorkerThread(ThreadPriority.Normal);
}
for (var i = 0; i < aboveNormal; i++)
{
this.CreateAndStartWorkerThread(ThreadPriority.AboveNormal);
}
for (var i = 0; i < highest; i++)
{
this.CreateAndStartWorkerThread(ThreadPriority.Highest);
}
}
#endregion
#region Public Methods
/// <summary>
/// Add a work item into the queue.
/// </summary>
/// <param name="waitCallback">WaitCallBack delegate that will be invoked by the threads.</param>
/// <param name="callbackState">State used as parameter during invoke.</param>
/// <param name="taskPriority">Priority of task. Affects its position into the queue.</param>
public void QueueUserWorkItem(WaitCallback waitCallback, object callbackState, TaskPriority taskPriority)
{
while (!Monitor.TryEnter(this.tasksLock))
{
;
}
this.tasks.Enqueue(new QueueEntry(taskPriority, waitCallback, callbackState));
Monitor.Exit(this.tasksLock);
}
/// <summary>
/// Add a work item into the queue.
/// </summary>
/// <param name="waitCallback">WaitCallBack delegate that will be invoked by the threads.</param>
/// <param name="callbackState">State used as parameter during invoke.</param>
public void QueueUserWorkItem(WaitCallback waitCallback, object callbackState)
{
this.QueueUserWorkItem(waitCallback, callbackState, TaskPriority.Normal);
}
#endregion
#region Private Methods
private void CreateAndStartWorkerThread(ThreadPriority threadPriority)
{
var worker = new WorkerThread();
worker.Thread = new Thread(() => this.ThreadMainLoop(ref worker));
worker.Thread.Name = "ThreadPool WorkerThread";
worker.Thread.Priority = threadPriority;
worker.Running = true;
worker.CancellationTokenSource = new CancellationTokenSource();
worker.Thread.Start();
this.threadpool.Add(worker);
}
private void CreateAndStartWorkerThread()
{
var worker = new WorkerThread();
worker.Thread = new Thread(() => this.ThreadMainLoop(ref worker));
worker.Thread.Name = "ThreadPool WorkerThread";
worker.Running = true;
worker.CancellationTokenSource = new CancellationTokenSource();
worker.Thread.Start();
this.threadpool.Add(worker);
}
/// <summary>
/// Main loop that a thread from the pool is running.
/// </summary>
/// <threadId>Id of thread</threadId>
private void ThreadMainLoop(ref WorkerThread thisWorkerThread)
{
thisWorkerThread.Running = true;
while (thisWorkerThread.Running)
{
if (thisWorkerThread.CancellationTokenSource.Token.IsCancellationRequested)
{
thisWorkerThread.Running = false;
return;
}
//Check if there are tasks
if (this.tasks.Count > 0)
{
//If there are tasks, acquire a lock onto the list
//and try to dequeue the highest priority task.
//Finally, release the lock and invoke the task.
thisWorkerThread.Working = true;
QueueEntry task = null;
while (!Monitor.TryEnter(this.tasksLock))
{
;
}
if (this.tasks.Count > 0)
{
task = this.tasks.Dequeue();
}
Monitor.Exit(this.tasksLock);
if (task != null)
{
System.Diagnostics.Debug.WriteLine(Thread.CurrentThread.Name + " - Running task!");
var waitCallback = task.WaitCallback;
waitCallback.Invoke(task.Object);
}
thisWorkerThread.Working = false;
}
}
}
/// <summary>
/// Loop of the thread that adjusts and manipulates the threadpool
/// </summary>
private void ObserverLoop()
{
var statistics = new Statistics();
while (true)
{
//Observer operates on a 100ms loop. Due to the low priority of the thread itself, this loop will almost always take
//considerably longer than 100ms.
//Checks if the queue is empty. If yes, counter is decremented, else, counter is incremented.
//If counter exceeds 5, it will try to add another thread to the thread, unless the threadpool has reached max size.
//If counter is under -10, it will try to remove a thread from the threadpool, unless the threadpool has reached less than
//max size / 4.
if (this.observerCancellationTokenSource.Token.IsCancellationRequested)
{
return;
}
//This part of code updates the statistics of the threadpool.
if (statistics.Initialized)
{
var loopDuration = (DateTime.Now - statistics.LastUpdate).TotalMilliseconds;
if (statistics.LoopFrequency == 0)
{
statistics.LoopFrequency = loopDuration;
}
else
{
statistics.LoopFrequency = (statistics.LoopFrequency + loopDuration) / 2;
}
}
else
{
statistics.Initialized = true;
}
statistics.LastUpdate = DateTime.Now;
Thread.Sleep(100);
//This part of code adjusts the performance counter. Ideally, the value should be 0.
if (this.tasks.Count > 0)
{
statistics.PerformanceCounter++;
if (statistics.PerformanceCounter > 5)
{
statistics.PerformanceCounter = 5;
}
}
else
{
statistics.PerformanceCounter--;
if (statistics.PerformanceCounter < -10)
{
statistics.PerformanceCounter = -10;
}
}
//This part of code adjusts thread priorities based on the current performance of the threadpool
if (this.tasks.Count > 0)
{
//If there are tasks pending, find a thread with priority under Normal and upgrade its priority.
var t = this.FindThreadWithLowPriority();
if (t != null)
{
this.UpgradeThreadPriority(t);
}
}
else
{
//If there are no tasks pending, find a thread with priority above Lowest and downgrade its priority.
var t = this.FindThreadWithAcceptablePriority();
if (t != null)
{
this.DowngradeThreadPriority(t);
}
}
//This part of code modifies the number of active threads based on the current performance of the threadpool
if (statistics.PerformanceCounter >= 5)
{
if (this.threadpool.Count < this.MaxThreads)
{
//Add a thread to the threadpool.
//Reset counter to 0.
statistics.PerformanceCounter = 0;
this.CreateAndStartWorkerThread();
}
}
else if (statistics.PerformanceCounter <= -10)
{
if (this.threadpool.Count > this.MaxThreads / 4)
{
//Remove the last thread in the threadpool.
//If thread is currently working, notify it to close.
//Else, abort the thread.
//Reset counter to 0.
var worker = this.threadpool[this.threadpool.Count - 1];
if (worker.Working)
{
worker.Running = false;
}
else
{
worker.CancellationTokenSource.Cancel();
}
this.threadpool.RemoveAt(this.threadpool.Count - 1);
statistics.PerformanceCounter = 0;
}
}
}
}
/// <summary>
/// Find a thread with Lowest or BelowNormal priority.
/// </summary>
/// <returns>Thread with low priority.</returns>
private Thread FindThreadWithLowPriority()
{
foreach (var t in this.threadpool)
{
if (t.Thread.Priority == ThreadPriority.Lowest || t.Thread.Priority == ThreadPriority.BelowNormal)
{
return t.Thread;
}
}
return null;
}
/// <summary>
/// Find a thread with BelowNormal or Normal priority.
/// </summary>
/// <returns>Thread with BelowNormal or Normal priority.</returns>
private Thread FindThreadWithAcceptablePriority()
{
foreach (var t in this.threadpool)
{
if (t.Thread.Priority == ThreadPriority.Normal || t.Thread.Priority == ThreadPriority.BelowNormal)
{
return t.Thread;
}
}
return null;
}
/// <summary>
/// Downgrades the priority of a thread one level.
/// </summary>
/// <param name="t">Thread to have its priority level downgraded.</param>
/// <returns></returns>
private void DowngradeThreadPriority(Thread t)
{
switch (t.Priority)
{
case ThreadPriority.Highest:
t.Priority = ThreadPriority.AboveNormal;
break;
case ThreadPriority.AboveNormal:
t.Priority = ThreadPriority.Normal;
break;
case ThreadPriority.Normal:
t.Priority = ThreadPriority.BelowNormal;
break;
case ThreadPriority.BelowNormal:
t.Priority = ThreadPriority.Lowest;
break;
case ThreadPriority.Lowest:
t.Priority = ThreadPriority.Lowest;
break;
}
}
/// <summary>
/// Upgrades the priority of a thread one level.
/// </summary>
/// <param name="t">Thread to have its priority level upgraded.</param>
/// <returns></returns>
private void UpgradeThreadPriority(Thread t)
{
switch (t.Priority)
{
case ThreadPriority.Highest:
t.Priority = ThreadPriority.Highest;
break;
case ThreadPriority.AboveNormal:
t.Priority = ThreadPriority.Highest;
break;
case ThreadPriority.Normal:
t.Priority = ThreadPriority.AboveNormal;
break;
case ThreadPriority.BelowNormal:
t.Priority = ThreadPriority.Normal;
break;
case ThreadPriority.Lowest:
t.Priority = ThreadPriority.BelowNormal;
break;
}
}
#endregion
#region IDisposable Support
private bool disposedValue = false;
/// <summary>
/// Disposes of the tasks as well as aborts all threads. Called by the public Dispose() method.
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (!this.disposedValue)
{
if (disposing)
{
if (this.observer != null)
{
this.observerCancellationTokenSource.Cancel();
this.observer.Join();
}
foreach (var worker in this.threadpool)
{
worker.CancellationTokenSource.Cancel();
worker.Thread.Join();
}
this.threadpool.Clear();
this.tasks.Clear();
}
this.threadpool = null;
this.tasks = null;
this.observer = null;
this.disposedValue = true;
}
}
/// <summary>
/// Disposes of the tasks as well as aborts all threads.
/// </summary>
public void Dispose()
{
this.Dispose(true);
}
#endregion
}
@@ -0,0 +1,159 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests;
[TestClass()]
public class AVLTreeTests
{
private AVLTree<int> avlTree = new AVLTree<int>();
[TestMethod()]
public void AVLTreeTest()
{
this.avlTree = new AVLTree<int>();
}
[TestMethod()]
public void AddTest()
{
try
{
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i * 20 - (50 + i));
}
}
catch (Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
if (this.avlTree.Count != 100)
{
Assert.Fail();
}
}
[TestMethod()]
public void ContainsTest()
{
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i);
}
if (!this.avlTree.Contains(50))
{
Assert.Fail();
}
}
[TestMethod()]
public void RemoveTest()
{
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i);
}
this.avlTree.Remove(50);
if (this.avlTree.Contains(50))
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest()
{
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i);
}
this.avlTree.Clear();
if(this.avlTree.Count > 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void CopyToTest()
{
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i);
}
var array = new int[100];
this.avlTree.CopyTo(array, 0);
}
[TestMethod()]
public void GetEnumeratorTest()
{
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i);
}
var count = 0;
foreach(var value in this.avlTree)
{
count++;
System.Diagnostics.Debug.WriteLine(value);
}
if(count != this.avlTree.Count)
{
Assert.Fail();
}
}
[TestMethod()]
public void ToArrayTest()
{
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i);
}
_ = this.avlTree.ToArray();
}
[TestMethod()]
public void Serialize()
{
var avlTree2 = new AVLTree<int>();
for (var i = 0; i < 100; i++)
{
this.avlTree.Add(i);
}
var serializer = new BinaryFormatter();
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, this.avlTree);
stream.Position = 0;
avlTree2 = (AVLTree<int>)serializer.Deserialize(stream);
}
var avlTreeEnum = this.avlTree.GetEnumerator();
var avlTree2Enum = avlTree2.GetEnumerator();
for(var i = 0; i < 100; i++)
{
if(avlTreeEnum.Current != avlTree2Enum.Current)
{
Assert.Fail();
}
avlTreeEnum.MoveNext();
avlTree2Enum.MoveNext();
}
}
}
@@ -0,0 +1,213 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests;
[TestClass()]
public class BinaryHeapTests
{
private BinaryHeap<int> binaryHeap = new BinaryHeap<int>();
[TestMethod()]
public void BinaryHeapTest()
{
this.binaryHeap = new BinaryHeap<int>();
}
[TestMethod()]
public void BinaryHeapTest1()
{
this.binaryHeap = new BinaryHeap<int>(100);
if (this.binaryHeap.Count != 0 && this.binaryHeap.Capacity != 100)
{
Assert.Fail();
}
}
[TestMethod()]
[Timeout(500)]
public void InsertTest()
{
try
{
for (var i = 0; i < 100; i++)
{
this.binaryHeap.Add(i * 20 - (50 + i));
}
}
catch (Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
if (this.binaryHeap.Count != 100)
{
Assert.Fail();
}
}
[TestMethod()]
[Timeout(500)]
public void DeleteMinTest()
{
try
{
var tries = this.binaryHeap.Count;
while (this.binaryHeap.Count > 0)
{
if (tries == 0)
{
Assert.Fail();
}
System.Diagnostics.Debug.WriteLine(this.binaryHeap.Remove());
tries--;
}
}
catch (Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
}
[TestMethod()]
public void MinMaxTest()
{
for (var i = 0; i < 1000; i++)
{
this.binaryHeap.Add(i);
}
if (this.binaryHeap.Min != 0 || this.binaryHeap.Max != 999)
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest()
{
for (var i = 0; i < 100; i++)
{
this.binaryHeap.Add(i);
}
this.binaryHeap.Clear();
if (this.binaryHeap.Count > 0 || this.binaryHeap.Capacity != 160)
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest2()
{
for (var i = 100; i < 200; i++)
{
this.binaryHeap.Add(i);
}
this.binaryHeap.Clear(false);
var array = this.binaryHeap.ToArray();
if (this.binaryHeap.Count > 0 || this.binaryHeap.Capacity == 10)
{
Assert.Fail();
}
if (array.Length != 0)
{
Assert.Fail();
}
this.binaryHeap.Clear(true);
if (this.binaryHeap.Capacity != 10 || this.binaryHeap.Count != 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void ContainsTest()
{
this.binaryHeap.Add(100);
if (!this.binaryHeap.Contains(100))
{
Assert.Fail();
}
}
[TestMethod()]
public void ToArrayTest()
{
for(var i = 0; i < 1000; i++)
{
this.binaryHeap.Add(i);
}
var array = this.binaryHeap.ToArray();
if(array.Length != this.binaryHeap.Count)
{
Assert.Fail();
}
if(array[0] != 0 && array[999] != 999)
{
Assert.Fail();
}
}
[TestMethod()]
public void GetEnumeratorTest()
{
for (var i = 0; i < 1000; i++)
{
this.binaryHeap.Add(i);
}
var count = 0;
foreach(var value in this.binaryHeap)
{
System.Diagnostics.Debug.WriteLine(value);
count++;
}
if(count == this.binaryHeap.Count)
{
return;
}
Assert.Fail();
}
[TestMethod()]
public void Serialize()
{
var binaryHeap2 = new BinaryHeap<int>();
for (var i = 0; i < 100; i++)
{
this.binaryHeap.Add(i);
}
var serializer = new BinaryFormatter();
using (var stream = new MemoryStream()) {
serializer.Serialize(stream, this.binaryHeap);
stream.Position = 0;
binaryHeap2 = (BinaryHeap<int>)serializer.Deserialize(stream);
}
var binaryHeapEnum = this.binaryHeap.GetEnumerator();
var binaryHeapEnum2 = binaryHeap2.GetEnumerator();
for (var i = 0; i < 100; i++)
{
if (binaryHeapEnum.Current != binaryHeapEnum2.Current)
{
Assert.Fail();
}
binaryHeapEnum.MoveNext();
binaryHeapEnum2.MoveNext();
}
}
}
@@ -0,0 +1,212 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests;
[TestClass()]
public class FibonacciHeapTests
{
FibonacciHeap<int> fibonacciHeap = new FibonacciHeap<int>();
[TestMethod()]
public void InsertTest()
{
for (var i = 0; i < 1000; i++)
{
this.fibonacciHeap.Add(i);
}
}
[TestMethod()]
public void FibonacciHeapTest()
{
this.fibonacciHeap = new FibonacciHeap<int>();
if(this.fibonacciHeap.Count != 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void MergeTest()
{
var fibonacciHeap1 = new FibonacciHeap<int>();
var fibonacciHeap2 = new FibonacciHeap<int>();
for(var i = 0; i < 100; i++)
{
fibonacciHeap1.Add(i);
}
for(var i = 100; i < 300; i++)
{
fibonacciHeap2.Add(i);
}
fibonacciHeap1.Merge(fibonacciHeap2);
for(var i = 1; i < 300; i++)
{
if (!fibonacciHeap1.Contains(i))
{
Assert.Fail();
}
}
}
[TestMethod()]
public void RemoveMinimumTest()
{
try
{
this.fibonacciHeap.Remove();
Assert.Fail();
}
catch (IndexOutOfRangeException)
{
}
catch (Exception)
{
Assert.Fail();
}
for (var i = 0; i < 1000; i++)
{
this.fibonacciHeap.Add(i);
}
for (var i = 0; i < 1000; i++)
{
if (this.fibonacciHeap.Remove() != i)
{
Assert.Fail();
}
}
}
[TestMethod()]
public void DecreaseKeyTest()
{
this.fibonacciHeap.Add(5);
this.fibonacciHeap.Add(10);
this.fibonacciHeap.Add(7);
this.fibonacciHeap.DecreaseKey(5, 3);
if(this.fibonacciHeap.Contains(5) || !this.fibonacciHeap.Contains(3))
{
Assert.Fail();
}
this.fibonacciHeap.DecreaseKey(10, 1);
if(this.fibonacciHeap.Remove() != 1)
{
Assert.Fail();
}
}
[TestMethod()]
public void ContainsTest()
{
for(var i = 0; i < 10000; i++)
{
this.fibonacciHeap.Add(i);
}
if (!this.fibonacciHeap.Contains(5124))
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest()
{
for(var i = 0; i < 10000; i++)
{
this.fibonacciHeap.Add(i);
}
this.fibonacciHeap.Remove();
this.fibonacciHeap.Clear();
if(this.fibonacciHeap.Count > 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void ToArrayTest()
{
for(var i = 999; i >= 0; i--)
{
this.fibonacciHeap.Add(i);
}
this.fibonacciHeap.Remove();
var arr = this.fibonacciHeap.ToArray();
for(var i = 1; i < 512; i++)
{
if(arr[i - 1] != i)
{
Assert.Fail();
}
}
}
[TestMethod()]
public void GetEnumeratorTest()
{
for (var i = 999; i >= 0; i--)
{
this.fibonacciHeap.Add(i);
}
var count = 0;
foreach(var value in this.fibonacciHeap)
{
System.Diagnostics.Debug.WriteLine(value);
count++;
}
if(count == this.fibonacciHeap.Count)
{
return;
}
Assert.Fail();
}
[TestMethod()]
public void Serialize()
{
var fibonacciHeap2 = new FibonacciHeap<int>();
for (var i = 0; i < 100; i++)
{
this.fibonacciHeap.Add(i);
}
var serializer = new BinaryFormatter();
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, this.fibonacciHeap);
stream.Position = 0;
fibonacciHeap2 = (FibonacciHeap<int>)serializer.Deserialize(stream);
}
var enum1 = this.fibonacciHeap.GetEnumerator();
var enum2 = fibonacciHeap2.GetEnumerator();
for (var i = 0; i < 100; i++)
{
if (enum1.Current != enum2.Current)
{
Assert.Fail();
}
enum1.MoveNext();
enum2.MoveNext();
}
}
}
@@ -0,0 +1,116 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests;
[TestClass()]
public class PriorityQueueTests
{
private PriorityQueue<int> priorityQueue = new PriorityQueue<int>();
[TestMethod()]
public void PriorityQueueTest()
{
try
{
this.priorityQueue = new PriorityQueue<int>();
if(this.priorityQueue.Count > 0)
{
Assert.Fail();
}
}
catch(Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
}
[TestMethod()]
public void EnqueueTest()
{
for(var i = 0; i < 100; i++)
{
this.priorityQueue.Enqueue(i);
}
if(this.priorityQueue.Count != 100)
{
Assert.Fail();
}
}
[TestMethod()]
public void DequeueTest()
{
for(var i = 0; i < 100; i++)
{
this.priorityQueue.Enqueue(i);
}
for(var i = 0; i < 100; i++)
{
if(i != this.priorityQueue.Dequeue())
{
Assert.Fail();
}
}
if(this.priorityQueue.Count > 0)
{
Assert.Fail();
}
try
{
this.priorityQueue.Dequeue();
Assert.Fail();
}
catch
{
}
}
[TestMethod()]
public void PeekTest()
{
this.priorityQueue.Enqueue(1051);
if(this.priorityQueue.Peek() != 1051 && this.priorityQueue.Count != 1)
{
Assert.Fail();
}
}
[TestMethod()]
public void Serialize()
{
var priorityQueue2 = new PriorityQueue<int>();
for (var i = 0; i < 100; i++)
{
this.priorityQueue.Enqueue(i);
}
var serializer = new BinaryFormatter();
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, this.priorityQueue);
stream.Position = 0;
priorityQueue2 = (PriorityQueue<int>)serializer.Deserialize(stream);
}
var enum1 = this.priorityQueue.GetEnumerator();
var enum2 = priorityQueue2.GetEnumerator();
for (var i = 0; i < 100; i++)
{
if (enum1.Current != enum2.Current)
{
Assert.Fail();
}
enum1.MoveNext();
enum2.MoveNext();
}
}
}
@@ -0,0 +1,161 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests;
[TestClass()]
public class SkipListTests
{
SkipList<int> skipList = new SkipList<int>();
[TestMethod()]
public void SkipListTest()
{
_ = new SkipList<int>();
}
[TestMethod()]
public void SkipListTest2()
{
_ = new SkipList<int>(30);
}
[TestMethod()]
public void AddTest()
{
for(var i = 0; i < 200; i++)
{
this.skipList.Add(i);
}
}
[TestMethod()]
public void ClearTest()
{
for (var i = 0; i < 200; i++)
{
this.skipList.Add(i);
}
this.skipList.Clear();
if(this.skipList.Count > 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void ContainsTest()
{
for (var i = 0; i < 200; i++)
{
this.skipList.Add(i);
}
if (!this.skipList.Contains(50))
{
Assert.Fail();
}
}
[TestMethod()]
public void CopyToTest()
{
for (var i = 0; i < 200; i++)
{
this.skipList.Add(i);
}
var array = new int[this.skipList.Count];
this.skipList.CopyTo(array, 0);
for (var i = 0; i < 200; i++)
{
if(array[i] != i)
{
Assert.Fail();
}
}
}
[TestMethod()]
public void ToArrayTest()
{
for (var i = 0; i < 200; i++)
{
this.skipList.Add(i);
}
var array = this.skipList.ToArray();
for (var i = 0; i < 200; i++)
{
if(array[i] != i)
{
Assert.Fail();
}
}
}
[TestMethod()]
public void GetEnumeratorTest()
{
for (var i = 0; i < 200; i++)
{
this.skipList.Add(i);
}
foreach(var i in this.skipList)
{
System.Diagnostics.Debug.WriteLine(i);
}
}
[TestMethod()]
public void RemoveTest()
{
for (var i = 0; i < 200; i++)
{
this.skipList.Add(i);
}
this.skipList.Remove(50);
if (this.skipList.Contains(50))
{
Assert.Fail();
}
}
[TestMethod()]
[Ignore("Binary serialization is obsolete and should not be used anymore")]
public void Serialize()
{
var skipList2 = new SkipList<int>();
for (var i = 0; i < 100; i++)
{
this.skipList.Add(i);
}
var serializer = new BinaryFormatter();
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, this.skipList);
stream.Position = 0;
skipList2 = (SkipList<int>)serializer.Deserialize(stream);
}
var enum1 = this.skipList.GetEnumerator();
var enum2 = skipList2.GetEnumerator();
for (var i = 0; i < 100; i++)
{
if (enum1.Current != enum2.Current)
{
Assert.Fail();
}
enum1.MoveNext();
enum2.MoveNext();
}
}
}
@@ -0,0 +1,145 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests;
[TestClass()]
public class TreapTests
{
Treap<int> treap = new Treap<int>();
[TestMethod()]
public void TreapTest()
{
this.treap = new Treap<int>();
}
[TestMethod()]
public void InsertTest()
{
var random = new Random();
for(var i = 0; i < 1000; i++)
{
this.treap.Add(random.Next(0, 5000));
}
}
[TestMethod()]
public void RemoveTest()
{
this.treap.Add(60);
this.treap.Add(6);
this.treap.Add(5);
this.treap.Remove(60);
if(this.treap.Contains(60) || this.treap.Count > 2)
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest()
{
var random = new Random();
for (var i = 0; i < 100; i++)
{
this.treap.Add(random.Next(0, 5000));
}
this.treap.Clear();
if(this.treap.Count > 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void ContainsTest()
{
this.treap.Add(50);
this.treap.Add(25);
this.treap.Add(991142);
this.treap.Add(12313);
this.treap.Add(24);
this.treap.Add(23);
if (!this.treap.Contains(24))
{
Assert.Fail();
}
}
[TestMethod()]
public void ToArrayTest()
{
for(var i = 0; i < 1000; i++)
{
this.treap.Add(i);
}
var arr = this.treap.ToArray();
for(var i = 0; i < 1000; i++)
{
if(arr[i] != i)
{
Assert.Fail();
}
}
}
[TestMethod()]
public void GetEnumeratorTest()
{
for (var i = 0; i < 1000; i++)
{
this.treap.Add(i);
}
var count = 0;
foreach(var value in this.treap)
{
System.Diagnostics.Debug.WriteLine(value);
count++;
}
if(count == this.treap.Count)
{
return;
}
Assert.Fail();
}
[TestMethod()]
[Ignore("Binary serialization is obsolete and should not be used anymore")]
public void Serialize()
{
var treap2 = new Treap<int>();
for (var i = 0; i < 100; i++)
{
this.treap.Add(i);
}
var serializer = new BinaryFormatter();
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, this.treap);
stream.Position = 0;
treap2 = (Treap<int>)serializer.Deserialize(stream);
}
var enum1 = this.treap.GetEnumerator();
var enum2 = treap2.GetEnumerator();
for (var i = 0; i < 100; i++)
{
if (enum1.Current != enum2.Current)
{
Assert.Fail();
}
enum1.MoveNext();
enum2.MoveNext();
}
}
}
@@ -0,0 +1,42 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace System.Extensions.Tests;
[TestClass]
public class ObjectExtensionsTests
{
[TestMethod]
public void ThrowIfNull_NetCore_ThrowsWithCorrectName()
{
object obj = null;
try
{
System.Core.Extensions.ObjectExtensions.ThrowIfNull(obj);
}
catch (ArgumentNullException ex)
{
ex.ParamName.Should().Be("obj");
return;
}
Assert.Fail("Null object should throw");
}
[TestMethod]
public void ThrowIfNull_NetStandard_ThrowsWithCorrectName()
{
object obj = null;
try
{
ObjectExtensions.ThrowIfNull(obj, nameof(obj));
}
catch (ArgumentNullException ex)
{
ex.ParamName.Should().Be("obj");
return;
}
Assert.Fail("Null object should throw");
}
}

Some files were not shown because too many files have changed in this diff Show More