Compare commits

...

87 Commits

Author SHA1 Message Date
amacocian 2c918cf4a4 Blittable strings generator implementation
Improvements to fixed array generator
2026-01-27 14:15:41 +01:00
amacocian dbdf5cfbe2 Remove deprecated packages 2026-01-12 22:24:26 +01:00
amacocian 124ae942b5 Remove Slim dependency and adhere to Microsoft.Extensions standards 2026-01-11 01:02:42 +01:00
amacocian 5e5cdb3b47 Improve logging 2026-01-11 00:53:44 +01:00
amacocian a4d0d5cab7 IClientWebSocket builder 2026-01-09 14:26:36 +01:00
amacocian 3b47d0bd0c Deprecate logging and config abstractions, in favor of standard MS provided ones 2026-01-09 13:20:18 +01:00
amacocian 4033f9d401 Remove gitlab mirror 2025-09-13 13:39:13 +02:00
amacocian c9f6541904 Migrate to slnx 2025-09-12 11:39:38 +02:00
amacocian 18cd59d414 Mark notifypropertychanged with nullable 2025-06-29 12:25:13 +02:00
amacocian c63fd45e38 Fix from managed to fixed array 2025-05-20 18:41:39 +02:00
amacocian 7ec573098b Allow multiple on fixed array generator 2025-05-20 18:15:51 +02:00
amacocian af1dfd6751 Expose SemaphoreSlim.Acquire with CancellationToken 2025-05-20 12:25:36 +02:00
amacocian bae28b2552 Implement FixedArrayGenerator 2025-05-17 17:50:29 +02:00
amacocian 818240fcea Fix nullability of parameter 2024-10-16 14:29:51 +02:00
amacocian 25eae4061b Fix ThrowIfNull 2024-10-16 14:09:13 +02:00
amacocian 33584e8a14 Update dependency injection package 2024-09-27 17:10:49 +02:00
amacocian 7d06595307 Implement value caches 2024-09-26 10:32:38 +02:00
amacocian d4ef0dc34e Base32 conversions 2024-09-08 18:37:38 +02:00
amacocian 0fc24b8307 Fix build issues 2024-09-08 17:13:48 +02:00
amacocian def0258695 More hashing services and HMAC services 2024-09-08 17:08:22 +02:00
amacocian 535a2cedc0 Argument to dispose inner http client 2024-09-06 23:44:27 +02:00
amacocian 2b23665145 Downgrade compiler version to support MAUI 2024-09-02 20:47:41 +02:00
amacocian 3b942c2102 Adjust generator namespace 2024-09-02 20:29:13 +02:00
amacocian 681bec01f2 Fix generators project 2024-08-28 15:33:32 +02:00
amacocian 552b72b1b9 Introduce source generators package
Update dependencies
2024-08-28 15:24:18 +02:00
amacocian 00459fc59b SemaphoreSlim context 2024-08-23 22:42:25 +02:00
amacocian 84a9f8e8d6 Clean csproj 2024-08-22 13:28:45 +02:00
amacocian a8de6cd15d ScopedLogger with implicit member name
Include NetStandard in NetCore
2024-08-22 13:21:45 +02:00
amacocian ec343797b4 Additional extension to scoped logger 2024-08-21 11:25:57 +02:00
amacocian 8a58a6e9a3 Improve HttpClient with client constructor 2024-08-16 12:51:20 +02:00
amacocian 3131558017 Change HttpClientBuilder to work on IServiceCollection 2024-06-04 12:37:09 +02:00
amacocian 6a541e07a3 Update dependencies 2024-04-18 17:53:42 +02:00
amacocian 791387fb29 Circular buffer implementation 2024-04-18 17:45:00 +02:00
amacocian b7244b2bdb Replace Moq with NSubstitute (#51) 2023-08-09 16:11:11 +02:00
amacocian fe049d4c00 Add extension to recycle websockets (#50)
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2023-05-15 09:21:29 +00:00
amacocian 0de73a967b Client Web Socket fix (#49)
* Fix for clientwebsocket

* Updated dependencies
2023-05-14 23:13:33 +00:00
amacocian 8d7676bedf Fix for ClientWebSocketResolver (#48) 2023-05-14 22:15:35 +00:00
amacocian 2ed3de439e Client web socket resolver (#47) 2023-05-14 21:19:32 +00:00
amacocian de08c0e3b0 Client Web Socket implementation (#46) 2023-05-14 20:46:38 +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
120 changed files with 9106 additions and 5410 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"
+38 -16
View File
@@ -4,10 +4,7 @@ on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
@@ -20,21 +17,23 @@ jobs:
env:
Configuration: Release
Solution_Path: SystemExtensions.sln
Test_Project_Path: SystemExtensions.Tests\SystemExtensions.Tests.csproj
Solution_Path: SystemExtensions.slnx
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@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '5.0.202'
dotnet-version: '10.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.1
@@ -44,12 +43,35 @@ jobs:
env:
RuntimeIdentifier: win-${{ matrix.targetplatform }}
- name: Build Slim project
- name: Build SystemExtensions.NetStandard project
run: dotnet build SystemExtensions.NetStandard -c $env:Configuration
- name: Push nuget package
uses: brandedoutcast/publish-nuget@v2.5.5
with:
PROJECT_FILE_PATH: SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
- 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: Build SystemExtensions.NetStandard.Generators project
run: dotnet build SystemExtensions.NetStandard.Generators -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: Package SystemExtensions.NetStandard.Generators
run: dotnet pack -c Release -o . SystemExtensions.NetStandard.Generators\SystemExtensions.NetStandard.Generators.csproj
- name: Publish
run: dotnet nuget push *.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
+13 -5
View File
@@ -19,21 +19,23 @@ jobs:
runs-on: windows-latest
env:
Solution_Path: Slim.sln
Test_Project_Path: SystemExtensions.Tests\SystemExtensions.Tests.csproj
Solution_Path: SystemExtensions.slnx
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@v2
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install .NET Core
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v3
with:
dotnet-version: '5.0.202'
dotnet-version: '10.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.1
@@ -41,6 +43,12 @@ jobs:
- 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:
@@ -0,0 +1,96 @@
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using NSubstitute.ExceptionExtensions;
using System;
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 IServiceCollection serviceProducerMock = Substitute.For<IServiceCollection>();
private readonly Uri baseAddress = new("http://contoso.co");
public HttpClientBuilderTests()
{
this.httpClientBuilder = new HttpClientBuilder<object>(this.serviceProducerMock);
}
[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().BeEquivalentTo(this.serviceProducerMock);
}
[TestMethod]
public async Task HttpClientBuilder_RegistersClientCorrectly_IServiceProviderReturnsExpected()
{
var container = new ServiceCollection();
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 di = container.BuildServiceProvider();
var client = di.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,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,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.5.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.5.2" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17">
<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,70 @@
using FluentAssertions;
using global::SystemExtensions.NetStandard.DependencyInjection.Tests.Http.Models;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using NSubstitute;
using System;
using System.Extensions;
using System.Linq;
using System.Net.Http;
using System.Net.WebSockets;
using System.Threading.Tasks;
namespace SystemExtensions.NetStandard.DependencyInjection.Tests.WebSockets;
[TestClass]
public sealed class HttpClientBuilderTests
{
private readonly ClientWebSocketBuilder<object> clientWebSocketBuilder;
private readonly IServiceCollection serviceProducerMock = Substitute.For<IServiceCollection>();
public HttpClientBuilderTests()
{
this.clientWebSocketBuilder = new ClientWebSocketBuilder<object>(this.serviceProducerMock);
}
[TestMethod]
public void Constructor_NullServiceProducer_Throws()
{
var action = () => new ClientWebSocketBuilder<object>(null);
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WithInnerWebSocket_NullHandler_Throws()
{
var action = () => this.clientWebSocketBuilder.WithInnerWebSocket(null);
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void WIthInnerWebSocketFactory_NullBaseAddress_Throws()
{
var action = () => this.clientWebSocketBuilder.WithInnerWebSocketFactory(null);
action.Should().Throw<ArgumentNullException>();
}
[TestMethod]
public void Build_ReturnsServiceProducer()
{
var producer = this.clientWebSocketBuilder.Build();
producer.Should().BeEquivalentTo(this.serviceProducerMock);
}
[TestMethod]
public async Task ClientWebSocketBuilder_RegistersClientCorrectly_IServiceProviderReturnsExpected()
{
var container = new ServiceCollection();
var innerWebSocket = new ClientWebSocket();
container.RegisterClientWebSocket<object>()
.WithInnerWebSocket(innerWebSocket)
.Build();
var di = container.BuildServiceProvider();
var client = di.GetService<IClientWebSocket<object>>();
client.Should().NotBeNull();
}
}
@@ -0,0 +1,17 @@
using Microsoft.Extensions.Logging;
using System.Logging;
using System.Runtime.CompilerServices;
namespace System.Extensions.Core;
public static class LoggingExtensions
{
public static ScopedLogger<T> CreateScopedLogger<T>(this ILogger<T> logger, string flowIdentifier, [CallerMemberName] string? methodName = default)
{
return ScopedLogger<T>.Create(logger, methodName ?? string.Empty, flowIdentifier);
}
public static ScopedLogger<T> CreateScopedLogger<T>(this ILogger<T> logger, [CallerMemberName] string? methodName = default)
{
return ScopedLogger<T>.Create(logger, methodName ?? string.Empty, string.Empty);
}
}
@@ -0,0 +1,18 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace System.Core.Extensions;
public static class ObjectExtensions
{
public static T ThrowIfNull<T>([NotNull] this T? obj, [CallerArgumentExpression("obj")] string? paramName = null)
where T : class
{
if (obj is null)
{
throw new ArgumentNullException(paramName);
}
return obj;
}
}
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Nullable>enable</Nullable>
<RootNamespace>System</RootNamespace>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<Version>1.9</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>
<ItemGroup>
<ProjectReference Include="..\SystemExtensions.NetStandard\SystemExtensions.NetStandard.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
using Microsoft.Extensions.DependencyInjection;
using System.Net.Http;
using System.Net.WebSockets;
namespace System.Extensions;
public static class ServiceCollectionExtensions
{
/// <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="services"><see cref="IServiceCollection"/>.</param>
/// <returns><see cref="HttpClientBuilder{T}"/> to build the http client.</returns>
public static HttpClientBuilder<T> RegisterHttpClient<T>(this IServiceCollection services)
{
services.ThrowIfNull(nameof(services));
return new HttpClientBuilder<T>(services);
}
/// <summary>
/// Register a scoped <see cref="IClientWebSocket{TScope}"/> to be used by the DI engine.
/// </summary>
/// <typeparam name="T">Type of the scoped <see cref="IClientWebSocket{TScope}"/>.</typeparam>
/// <param name="services"><see cref="IServiceCollection"/>.</param>
/// <returns><see cref="ClientWebSocketBuilder{T}"/> to build the websocket client.</returns>
public static ClientWebSocketBuilder<T> RegisterClientWebSocket<T>(this IServiceCollection services)
{
services.ThrowIfNull(nameof(services));
return new ClientWebSocketBuilder<T>(services);
}
}
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("SystemExtensions.NetStandard.DependencyInjection.Tests")]
@@ -0,0 +1,88 @@
using Microsoft.Extensions.DependencyInjection;
using System.Extensions;
using System.Net.Http.Headers;
namespace System.Net.Http;
public sealed class HttpClientBuilder<T>
{
private readonly IServiceCollection services;
private Uri? baseAddress;
private Func<IServiceProvider, HttpMessageHandler>? httpMessageHandlerFactory;
private Action<HttpRequestHeaders>? defaultRequestHeadersSetup;
private bool disposeMessageHandler;
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(IServiceCollection services)
{
services.ThrowIfNull(nameof(services));
this.services = services;
}
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 IServiceCollection Build()
{
this.services.AddScoped<IHttpClient<T>>(sp =>
{
var client = this.httpMessageHandlerFactory is not null ?
new HttpClient<T>(this.httpMessageHandlerFactory(sp), this.disposeMessageHandler) :
new HttpClient<T>(true);
if (this.baseAddress is not null)
{
client.BaseAddress = this.baseAddress;
}
this.defaultRequestHeadersSetup?.Invoke(client.DefaultRequestHeaders);
client.MaxResponseContentBufferSize = this.maxResponseBufferSize;
client.Timeout = this.timeout;
return client;
});
return this.services;
}
}
@@ -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.8</Version>
<Authors>Alexandru Macocian</Authors>
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
<Description>Extensions for the Slim Dependency Injection engine</Description>
<Nullable>enable</Nullable>
</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="10.0.1" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.8.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,58 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.Extensions;
using System.Net.WebSockets;
namespace System.Net.Http;
public sealed class ClientWebSocketBuilder<T>
{
private readonly IServiceCollection services;
private ClientWebSocket? innerWebSocket;
private Func<IServiceProvider, ClientWebSocket>? innerWebSocketFactory;
internal ClientWebSocketBuilder(IServiceCollection services)
{
services.ThrowIfNull(nameof(services));
this.services = services;
}
public ClientWebSocketBuilder<T> WithInnerWebSocket(ClientWebSocket innerWebSocket)
{
this.innerWebSocket = innerWebSocket.ThrowIfNull(nameof(innerWebSocket));
return this;
}
public ClientWebSocketBuilder<T> WithInnerWebSocketFactory(Func<IServiceProvider, ClientWebSocket> innerWebSocketFactory)
{
this.innerWebSocketFactory = innerWebSocketFactory.ThrowIfNull(nameof(innerWebSocketFactory));
return this;
}
public IServiceCollection Build()
{
this.services.AddScoped<IClientWebSocket<T>>(sp =>
{
var logger = sp.GetService<ILogger<T>>();
if (logger is null)
{
return new ClientWebSocket<T>();
}
if (this.innerWebSocketFactory is not null)
{
return new ClientWebSocket<T>(this.innerWebSocketFactory(sp), logger);
}
if (this.innerWebSocket is not null)
{
return new ClientWebSocket<T>(this.innerWebSocket, logger);
}
return new ClientWebSocket<T>(logger);
});
return this.services;
}
}
@@ -0,0 +1,142 @@
using Microsoft.CodeAnalysis;
using Sybil;
using System.Text;
using System.Linq;
namespace System.Extensions;
#nullable enable
[Generator(LanguageNames.CSharp)]
public class BlittableStringGenerator : IIncrementalGenerator
{
private const string UsingSystem = "System";
private const string UsingSystemRuntimeInterop = "System.Runtime.InteropServices";
private const string AttributeNamespace = "System.Extensions";
private const string AttributeName = "GenerateBlittableStringAttribute";
private const string AttributeShortName = "GenerateBlittableString";
private const string String = "String";
private const string Public = "public";
private const string Required = "required";
private const string Size = "Size";
private const string Int = "int";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(context =>
{
var compilationUnitBuilder = SyntaxBuilder.CreateCompilationUnit()
.WithNamespace(
SyntaxBuilder.CreateNamespace(AttributeNamespace)
.WithClass(SyntaxBuilder.CreateClass(AttributeName)
.WithModifier(Public)
.WithConstructor(SyntaxBuilder.CreateConstructor(AttributeName)
.WithModifier(Public))
.WithAttribute(SyntaxBuilder.CreateAttribute("AttributeUsage")
.WithArgument(AttributeTargets.Assembly)
.WithArgument("Inherited", false)
.WithArgument("AllowMultiple", true))
.WithProperty(SyntaxBuilder.CreateProperty(Int, Size)
.WithModifier(Public)
.WithModifier(Required)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter()))
.WithBaseClass(nameof(Attribute))));
var compilationUnitSyntax = compilationUnitBuilder.Build();
var source = compilationUnitSyntax.ToFullString();
context.AddSource($"{AttributeName}.g", source);
});
context.RegisterSourceOutput(context.CompilationProvider, (sourceProductionContext, compilation) =>
{
var assemblyAttributes =
compilation.Assembly.GetAttributes().Where(a => a.AttributeClass != null &&
(a.AttributeClass.Name == AttributeName || a.AttributeClass.Name == AttributeShortName));
foreach (var attr in assemblyAttributes)
{
// Extract the Size parameter from the named arguments.
int? fixedSize = default;
foreach (var arg in attr.NamedArguments)
{
if (arg.Key == Size && arg.Value.Value is int intValue)
{
fixedSize = intValue;
break;
}
}
if (fixedSize is null)
{
// As Size is required, skip if not provided.
continue;
}
Execute(sourceProductionContext, (int)fixedSize);
}
});
}
private static void Execute(SourceProductionContext sourceProductionContext, int size)
{
var structName = $"{String}{size}";
var builder = new StringBuilder();
builder.AppendLine($"using {UsingSystem};");
builder.AppendLine($"using {UsingSystemRuntimeInterop};");
builder.AppendLine();
builder.AppendLine($"namespace {AttributeNamespace};");
builder.AppendLine();
builder.AppendLine("[StructLayout(LayoutKind.Sequential, Pack = 1)]");
builder.AppendLine($"public unsafe partial struct {structName}");
builder.AppendLine("{");
builder.AppendLine($" public const int Size = {size};");
builder.AppendLine();
builder.AppendLine(" public fixed char Value[Size];");
builder.AppendLine();
builder.AppendLine(" public Span<char> AsSpan()");
builder.AppendLine(" {");
builder.AppendLine(" fixed (char* ptr = this.Value)");
builder.AppendLine(" {");
builder.AppendLine(" return MemoryMarshal.CreateSpan(ref *ptr, Size);");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine(" public readonly ReadOnlySpan<char> AsReadOnlySpan()");
builder.AppendLine(" {");
builder.AppendLine(" fixed (char* ptr = this.Value)");
builder.AppendLine(" {");
builder.AppendLine(" return new ReadOnlySpan<char>(ptr, Size);");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine(" public void Set(ReadOnlySpan<char> value)");
builder.AppendLine(" {");
builder.AppendLine(" fixed (char* ptr = this.Value)");
builder.AppendLine(" {");
builder.AppendLine(" var span = MemoryMarshal.CreateSpan(ref *ptr, Size);");
builder.AppendLine(" span.Clear();");
builder.AppendLine(" value[..Math.Min(value.Length, Size - 1)].CopyTo(span);");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine(" public override readonly string ToString()");
builder.AppendLine(" {");
builder.AppendLine(" var span = this.AsReadOnlySpan();");
builder.AppendLine(" var nullIndex = span.IndexOf('\\0');");
builder.AppendLine(" return nullIndex >= 0 ? new string(span[..nullIndex]) : new string(span);");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" public static implicit operator string({structName} value) => value.ToString();");
builder.AppendLine();
builder.AppendLine($" public static implicit operator {structName}(string value)");
builder.AppendLine(" {");
builder.AppendLine($" var result = default({structName});");
builder.AppendLine(" result.Set(value);");
builder.AppendLine(" return result;");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" public static implicit operator ReadOnlySpan<char>({structName} value) => value.AsReadOnlySpan();");
builder.AppendLine("}");
sourceProductionContext.AddSource($"{structName}.g.cs", builder.ToString());
}
}
#nullable disable
@@ -0,0 +1,78 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace System.Extensions;
internal static class Diagnostics
{
public const string DependencyPropertyGenerator_NoAttributeFound = "DGD 0001";
public const string DependencyPropertyGenerator_ClassMustBeTopLevel = "DGD 1001";
public const string DependencyPropertyGenerator_ClassMustBePartial = "DGD 1002";
public const string DependencyPropertyGenerator_ClassMustBeInNamespace = "DGD 1003";
public const string DependencyPropertyGenerator_ClassMustImplementINotifyPropertyChanged = "DGD 1004";
public static Diagnostic MissingAttributeDiagnostic(string attributeName, string attributeNamespace) => Diagnostic.Create(
new DiagnosticDescriptor(
DependencyPropertyGenerator_NoAttributeFound,
$"{attributeNamespace}.{attributeName} not found",
"Could not find attribute with name {0} in namespace {1}.",
"SystemExtensions.NetStandard.Generators",
DiagnosticSeverity.Error,
true,
$"This error occurs when the attribute generated by the {nameof(NotifyPropertyChangedGenerator)} is not found",
null),
Location.None,
attributeName, attributeNamespace);
public static Diagnostic ClassNotTopLevelDiagnostic(string className, string containingSymbol, SyntaxTree syntaxTree, TextSpan textSpan) => Diagnostic.Create(
new DiagnosticDescriptor(
DependencyPropertyGenerator_ClassMustBeTopLevel,
$"{className} must be top level",
"Class {0} must be top level. It is currently declared under {1}.",
"SystemExtensions.NetStandard.Generators",
DiagnosticSeverity.Error,
true,
null,
null),
Location.Create(syntaxTree, textSpan),
className, containingSymbol);
public static Diagnostic ClassNotPartial(string className) => Diagnostic.Create(
new DiagnosticDescriptor(
DependencyPropertyGenerator_ClassMustBePartial,
$"{className} is not partial",
"Class {0} must be marked as partial in order for the generator to work",
"SystemExtensions.NetStandard.Generators",
DiagnosticSeverity.Error,
true,
null,
null),
Location.None,
className);
public static Diagnostic ClassNotInNamespace(string className) => Diagnostic.Create(
new DiagnosticDescriptor(
DependencyPropertyGenerator_ClassMustBeInNamespace,
$"{className} is not defined in a namespace",
"Class {0} must be defined inside a namespace for the generator to work",
"SystemExtensions.NetStandard.Generators",
DiagnosticSeverity.Error,
true,
null,
null),
Location.None,
className);
public static Diagnostic ClassDoesNotImplementINotifyPropertyChanged(string className) => Diagnostic.Create(
new DiagnosticDescriptor(
DependencyPropertyGenerator_ClassMustImplementINotifyPropertyChanged,
$"{className} must implement INotifyPropertyChanged",
"Class {0} must implement INotifyPropertyChanged for the generator to work",
"SystemExtensions.NetStandard.Generators",
DiagnosticSeverity.Error,
true,
null,
null),
Location.None,
className);
}
@@ -0,0 +1,164 @@
using Microsoft.CodeAnalysis;
using Sybil;
using System.Text;
using System.Linq;
namespace System.Extensions;
#nullable enable
[Generator(LanguageNames.CSharp)]
public class FixedArrayGenerator : IIncrementalGenerator
{
private const string UsingSystem = "System";
private const string UsingSystemRuntimeInterop = "System.Runtime.InteropServices";
private const string AttributeNamespace = "System.Extensions";
private const string AttributeName = "GenerateFixedArrayAttribute";
private const string AttributeShortName = "GenerateFixedArray";
private const string Array = "Array";
private const string Public = "public";
private const string Struct = "struct";
private const string Required = "required";
private const string Size = "Size";
private const string Int = "int";
private const string TType = "T";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(context =>
{
var compilationUnitBuilder = SyntaxBuilder.CreateCompilationUnit()
.WithNamespace(
SyntaxBuilder.CreateNamespace(AttributeNamespace)
.WithClass(SyntaxBuilder.CreateClass(AttributeName)
.WithModifier(Public)
.WithTypeParameter(SyntaxBuilder.CreateTypeParameter(TType))
.WithTypeParameterConstraint(SyntaxBuilder.CreateTypeParameterConstraint(TType)
.WithType(Struct))
.WithConstructor(SyntaxBuilder.CreateConstructor(AttributeName)
.WithModifier(Public))
.WithAttribute(SyntaxBuilder.CreateAttribute("AttributeUsage")
.WithArgument(AttributeTargets.Assembly)
.WithArgument("Inherited", false)
.WithArgument("AllowMultiple", true))
.WithProperty(SyntaxBuilder.CreateProperty(Int, Size)
.WithModifier(Public)
.WithModifier(Required)
.WithAccessor(SyntaxBuilder.CreateGetter())
.WithAccessor(SyntaxBuilder.CreateSetter()))
.WithBaseClass(nameof(Attribute))));
var compilationUnitSyntax = compilationUnitBuilder.Build();
var source = compilationUnitSyntax.ToFullString();
context.AddSource($"{AttributeName}.g", source);
});
context.RegisterSourceOutput(context.CompilationProvider, (sourceProductionContext, compilation) =>
{
var assemblyAttributes =
compilation.Assembly.GetAttributes().Where(a => a.AttributeClass != null &&
(a.AttributeClass.Name == AttributeName || a.AttributeClass.Name == AttributeShortName));
foreach (var attr in assemblyAttributes)
{
// Extract the type argument (T) from GenerateFixedArrayAttribute<T>
var namedTypeSymbol = attr.AttributeClass as INamedTypeSymbol;
if (namedTypeSymbol is null || namedTypeSymbol.TypeArguments.Length != 1)
{
continue;
}
var targetType = namedTypeSymbol.TypeArguments[0].ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
// Extract the Size parameter from the named arguments.
int? fixedSize = default;
foreach (var arg in attr.NamedArguments)
{
if (arg.Key == Size && arg.Value.Value is int intValue)
{
fixedSize = intValue;
break;
}
}
if (fixedSize is null)
{
// As Size is required, skip if not provided.
continue;
}
Execute(sourceProductionContext, targetType, (int)fixedSize);
}
});
}
private static void Execute(SourceProductionContext sourceProductionContext, string targetType, int size)
{
var capitalizedType = targetType.ToUpperInvariant()[0] + targetType.Substring(1);
var structName = $"{Array}{size}{capitalizedType}";
var builder = new StringBuilder();
builder.AppendLine($"using {UsingSystem};");
builder.AppendLine($"using {UsingSystemRuntimeInterop};");
builder.AppendLine();
builder.AppendLine($"namespace {AttributeNamespace};");
builder.AppendLine();
builder.AppendLine("[StructLayout(LayoutKind.Sequential, Pack = 1)]");
builder.AppendLine($"public unsafe struct {structName}");
builder.AppendLine("{");
builder.AppendLine($" private const int Size = {size};");
builder.AppendLine();
builder.AppendLine($" private fixed {targetType} elements[Size];");
builder.AppendLine();
builder.AppendLine(" public int Length => Size;");
builder.AppendLine();
builder.AppendLine($" public ref {targetType} this[int index]");
builder.AppendLine(" {");
builder.AppendLine(" get");
builder.AppendLine(" {");
builder.AppendLine(" if ((uint)index >= Size) throw new IndexOutOfRangeException();");
builder.AppendLine($" return ref this.elements[index];");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" public Span<{targetType}> AsSpan()");
builder.AppendLine(" {");
builder.AppendLine($" fixed ({targetType}* ptr = this.elements)");
builder.AppendLine(" {");
builder.AppendLine($" return MemoryMarshal.CreateSpan(ref *ptr, Size);");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" public readonly ReadOnlySpan<{targetType}> AsReadOnlySpan()");
builder.AppendLine(" {");
builder.AppendLine($" fixed ({targetType}* ptr = this.elements)");
builder.AppendLine(" {");
builder.AppendLine($" return new ReadOnlySpan<{targetType}>(ptr, Size);");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" public void Set(ReadOnlySpan<{targetType}> value)");
builder.AppendLine(" {");
builder.AppendLine($" fixed ({targetType}* ptr = this.elements)");
builder.AppendLine(" {");
builder.AppendLine($" var span = MemoryMarshal.CreateSpan(ref *ptr, Size);");
builder.AppendLine(" span.Clear();");
builder.AppendLine(" value[..Math.Min(value.Length, Size)].CopyTo(span);");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" public void Clear()");
builder.AppendLine(" {");
builder.AppendLine($" fixed ({targetType}* ptr = this.elements)");
builder.AppendLine(" {");
builder.AppendLine($" MemoryMarshal.CreateSpan(ref *ptr, Size).Clear();");
builder.AppendLine(" }");
builder.AppendLine(" }");
builder.AppendLine();
builder.AppendLine($" public static {structName} From{capitalizedType}Array({targetType}[] array)");
builder.AppendLine(" {");
builder.AppendLine($" var arr = default({structName});");
builder.AppendLine(" arr.Set(array);");
builder.AppendLine(" return arr;");
builder.AppendLine(" }");
builder.AppendLine("}");
sourceProductionContext.AddSource($"{structName}.g.cs", builder.ToString());
}
}
#nullable disable
@@ -0,0 +1,173 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Sybil;
using System.Collections.Immutable;
using System.Linq;
namespace System.Extensions;
#nullable enable
[Generator(LanguageNames.CSharp)]
public class NotifyPropertyChangedGenerator : IIncrementalGenerator
{
private const string AttributeNamespace = "System.Extensions";
private const string AttributeName = "GenerateNotifyPropertyChangedAttribute";
private const string AttributeShortName = "GenerateNotifyPropertyChanged";
private const string PropertyChangedEventHandler = "PropertyChangedEventHandler";
private const string NullablePropertyChangedEventHandler = "PropertyChangedEventHandler?";
private const string PropertyChanged = "PropertyChanged";
private const string Partial = "partial";
private const string Public = "public";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(context =>
{
var compilationUnitBuilder = SyntaxBuilder.CreateCompilationUnit()
.WithNamespace(
SyntaxBuilder.CreateNamespace(AttributeNamespace)
.WithClass(SyntaxBuilder.CreateClass(AttributeName)
.WithModifier(Public)
.WithConstructor(SyntaxBuilder.CreateConstructor(AttributeName)
.WithModifier(Public))
.WithAttribute(SyntaxBuilder.CreateAttribute("AttributeUsage")
.WithArgument(AttributeTargets.Field)
.WithArgument("Inherited", false)
.WithArgument("AllowMultiple", false))
.WithBaseClass(nameof(Attribute))));
var compilationUnitSyntax = compilationUnitBuilder.Build();
var source = compilationUnitSyntax.ToFullString();
context.AddSource($"{AttributeName}.g", source);
});
var classDeclarations = context.SyntaxProvider.CreateSyntaxProvider(
predicate: static (s, _) => s is FieldDeclarationSyntax,
transform: static (ctx, _) => GetFilteredFieldDeclarationSyntax(ctx)).Where(static c => c is not null);
var compilationAndClasses = context.CompilationProvider.Combine(classDeclarations.Collect());
context.RegisterSourceOutput(compilationAndClasses, (sourceProductionContext, tuple) => Execute(tuple.Left, tuple.Right, sourceProductionContext));
}
private static ClassDeclarationSyntax? GetFilteredFieldDeclarationSyntax(GeneratorSyntaxContext context)
{
var fieldDeclarationSyntax = (FieldDeclarationSyntax)context.Node;
if (fieldDeclarationSyntax.AttributeLists
.SelectMany(l => l.Attributes)
.OfType<AttributeSyntax>()
.Any(s => s.Name.ToString() == AttributeName || s.Name.ToString() == AttributeShortName))
{
return fieldDeclarationSyntax.Parent as ClassDeclarationSyntax;
}
return default;
}
private static void Execute(Compilation compilation, ImmutableArray<ClassDeclarationSyntax?> classes, SourceProductionContext sourceProductionContext)
{
if (classes.IsDefaultOrEmpty)
{
return;
}
var maybeLanguageVersion = (compilation.SyntaxTrees.FirstOrDefault()?.Options as CSharpParseOptions)?.LanguageVersion;
if (!maybeLanguageVersion.HasValue)
{
return;
}
var languageVersion = maybeLanguageVersion.Value;
var distinctClasses = classes.Distinct();
foreach (var c in distinctClasses.OfType<ClassDeclarationSyntax>())
{
var className = c.Identifier.ToFullString();
if (!c.Modifiers.Any(m => m.ToString() == Partial))
{
Diagnostics.ClassNotPartial(className);
continue;
}
var originalNamespaceSyntax = GetParentOfType<BaseNamespaceDeclarationSyntax>(c);
if (originalNamespaceSyntax is null)
{
Diagnostics.ClassNotInNamespace(className);
continue;
}
if (!c.Members
.OfType<EventFieldDeclarationSyntax>()
.Any(eventField =>
(eventField.Declaration.Variables.Any(variable => variable.Identifier.Text == PropertyChanged) &&
eventField.Modifiers.Any(modifier => modifier.Text == Public) &&
eventField.Declaration.Type.ToString() == PropertyChangedEventHandler) ||
eventField.Declaration.Type.ToString() == NullablePropertyChangedEventHandler))
{
Diagnostics.ClassDoesNotImplementINotifyPropertyChanged(className);
continue;
}
var generatedClassBuilder = SyntaxBuilder.CreateClass(className)
.WithModifiers(string.Join(" ", c.Modifiers.Select(m => m.ToFullString())));
var compilationUnitBuilder = SyntaxBuilder.CreateCompilationUnit()
.WithNamespace(
(languageVersion >= LanguageVersion.CSharp10 ?
SyntaxBuilder.CreateFileScopedNamespace(originalNamespaceSyntax.Name.ToFullString()) :
SyntaxBuilder.CreateNamespace(originalNamespaceSyntax.Name.ToFullString()))
.WithClass(generatedClassBuilder));
foreach (var field in c.DescendantNodes().OfType<FieldDeclarationSyntax>())
{
if (field.AttributeLists
.SelectMany(l => l.Attributes)
.OfType<AttributeSyntax>()
.Any(s => s.Name.ToString() == AttributeName ||
s.Name.ToString() == AttributeShortName) is false)
{
continue;
}
var fieldName = field.Declaration.Variables.First().Identifier.ToFullString();
var propertyName = fieldName.TrimStart('_');
propertyName = $"{char.ToUpper(propertyName[0])}{propertyName.Substring(1)}";
var propertyBuilder = SyntaxBuilder.CreateProperty(field.Declaration.Type.ToFullString(), propertyName)
.WithModifier(Public)
.WithAccessor(SyntaxBuilder.CreateGetter()
.WithBody($"return this.{fieldName};"))
.WithAccessor(SyntaxBuilder.CreateSetter()
.WithBody($"this.{fieldName} = value;\r\nthis.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof({propertyName})));"));
generatedClassBuilder.WithProperty(propertyBuilder);
}
var generatedSyntax = compilationUnitBuilder.Build();
if (originalNamespaceSyntax.Usings.Count != 0)
{
generatedSyntax = generatedSyntax.AddUsings([.. originalNamespaceSyntax.Usings]);
}
else
{
if (originalNamespaceSyntax.Parent is CompilationUnitSyntax compilationUnit)
{
generatedSyntax = generatedSyntax.AddUsings([.. compilationUnit.Usings]);
}
}
var fileSource = $"#nullable enable\n{generatedSyntax.ToFullString()}\n#nullable disable";
sourceProductionContext.AddSource($"{className}.g", fileSource);
}
}
private static T? GetParentOfType<T>(SyntaxNode syntaxNode)
{
if (syntaxNode.Parent is null)
{
return default;
}
if (syntaxNode.Parent is T parentNode)
{
return parentNode;
}
return GetParentOfType<T>(syntaxNode.Parent);
}
}
#nullable disable
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Alexandru Macocian</Authors>
<Description>Source generators extensions for netstandard2.0.</Description>
<Version>0.1.7</Version>
<LangVersion>latest</LangVersion>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
<PackageReference Include="Sybil" Version="0.8.4" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<!-- Package the generator in the analyzer directory of the nuget package -->
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\Sybil.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</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>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.5.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.5.2" />
<PackageReference Include="coverlet.collector" Version="6.0.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,108 @@
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)) : string.Empty;
private set => this.encryptedValue = value is not null
? ProtectedData.Protect(Encoding.UTF8.GetBytes(value), optionalEntropy, DataProtectionScope.CurrentUser)
: null;
}
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(null);
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)
{
if (ss1 is null && ss2 is null)
{
return true;
}
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,64 @@
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace System.Security.Hashing;
public abstract class BaseHMACService<T> : IHMACService
where T : HMAC
{
protected abstract T GetHashAlgorithm(byte[] key);
public async Task<string> Hash(string key, string raw)
{
using var rawStream = BytesToStream(StringToBytes(raw));
var keyBytes = StringToBytes(key);
using var hashedStream = await this.HashInternal(keyBytes, rawStream).ConfigureAwait(false);
return StreamToString(hashedStream);
}
public async Task<byte[]> Hash(byte[] key, byte[] raw)
{
using var rawStream = BytesToStream(raw);
using var hashedStream = await this.HashInternal(key, rawStream).ConfigureAwait(false);
return StreamToBytes(hashedStream);
}
public async Task<Stream> Hash(byte[] key, Stream raw)
{
return await this.HashInternal(key, 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 async Task<Stream> HashInternal(byte[] key, Stream raw)
{
if (raw is null)
{
throw new ArgumentNullException(nameof(raw));
}
using var algo = this.GetHashAlgorithm(key);
return await Task.Run(() => new MemoryStream(algo.ComputeHash(raw)));
}
}
@@ -0,0 +1,63 @@
using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace System.Security.Hashing;
public abstract class BaseHashingService<T>
where T : HashAlgorithm
{
protected abstract T GetHashAlgorithm();
public async Task<string> Hash(string raw)
{
using var rawStream = BytesToStream(StringToBytes(raw));
using var hashedStream = await this.HashInternal(rawStream).ConfigureAwait(false);
return StreamToString(hashedStream);
}
public async Task<byte[]> Hash(byte[] raw)
{
using var rawStream = BytesToStream(raw);
using var hashedStream = await this.HashInternal(rawStream).ConfigureAwait(false);
return StreamToBytes(hashedStream);
}
public async Task<Stream> Hash(Stream raw)
{
return await this.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 async Task<Stream> HashInternal(Stream raw)
{
if (raw is null)
{
throw new ArgumentNullException(nameof(raw));
}
using var algo = this.GetHashAlgorithm();
return await Task.Run(() => new MemoryStream(algo.ComputeHash(raw)));
}
}
@@ -0,0 +1,10 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class HMACMD5Service : BaseHMACService<HMACMD5>, IHMACMD5Service
{
protected override HMACMD5 GetHashAlgorithm(byte[] key)
{
return new HMACMD5(key);
}
}
@@ -0,0 +1,10 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class HMACSha1Service : BaseHMACService<HMACSHA1>, IHMACSha1Service
{
protected override HMACSHA1 GetHashAlgorithm(byte[] key)
{
return new HMACSHA1(key);
}
}
@@ -0,0 +1,10 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class HMACSha256Service : BaseHMACService<HMACSHA256>, IHMACSha256Service
{
protected override HMACSHA256 GetHashAlgorithm(byte[] key)
{
return new HMACSHA256(key);
}
}
@@ -0,0 +1,10 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class HMACSha384Service : BaseHMACService<HMACSHA384>, IHMACSha384Service
{
protected override HMACSHA384 GetHashAlgorithm(byte[] key)
{
return new HMACSHA384(key);
}
}
@@ -0,0 +1,10 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class HMACSha512Service : BaseHMACService<HMACSHA512>, IHMACSha512Service
{
protected override HMACSHA512 GetHashAlgorithm(byte[] key)
{
return new HMACSHA512(key);
}
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface IHMACMD5Service : IHMACService
{
}
@@ -0,0 +1,10 @@
using System.IO;
using System.Threading.Tasks;
namespace System.Security.Hashing;
public interface IHMACService
{
Task<string> Hash(string key, string raw);
Task<byte[]> Hash(byte[] key, byte[] raw);
Task<Stream> Hash(byte[] key, Stream raw);
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface IHMACSha1Service : IHMACService
{
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface IHMACSha256Service : IHMACService
{
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface IHMACSha384Service : IHMACService
{
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface IHMACSha512Service : IHMACService
{
}
@@ -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,4 @@
namespace System.Security.Hashing;
public interface IMD5HashingService : IHashingService
{
}
@@ -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,4 @@
namespace System.Security.Hashing;
public interface ISha1HashingService : IHashingService
{
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface ISha256HashingService : IHashingService
{
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface ISha384HashingService : IHashingService
{
}
@@ -0,0 +1,4 @@
namespace System.Security.Hashing;
public interface ISha512HashingService : IHashingService
{
}
@@ -0,0 +1,10 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class MD5HashingService : BaseHashingService<MD5>, IMD5HashingService
{
protected override MD5 GetHashAlgorithm()
{
return MD5.Create();
}
}
@@ -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,7 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class Sha1HashingService : BaseHashingService<SHA1>, ISha1HashingService
{
protected override SHA1 GetHashAlgorithm() => SHA1.Create();
}
@@ -0,0 +1,8 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class Sha256HashingService : BaseHashingService<SHA256>, ISha256HashingService
{
protected override SHA256 GetHashAlgorithm() => SHA256.Create();
}
@@ -0,0 +1,10 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class Sha384HashingService : BaseHashingService<SHA384>, ISha384HashingService
{
protected override SHA384 GetHashAlgorithm()
{
return SHA384.Create();
}
}
@@ -0,0 +1,7 @@
using System.Security.Cryptography;
namespace System.Security.Hashing;
public sealed class Sha512HashingService : BaseHashingService<SHA512>, ISha512HashingService
{
protected override SHA512 GetHashAlgorithm() => SHA512.Create();
}
@@ -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.3.1</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="8.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,36 @@
using System.Extensions;
using System.Threading.Tasks;
namespace System.Cache;
public sealed class AsyncValueCache<T>(Func<Task<T>> cacheRefreshOperation, TimeSpan cacheDuration)
where T : class
{
private readonly Func<Task<T>> cacheRefreshOperation = cacheRefreshOperation.ThrowIfNull(nameof(cacheRefreshOperation));
private readonly TimeSpan cacheDuration = cacheDuration;
private T? value;
private DateTime lastCacheRefresh = DateTime.MinValue;
public async Task<T> GetValue()
{
if (DateTime.Now - this.lastCacheRefresh < this.cacheDuration &&
this.value is not null)
{
return this.value;
}
return await this.PerformCacheRefresh();
}
public async Task<T> ForceCacheRefresh()
{
return await this.PerformCacheRefresh();
}
private async Task<T> PerformCacheRefresh()
{
this.value = await this.cacheRefreshOperation();
this.lastCacheRefresh = DateTime.Now;
return this.value;
}
}
@@ -0,0 +1,35 @@
using System.Extensions;
namespace System.Cache;
public sealed class ValueCache<T>(Func<T> cacheRefreshOperation, TimeSpan cacheDuration)
where T : class
{
private readonly Func<T> cacheRefreshOperation = cacheRefreshOperation.ThrowIfNull(nameof(cacheRefreshOperation));
private readonly TimeSpan cacheDuration = cacheDuration;
private T? value;
private DateTime lastCacheRefresh = DateTime.MinValue;
public T GetValue()
{
if (DateTime.Now - this.lastCacheRefresh < this.cacheDuration &&
this.value is not null)
{
return this.value;
}
return this.PerformCacheRefresh();
}
public T ForceCacheRefresh()
{
return this.PerformCacheRefresh();
}
private T PerformCacheRefresh()
{
this.value = this.cacheRefreshOperation();
this.lastCacheRefresh = DateTime.Now;
return this.value;
}
}
@@ -1,389 +1,401 @@
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 count;
}
}
/// <summary>
/// True if the collection is readonly. False otherwise.
/// </summary>
public bool IsReadOnly => isReadOnly;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of an AVLTree collection.
/// </summary>
public AVLTree()
{
namespace System.Collections.Generic;
}
#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)
/// <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 = default!;
public AVLNode<TKey> Right = default!;
public AVLNode(TKey value)
{
count++;
AVLNode<T> newItem = new AVLNode<T>(value);
if (root == null)
{
root = newItem;
}
else
{
root = RecursiveInsertion(root, newItem);
}
this.Value = value;
}
/// <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)
}
AVLNode<T> root = default!;
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
{
AVLNode<T> node = Find(value, root);
if (node == null)
{
return false;
}
if (node.Value.CompareTo(value) == 0)
{
return true;
}
else
{
return false;
}
return this.count;
}
/// <summary>
/// Removes the specified value from the tree.
/// </summary>
/// <param name="value">Value to be deleted.</param>
public bool Remove(T value)
}
/// <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)
{
root = Delete(root, value);
return true;
}
/// <summary>
/// Clears the tree.
/// </summary>
public void Clear()
else
{
Queue<AVLNode<T>> queue = new Queue<AVLNode<T>>();
queue.Enqueue(root);
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)
{
AVLNode<T> currentNode = queue.Dequeue();
if (currentNode.Left != null)
{
queue.Enqueue(currentNode.Left);
currentNode.Left = null;
count--;
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
currentNode.Right = null;
count--;
}
}
root = null;
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)
while (queue.Count > 0)
{
Queue<AVLNode<T>> queue = new Queue<AVLNode<T>>();
queue.Enqueue(root);
while (queue.Count > 0)
var currentNode = queue.Dequeue();
if (currentNode.Left != null)
{
AVLNode<T> currentNode = queue.Dequeue();
array[arrayIndex++] = currentNode.Value;
if (currentNode.Left != null)
{
queue.Enqueue(currentNode.Left);
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
}
queue.Enqueue(currentNode.Left);
currentNode.Left = default!;
this.count--;
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
currentNode.Right = default!;
this.count--;
}
}
/// <summary>
/// Enumerator that iterates over the tree.
/// </summary>
/// <returns></returns>
public IEnumerator<T> GetEnumerator()
this.root = default!;
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)
{
return GetEnumerator(root);
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>
/// Copies the tree structure into an array.
/// </summary>
/// <returns>Array containing the values contained in the tree.</returns>
public T[] ToArray()
}
/// <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)
{
T[] array = new T[count];
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 = RecursiveInsertion(current.Left, n);
current = BalanceTree(current);
}
else if (n.Value.CompareTo(current.Value) > 0)
{
current.Right = RecursiveInsertion(current.Right, n);
current = BalanceTree(current);
}
current = n;
return current;
}
private AVLNode<T> BalanceTree(AVLNode<T> current)
else if (n.Value.CompareTo(current.Value) < 0)
{
int b_factor = BalanceFactor(current);
if (b_factor > 1)
{
if (BalanceFactor(current.Left) > 0)
{
current = RotateLL(current);
}
else
{
current = RotateLR(current);
}
}
else if (b_factor < -1)
{
if (BalanceFactor(current.Right) > 0)
{
current = RotateRL(current);
}
else
{
current = RotateRR(current);
}
}
return current;
current.Left = this.RecursiveInsertion(current.Left, n);
current = this.BalanceTree(current);
}
private AVLNode<T> Delete(AVLNode<T> current, T target)
else if (n.Value.CompareTo(current.Value) > 0)
{
AVLNode<T> parent;
if (current == null)
{ return null; }
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
{
//left subtree
if (target.CompareTo(current.Value) < 0)
{
current.Left = Delete(current.Left, target);
if (BalanceFactor(current) == -2)//here
{
if (BalanceFactor(current.Right) <= 0)
{
current = RotateRR(current);
}
else
{
current = RotateRL(current);
}
}
}
//right subtree
else if (target.CompareTo(current.Value) > 0)
{
current.Right = Delete(current.Right, target);
if (BalanceFactor(current) == 2)
{
if (BalanceFactor(current.Left) >= 0)
{
current = RotateLL(current);
}
else
{
current = RotateLR(current);
}
}
}
//if target is found
else
{
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 = Delete(current.Right, parent.Value);
if (BalanceFactor(current) == 2)//rebalancing
{
if (BalanceFactor(current.Left) >= 0)
{
current = RotateLL(current);
}
else { current = RotateLR(current); }
}
}
else
{ //if current.left != null
return current.Left;
}
}
current = this.RotateLR(current);
}
return current;
}
private AVLNode<T> Find(T target, AVLNode<T> current)
else if (b_factor < -1)
{
if (current == null)
if (this.BalanceFactor(current.Right) > 0)
{
return null;
current = this.RotateRL(current);
}
else
{
current = this.RotateRR(current);
}
}
return current;
}
private AVLNode<T>? Delete(AVLNode<T> current, T target)
{
if (current == null)
{ return null; }
else
{
//left subtree
if (target.CompareTo(current.Value) < 0)
{
if (target.CompareTo(current.Value) == 0)
current.Left = this.Delete(current.Left, target)!;
if (this.BalanceFactor(current) == -2)//here
{
return current;
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
var 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
return Find(target, current.Left);
{ //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
{
if (target.CompareTo(current.Value) == 0)
{
return current;
}
else
return Find(target, current.Right);
return this.Find(target, current.Left);
}
}
private int Max(int l, int r)
else
{
return l > r ? l : r;
}
private int GetHeight(AVLNode<T> current)
{
int height = 0;
if (current != null)
if (target.CompareTo(current.Value) == 0)
{
int l = GetHeight(current.Left);
int r = GetHeight(current.Right);
int m = Max(l, r);
height = m + 1;
return current;
}
return height;
}
private int BalanceFactor(AVLNode<T> current)
{
int l = GetHeight(current.Left);
int r = GetHeight(current.Right);
int b_factor = l - r;
return b_factor;
}
private AVLNode<T> RotateRR(AVLNode<T> parent)
{
AVLNode<T> pivot = parent.Right;
parent.Right = pivot.Left;
pivot.Left = parent;
return pivot;
}
private AVLNode<T> RotateLL(AVLNode<T> parent)
{
AVLNode<T> pivot = parent.Left;
parent.Left = pivot.Right;
pivot.Right = parent;
return pivot;
}
private AVLNode<T> RotateLR(AVLNode<T> parent)
{
AVLNode<T> pivot = parent.Left;
parent.Left = RotateRR(pivot);
return RotateLL(parent);
}
private AVLNode<T> RotateRL(AVLNode<T> parent)
{
AVLNode<T> pivot = parent.Right;
parent.Right = RotateLL(pivot);
return RotateRR(parent);
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
private IEnumerator<T> GetEnumerator(AVLNode<T> rootNode)
{
Queue<AVLNode<T>> queue = new Queue<AVLNode<T>>();
queue.Enqueue(rootNode);
while (queue.Count > 0)
else
{
AVLNode<T> currentNode = queue.Dequeue();
yield return currentNode.Value;
if (currentNode.Left != null)
{
queue.Enqueue(currentNode.Left);
}
if (currentNode.Right != null)
{
queue.Enqueue(currentNode.Right);
}
return this.Find(target, current.Right);
}
}
#endregion
}
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
}
@@ -1,214 +1,219 @@
using System.Linq;
namespace System.Collections.Generic
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>
/// Binary heap implementation.
/// Minimum value from the heap.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
[Serializable]
public sealed class BinaryHeap<T> : IEnumerable<T> where T : IComparable<T>
public T Min
{
#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
{
get
{
return items[1];
}
return this.items[1];
}
/// <summary>
/// Maximum value from the heap.
/// </summary>
public T Max
{
get
{
return items[count];
}
}
/// <summary>
/// Capacity of the heap.
/// </summary>
public int Capacity
{
get => capacity;
set
{
if (value > capacity)
{
Array.Resize(ref items, value);
capacity = value;
}
}
}
/// <summary>
/// Number of elements in the heap.
/// </summary>
public int Count { get => count; }
#endregion
#region Constructors
/// <summary>
/// Constructor for a binary heap data structure.
/// </summary>
public BinaryHeap()
{
capacity = 10;
initialCapacity = capacity;
items = new T[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;
initialCapacity = capacity;
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 (count == Capacity - 1)
{
Capacity = 2 * Capacity;
}
int position = ++count;
for (; position > 1 && value.CompareTo(items[position / 2]) < 0; position /= 2)
{
items[position] = items[position / 2];
}
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 (count == 0)
{
throw new IndexOutOfRangeException("Heap is empty!");
}
T min = items[1];
items[1] = items[count--];
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 (count == 0)
{
throw new IndexOutOfRangeException("Heap is empty!");
}
T min = 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()
{
T[] newArray = new T[count];
Array.Copy(items, 1, newArray, 0, 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 items.Contains(value);
}
/// <summary>
/// Clears the heap
/// </summary>
public void Clear()
{
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)
{
count = 0;
if (completeClear)
{
capacity = initialCapacity;
items = new T[initialCapacity];
}
}
/// <summary>
/// Returns an enumerator that iterates over the heap.
/// </summary>
/// <returns>Enumerator that iterates over the heap.</returns>
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < count; i++)
{
yield return 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)
{
T temp = items[index];
int childIndex;
for (; 2 * index <= count; index = childIndex)
{
childIndex = 2 * index;
if (childIndex != Count && items[childIndex].CompareTo(items[childIndex + 1]) > 0)
{
childIndex++;
}
if (temp.CompareTo(items[childIndex]) > 0)
{
items[index] = items[childIndex];
}
else
{
break;
}
}
items[index] = temp;
}
/// <summary>
/// Implementation of IEnumerator.
/// </summary>
/// <returns>Enumerator over the array.</returns>
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
/// <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,208 @@
using System.Linq;
using System.Threading;
namespace System.Collections.Generic;
public sealed class CircularBuffer<T> : IList<T>
{
private readonly T[] buffer;
private int head;
private int tail;
public int Count { get; private set; }
public bool IsReadOnly { get; } = false;
public T this[int index]
{
get
{
if (index > this.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
return this.buffer[(this.head + index) % this.buffer.Length];
}
set
{
if (index > this.Count)
{
throw new ArgumentOutOfRangeException(nameof(index));
}
this.buffer[(this.head + index) % this.buffer.Length] = value;
}
}
public CircularBuffer(int capacity)
{
if (capacity < 1) throw new ArgumentException($"{nameof(capacity)} cannot be smaller than 1");
this.buffer = new T[capacity];
this.head = 0;
this.tail = 0;
}
public CircularBuffer(T[] buffer, int head, int tail)
{
if (buffer.Length == 0) throw new ArgumentException($"{nameof(buffer)} cannot be of length 0");
this.buffer = buffer;
this.head = head;
this.tail = tail;
this.Count = this.tail < this.head ?
this.tail + this.buffer.Length - this.head + 1 :
this.tail - this.head + 1;
}
public CircularBuffer(T[] buffer)
{
if (buffer.Length == 0) throw new ArgumentException($"{nameof(buffer)} cannot be of length 0");
this.buffer = buffer;
this.head = 0;
this.tail = this.buffer.Length - 1;
this.Count = this.buffer.Length;
}
public void Add(T item)
{
if (this.Count == this.buffer.Length)
{
this.buffer[this.tail] = item;
this.tail = (this.tail + 1) % this.buffer.Length;
this.head = (this.head + 1) % this.buffer.Length;
}
else
{
this.buffer[this.tail] = item;
this.tail = (this.tail + 1) % this.buffer.Length;
this.Count++;
}
}
public void Clear()
{
this.head = 0;
this.tail = 0;
this.Count = 0;
}
public void DeepClear()
{
this.Clear();
Array.Clear(this.buffer, 0, this.buffer.Length);
}
public bool Contains(T item)
{
var comparer = EqualityComparer<T>.Default;
foreach(var itItem in this)
{
if (comparer.Equals(itItem, item))
{
return true;
}
}
return false;
}
public bool Remove(T item)
{
var index = this.head;
var comparer = EqualityComparer<T>.Default;
for (var i = 0; i < this.Count; i++)
{
if (comparer.Equals(this.buffer[index % this.buffer.Length], item))
{
int nextIndex;
for (var j = 0; j < this.Count - i - 1; j++)
{
nextIndex = (index + 1) % this.buffer.Length;
this.buffer[index] = this.buffer[nextIndex];
index = nextIndex;
}
this.Count--;
this.tail = this.tail == 0 ? this.buffer.Length - 1 : this.tail - 1;
return true;
}
index = (index + 1) % this.buffer.Length;
}
return false;
}
public void CopyTo(T[] array, int arrayIndex)
{
foreach(var item in this)
{
array[arrayIndex++] = item;
}
}
public IEnumerator<T> GetEnumerator()
{
var index = this.head;
for (var i = 0; i < this.Count; i++)
{
yield return this.buffer[index];
index = (index + 1) % this.buffer.Length;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public int IndexOf(T item)
{
var comparer = EqualityComparer<T>.Default;
for (var i = 0; i < this.Count; i++)
{
if (comparer.Equals(this[i], item))
{
return i;
}
}
return -1;
}
public void Insert(int index, T item)
{
if (index < 0 || index > this.Count) throw new ArgumentOutOfRangeException(nameof(index));
if (this.Count == this.buffer.Length) throw new InvalidOperationException("Cannot insert into a full buffer.");
if (index == this.Count)
{
this.Add(item);
return;
}
for (var i = this.Count; i > index; i--)
{
this.buffer[(this.head + i) % this.buffer.Length] = this.buffer[(this.head + i - 1) % this.buffer.Length];
}
this.buffer[(this.head + index) % this.buffer.Length] = item;
this.tail = (this.tail + 1) % this.buffer.Length;
this.Count++;
}
public void RemoveAt(int index)
{
if (index < 0 || index >= this.Count) throw new ArgumentOutOfRangeException(nameof(index));
for (var i = index; i < this.Count - 1; i++)
{
this.buffer[(this.head + i) % this.buffer.Length] = this.buffer[(this.head + i + 1) % this.buffer.Length];
}
this.tail = (this.tail - 1 + this.buffer.Length) % this.buffer.Length;
this.Count--;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,39 +1,38 @@
namespace System.Collections.Generic
namespace System.Collections.Generic;
/// <summary>
/// Interface for queue implementations.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
public interface IQueue<T> : IEnumerable<T>
{
/// <summary>
/// Interface for queue implementations.
/// Returns the number of items in the queue.
/// </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);
}
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);
}
@@ -1,103 +1,102 @@
namespace System.Collections.Generic
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>
/// 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.
/// Returns the number of elements stored into the queue.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
[Serializable]
public sealed class PriorityQueue<T> : IQueue<T> where T : IComparable<T>
public int Count
{
#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
{
get
{
return binaryHeap.Count;
}
return this.binaryHeap.Count;
}
#endregion
#region Constructors
/// <summary>
/// Constructor for priority queue data structure.
/// </summary>
public PriorityQueue()
{
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)
{
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 (Count > 0)
{
return 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 binaryHeap.Min;
}
/// <summary>
/// Clears the queue contents, removing any value stored into the queue.
/// </summary>
public void Clear()
{
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 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 binaryHeap.GetEnumerator();
}
#endregion
#region Private Methods
/// <summary>
/// Necesarry for the implementatio of IQueue.
/// </summary>
/// <returns></returns>
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
#endregion
}
#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
}
@@ -1,236 +1,248 @@
namespace System.Collections.Generic
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>
{
/// <summary>
/// Skip list implementation.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
#region Fields
[Serializable]
public sealed class SkipList<T> : ICollection<T> where T : IComparable<T>
private class NodeSet<TKey>
{
#region Fields
[Serializable]
private class NodeSet<TKey>
{
public TKey Key;
public int Level;
public NodeSet<TKey>[] Next;
public TKey Key;
public int Level;
public NodeSet<TKey>[] Next;
public NodeSet(TKey key, int level)
{
this.Key = key;
this.Level = level;
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 => 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)
public NodeSet(TKey key, int level)
{
this.maxLevel = maxLevel;
random = new Random();
head = new NodeSet<T>(default, maxLevel);
end = head;
for (int i = 0; i <= maxLevel; i++)
{
head.Next[i] = end;
}
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++;
}
#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)
if (newLevel > this.level)
{
NodeSet<T> curNode = head;
int newLevel = 0;
while (random.Next(0, 2) > 0 && newLevel < maxLevel)
{
newLevel++;
}
if (newLevel > level)
{
level = newLevel;
}
NodeSet<T> newNode = new NodeSet<T>(item, newLevel);
for (var i = 0; i <= newLevel; i++)
{
if (i > curNode.Level)
{
curNode = head;
}
while (curNode.Next[i] != end && curNode.Next[i].Key.CompareTo(item) < 0)
{
curNode = curNode.Next[i];
}
newNode.Next[i] = curNode.Next[i];
curNode.Next[i] = newNode;
}
count++;
this.level = newLevel;
}
/// <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 newNode = new NodeSet<T>(item, newLevel);
for (var i = 0; i <= newLevel; i++)
{
bool removed = false;
NodeSet<T> curNode = head;
for (var i = 0; i <= maxLevel; i++)
if (i > curNode.Level)
{
if (i > curNode.Level)
{
curNode = head;
}
while (curNode.Next[i] != 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;
}
curNode = this.head;
}
if (removed)
while (curNode.Next[i] != this.end && curNode.Next[i].Key.CompareTo(item) < 0)
{
count--;
return true;
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
{
return false;
break;
}
}
/// <summary>
/// Clears the collection.
/// </summary>
public void Clear()
if (removed)
{
for (int i = 0; i < maxLevel; i++)
{
head.Next[i] = end;
}
count = 0;
this.count--;
return true;
}
/// <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)
else
{
if (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)
{
NodeSet<T> node = head.Next[0];
while (node != 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()
{
T[] array = new T[count];
int index = 0;
NodeSet<T> curNode = head.Next[0];
while (curNode != 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()
{
NodeSet<T> curNode = head.Next[0];
while (curNode != 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)
{
NodeSet<T> curNode = head;
for (int i = level; i >= 0; i--)
{
while (curNode.Next[i] != 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 != end && curNode.Key.CompareTo(key) == 0)
{
return curNode;
}
return null;
}
#endregion
}
/// <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 default!;
}
#endregion
}
+280 -273
View File
@@ -1,280 +1,287 @@
namespace System.Collections.Generic
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>
{
/// <summary>
/// Treap implementation.
/// </summary>
/// <typeparam name="T">Provided type.</typeparam>
#region Fields
[Serializable]
public sealed class Treap<T> : ICollection<T> where T : IComparable<T>
private class Node<TKey>
{
#region Fields
[Serializable]
private class Node<TKey>
public TKey Key;
public int Priority;
public Node<TKey>? Left, Right;
public Node(TKey key, int priority)
{
public TKey Key;
public int Priority;
public Node<TKey> Left, Right;
public Node(TKey key, int priority)
{
Key = key;
Priority = priority;
Left = null;
Right = null;
}
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 count;
}
}
/// <summary>
/// Not implemented.
/// </summary>
public bool IsReadOnly => false;
#endregion
#region Constructors
/// <summary>
/// Constructor for treap.
/// </summary>
public Treap()
{
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)
{
root = InsertNode(root, value);
count++;
}
/// <summary>
/// Removes value from treap.
/// </summary>
/// <param name="value">Value to be removed.</param>
public bool Remove(T value)
{
root = RemoveNode(root, value);
count--;
return true;
}
/// <summary>
/// Clears the treap.
/// </summary>
public void Clear()
{
Clear(root);
root = null;
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 Find(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 (root != null)
{
T[] array = new T[count];
int index = 0;
ToArray(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)
{
ToArray(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 GetEnumerator(root);
}
#endregion
#region Private Methods
private Node<T> InsertNode(Node<T> node, T key)
{
if (node == null)
{
node = new Node<T>(key, randomGen.Next(0, 100));
return node;
}
else if (key.CompareTo(node.Key) <= 0)
{
node.Left = InsertNode(node.Left, key);
if (node.Left.Priority > node.Priority)
{
node = RotateRight(node);
}
}
else
{
node.Right = InsertNode(node.Right, key);
if (node.Right.Priority > node.Priority)
{
node = 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 = RemoveNode(node.Left, key);
}
else if (key.CompareTo(node.Key) > 0)
{
node.Right = 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 = RotateLeft(node);
node.Left = RemoveNode(node.Left, key);
}
else
{
node = RotateRight(node);
node.Right = 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)
{
Clear(node.Left);
}
if (node.Right != null)
{
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)
{
Node<T> found = Find(node.Left, key);
if (found == null)
{
found = Find(node.Right, key);
}
return found;
}
else if (node.Key.CompareTo(key) > 0)
{
Node<T> found = Find(node.Right, key);
if (found == null)
{
found = Find(node.Left, key);
}
return found;
}
else
{
return node;
}
}
}
private void ToArray(Node<T> node, ref T[] array, ref int index)
{
if (node != null)
{
ToArray(node.Left, ref array, ref index);
array[index] = node.Key;
index++;
ToArray(node.Right, ref array, ref index);
}
}
private IEnumerator<T> GetEnumerator(Node<T> currentNode)
{
Queue<Node<T>> 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
}
private readonly Random randomGen;
private Node<T> root = default!;
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 = default!;
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 default!;
}
}
/// <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,128 @@
namespace System.Extensions;
/// <summary>
/// https://stackoverflow.com/questions/641361/base32-decoding
/// </summary>
public static partial class Convert
{
public static byte[] FromBase32String(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException("input");
}
input = input.TrimEnd('='); //remove padding characters
var byteCount = input.Length * 5 / 8; //this must be TRUNCATED
var returnArray = new byte[byteCount];
byte curByte = 0, bitsRemaining = 8;
int arrayIndex = 0;
foreach (var c in input)
{
var cValue = CharToValue(c);
int mask;
if (bitsRemaining > 5)
{
mask = cValue << (bitsRemaining - 5);
curByte = (byte)(curByte | mask);
bitsRemaining -= 5;
}
else
{
mask = cValue >> (5 - bitsRemaining);
curByte = (byte)(curByte | mask);
returnArray[arrayIndex++] = curByte;
curByte = (byte)(cValue << (3 + bitsRemaining));
bitsRemaining += 3;
}
}
//if we didn't end with a full byte
if (arrayIndex != byteCount)
{
returnArray[arrayIndex] = curByte;
}
return returnArray;
}
public static string ToBase32String(byte[] input)
{
if (input == null || input.Length == 0)
{
throw new ArgumentNullException("input");
}
var charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
var returnArray = new char[charCount];
byte nextChar = 0, bitsRemaining = 5;
var arrayIndex = 0;
foreach (var b in input)
{
nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
returnArray[arrayIndex++] = ValueToChar(nextChar);
if (bitsRemaining < 4)
{
nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
returnArray[arrayIndex++] = ValueToChar(nextChar);
bitsRemaining += 5;
}
bitsRemaining -= 3;
nextChar = (byte)((b << bitsRemaining) & 31);
}
//if we didn't end with a full char
if (arrayIndex != charCount)
{
returnArray[arrayIndex++] = ValueToChar(nextChar);
while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
}
return new string(returnArray);
}
private static int CharToValue(char c)
{
var value = (int)c;
//65-90 == uppercase letters
if (value < 91 && value > 64)
{
return value - 65;
}
//50-55 == numbers 2-7
if (value < 56 && value > 49)
{
return value - 24;
}
//97-122 == lowercase letters
if (value < 123 && value > 96)
{
return value - 97;
}
throw new ArgumentException($"Character {c} is not a Base32 character.");
}
private static char ValueToChar(byte b)
{
if (b < 26)
{
return (char)(b + 65);
}
if (b < 32)
{
return (char)(b + 24);
}
throw new ArgumentException($"Byte {b} is not a value Base32 value.");
}
}
@@ -1,24 +1,24 @@
using System.IO;
namespace System.Extensions
namespace System.Extensions;
public static class BytesExtensions
{
public static class BytesExtensions
public static byte[] ReadAllBytes(this Stream stream)
{
public static byte[] ReadAllBytes(this Stream stream)
if (stream is null)
{
if (stream is null) throw new ArgumentNullException(nameof(stream));
var buffer = new byte[256];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stream.Read(buffer, 0, 256)) > 0)
{
ms.Write(buffer, 0, read);
}
return ms.ToArray();
}
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();
}
}
@@ -1,50 +1,49 @@
namespace System.Extensions
namespace System.Extensions;
public static class CastExtensions
{
public static class CastExtensions
public static int Floor(this double value)
{
public static int Floor(this double value)
{
return (int)Math.Floor(value);
}
return (int)Math.Floor(value);
}
public static int Ceiling(this double value)
{
return (int)Math.Ceiling(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 Round(this double value)
{
return (int)Math.Round(value);
}
public static int Floor(this float value)
{
return (int)Math.Floor(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 Ceiling(this float value)
{
return (int)Math.Ceiling(value);
}
public static int Round(this float value)
{
return (int)Math.Round(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 double value)
{
return (int)value;
}
public static int ToInt(this float value)
{
return (int)value;
}
public static int ToInt(this float value)
{
return (int)value;
}
public static int ToInt(this decimal value)
{
return (int)value;
}
public static int ToInt(this decimal value)
{
return (int)value;
}
}
@@ -1,71 +1,88 @@
using System.Collections.Generic;
using System.Linq;
namespace System.Extensions
namespace System.Extensions;
public static class LinqExtensions
{
public static class LinqExtensions
public static bool None<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
public static bool None<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate)
{
enumerable.ThrowIfNull(nameof(enumerable));
predicate.ThrowIfNull(nameof(predicate));
enumerable.ThrowIfNull(nameof(enumerable));
predicate.ThrowIfNull(nameof(predicate));
return !enumerable.Any(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));
}
public static bool None<T>(this IEnumerable<T> enumerable)
collection.Clear();
return collection;
}
public static ICollection<T> AddRange<T>(this ICollection<T> collection, IEnumerable<T> values)
{
if (collection is null)
{
enumerable.ThrowIfNull(nameof(enumerable));
return !enumerable.Any();
throw new ArgumentNullException(nameof(collection));
}
public static ICollection<T> ClearAnd<T>(this ICollection<T> collection)
if (values is null)
{
if (collection is null) throw new ArgumentNullException(nameof(collection));
collection.Clear();
return collection;
throw new ArgumentNullException(nameof(values));
}
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)
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))
{
collection.Add(item);
return index;
}
return collection;
index++;
}
public static int IndexOfWhere<T>(this ICollection<T> collection, Func<T, bool> selector)
return -1;
}
public static IEnumerable<T> Do<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach(var value in enumerable)
{
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;
}
action(value);
yield return value;
}
}
}
@@ -0,0 +1,17 @@
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);
}
public static ScopedLogger<T> CreateScopedLogger<T>(this ILogger<T> logger, string methodName)
{
return ScopedLogger<T>.Create(logger, methodName, string.Empty);
}
}
@@ -1,32 +1,29 @@
namespace System.Extensions
namespace System.Extensions;
public static class ObjectExtensions
{
public static class ObjectExtensions
public static T Cast<T>(this object 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 T ThrowIfNull<T>([ValidatedNotNull] this T? obj, string name) where T : class
{
if (obj is null)
{
return (T)obj;
throw new ArgumentNullException(name);
}
public static T As<T>(this object obj) where T : class
{
return obj as T;
}
return obj;
}
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
{
}
[AttributeUsage(AttributeTargets.Parameter)]
sealed class ValidatedNotNullAttribute : Attribute
{
}
}
@@ -1,178 +0,0 @@
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 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)
{
}
}
}
}
@@ -1,185 +0,0 @@
namespace System.Extensions
{
public class Result<TSuccess, TFailure>
{
private readonly object value;
public Result(TSuccess successValue)
{
value = successValue;
}
public Result(TFailure failureValue)
{
value = failureValue;
}
public bool TryExtractSuccess(out TSuccess successValue)
{
if (value is TSuccess success)
{
successValue = success;
return true;
}
else
{
successValue = default;
return false;
}
}
public bool TryExtractFailure(out TFailure failureValue)
{
if (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 (value is TSuccess)
{
onSuccess.Invoke();
}
else if (value is TFailure)
{
onFailure.Invoke();
}
return this;
}
public Result<TSuccess, TFailure> DoAny(Action onSuccess = null, Action onFailure = null)
{
if (value is TSuccess)
{
onSuccess?.Invoke();
}
else if (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 (value is TSuccess success)
{
onSuccess.Invoke(success);
}
else if (value is TFailure failure)
{
onFailure.Invoke(failure);
}
return this;
}
public Result<TSuccess, TFailure> DoAny(Action<TSuccess> onSuccess = null, Action<TFailure> onFailure = null)
{
if (value is TSuccess success)
{
onSuccess?.Invoke(success);
}
else if (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 (value is TSuccess success)
{
return onSuccess.Invoke(success);
}
else if (value is TFailure failure)
{
return onFailure.Invoke(failure);
}
throw new InvalidOperationException($"{nameof(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 (value is TSuccess success)
{
return onSuccess is null ? default : onSuccess.Invoke(success);
}
else if (value is TFailure failure)
{
return onFailure is null ? default : onFailure.Invoke(failure);
}
throw new InvalidOperationException($"{nameof(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 (value is TSuccess success)
{
return Result<V, K>.Success(onSuccess.Invoke(success));
}
else if (value is TFailure failure)
{
return Result<V, K>.Failure(onFailure.Invoke(failure));
}
throw new InvalidOperationException($"{nameof(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 (value is TSuccess success)
{
return Result<V, K>.Success(onSuccess is null ? default : onSuccess.Invoke(success));
}
else if (value is TFailure failure)
{
return Result<V, K>.Failure(onFailure is null ? default : onFailure.Invoke(failure));
}
throw new InvalidOperationException($"{nameof(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 (value is TSuccess)
{
return Optional.FromValue(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,16 @@
using System.Threading;
using System.Threading.Tasks;
namespace System.Extensions;
public static class SemaphoreSlimExtensions
{
public static async Task<SemaphoreSlimContext> Acquire(this SemaphoreSlim semaphore)
{
return await SemaphoreSlimContext.Create(semaphore);
}
public static async Task<SemaphoreSlimContext> Acquire(this SemaphoreSlim semaphore, CancellationToken cancellationToken)
{
return await SemaphoreSlimContext.Create(semaphore, cancellationToken);
}
}
@@ -1,67 +1,63 @@
using System.IO;
using System.Text;
namespace System.Extensions
namespace System.Extensions;
public static class StreamExtensions
{
public static class StreamExtensions
public static byte[] ReadBytes(this Stream stream, int count)
{
public static void DoWhileReading(this Stream stream, Action<int, byte[]> action, int bufferLength = 256)
if (stream is null)
{
if (stream is null) throw new ArgumentNullException(nameof(stream));
if (action is null) throw new ArgumentNullException(nameof(action));
throw new ArgumentNullException(nameof(stream));
}
var buffer = new byte[bufferLength];
var read = stream.Read(buffer, 0, bufferLength);
while(read > 0)
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++)
{
action(bufferLength, buffer);
read = stream.Read(buffer, 0, bufferLength);
}
}
var c = (char)sr.Read();
sb.Append(c);
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();
bool found = false;
while (!found && !sr.EndOfStream)
{
for (int i = 0; i < delim.Length; i++)
if (c != delim[i])
{
char c = (char)sr.Read();
sb.Append(c);
break;
}
if (c != delim[i])
break;
if (i == delim.Length - 1)
{
sb.Remove(sb.Length - delim.Length, delim.Length);
found = true;
}
if (i == delim.Length - 1)
{
sb.Remove(sb.Length - delim.Length, delim.Length);
found = true;
}
}
return sb.ToString();
}
return sb.ToString();
}
}
@@ -1,27 +1,26 @@
using System.Text;
namespace System.Extensions
namespace System.Extensions;
public static class StringExtensions
{
public static class StringExtensions
public static bool IsNullOrEmpty(this string s)
{
public static bool IsNullOrEmpty(this string s)
{
return string.IsNullOrEmpty(s);
}
return string.IsNullOrEmpty(s);
}
public static bool IsNullOrWhiteSpace(this string s)
{
return string.IsNullOrWhiteSpace(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 byte[] GetBytes(this string s)
{
return Encoding.UTF8.GetBytes(s);
}
public static string GetString(this byte[] bytes)
{
return Encoding.UTF8.GetString(bytes);
}
public static string GetString(this byte[] bytes)
{
return Encoding.UTF8.GetString(bytes);
}
}
@@ -1,180 +0,0 @@
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);
T 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 (items)
{
items.Enqueue(Tuple.Create(d, state));
}
workItemsWaiting.Set();
}
public void EndMessageLoop()
{
Post(_ => done = true, null);
}
public void BeginMessageLoop()
{
while (!done)
{
Tuple<SendOrPostCallback, object> task = null;
lock (items)
{
if (items.Count > 0)
{
task = items.Dequeue();
}
}
if (task != null)
{
task.Item1(task.Item2);
if (InnerException != null) // the method threw an exeption
{
throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
}
}
else
{
workItemsWaiting.WaitOne();
}
}
}
public override SynchronizationContext CreateCopy()
{
return this;
}
}
}
}
+52 -48
View File
@@ -1,69 +1,73 @@
using System.Collections.Generic;
namespace System.Extensions
namespace System.Extensions;
public static class Try
{
public static class Try
public static Try<TResult> Action<TResult>(Func<TResult> act)
{
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 new Try<TResult>(act);
return this.action();
}
catch (Exception ex)
{
foreach (var catchAction in this.catchActions)
{
return catchAction(ex);
}
throw;
}
}
public class Try<TResult>
public TResult Finally(Action act)
{
private readonly Func<TResult> action;
private readonly List<Func<Exception, TResult>> catchActions = new List<Func<Exception, TResult>>();
internal Try(Func<TResult> action)
if (act is null)
{
this.action = action;
}
public static Try<TResult> Action(Func<TResult> act)
{
return new Try<TResult>(act);
throw new ArgumentNullException(nameof(act));
}
public Try<TResult> Catch<TException>(Func<TException, TResult> act) where TException : Exception
try
{
this.catchActions.Add((Func<Exception, TResult>)act);
return this;
return this.action();
}
public TResult Run()
catch (Exception ex)
{
try
foreach (var catchAction in this.catchActions)
{
return action();
}
catch (Exception ex)
{
foreach (var catchAction in this.catchActions)
{
return catchAction(ex);
}
throw;
return catchAction(ex);
}
throw;
}
public TResult Finally(Action act)
finally
{
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();
}
act();
}
}
}
+227 -200
View File
@@ -1,217 +1,244 @@
using System.IO;
using System.Net.Http;
using System.Extensions;
using System.IO;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace System.Http
namespace System.Net.Http;
public sealed class HttpClient<Tscope> : IHttpClient<Tscope>, IDisposable
{
public sealed class HttpClient<Tscope> : IHttpClient<Tscope>, IDisposable
private readonly bool disposeInnerClient = true;
private readonly HttpClient httpClient;
private readonly Type scope;
private EventHandler<HttpClientEventMessage> eventEmitted;
public event EventHandler<HttpClientEventMessage> EventEmitted
{
private readonly HttpClient httpClient;
private readonly Type scope;
private EventHandler<HttpClientEventMessage> eventEmitted;
add
{
this.eventEmitted += value;
}
remove
{
#pragma warning disable CS8601 // Possible null reference assignment.
this.eventEmitted -= value;
#pragma warning restore CS8601 // Possible null reference assignment.
}
}
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 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; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public HttpClient(bool disposeInnerClient = true)
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
{
this.disposeInnerClient = disposeInnerClient;
this.httpClient = new HttpClient();
this.scope = typeof(Tscope);
}
public HttpClient()
{
this.httpClient = new HttpClient();
this.scope = typeof(Tscope);
}
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public HttpClient(
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
HttpMessageHandler handler,
bool disposeInnerClient = true)
{
this.disposeInnerClient = disposeInnerClient;
this.httpClient = new HttpClient(handler);
this.scope = typeof(Tscope);
}
public HttpClient(
HttpMessageHandler handler)
{
this.httpClient = new HttpClient(handler);
this.scope = typeof(Tscope);
}
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public HttpClient(
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
HttpMessageHandler handler,
bool disposeHandler,
bool disposeInnerClient = true)
{
this.disposeInnerClient = disposeInnerClient;
this.httpClient = new HttpClient(handler, disposeHandler);
this.scope = typeof(Tscope);
}
public HttpClient(
HttpMessageHandler handler,
bool disposeHandler)
{
this.httpClient = new HttpClient(handler, disposeHandler);
this.scope = typeof(Tscope);
}
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
public HttpClient(
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
HttpClient httpClient,
bool disposeInnerClient = true)
{
this.disposeInnerClient = disposeInnerClient;
this.httpClient = httpClient.ThrowIfNull(nameof(httpClient));
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()
public void CancelPendingRequests()
{
this.LogDebug(string.Empty, "Canceling request");
this.httpClient.CancelPendingRequests();
}
public Task<HttpResponseMessage> DeleteAsync(string requestUri)
{
this.LogDebug(requestUri, nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri);
}
public Task<HttpResponseMessage> DeleteAsync(string requestUri, CancellationToken cancellationToken)
{
this.LogDebug(requestUri, nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri, cancellationToken);
}
public Task<HttpResponseMessage> DeleteAsync(Uri requestUri)
{
this.LogDebug(requestUri.ToString(), nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri);
}
public Task<HttpResponseMessage> DeleteAsync(Uri requestUri, CancellationToken cancellationToken)
{
this.LogDebug(requestUri.ToString(), nameof(DeleteAsync));
return this.httpClient.DeleteAsync(requestUri, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(string requestUri)
{
this.LogDebug(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri);
}
public Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption)
{
this.LogDebug(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption);
}
public Task<HttpResponseMessage> GetAsync(string requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
this.LogDebug(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(string requestUri, CancellationToken cancellationToken)
{
this.LogDebug(requestUri, nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri)
{
this.LogDebug(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption)
{
this.LogDebug(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
this.LogDebug(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, completionOption, cancellationToken);
}
public Task<HttpResponseMessage> GetAsync(Uri requestUri, CancellationToken cancellationToken)
{
this.LogDebug(requestUri.ToString(), nameof(GetAsync));
return this.httpClient.GetAsync(requestUri, cancellationToken);
}
public Task<byte[]> GetByteArrayAsync(string requestUri)
{
this.LogDebug(requestUri, nameof(GetByteArrayAsync));
return this.httpClient.GetByteArrayAsync(requestUri);
}
public Task<byte[]> GetByteArrayAsync(Uri requestUri)
{
this.LogDebug(requestUri.ToString(), nameof(GetByteArrayAsync));
return this.httpClient.GetByteArrayAsync(requestUri);
}
public Task<Stream> GetStreamAsync(string requestUri)
{
this.LogDebug(requestUri, nameof(GetStreamAsync));
return this.httpClient.GetStreamAsync(requestUri);
}
public Task<Stream> GetStreamAsync(Uri requestUri)
{
this.LogDebug(requestUri.ToString(), nameof(GetStreamAsync));
return this.httpClient.GetStreamAsync(requestUri);
}
public Task<string> GetStringAsync(string requestUri)
{
this.LogDebug(requestUri, nameof(GetStringAsync));
return this.httpClient.GetStringAsync(requestUri);
}
public Task<string> GetStringAsync(Uri requestUri)
{
this.LogDebug(requestUri.ToString(), nameof(GetStringAsync));
return this.httpClient.GetStringAsync(requestUri);
}
public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content)
{
this.LogDebug(requestUri, nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content);
}
public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogDebug(requestUri, nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content)
{
this.LogDebug(requestUri.ToString(), nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content);
}
public Task<HttpResponseMessage> PostAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogDebug(requestUri.ToString(), nameof(PostAsync));
return this.httpClient.PostAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content)
{
this.LogDebug(requestUri, nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content);
}
public Task<HttpResponseMessage> PutAsync(string requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogDebug(requestUri, nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content)
{
this.LogDebug(requestUri.ToString(), nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content);
}
public Task<HttpResponseMessage> PutAsync(Uri requestUri, HttpContent content, CancellationToken cancellationToken)
{
this.LogDebug(requestUri.ToString(), nameof(PutAsync));
return this.httpClient.PutAsync(requestUri, content, cancellationToken);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
{
this.LogDebug(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption)
{
this.LogDebug(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request, completionOption);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationToken cancellationToken)
{
this.LogDebug(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request, completionOption, cancellationToken);
}
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
this.LogDebug(request.RequestUri.ToString(), nameof(SendAsync));
return this.httpClient.SendAsync(request, cancellationToken);
}
public void Dispose()
{
if (this.disposeInnerClient)
{
this.httpClient.Dispose();
}
}
private void LogInformation(string url, string method)
{
this.eventEmitted?.Invoke(this, new HttpClientEventMessage(this.scope, method, url));
}
private void LogDebug(string url, string method)
{
this.eventEmitted?.Invoke(this, new HttpClientEventMessage(this.scope, method, url));
}
}
@@ -1,16 +1,15 @@
namespace System.Http
{
public sealed class HttpClientEventMessage
{
public Type Scope { get; }
public string Method { get; }
public string Url { get; }
namespace System.Net.Http;
internal HttpClientEventMessage(Type scope, string method, string url)
{
this.Scope = scope;
this.Method = method;
this.Url = url;
}
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;
}
}
@@ -1,49 +1,47 @@
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
namespace System.Http
{
public interface IHttpClient<TScope>
{
event EventHandler<HttpClientEventMessage> EventEmitted;
Uri BaseAddress { get; set; }
HttpRequestHeaders DefaultRequestHeaders { get; }
long MaxResponseContentBufferSize { get; set; }
TimeSpan Timeout { get; set; }
namespace System.Net.Http;
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);
}
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 readonly 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);
}
}
@@ -1,175 +1,174 @@
namespace System.Structures.BitStructures
namespace System.Structures.BitStructures;
public struct Int32BitStruct : IEquatable<Int32BitStruct>
{
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)
{
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); }
return first.Value == second.Value;
}
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, 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, uint second)
{
return first.Value != second;
}
public static bool operator !=(Int32BitStruct first, uint second)
public static bool operator ==(Int32BitStruct first, int second)
{
unchecked
{
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();
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();
}
}
@@ -1,205 +1,204 @@
using System;
namespace System.Structures.BitStructures
namespace System.Structures.BitStructures;
public struct Int64BitStruct : IEquatable<Int64BitStruct>
{
public struct Int64BitStruct : IEquatable<Int64BitStruct>
public Int64BitStruct(uint low, uint high)
{
public Int64BitStruct(uint low, uint high)
{
this.Value = low + ((ulong)high << 32);
}
this.Value = low + ((ulong)high << 32);
}
public Int64BitStruct(int low, int high)
public Int64BitStruct(int low, int high)
{
unchecked
{
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();
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();
}
}
@@ -2,13 +2,16 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<RootNamespace>System</RootNamespace>
<Version>1.3.0</Version>
<Version>1.9</Version>
<Authors>Alexandru Macocian</Authors>
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
<Description>Extensions for the System namespace</Description>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
@@ -16,6 +19,12 @@
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="System.Text.Json" Version="10.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())
{
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
using System.Extensions;
using System.Threading.Tasks;
namespace System.Threading;
public readonly struct SemaphoreSlimContext : IDisposable
{
private readonly SemaphoreSlim semaphore;
private SemaphoreSlimContext(SemaphoreSlim semaphoreSlim)
{
this.semaphore = semaphoreSlim.ThrowIfNull(nameof(semaphoreSlim));
}
public void Dispose()
{
this.semaphore.Release();
}
public static async Task<SemaphoreSlimContext> Create(SemaphoreSlim semaphore)
{
await semaphore.ThrowIfNull(nameof(semaphore)).WaitAsync();
return new SemaphoreSlimContext(semaphore);
}
public static async Task<SemaphoreSlimContext> Create(SemaphoreSlim semaphore, CancellationToken cancellationToken)
{
await semaphore.ThrowIfNull(nameof(semaphore)).WaitAsync(cancellationToken);
return new SemaphoreSlimContext(semaphore);
}
}
@@ -0,0 +1,98 @@
using Microsoft.Extensions.Logging;
using System.Extensions;
using System.Threading;
using System.Threading.Tasks;
namespace System.Net.WebSockets;
// TODO: Cannot properly test because ClientWebSocket is sealed
public sealed class ClientWebSocket<TScope> : IClientWebSocket<TScope>, IDisposable
{
private readonly ILogger<TScope>? logger;
private ClientWebSocket internalWebSocket = new();
public ClientWebSocketOptions Options => this.internalWebSocket.Options;
public WebSocketCloseStatus? CloseStatus => this.internalWebSocket.CloseStatus;
public string CloseStatusDescription => this.internalWebSocket.CloseStatusDescription;
public string SubProtocol => this.internalWebSocket.SubProtocol;
public WebSocketState State => this.internalWebSocket.State;
public ClientWebSocket()
{
}
public ClientWebSocket(ILogger<TScope> logger)
{
this.logger = logger.ThrowIfNull(nameof(logger));
}
public ClientWebSocket(ClientWebSocket clientWebSocket, ILogger<TScope> logger)
{
this.internalWebSocket = clientWebSocket.ThrowIfNull(nameof(clientWebSocket));
this.logger = logger.ThrowIfNull(nameof(logger));
}
public void Dispose()
{
var scopedLogger = this.logger?.CreateScopedLogger(nameof(Dispose), string.Empty);
scopedLogger?.LogDebug($"Disposing websocket");
this.internalWebSocket?.Dispose();
}
public void Abort()
{
var scopedLogger = this.logger?.CreateScopedLogger(nameof(Abort), string.Empty);
scopedLogger?.LogDebug($"Aborting websocket");
this.internalWebSocket.Abort();
}
public Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
{
var scopedLogger = this.logger?.CreateScopedLogger(nameof(CloseAsync), string.Empty);
scopedLogger?.LogDebug($"Closing websocket. Status [{closeStatus}]. Status Description [{statusDescription}]");
return this.internalWebSocket.CloseAsync(closeStatus, statusDescription, cancellationToken);
}
public Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
{
var scopedLogger = this.logger?.CreateScopedLogger(nameof(CloseOutputAsync), string.Empty);
scopedLogger?.LogDebug($"Closing output. Status [{closeStatus}]. Status Description [{statusDescription}]");
return this.internalWebSocket.CloseOutputAsync(closeStatus, statusDescription, cancellationToken);
}
public Task ConnectAsync(Uri uri, CancellationToken cancellationToken)
{
var scopedLogger = this.logger?.CreateScopedLogger(nameof(ConnectAsync), string.Empty);
scopedLogger?.LogDebug($"Connecting to {uri}");
return this.internalWebSocket.ConnectAsync(uri, cancellationToken);
}
public async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
{
var scopedLogger = this.logger?.CreateScopedLogger(nameof(ConnectAsync), string.Empty);
scopedLogger?.LogDebug($"Attempting to receive bytes");
var result = await this.internalWebSocket.ReceiveAsync(buffer, cancellationToken);
scopedLogger?.LogDebug($"Received message [{result.MessageType}]");
scopedLogger?.LogDebug($"Type: {result.MessageType}\nCount: {result.Count}\nEndOfMessage: {result.EndOfMessage}\nCloseStatus: {result.CloseStatus}\nCloseStatusDescription: {result.CloseStatusDescription}");
return result;
}
public Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken)
{
var scopedLogger = this.logger?.CreateScopedLogger(nameof(SendAsync), string.Empty);
scopedLogger?.LogDebug($"Sending bytes");
scopedLogger?.LogDebug($"Type: {messageType}\nCount: {buffer.Count}\nEndOfMessage: {endOfMessage}");
return this.internalWebSocket.SendAsync(buffer, messageType, endOfMessage, cancellationToken);
}
public void RefreshSocket(ClientWebSocket? clientWebSocket = null)
{
this.internalWebSocket?.Dispose();
this.internalWebSocket = clientWebSocket ?? new ClientWebSocket();
}
}
@@ -0,0 +1,33 @@
using System.Threading.Tasks;
using System.Threading;
namespace System.Net.WebSockets;
public interface IClientWebSocket<TScope>
{
WebSocketCloseStatus? CloseStatus { get; }
string CloseStatusDescription { get; }
ClientWebSocketOptions Options { get; }
WebSocketState State { get; }
string SubProtocol { get; }
void Abort();
Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken);
Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken);
Task ConnectAsync(Uri uri, CancellationToken cancellationToken);
void Dispose();
Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken);
Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken);
void RefreshSocket(ClientWebSocket? clientWebSocket = default);
}
+108 -136
View File
@@ -3,153 +3,125 @@ using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests
namespace System.Collections.Tests;
[TestClass()]
public class AVLTreeTests
{
[TestClass()]
public class AVLTreeTests
private AVLTree<int> avlTree = new AVLTree<int>();
[TestMethod()]
public void AVLTreeTest()
{
private AVLTree<int> avlTree = new AVLTree<int>();
[TestMethod()]
public void AVLTreeTest()
{
avlTree = new AVLTree<int>();
}
this.avlTree = new AVLTree<int>();
}
[TestMethod()]
public void AddTest()
[TestMethod()]
public void AddTest()
{
try
{
try
for (var i = 0; i < 100; i++)
{
for (int i = 0; i < 100; i++)
{
avlTree.Add(i * 20 - (50 + i));
}
}
catch (Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
if (avlTree.Count != 100)
{
Assert.Fail();
this.avlTree.Add(i * 20 - (50 + i));
}
}
[TestMethod()]
public void ContainsTest()
catch (Exception e)
{
for (int i = 0; i < 100; i++)
{
avlTree.Add(i);
}
if (!avlTree.Contains(50))
{
Assert.Fail();
}
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
[TestMethod()]
public void RemoveTest()
if (this.avlTree.Count != 100)
{
for (int i = 0; i < 100; i++)
{
avlTree.Add(i);
}
avlTree.Remove(50);
if (avlTree.Contains(50))
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest()
{
for (int i = 0; i < 100; i++)
{
avlTree.Add(i);
}
avlTree.Clear();
if(avlTree.Count > 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void CopyToTest()
{
for (int i = 0; i < 100; i++)
{
avlTree.Add(i);
}
int[] array = new int[100];
avlTree.CopyTo(array, 0);
}
[TestMethod()]
public void GetEnumeratorTest()
{
for (int i = 0; i < 100; i++)
{
avlTree.Add(i);
}
int count = 0;
foreach(int value in avlTree)
{
count++;
System.Diagnostics.Debug.WriteLine(value);
}
if(count != avlTree.Count)
{
Assert.Fail();
}
}
[TestMethod()]
public void ToArrayTest()
{
for (int i = 0; i < 100; i++)
{
avlTree.Add(i);
}
int[] array = avlTree.ToArray();
}
[TestMethod()]
public void Serialize()
{
AVLTree<int> avlTree2 = new AVLTree<int>();
for (int i = 0; i < 100; i++)
{
avlTree.Add(i);
}
BinaryFormatter serializer = new BinaryFormatter();
string s = string.Empty;
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, avlTree);
stream.Position = 0;
avlTree2 = (AVLTree<int>)serializer.Deserialize(stream);
}
IEnumerator<int> avlTreeEnum = avlTree.GetEnumerator();
IEnumerator<int> avlTree2Enum = avlTree2.GetEnumerator();
for(int i = 0; i < 100; i++)
{
if(avlTreeEnum.Current != avlTree2Enum.Current)
{
Assert.Fail();
}
avlTreeEnum.MoveNext();
avlTree2Enum.MoveNext();
}
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();
}
}
@@ -3,200 +3,180 @@ using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace System.Collections.Tests
namespace System.Collections.Tests;
[TestClass()]
public class BinaryHeapTests
{
[TestClass()]
public class BinaryHeapTests
private BinaryHeap<int> binaryHeap = new BinaryHeap<int>();
[TestMethod()]
public void BinaryHeapTest()
{
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)
{
binaryHeap = new BinaryHeap<int>();
}
[TestMethod()]
public void BinaryHeapTest1()
{
binaryHeap = new BinaryHeap<int>(100);
if (binaryHeap.Count != 0 && binaryHeap.Capacity != 100)
{
Assert.Fail();
}
}
[TestMethod()]
[Timeout(500)]
public void InsertTest()
{
try
{
for (int i = 0; i < 100; i++)
{
binaryHeap.Add(i * 20 - (50 + i));
}
}
catch (Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
if (binaryHeap.Count != 100)
{
Assert.Fail();
}
}
[TestMethod()]
[Timeout(500)]
public void DeleteMinTest()
{
try
{
int tries = binaryHeap.Count;
while (binaryHeap.Count > 0)
{
if (tries == 0)
{
Assert.Fail();
}
System.Diagnostics.Debug.WriteLine(binaryHeap.Remove());
tries--;
}
}
catch (Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
}
[TestMethod()]
public void MinMaxTest()
{
for (int i = 0; i < 1000; i++)
{
binaryHeap.Add(i);
}
if (binaryHeap.Min != 0 || binaryHeap.Max != 999)
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest()
{
for (int i = 0; i < 100; i++)
{
binaryHeap.Add(i);
}
binaryHeap.Clear();
if (binaryHeap.Count > 0 || binaryHeap.Capacity != 160)
{
Assert.Fail();
}
}
[TestMethod()]
public void ClearTest2()
{
for (int i = 100; i < 200; i++)
{
binaryHeap.Add(i);
}
binaryHeap.Clear(false);
int[] array = binaryHeap.ToArray();
if (binaryHeap.Count > 0 || binaryHeap.Capacity == 10)
{
Assert.Fail();
}
if (array.Length != 0)
{
Assert.Fail();
}
binaryHeap.Clear(true);
if (binaryHeap.Capacity != 10 || binaryHeap.Count != 0)
{
Assert.Fail();
}
}
[TestMethod()]
public void ContainsTest()
{
binaryHeap.Add(100);
if (!binaryHeap.Contains(100))
{
Assert.Fail();
}
}
[TestMethod()]
public void ToArrayTest()
{
for(int i = 0; i < 1000; i++)
{
binaryHeap.Add(i);
}
int[] array = binaryHeap.ToArray();
if(array.Length != binaryHeap.Count)
{
Assert.Fail();
}
if(array[0] != 0 && array[999] != 999)
{
Assert.Fail();
}
}
[TestMethod()]
public void GetEnumeratorTest()
{
for (int i = 0; i < 1000; i++)
{
binaryHeap.Add(i);
}
int count = 0;
foreach(int value in binaryHeap)
{
System.Diagnostics.Debug.WriteLine(value);
count++;
}
if(count == binaryHeap.Count)
{
return;
}
Assert.Fail();
}
}
[TestMethod()]
public void Serialize()
[TestMethod()]
[Timeout(500)]
public void InsertTest()
{
try
{
BinaryHeap<int> binaryHeap2 = new BinaryHeap<int>();
for (int i = 0; i < 100; i++)
for (var i = 0; i < 100; i++)
{
binaryHeap.Add(i);
this.binaryHeap.Add(i * 20 - (50 + i));
}
BinaryFormatter serializer = new BinaryFormatter();
string s = string.Empty;
using (var stream = new MemoryStream()) {
serializer.Serialize(stream, binaryHeap);
stream.Position = 0;
binaryHeap2 = (BinaryHeap<int>)serializer.Deserialize(stream);
}
IEnumerator<int> binaryHeapEnum = binaryHeap.GetEnumerator();
IEnumerator<int> binaryHeapEnum2 = binaryHeap2.GetEnumerator();
}
catch (Exception e)
{
Assert.Fail(e.Message + "\n" + e.StackTrace);
}
for (int i = 0; i < 100; i++)
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 (binaryHeapEnum.Current != binaryHeapEnum2.Current)
if (tries == 0)
{
Assert.Fail();
}
binaryHeapEnum.MoveNext();
binaryHeapEnum2.MoveNext();
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();
}
}
@@ -0,0 +1,395 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;
namespace System.Collections.Tests;
[TestClass]
public class CircularBufferTests
{
[TestMethod]
public void Constructor_WithCapacity_RespectsCapacity()
{
var buffer = new CircularBuffer<int>(10);
buffer.Count.Should().Be(0);
for (var i = 0; i < 11; i++)
{
buffer.Add(i);
}
buffer.Count.Should().Be(10);
}
[TestMethod]
public void Constructor_WithBuffer_ReturnsExpectedElements()
{
var array = new int[10];
for(var i = 0; i < 10; i++)
{
array[i] = i;
}
var buffer = new CircularBuffer<int>(array, 0, 9);
buffer.Count.Should().Be(10);
buffer.Should().BeEquivalentTo(array);
}
[TestMethod]
public void Constructor_WithBuffer_RespectsOrder()
{
var expectedArray = new int[] { 3, 4, 5, 6, 7, 8, 9, 0, 1, 2 };
var array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var buffer = new CircularBuffer<int>(array, 3, 2);
buffer.Count.Should().Be(10);
buffer.Should().BeEquivalentTo(expectedArray);
}
[TestMethod]
public void Constructor_WithBuffer_RespectsInternalBuffer()
{
var buffer = new CircularBuffer<int>(new int[] { 0, 1, 2, 3, 4, 5 });
buffer.Count.Should().Be(6);
buffer.Should().BeEquivalentTo(new int[] { 0, 1, 2, 3, 4, 5 });
}
[TestMethod]
public void AddItem_AddsItem()
{
var buffer = new CircularBuffer<int>(10)
{
1
};
buffer.Should().HaveCount(1);
buffer.First().Should().Be(1);
}
[TestMethod]
public void AddItem_AddsItem_RespectsCapacity()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
buffer.Add(0);
buffer.Should().HaveCount(10);
buffer.Last().Should().Be(0);
buffer.First().Should().Be(2);
}
[TestMethod]
public void RemoveItem_RemovesDesiredItem()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
var result = buffer.Remove(5);
result.Should().BeTrue();
buffer.Count.Should().Be(9);
buffer.Should().BeEquivalentTo(new int[] { 1, 2, 3, 4, 6, 7, 8, 9, 10 });
}
[TestMethod]
public void RemoveItem_UnknownItem_DoesNotRemoveItem()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
var result = buffer.Remove(15);
result.Should().BeFalse();
buffer.Count.Should().Be(10);
buffer.Should().BeEquivalentTo(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
}
[TestMethod]
public void Contains_ItemExists_ReturnsTrue()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
var result = buffer.Contains(5);
result.Should().BeTrue();
}
[TestMethod]
public void Contains_ItemDoesNotExists_ReturnsFalse()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
var result = buffer.Contains(15);
result.Should().BeFalse();
}
[TestMethod]
public void Clear_ClearsBuffer()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
buffer.Clear();
buffer.Count.Should().Be(0);
buffer.Should().BeEquivalentTo(Array.Empty<int>());
}
[TestMethod]
public void DeepClear_ClearsBuffer()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
buffer.DeepClear();
buffer.Count.Should().Be(0);
buffer.Should().BeEquivalentTo(Array.Empty<int>());
}
[TestMethod]
public void Insert_FullBuffer_ThrowsException()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
};
var action = () =>
{
buffer.Insert(0, 1);
};
action.Should().Throw<InvalidOperationException>();
}
[TestMethod]
public void Insert_ShouldInsertItemAtPosition()
{
var buffer = new CircularBuffer<int>(10)
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
};
buffer.Insert(0, 0);
buffer.Should().BeEquivalentTo(new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
}
[TestMethod]
public void RemoveAt_RemovesExpectedItem()
{
var buffer = new CircularBuffer<int>(10)
{
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
};
buffer.RemoveAt(0);
buffer.Should().BeEquivalentTo(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
}
[TestMethod]
public void Indexer_ReturnsExpectedItem()
{
var buffer = new CircularBuffer<int>(10)
{
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
};
buffer[5].Should().Be(5);
}
[TestMethod]
public void Indexer_SetsExpectedItem()
{
var buffer = new CircularBuffer<int>(10)
{
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
};
buffer[6] = 5;
buffer.Should().BeEquivalentTo(new int[] { 0, 1, 2, 3, 4, 5, 5, 7, 8, 9 });
}
[TestMethod]
public void CircularBuffer_AddAndRemoveOperations_PerformAsExpected()
{
var buffer = new CircularBuffer<int>(10)
{
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
};
buffer.Add(10);
buffer.Remove(9);
buffer.RemoveAt(5);
buffer.Add(11);
buffer.Should().BeEquivalentTo(new int[] { 1, 2, 3, 4, 5, 7, 8, 10, 11 });
}
[TestMethod]
public void CircularBuffer_AddItems_RotatesBuffer()
{
var buffer = new CircularBuffer<int>(10)
{
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
};
for(var i = 0; i < 10; i++)
{
buffer.Add(i + 10);
}
buffer.Should().BeEquivalentTo(new int[] { 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 });
}
}

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