├── .github
└── workflows
│ ├── benchmarks.yml
│ ├── build.yml
│ └── publish-release.yml
├── .gitignore
├── Directory.Build.props
├── LICENSE
├── NetworkPrimitives.sln
├── benchmarks
├── Ipv4AddressParsing.cs
├── Ipv4AddressRange.cs
├── Ipv4AddressRangeLists.cs
├── Ipv4SubnetParsing.cs
├── NetworkPrimitives.Benchmarks.csproj
├── Program.cs
└── TestData.cs
├── docs
├── benchmarks.md
├── json-converters.md
├── performance.md
├── range-lists.md
└── subnetting.md
├── global.json
├── readme.md
├── samples
└── Demo
│ ├── Demo.Core.csproj
│ ├── Demo.Framework.csproj
│ ├── Directory.Build.props
│ ├── Program.cs
│ └── TestData.cs
├── src
├── Directory.Build.props
├── NetworkPrimitives.JsonConverters
│ ├── Ipv4
│ │ ├── Ipv4AddressJsonConverter.cs
│ │ ├── Ipv4AddressRangeJsonConverter.cs
│ │ ├── Ipv4AddressRangeListJsonConverter.cs
│ │ ├── Ipv4CidrJsonConverter.cs
│ │ ├── Ipv4NetworkMatchJsonConverter.cs
│ │ ├── Ipv4SubnetJsonConverter.cs
│ │ ├── Ipv4SubnetMaskJsonConverter.cs
│ │ └── Ipv4WildcardMaskJsonConverter.cs
│ ├── Ipv6
│ │ ├── Ipv6AddressJsonConverter.cs
│ │ ├── Ipv6CidrJsonConverter.cs
│ │ ├── Ipv6SubnetJsonConverter.cs
│ │ └── Ipv6SubnetMaskJsonConverter.cs
│ ├── JsonSerializerExtensions.cs
│ └── NetworkPrimitives.JsonConverters.csproj
└── NetworkPrimitives
│ ├── BitOperations.cs
│ ├── Common
│ ├── BuildInformationAttribute.cs
│ ├── ExcludeFromCodeCoverageAttribute.cs
│ ├── Extensions
│ │ ├── CharExtensions.cs
│ │ ├── IpAddressExtensions.cs
│ │ ├── NumericExtensions.cs
│ │ ├── QueueExtensions.cs
│ │ ├── SliceExtensions.cs
│ │ ├── SpanExtensions.cs
│ │ ├── StackExtensions.cs
│ │ └── StringExtensions.cs
│ ├── INetworkPrimitive.cs
│ ├── ISlice.cs
│ ├── ITryFormat.cs
│ ├── JetBrainsAnnotations.cs
│ └── Utilities
│ │ ├── Formatting.cs
│ │ ├── ImmutableListWrapper.cs
│ │ ├── ImmutableListWrapperBuilder.cs
│ │ ├── Parsing.cs
│ │ └── ReadOnlyListSpan.cs
│ ├── Ipv4
│ ├── Address
│ │ ├── Ipv4Address.cs
│ │ ├── Ipv4AddressClass.cs
│ │ ├── Ipv4AddressRangeType.cs
│ │ └── Ipv4WellKnownRanges.cs
│ ├── Formatting
│ │ ├── Ipv4Formatting.cs
│ │ └── Ipv4Parsing.cs
│ ├── Ipv4Extensions.cs
│ ├── Match
│ │ ├── Ipv4NetworkMatch.cs
│ │ └── Ipv4WildcardMask.cs
│ ├── Range
│ │ ├── Ipv4AddressRange.ClassEnumerator.cs
│ │ ├── Ipv4AddressRange.RefStructEnumerator.cs
│ │ └── Ipv4AddressRange.cs
│ ├── RangeList
│ │ ├── Ipv4AddressListSpan.cs
│ │ ├── Ipv4AddressRangeList.cs
│ │ └── Ipv4AddressRangeListEnumerator.cs
│ ├── Subnet
│ │ ├── Ipv4Cidr.cs
│ │ ├── Ipv4Subnet.cs
│ │ ├── Ipv4SubnetMask.cs
│ │ ├── SubnetMaskLookups.cs
│ │ └── SubnetOperations.cs
│ └── SubnetTree
│ │ ├── Ipv4SubnetDictionary.Add.cs
│ │ ├── Ipv4SubnetDictionary.Collections.cs
│ │ ├── Ipv4SubnetDictionary.Consolidate.cs
│ │ ├── Ipv4SubnetDictionary.KeyCollection.cs
│ │ ├── Ipv4SubnetDictionary.Lookups.cs
│ │ ├── Ipv4SubnetDictionary.NodeEnumerator.cs
│ │ ├── Ipv4SubnetDictionary.Nodes.cs
│ │ ├── Ipv4SubnetDictionary.Remove.cs
│ │ └── Ipv4SubnetDictionary.cs
│ ├── Ipv6
│ ├── Address
│ │ └── Ipv6Address.cs
│ ├── Formatting
│ │ ├── Ipv6FormatInfo.cs
│ │ ├── Ipv6Formatting.cs
│ │ └── Ipv6Parsing.cs
│ └── Subnet
│ │ ├── Ipv6Cidr.cs
│ │ ├── Ipv6Subnet.cs
│ │ ├── Ipv6SubnetMask.cs
│ │ └── SubnetMaskLookups.cs
│ ├── NetworkPrimitives - Backup.Ipv4.csproj
│ ├── NetworkPrimitives.csproj
│ ├── NetworkPrimitives.csproj.DotSettings
│ └── PublicAPI
│ ├── net5.0
│ ├── PublicAPI.Shipped.txt
│ └── PublicAPI.Unshipped.txt
│ └── netstandard2.0
│ ├── PublicAPI.Shipped.txt
│ └── PublicAPI.Unshipped.txt
└── tests
├── Directory.Build.props
├── EmbeddedResourceUtils.cs
├── GenerateTestCases.ps1
├── NetworkPrimitives.Tests
├── Common
│ ├── CharExtensionTests.cs
│ ├── EndianSwapTests.cs
│ ├── ListSpanTests.cs
│ └── ParsingTests.cs
├── Ipv4
│ ├── Cidr
│ │ ├── ComparisonTests.cs
│ │ ├── Ipv4CidrTests.cs
│ │ ├── ParsingTests.cs
│ │ └── ValueTests.cs
│ ├── Ipv4AddressTests.cs
│ ├── Ipv4MatchTests.cs
│ ├── Ipv4SubnetMaskTests.cs
│ ├── Ipv4SubnetTests.cs
│ ├── Ipv4TestCase.cs
│ ├── Ipv4WildcardMaskTests.cs
│ └── RangeList
│ │ ├── AddressListSpanTests.cs
│ │ ├── Parsing.cs
│ │ ├── RangeListTestCase.cs
│ │ └── RangeListTests.cs
├── Ipv6
│ └── Ipv6AddressTests.cs
├── NetworkPrimitives.Tests.Core.csproj
├── NetworkPrimitives.Tests.Framework.csproj
└── TestData.cs
├── randomips.json
└── range-test-cases.json
/.github/workflows/benchmarks.yml:
--------------------------------------------------------------------------------
1 | name: benchmarks
2 |
3 | on:
4 | workflow_dispatch:
5 | #push:
6 | # branches: [ master ]
7 | #pull_request:
8 | # branches: [ master ]
9 |
10 |
11 | jobs:
12 | benchmarks:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v2
18 |
19 | - name: Setup .NET
20 | uses: actions/setup-dotnet@v1
21 | with:
22 | dotnet-version: 5.0.x
23 | include-prerelease: true
24 |
25 | - name: Build
26 | run: dotnet build -c Release ./benchmarks/NetworkPrimitives.Benchmarks.csproj
27 |
28 | - name: Run Benchmarks
29 | run: dotnet run --project ./benchmarks/NetworkPrimitives.Benchmarks.csproj -- --filter "*Benchmarks.*"
30 |
31 | - name: Upload artifacts
32 | uses: actions/upload-artifact@v2
33 | with:
34 | name: benchmark-results
35 | path: BenchmarkResults
36 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: [ dev, master ]
7 | pull_request:
8 | branches: [ dev, master ]
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 |
18 | - name: Get Timestamp
19 | uses: gerred/actions/current-time@master
20 | env:
21 | $TIMESTAMP: "${{ steps.current-time.outputs.time }}"
22 |
23 | - name: Setup .NET
24 | uses: actions/setup-dotnet@v1
25 | with:
26 | dotnet-version: 6.0.x
27 | include-prerelease: true
28 |
29 | - name: Build
30 | run: dotnet build --configuration Release NetworkPrimitives.sln
31 |
32 | - name: Test
33 | run: dotnet test --no-build --configuration Release NetworkPrimitives.sln --logger:"nunit;LogFilePath=../../test-results/{framework}.xml"
34 |
35 | - name: Upload artifacts
36 | uses: actions/upload-artifact@v2
37 | with:
38 | name: test-results
39 | path: test-results
40 |
41 |
42 |
--------------------------------------------------------------------------------
/.github/workflows/publish-release.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - '*'
5 |
6 | jobs:
7 | github-release:
8 | runs-on: ubuntu-latest
9 | timeout-minutes: 15
10 | env:
11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
12 | NUGET_FEED: https://api.nuget.org/v3/index.json
13 | NUGET_KEY: ${{ secrets.NUGET_API_KEY }}
14 |
15 | steps:
16 | - name: Checkout
17 | uses: actions/checkout@v2
18 |
19 | - name: Verify commit exists in origin/master
20 | run: |
21 | git fetch --no-tags --prune --depth=1 origin +refs/heads/*:refs/remotes/origin/*
22 | git branch --remote --contains | grep origin/master
23 |
24 | - name: Add nuget source
25 | run: |
26 | dotnet nuget add source --username binarycow --password ${{ secrets.GITHUB_TOKEN }} --store-password-in-clear-text --name github "https://nuget.pkg.github.com/binarycow/index.json"
27 |
28 | - name: Build
29 | run: dotnet build --configuration Release NetworkPrimitives.sln
30 |
31 | - name: Test
32 | run: dotnet test --no-build --configuration Release NetworkPrimitives.sln --logger:"nunit;LogFilePath=../../test-results/{framework}.xml"
33 |
34 | - name: Pack
35 | run: dotnet pack --configuration Release --no-build --output . --include-symbols --include-source NetworkPrimitives.sln
36 |
37 | - name: Upload test result artifacts
38 | uses: actions/upload-artifact@v2
39 | with:
40 | name: test-results
41 | path: test-results
42 |
43 | - name: Upload nuget package artifacts
44 | uses: actions/upload-artifact@v2
45 | with:
46 | name: packages
47 | path: NetworkPrimitives*.nupkg
48 |
49 | - name: Create Release
50 | uses: ncipollo/release-action@v1.8.10
51 | with:
52 | artifacts: "NetworkPrimitives*.nupkg"
53 | artifactErrorsFailBuild: true
54 | omitBody: true
55 | prerelease: true
56 | token: ${{ secrets.GITHUB_TOKEN }}
57 |
58 | - name: Push to GitHub nuget feed
59 | run: dotnet nuget push --source github --api-key ${{ secrets.GITHUB_TOKEN }} NetworkPrimitives*.nupkg --skip-duplicate
60 |
61 | - name: Push to official nuget feed
62 | run: dotnet nuget push --source $NUGET_FEED --api-key $NUGET_KEY NetworkPrimitives*.nupkg --skip-duplicate
63 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | enable
4 | preview
5 | latest
6 | en-US
7 |
8 |
9 |
10 | true
11 |
12 |
13 |
14 | $(WarningsNotAsErrors);RS0016
15 |
16 |
17 |
18 | $(MSBuildThisFileDirectory)
19 |
20 |
21 |
22 |
23 |
24 | $(NoWarn);NU5104
25 |
26 |
27 |
28 | binarycow
29 | © 2022 Mike Christiansen
30 | https://github.com/binarycow/NetworkPrimitives
31 | git
32 | MIT
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Mike Christiansen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/NetworkPrimitives.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31717.71
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9A24AE3F-E98C-406F-8EF6-8816224ECB2B}"
7 | ProjectSection(SolutionItems) = preProject
8 | src\Directory.Build.props = src\Directory.Build.props
9 | EndProjectSection
10 | EndProject
11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1044DFB0-ADCA-41F0-AFA3-B942A47792A1}"
12 | ProjectSection(SolutionItems) = preProject
13 | tests\Directory.Build.props = tests\Directory.Build.props
14 | tests\randomips.json = tests\randomips.json
15 | tests\range-test-cases.json = tests\range-test-cases.json
16 | tests\range-list-test-cases.json = tests\range-list-test-cases.json
17 | EndProjectSection
18 | EndProject
19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkPrimitives", "src\NetworkPrimitives\NetworkPrimitives.csproj", "{F30DAE77-C764-4CE0-9E63-FF992AEEE251}"
20 | EndProject
21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C004A053-6172-4403-935C-6B60AC3B2F53}"
22 | ProjectSection(SolutionItems) = preProject
23 | LICENSE = LICENSE
24 | readme.md = readme.md
25 | docs\subnetting.md = docs\subnetting.md
26 | docs\performance.md = docs\performance.md
27 | docs\json-converters.md = docs\json-converters.md
28 | docs\range-lists.md = docs\range-lists.md
29 | docs\benchmarks.md = docs\benchmarks.md
30 | EndProjectSection
31 | EndProject
32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{27430A89-25BF-45CC-9F7B-A7F3432045D5}"
33 | ProjectSection(SolutionItems) = preProject
34 | samples\Demo\Directory.Build.props = samples\Demo\Directory.Build.props
35 | EndProjectSection
36 | EndProject
37 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo.Core", "samples\Demo\Demo.Core.csproj", "{96F4A4B9-9A95-4ABA-89AC-9279DD385331}"
38 | EndProject
39 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo.Framework", "samples\Demo\Demo.Framework.csproj", "{C53DE5F2-870E-4A8B-A9E6-0C714A7FC14E}"
40 | EndProject
41 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkPrimitives.Tests.Core", "tests\NetworkPrimitives.Tests\NetworkPrimitives.Tests.Core.csproj", "{43216EA7-3C51-424D-8A17-E49A77B0BF19}"
42 | EndProject
43 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetworkPrimitives.Tests.Framework", "tests\NetworkPrimitives.Tests\NetworkPrimitives.Tests.Framework.csproj", "{07E0DA29-1157-4C7E-9211-45EB25E90C69}"
44 | EndProject
45 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{9C775FC2-BF3D-4316-9800-5B1FE338E8A0}"
46 | EndProject
47 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetworkPrimitives.Benchmarks", "benchmarks\NetworkPrimitives.Benchmarks.csproj", "{5CBAECD8-2497-48AD-85B5-9E961671E988}"
48 | EndProject
49 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetworkPrimitives.JsonConverters", "src\NetworkPrimitives.JsonConverters\NetworkPrimitives.JsonConverters.csproj", "{42CF6E7C-297B-4428-BB8E-66DFB05BB6DA}"
50 | EndProject
51 | Global
52 | GlobalSection(SharedMSBuildProjectFiles) = preSolution
53 | src\NetworkPrimitives.Shared\NetworkPrimitives.Shared.projitems*{f30dae77-c764-4ce0-9e63-ff992aeee251}*SharedItemsImports = 5
54 | src\NetworkPrimitives.Shared\NetworkPrimitives.Shared.projitems*{f91cd3ed-a1ca-4515-9de9-264f8ac68625}*SharedItemsImports = 13
55 | EndGlobalSection
56 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
57 | Debug|Any CPU = Debug|Any CPU
58 | Release|Any CPU = Release|Any CPU
59 | EndGlobalSection
60 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
61 | {F30DAE77-C764-4CE0-9E63-FF992AEEE251}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 | {F30DAE77-C764-4CE0-9E63-FF992AEEE251}.Release|Any CPU.Build.0 = Release|Any CPU
63 | {F30DAE77-C764-4CE0-9E63-FF992AEEE251}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
64 | {F30DAE77-C764-4CE0-9E63-FF992AEEE251}.Debug|Any CPU.Build.0 = Debug|Any CPU
65 | {96F4A4B9-9A95-4ABA-89AC-9279DD385331}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
66 | {96F4A4B9-9A95-4ABA-89AC-9279DD385331}.Debug|Any CPU.Build.0 = Debug|Any CPU
67 | {96F4A4B9-9A95-4ABA-89AC-9279DD385331}.Release|Any CPU.ActiveCfg = Release|Any CPU
68 | {96F4A4B9-9A95-4ABA-89AC-9279DD385331}.Release|Any CPU.Build.0 = Release|Any CPU
69 | {C53DE5F2-870E-4A8B-A9E6-0C714A7FC14E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
70 | {C53DE5F2-870E-4A8B-A9E6-0C714A7FC14E}.Debug|Any CPU.Build.0 = Debug|Any CPU
71 | {C53DE5F2-870E-4A8B-A9E6-0C714A7FC14E}.Release|Any CPU.ActiveCfg = Release|Any CPU
72 | {C53DE5F2-870E-4A8B-A9E6-0C714A7FC14E}.Release|Any CPU.Build.0 = Release|Any CPU
73 | {43216EA7-3C51-424D-8A17-E49A77B0BF19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
74 | {43216EA7-3C51-424D-8A17-E49A77B0BF19}.Debug|Any CPU.Build.0 = Debug|Any CPU
75 | {43216EA7-3C51-424D-8A17-E49A77B0BF19}.Release|Any CPU.ActiveCfg = Release|Any CPU
76 | {43216EA7-3C51-424D-8A17-E49A77B0BF19}.Release|Any CPU.Build.0 = Release|Any CPU
77 | {07E0DA29-1157-4C7E-9211-45EB25E90C69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
78 | {07E0DA29-1157-4C7E-9211-45EB25E90C69}.Debug|Any CPU.Build.0 = Debug|Any CPU
79 | {07E0DA29-1157-4C7E-9211-45EB25E90C69}.Release|Any CPU.ActiveCfg = Release|Any CPU
80 | {07E0DA29-1157-4C7E-9211-45EB25E90C69}.Release|Any CPU.Build.0 = Release|Any CPU
81 | {5CBAECD8-2497-48AD-85B5-9E961671E988}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
82 | {5CBAECD8-2497-48AD-85B5-9E961671E988}.Debug|Any CPU.Build.0 = Debug|Any CPU
83 | {5CBAECD8-2497-48AD-85B5-9E961671E988}.Release|Any CPU.ActiveCfg = Release|Any CPU
84 | {5CBAECD8-2497-48AD-85B5-9E961671E988}.Release|Any CPU.Build.0 = Release|Any CPU
85 | {42CF6E7C-297B-4428-BB8E-66DFB05BB6DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
86 | {42CF6E7C-297B-4428-BB8E-66DFB05BB6DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
87 | {42CF6E7C-297B-4428-BB8E-66DFB05BB6DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
88 | {42CF6E7C-297B-4428-BB8E-66DFB05BB6DA}.Release|Any CPU.Build.0 = Release|Any CPU
89 | EndGlobalSection
90 | GlobalSection(SolutionProperties) = preSolution
91 | HideSolutionNode = FALSE
92 | EndGlobalSection
93 | GlobalSection(NestedProjects) = preSolution
94 | {F30DAE77-C764-4CE0-9E63-FF992AEEE251} = {9A24AE3F-E98C-406F-8EF6-8816224ECB2B}
95 | {96F4A4B9-9A95-4ABA-89AC-9279DD385331} = {27430A89-25BF-45CC-9F7B-A7F3432045D5}
96 | {C53DE5F2-870E-4A8B-A9E6-0C714A7FC14E} = {27430A89-25BF-45CC-9F7B-A7F3432045D5}
97 | {43216EA7-3C51-424D-8A17-E49A77B0BF19} = {1044DFB0-ADCA-41F0-AFA3-B942A47792A1}
98 | {07E0DA29-1157-4C7E-9211-45EB25E90C69} = {1044DFB0-ADCA-41F0-AFA3-B942A47792A1}
99 | {5CBAECD8-2497-48AD-85B5-9E961671E988} = {9C775FC2-BF3D-4316-9800-5B1FE338E8A0}
100 | {42CF6E7C-297B-4428-BB8E-66DFB05BB6DA} = {9A24AE3F-E98C-406F-8EF6-8816224ECB2B}
101 | EndGlobalSection
102 | GlobalSection(ExtensibilityGlobals) = postSolution
103 | SolutionGuid = {157805D8-FB4B-4043-ABB9-61C17CC21945}
104 | EndGlobalSection
105 | EndGlobal
106 |
--------------------------------------------------------------------------------
/benchmarks/Ipv4AddressParsing.cs:
--------------------------------------------------------------------------------
1 | extern alias Lib_IPN2;
2 | // ReSharper disable InconsistentNaming
3 | #nullable enable
4 | using BenchmarkDotNet.Jobs;
5 | using BenchmarkDotNet.Attributes;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | using IPN2 = Lib_IPN2::System.Net.IPNetwork;
9 |
10 | namespace NetworkPrimitives.Benchmarks;
11 |
12 | public class Ipv4AddressParsing
13 | {
14 | [Benchmark]
15 | public void NetworkPrimitives()
16 | {
17 | foreach (var address in TestData.RandomIpAddresses)
18 | {
19 | _ = Ipv4Address.Parse(address);
20 | }
21 | }
22 |
23 | [Benchmark]
24 | public void DotNet()
25 | {
26 | foreach (var address in TestData.RandomIpAddresses)
27 | {
28 | _ = System.Net.IPAddress.Parse(address);
29 | }
30 | }
31 |
32 | [Benchmark]
33 | public void IpNetwork2()
34 | {
35 | foreach (var address in TestData.RandomIpAddresses)
36 | {
37 | _ = IPN2.Parse(address);
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/benchmarks/Ipv4AddressRange.cs:
--------------------------------------------------------------------------------
1 | extern alias Lib_IPN2;
2 | // ReSharper disable InconsistentNaming
3 | #nullable enable
4 |
5 | using BenchmarkDotNet.Attributes;
6 | using BenchmarkDotNet.Jobs;
7 | using NetworkPrimitives.Ipv4;
8 |
9 | using IPN2 = Lib_IPN2::System.Net.IPNetwork;
10 |
11 | namespace NetworkPrimitives.Benchmarks;
12 |
13 | public class Ipv4AddressRange
14 | {
15 | [Benchmark]
16 | public void NetworkPrimitives()
17 | {
18 | foreach (var subnetString in TestData.RandomSubnets)
19 | {
20 | var subnet = Ipv4Subnet.Parse(subnetString);
21 | foreach (var address in subnet.GetAllAddresses())
22 | {
23 | _ = address;
24 | }
25 | }
26 | }
27 |
28 | [Benchmark]
29 | public void IpNetwork2()
30 | {
31 | foreach (var subnetString in TestData.RandomSubnets)
32 | {
33 | var subnet = IPN2.Parse(subnetString);
34 | foreach (var address in subnet.ListIPAddress())
35 | {
36 | _ = address;
37 | }
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/benchmarks/Ipv4AddressRangeLists.cs:
--------------------------------------------------------------------------------
1 | using BenchmarkDotNet.Attributes;
2 | using BenchmarkDotNet.Jobs;
3 | using NetworkPrimitives.Ipv4;
4 |
5 | namespace NetworkPrimitives.Benchmarks;
6 |
7 | public class Ipv4AddressRangeLists
8 | {
9 | [Benchmark]
10 | public void NetworkPrimitives()
11 | {
12 | foreach (var rangeListString in TestData.RangeLists)
13 | {
14 | var rangeList = Ipv4AddressRangeList.Parse(rangeListString);
15 | foreach (var range in rangeList)
16 | {
17 | foreach (var address in range)
18 | {
19 | _ = address;
20 | }
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/benchmarks/Ipv4SubnetParsing.cs:
--------------------------------------------------------------------------------
1 | extern alias Lib_IPN2;
2 | // ReSharper disable InconsistentNaming
3 | #nullable enable
4 |
5 | using BenchmarkDotNet.Attributes;
6 | using BenchmarkDotNet.Jobs;
7 | using NetworkPrimitives.Ipv4;
8 |
9 | using IPN2 = Lib_IPN2::System.Net.IPNetwork;
10 |
11 | namespace NetworkPrimitives.Benchmarks;
12 |
13 | public class Ipv4SubnetParsing
14 | {
15 |
16 | [Benchmark]
17 | public void NetworkPrimitives()
18 | {
19 | foreach (var address in TestData.RandomSubnets)
20 | {
21 | _ = Ipv4Subnet.Parse(address);
22 | }
23 | }
24 |
25 | [Benchmark]
26 | public void IpNetwork2()
27 | {
28 | foreach (var address in TestData.RandomSubnets)
29 | {
30 | _ = IPN2.Parse(address);
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/benchmarks/NetworkPrimitives.Benchmarks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | net48;net5.0;net6.0;netcoreapp3.0
5 |
6 |
7 | AnyCPU
8 | pdbonly
9 | true
10 | true
11 | true
12 | Release
13 | false
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/benchmarks/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using BenchmarkDotNet.Columns;
5 | using BenchmarkDotNet.Configs;
6 | using BenchmarkDotNet.Diagnosers;
7 | using BenchmarkDotNet.Environments;
8 | using BenchmarkDotNet.Exporters;
9 | using BenchmarkDotNet.Exporters.Csv;
10 | using BenchmarkDotNet.Jobs;
11 | using BenchmarkDotNet.Loggers;
12 | using BenchmarkDotNet.Running;
13 |
14 | namespace NetworkPrimitives.Benchmarks
15 | {
16 | internal static class Program
17 | {
18 | internal const int LAUNCH_COUNT = 1;
19 | internal const int WARMUP_COUNT = 0;
20 | internal const int TARGET_COUNT = 1;
21 |
22 | private static void Main(string[] args)
23 | {
24 | var path = "../../../../BenchmarkResults";
25 | path = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, path));
26 | Directory.CreateDirectory(path);
27 | Environment.CurrentDirectory = path;
28 | Console.WriteLine("===========================");
29 | Console.WriteLine("===========================");
30 | Console.WriteLine($"Output Path: {path}");
31 | Console.WriteLine("===========================");
32 | Console.WriteLine("===========================");
33 | BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly)?.RunAll(new Config());
34 | }
35 | }
36 |
37 | public class Config : ManualConfig
38 | {
39 | public Config()
40 | {
41 | this.AddColumnProvider(DefaultColumnProviders.Instance!);
42 | this.AddExporter(
43 | MarkdownExporter.GitHub!
44 | , CsvMeasurementsExporter.Default!
45 | , RPlotExporter.Default!
46 | );
47 | this.AddDiagnoser(MemoryDiagnoser.Default!);
48 | this.AddLogger(ConsoleLogger.Default!);
49 | this.AddJob(
50 | Job.Dry!
51 | .WithWarmupCount(Program.WARMUP_COUNT)!
52 | .WithLaunchCount(Program.LAUNCH_COUNT)!
53 | .WithRuntime(CoreRuntime.Core60!)!
54 | .WithRuntime(CoreRuntime.Core50!)!
55 | .WithRuntime(CoreRuntime.Core30!)!
56 | .WithRuntime(ClrRuntime.Net48!)!
57 | );
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/docs/benchmarks.md:
--------------------------------------------------------------------------------
1 |
2 | ## Benchmarks
3 |
4 | These benchmarks compare the performance of this library against [IPNetwork2](https://github.com/lduchosal/ipnetwork),
5 | using the benchmark library [BenchmarkDotNet](https://benchmarkdotnet.org/articles/overview.html).
6 |
7 | *Note:* It is not an error when the NetworkPrimitives benchmarks show `-` in the `Gen0` and `Allocated` columns.
8 | This means that there were no allocations. (or at least, below whatever reporting thresholds BenchmarkDotNet may have...)
9 |
10 | To run your own benchmarks, use [NetworkPrimitives.Benchmarks.csproj](../benchmarks/NetworkPrimitives.Benchmarks.csproj)
11 |
12 | ### Parsing 100 IPv4 Addresses
13 |
14 | ```c#
15 | public void NetworkPrimitives()
16 | {
17 | foreach (var address in TestData.RandomIpAddresses)
18 | {
19 | _ = NetworkPrimitives.Ipv4.Ipv4Address.Parse(address);
20 | }
21 | }
22 |
23 | public void DotNet()
24 | {
25 | foreach (var address in TestData.RandomIpAddresses)
26 | {
27 | _ = System.Net.IPAddress.Parse(address);
28 | }
29 | }
30 |
31 | public void IpNetwork2()
32 | {
33 | foreach (var address in TestData.RandomIpAddresses)
34 | {
35 | _ = System.Net.IPNetwork.Parse(address);
36 | }
37 | }
38 | ```
39 |
40 |
41 | | Method | Mean | Error | StdDev | Gen 0 | Allocated |
42 | |------------------ |-----------:|----------:|-----------:|--------:|----------:|
43 | | NetworkPrimitives | 8.450 μs | 0.2642 μs | 0.7052 μs | - | - |
44 | | DotNet | 6.634 μs | 0.1577 μs | 0.4290 μs | 0.9537 | 4,000 B |
45 | | IpNetwork2 | 207.892 μs | 4.8725 μs | 13.5017 μs | 34.6680 | 145,053 B |
46 |
47 |
48 | ### Iterating 1 subnet containing 256 IPv4 addresses
49 |
50 | ```c#
51 | public void NetworkPrimitives()
52 | {
53 | var subnet = Ipv4Subnet.Parse("10.0.0.0/24");
54 | foreach (var address in subnet.GetAllAddresses())
55 | {
56 |
57 | }
58 | }
59 |
60 | public void IpNetwork2()
61 | {
62 | var subnet = System.Net.IPNetwork.Parse("10.0.0.0/24");
63 | foreach (var address in subnet.ListIPAddress())
64 | {
65 |
66 | }
67 | }
68 | ```
69 |
70 | | Method | Mean | Error | StdDev | Median | Gen 0 | Allocated |
71 | |------------------ |-------------:|------------:|-------------:|-------------:|---------:|----------:|
72 | | NetworkPrimitives | 970.7 ns | 28.79 ns | 75.84 ns | 932.9 ns | - | - |
73 | | IpNetwork2 | 450,398.3 ns | 5,960.03 ns | 15,908.54 ns | 447,500.9 ns | 134.7656 | 564,865 B |
74 |
75 |
--------------------------------------------------------------------------------
/docs/json-converters.md:
--------------------------------------------------------------------------------
1 | [Back to readme.md](../readme.md)
2 |
3 | ## NetworkPrimitives.JsonConverters
4 |
5 | The nuget package `NetworkPrimitives.JsonConverters` contains some basic `System.Test.Json` converters for the `NetworkPrimitive` types.
6 |
7 | **[Download from nuget.org](https://www.nuget.org/packages/NetworkPrimitives.JsonConverters)**
8 |
9 | [](https://www.nuget.org/packages/NetworkPrimitives.JsonConverters)
10 | [](https://www.nuget.org/packages/NetworkPrimitives.JsonConverters)
11 |
12 | ### Usage Instructions
13 |
14 | ```c#
15 | var jsonSerializerOptions = new JsonSerializerOptions();
16 |
17 | // Option 1: Add both IPv4 and IPv6 converters
18 | jsonSerializerOptions = jsonSerializerOptions.AddNetworkPrimitivesConverters();
19 |
20 | // Option 2: Add only IPv4 converters
21 | jsonSerializerOptions = jsonSerializerOptions.AddIpv4Converters();
22 |
23 | // Option 3: Add only IPv6 converters
24 | jsonSerializerOptions = jsonSerializerOptions.AddIpv6Converters();
25 | ```
--------------------------------------------------------------------------------
/docs/performance.md:
--------------------------------------------------------------------------------
1 | # Performance Techniques
2 |
3 | `NetworkPrimitives` uses the following techniques to maintain high performance:
4 |
5 | 1. Reduce allocations whenever possible.
6 | 2. When appropriate, use `struct` rather than `class`
7 | 3. Provide `ReadOnlySpan` overloads for parsing for all types.
8 | 4. For types that can be serialized to binary, provide `Span` overloads.
9 | 5. Unless absolutely necessary, use immutable types only.
10 | 6. When possible, provide allocation-free enumerators.
11 |
12 | ## Enumerators
13 |
14 | One often overlooked allocation that occurs are enumerators. A type that implements
15 | `IEnumerable` has a method `GetEnumerator()` that will instantiate an `IEnumerator`.
16 |
17 | Additionally, anytime a `struct` is converted to an `interface`, it is boxed - which
18 | creates an allocation.
19 |
20 | C# uses duck-typing for `foreach` loops. `NetworkPrimitives` leverages this to
21 | allow you to iterate over collections without allocating an enumerator.
22 |
23 | For example, this code does not allocate an enumerator:
24 |
25 | ```c#
26 | Ipv4AddressRange range = subnet.GetAllAddresses();
27 | foreach (var address in range)
28 | {
29 | Console.WriteLine(address.ToString());
30 | }
31 | ```
32 |
33 | This is because the `Ipv4AddressRange` type has a `GetEnumerator` method that
34 | returns an instance of the `Ipv4AddressEnumerator` `ref struct` - no allocation.
35 | The `Ipv4AddressEnumerator` type has a `MoveNext()` method and a `Current`
36 | property that returns an `Ipv4Address` `struct` - no allocation.
37 |
38 |
39 | If you prefer to have a `class` enumerator, you can do one of two things.
40 |
41 | First option, is to call the `ToEnumerable()` method on an `Ipv4AddressRange`.
42 | Note, that this will cause one allocation.
43 |
44 | ```c#
45 | Ipv4AddressRange range = subnet.GetAllAddresses();
46 | foreach (var address in range.ToEnumerable())
47 | {
48 | Console.WriteLine(address.ToString());
49 | }
50 | ```
51 |
52 | Your other option is to simply pass the `Ipv4AddressRange` to a method that
53 | needs an `IEnumerable`.
54 |
55 |
56 | ```c#
57 |
58 | private static void Main()
59 | {
60 | subnet = Ipv4Subnet.Parse("10.0.0.0/24");
61 | Ipv4AddressRange range = subnet.GetAllAddresses();
62 | WriteRange(range);
63 | }
64 |
65 | private static void WriteRange(IEnumerable range)
66 | {
67 | foreach (var address in range)
68 | {
69 | Console.WriteLine(address.ToString());
70 | }
71 | }
72 | ```
73 |
74 |
75 |
76 | ## Framework Versions
77 |
78 | ### .NET Core 3.0+ or .NET Standard 2.1
79 |
80 | Using either .NET Core 3.0 (or higher) or .NET Standard 2.1 will give the
81 | best performance when using the `NetworkPrimitives` library.
82 |
83 | ### .NET Standard 2.0
84 |
85 | If you're stuck on .NET Framework, a .NET Core version prior to 3.0,
86 | or some other version of .NET, that doesn't mean that `NetworkPrimitives`
87 | is not a high performance library. It just will not perform as well as
88 | on .NET Core 3.0 or higher.
89 |
90 | Some methods are not available in .NET Standard, which prevents
91 | `NetworkPrimitives` from gaining the full performance benefits.
92 |
93 | For example:
94 |
95 | - Constructor `System.String(ReadOnlySpan)` ([docs](https://docs.microsoft.com/en-us/dotnet/api/system.string.-ctor?#System_String__ctor_System_ReadOnlySpan_System_Char__))
96 | without this constructor, we must instantiate a `char[]` to create the `string`
97 | - Constructor `System.Net.IPAddress(ReadOnlySpan)` ([docs](https://docs.microsoft.com/en-us/dotnet/api/system.net.ipaddress.-ctor?#System_Net_IPAddress__ctor_System_ReadOnlySpan_System_Byte__))
98 | without this constructor, we must instantiate a `byte[]` to create the `IPAddress`
99 | - Method `System.Net.IPAddress.TryWriteBytes` ([docs](https://docs.microsoft.com/en-us/dotnet/api/system.net.ipaddress.trywritebytes))
100 | without this method, we must call `IPAddress.GetAddressBytes()`, which instantiates a `byte[]`.
--------------------------------------------------------------------------------
/docs/range-lists.md:
--------------------------------------------------------------------------------
1 | [Back to readme.md](../readme.md)
2 |
3 | ## Address Ranges / Lists
4 |
5 | **Parse/enumerate a single address range:**
6 |
7 | ```c#
8 | var range = Ipv4AddressRange.Parse("10.0.0.0-15");
9 | foreach(var address in range)
10 | {
11 | Console.WriteLine(address);
12 | }
13 | ```
14 |
15 | **Parse a range list**
16 |
17 | ```c#
18 | var rangeText = @"
19 | 10.0.0.0-10
20 | 10.0.1.0/29
21 | 10.1.0.40
22 | ";
23 | var rangeList = Ipv4AddressRangeList.Parse(rangeText);
24 | ```
25 |
26 | **Enumerate a range list**
27 |
28 | Option 1: Nested enumeration
29 |
30 | ```c#
31 | foreach (var range in rangeList)
32 | {
33 | Console.WriteLine(range);
34 | foreach (var address in range)
35 | {
36 | Console.WriteLine(address);
37 | }
38 | }
39 | ```
40 |
41 | Option 2: Enumerate all addresses
42 |
43 | ```c#
44 | foreach (var address in rangeList.GetAllAddresses())
45 | {
46 | Console.WriteLine(address);
47 | }
48 | ```
--------------------------------------------------------------------------------
/docs/subnetting.md:
--------------------------------------------------------------------------------
1 | [Back to readme.md](../readme.md)
2 |
3 | ## Get basic subnet info
4 |
5 | ```c#
6 | var subnet = Ipv4Subnet.Parse("10.0.0.0/29");
7 | Console.WriteLine($" Network Address: {subnet.NetworkAddress}"); // 10.0.0.0
8 | Console.WriteLine($" First Usable: {subnet.FirstUsable}"); // 10.0.0.1
9 | Console.WriteLine($" Last Usable: {subnet.LastUsable}"); // 10.0.0.6
10 | Console.WriteLine($"Broadcast Address: {subnet.BroadcastAddress}"); // 10.0.0.7
11 | Console.WriteLine($" Total Hosts: {subnet.TotalHosts}"); // 8
12 | Console.WriteLine($" Usable Hosts: {subnet.UsableHosts}"); // 6
13 | foreach (var address in subnet.GetUsableAddresses())
14 | {
15 | Console.WriteLine(address);
16 | }
17 | ```
18 |
19 | ## Subnet Operations
20 |
21 |
22 | **Attempt to split a subnet in half:**
23 |
24 | ```c#
25 | var slash24 = Ipv4Subnet.Parse("10.0.0.0/24");
26 | Console.WriteLine($"Splitting subnet {slash24}"); // 10.0.0.0/24
27 | var success = slash24.TrySplit(out var lowHalf, out var highHalf);
28 | Console.WriteLine($"Success: {success}"); // true
29 | Console.WriteLine($"Low subnet: {lowHalf}"); // 10.0.0.0/25
30 | Console.WriteLine($"High subnet: {highHalf}"); // 10.0.0.128/25
31 | ```
32 |
33 | **Supernet two or more subnets:**
34 |
35 | ```c#
36 | var subnetA = Ipv4Subnet.Parse("10.0.0.0/24");
37 | var subnetB = Ipv4Subnet.Parse("10.0.1.0/24");
38 | var subnetC = Ipv4Subnet.Parse("10.0.3.0/24");
39 | var subnetD = Ipv4Subnet.Parse("10.0.255.0/24");
40 | var supernet = Ipv4Subnet.GetContainingSupernet(subnetA, subnetB, subnetC, subnetD);
41 | Console.WriteLine($"Supernet: {supernet}"); // 10.0.0.0/16
42 | ```
43 |
44 | **Contains:**
45 |
46 | ```c#
47 | var subnet = Ipv4Subnet.Parse("10.0.0.0/24");
48 |
49 | Console.WriteLine(subnet.Contains(Ipv4Address.Parse("10.50.0.0"))); // False
50 | Console.WriteLine(subnet.Contains(Ipv4Address.Parse("10.0.0.50"))); // True
51 | Console.WriteLine(subnet.Contains(Ipv4Subnet.Parse("10.0.0.0/24"))); // True
52 | Console.WriteLine(subnet.Contains(Ipv4Subnet.Parse("10.0.0.128/26"))); // True
53 | Console.WriteLine(subnet.Contains(Ipv4Subnet.Parse("8.0.0.0/8"))); // False
54 | ```
55 |
56 | ## Address Listing
57 |
58 | **All Addresses**
59 |
60 | Includes network/broadcast addresses
61 |
62 | ```c#
63 | var subnet = Ipv4Subnet.Parse("10.0.0.0/24");
64 | var addresses = subnet.GetAllAddresses();
65 | foreach(var address in addresses)
66 | {
67 | }
68 | ```
69 |
70 | **Usable Addresses**
71 |
72 | Does not include network/broadcast addresses
73 |
74 | (_Note:_ Respects [RFC 3021](https://datatracker.ietf.org/doc/html/rfc3021))
75 |
76 | ```c#
77 | var subnet = Ipv4Subnet.Parse("10.0.0.0/24");
78 | var addresses = subnet.GetUsableAddresses();
79 | foreach(var address in addresses)
80 | {
81 | }
82 | ```
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "5.0",
4 | "rollForward": "latestMajor",
5 | "allowPrerelease": false
6 | }
7 | }
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # NetworkPrimitives
2 |
3 | Lightweight library for working with various networking objects
4 |
5 | ### Basic usage
6 |
7 | ```c#
8 | var subnet = Ipv4Subnet.Parse("10.0.0.0/29");
9 | Console.WriteLine($" Network Address: {subnet.NetworkAddress}"); // 10.0.0.0
10 | Console.WriteLine($" First Usable: {subnet.FirstUsable}"); // 10.0.0.1
11 | Console.WriteLine($" Last Usable: {subnet.LastUsable}"); // 10.0.0.6
12 | Console.WriteLine($"Broadcast Address: {subnet.BroadcastAddress}"); // 10.0.0.7
13 | Console.WriteLine($" Total Hosts: {subnet.TotalHosts}"); // 8
14 | Console.WriteLine($" Usable Hosts: {subnet.UsableHosts}"); // 6
15 | foreach (var address in subnet.GetUsableAddresses())
16 | {
17 | Console.WriteLine(address);
18 | }
19 | ```
20 |
21 | ### Download
22 |
23 | The core `NetworkPrimitives` package contains the types themselves.
24 |
25 | [](https://www.nuget.org/packages/NetworkPrimitives)
26 | [](https://www.nuget.org/packages/NetworkPrimitives)
27 |
28 | The package `NetworkPrimitives.JsonConverters` contains converters for `System.Text.Json`
29 |
30 | [](https://www.nuget.org/packages/NetworkPrimitives.JsonConverters)
31 | [](https://www.nuget.org/packages/NetworkPrimitives.JsonConverters)
32 |
33 |
34 | ### Performance
35 |
36 | Overall goals are:
37 |
38 | 1. Fast parsing times
39 | 2. Low (preferably zero) allocations
40 | 3. Overall efficiency
41 |
42 | ### Detailed usage
43 |
44 | - [Benchmarks](docs/benchmarks.md)
45 | - [Subnetting](docs/subnetting.md)
46 | - [Address Ranges / Lists](docs/range-lists.md)
47 | - [Performance](docs/performance.md)
48 | - [Json Converters](docs/json-converters.md)
--------------------------------------------------------------------------------
/samples/Demo/Demo.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/samples/Demo/Demo.Framework.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net48
6 | enable
7 | 9
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/samples/Demo/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | obj\$(MSBuildProjectName)\
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/samples/Demo/Program.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | using System;
5 | using System.Collections;
6 | using System.Collections.Generic;
7 | using NetworkPrimitives.Ipv4;
8 |
9 |
10 | namespace Demo
11 | {
12 | internal static class Program
13 | {
14 | private static void Main()
15 | {
16 | var subnet = Ipv4Subnet.Parse("10.0.0.0/28");
17 | Console.WriteLine("Option 1: ref struct");
18 | // Option 1: ref struct
19 | foreach (var address in subnet.GetAllAddresses())
20 | {
21 | Console.WriteLine($" {address.ToString()}");
22 | }
23 | Console.WriteLine();
24 | Console.WriteLine("Option 2: ToEnumerable() method");
25 | foreach (var address in subnet.GetUsableAddresses().ToEnumerable())
26 | {
27 | Console.WriteLine($" {address.ToString()}");
28 | }
29 | Console.WriteLine();
30 | Console.WriteLine("Option 3: Explicit implementation of IEnumerable");
31 | WriteRange(subnet.GetUsableAddresses());
32 | }
33 |
34 | private static void WriteRange(IEnumerable range)
35 | {
36 | foreach (var address in range)
37 | {
38 | Console.WriteLine($" {address.ToString()}");
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/samples/Demo/TestData.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace Demo
6 | {
7 | public static class TestData
8 | {
9 | public static readonly string[] RandomIpAddresses = @"
10 | 110.16.23.222
11 | 68.231.174.107
12 | 3.82.172.189
13 | 243.32.132.113
14 | 225.12.72.166
15 | 13.127.252.250
16 | 147.161.211.134
17 | 82.156.232.8
18 | 58.60.52.142
19 | 183.226.206.20
20 | 63.132.185.33
21 | 143.25.217.18
22 | 71.71.26.123
23 | 247.247.163.201
24 | 67.88.214.99
25 | 78.151.142.0
26 | 134.80.69.85
27 | 155.11.248.103
28 | 236.6.93.134
29 | 229.0.191.199
30 | 124.10.48.201
31 | 91.184.110.3
32 | 182.86.82.16
33 | 22.139.1.247
34 | 109.57.80.80
35 | 165.228.84.20
36 | 145.124.181.137
37 | 85.157.141.88
38 | 189.136.64.168
39 | 158.226.81.64
40 | 66.215.77.47
41 | 213.73.79.44
42 | 156.212.10.4
43 | 118.41.163.76
44 | 238.129.60.191
45 | 189.75.132.142
46 | 94.184.158.100
47 | 100.169.150.44
48 | 201.169.186.149
49 | 70.91.111.41
50 | 157.201.209.234
51 | 217.213.64.28
52 | 35.128.131.116
53 | 196.220.169.212
54 | 0.201.76.200
55 | 241.203.244.189
56 | 243.152.69.175
57 | 115.174.109.202
58 | 77.142.18.206
59 | 69.119.230.16
60 | 19.130.197.235
61 | 11.255.204.97
62 | 247.244.180.141
63 | 179.179.224.34
64 | 222.208.30.31
65 | 91.249.41.199
66 | 170.156.185.230
67 | 28.208.57.185
68 | 101.29.242.129
69 | 206.15.21.85
70 | 123.129.145.146
71 | 228.96.110.131
72 | 223.29.148.68
73 | 85.107.12.36
74 | 201.231.93.112
75 | 23.180.49.15
76 | 253.120.213.44
77 | 35.52.211.236
78 | 13.238.58.205
79 | 6.245.123.129
80 | 176.39.70.178
81 | 111.128.165.209
82 | 28.210.69.156
83 | 149.56.223.117
84 | 149.59.117.225
85 | 230.193.219.196
86 | 198.219.76.26
87 | 229.115.52.222
88 | 69.239.133.185
89 | 148.122.122.200
90 | 226.59.238.133
91 | 85.25.206.138
92 | 178.41.253.173
93 | 237.30.216.96
94 | 105.89.205.123
95 | 157.76.51.182
96 | 88.250.141.27
97 | 113.197.178.251
98 | 203.13.101.227
99 | 26.111.49.164
100 | 84.237.14.222
101 | 134.209.92.181
102 | 116.198.208.91
103 | 237.170.209.135
104 | 205.197.110.251
105 | 10.85.28.143
106 | 80.221.147.135
107 | 198.8.241.68
108 | 215.87.21.24
109 | 68.127.97.81
110 | ".Split(new[]{Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries);
111 |
112 | }
113 | }
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | net6.0;net5.0;netstandard2.0
6 | 1.1.0
7 | network; networking; subnet; ip-address; ipaddress; ip address; mac address; networking.primitives; network.primitives; primitives;
8 | Lightweight package for working with networking types such as IPv4 addresses, IPv6 addresses, ranges, and subnets.
9 | True
10 | $(WarningsNotAsErrors);CS1591
11 | true
12 |
13 |
14 |
15 |
16 | true
17 | true
18 | true
19 | snupkg
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | all
29 | runtime; build; native; contentfiles; analyzers; buildtransitive
30 |
31 |
32 |
33 |
34 |
35 |
45 |
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4AddressJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4AddressJsonConverter : JsonConverter
11 | {
12 | private Ipv4AddressJsonConverter() { }
13 | public static readonly Ipv4AddressJsonConverter Instance = new ();
14 | public override Ipv4Address Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4Address.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4Address value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4AddressRangeJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4AddressRangeJsonConverter : JsonConverter
11 | {
12 | private Ipv4AddressRangeJsonConverter() { }
13 | public static readonly Ipv4AddressRangeJsonConverter Instance = new ();
14 | public override Ipv4AddressRange Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4AddressRange.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4AddressRange value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4AddressRangeListJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4AddressRangeListJsonConverter : JsonConverter
11 | {
12 | private Ipv4AddressRangeListJsonConverter() { }
13 | public static readonly Ipv4AddressRangeListJsonConverter Instance = new ();
14 | public override Ipv4AddressRangeList Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4AddressRangeList.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4AddressRangeList value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4CidrJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4CidrJsonConverter : JsonConverter
11 | {
12 | private Ipv4CidrJsonConverter() { }
13 | public static readonly Ipv4CidrJsonConverter Instance = new ();
14 | public override Ipv4Cidr Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4Cidr.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4Cidr value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4NetworkMatchJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4NetworkMatchJsonConverter : JsonConverter
11 | {
12 | private Ipv4NetworkMatchJsonConverter() { }
13 | public static readonly Ipv4NetworkMatchJsonConverter Instance = new ();
14 | public override Ipv4NetworkMatch Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4NetworkMatch.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4NetworkMatch value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4SubnetJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4SubnetJsonConverter : JsonConverter
11 | {
12 | private Ipv4SubnetJsonConverter() { }
13 | public static readonly Ipv4SubnetJsonConverter Instance = new ();
14 | public override Ipv4Subnet Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4Subnet.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4Subnet value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4SubnetMaskJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4SubnetMaskJsonConverter : JsonConverter
11 | {
12 | private Ipv4SubnetMaskJsonConverter() { }
13 | public static readonly Ipv4SubnetMaskJsonConverter Instance = new ();
14 | public override Ipv4SubnetMask Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4SubnetMask.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4SubnetMask value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv4/Ipv4WildcardMaskJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv4;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv4
9 | {
10 | public class Ipv4WildcardMaskJsonConverter : JsonConverter
11 | {
12 | private Ipv4WildcardMaskJsonConverter() { }
13 | public static readonly Ipv4WildcardMaskJsonConverter Instance = new ();
14 | public override Ipv4WildcardMask Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv4WildcardMask.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv4WildcardMask value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv6/Ipv6AddressJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv6;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv6
9 | {
10 | public class Ipv6AddressJsonConverter : JsonConverter
11 | {
12 | private Ipv6AddressJsonConverter() { }
13 | public static readonly Ipv6AddressJsonConverter Instance = new ();
14 | public override Ipv6Address Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv6Address.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv6Address value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv6/Ipv6CidrJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv6;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv6
9 | {
10 | public class Ipv6CidrJsonConverter : JsonConverter
11 | {
12 | private Ipv6CidrJsonConverter() { }
13 | public static readonly Ipv6CidrJsonConverter Instance = new ();
14 | public override Ipv6Cidr Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv6Cidr.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv6Cidr value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv6/Ipv6SubnetJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv6;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv6
9 | {
10 | public class Ipv6SubnetJsonConverter : JsonConverter
11 | {
12 | private Ipv6SubnetJsonConverter() { }
13 | public static readonly Ipv6SubnetJsonConverter Instance = new ();
14 | public override Ipv6Subnet Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv6Subnet.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv6Subnet value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/Ipv6/Ipv6SubnetMaskJsonConverter.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using NetworkPrimitives.Ipv6;
7 |
8 | namespace NetworkPrimitives.JsonConverters.Ipv6
9 | {
10 | public class Ipv6SubnetMaskJsonConverter : JsonConverter
11 | {
12 | private Ipv6SubnetMaskJsonConverter() { }
13 | public static readonly Ipv6SubnetMaskJsonConverter Instance = new ();
14 | public override Ipv6SubnetMask Read(
15 | ref Utf8JsonReader reader,
16 | Type typeToConvert,
17 | JsonSerializerOptions options
18 | ) => Ipv6SubnetMask.Parse(reader.GetString());
19 |
20 | public override void Write(
21 | Utf8JsonWriter writer,
22 | Ipv6SubnetMask value,
23 | JsonSerializerOptions options
24 | ) => writer.WriteStringValue(value.ToString());
25 | }
26 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/JsonSerializerExtensions.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System.Text.Json;
4 | using NetworkPrimitives.JsonConverters.Ipv4;
5 | using NetworkPrimitives.JsonConverters.Ipv6;
6 |
7 | namespace NetworkPrimitives.JsonConverters
8 | {
9 | public static class JsonSerializerExtensions
10 | {
11 | public static JsonSerializerOptions AddNetworkPrimitivesConverters(
12 | this JsonSerializerOptions options
13 | )
14 | {
15 | return options
16 | .AddIpv4Converters()
17 | .AddIpv6Converters();
18 | }
19 |
20 |
21 | public static JsonSerializerOptions AddIpv4Converters(
22 | this JsonSerializerOptions options
23 | )
24 | {
25 | options.Converters.Add(Ipv4AddressJsonConverter.Instance);
26 | options.Converters.Add(Ipv4AddressRangeJsonConverter.Instance);
27 | options.Converters.Add(Ipv4AddressRangeListJsonConverter.Instance);
28 | options.Converters.Add(Ipv4CidrJsonConverter.Instance);
29 | options.Converters.Add(Ipv4NetworkMatchJsonConverter.Instance);
30 | options.Converters.Add(Ipv4SubnetJsonConverter.Instance);
31 | options.Converters.Add(Ipv4SubnetMaskJsonConverter.Instance);
32 | options.Converters.Add(Ipv4WildcardMaskJsonConverter.Instance);
33 | return options;
34 | }
35 |
36 | public static JsonSerializerOptions AddIpv6Converters(
37 | this JsonSerializerOptions options
38 | )
39 | {
40 | options.Converters.Add(Ipv6AddressJsonConverter.Instance);
41 | options.Converters.Add(Ipv6CidrJsonConverter.Instance);
42 | options.Converters.Add(Ipv6SubnetJsonConverter.Instance);
43 | options.Converters.Add(Ipv6SubnetMaskJsonConverter.Instance);
44 | return options;
45 | }
46 |
47 |
48 | }
49 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives.JsonConverters/NetworkPrimitives.JsonConverters.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net5.0;netstandard2.0
5 | enable
6 | enable
7 |
8 |
9 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/NetworkPrimitives/BitOperations.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | #if !NETCOREAPP3_0_OR_GREATER
4 |
5 | // ReSharper disable once CheckNamespace
6 | namespace System.Numerics
7 | {
8 | internal static class BitOperations
9 | {
10 | public static int PopCount(uint value)
11 | {
12 | const uint c1 = 0x_55555555u;
13 | const uint c2 = 0x_33333333u;
14 | const uint c3 = 0x_0F0F0F0Fu;
15 | const uint c4 = 0x_01010101u;
16 |
17 | value -= (value >> 1) & c1;
18 | value = (value & c2) + ((value >> 2) & c2);
19 | value = (((value + (value >> 4)) & c3) * c4) >> 24;
20 |
21 | return (int)value;
22 | }
23 |
24 | }
25 | }
26 | #endif
27 |
28 | namespace NetworkPrimitives
29 | {
30 | internal static class BitOperationsEx
31 | {
32 | public static uint RoundUpToPowerOf2(uint value)
33 | {
34 | #if NET6_0_OR_GREATER
35 | return System.Numerics.BitOperations.RoundUpToPowerOf2(value);
36 | #else
37 | --value;
38 | value |= value >> 1;
39 | value |= value >> 2;
40 | value |= value >> 4;
41 | value |= value >> 8;
42 | value |= value >> 16;
43 | return value + 1;
44 | #endif
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/BuildInformationAttribute.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives
6 | {
7 | ///
8 | /// Provides build information
9 | ///
10 | [AttributeUsage(AttributeTargets.Assembly)]
11 | public class BuildInformationAttribute : Attribute
12 | {
13 | ///
14 | /// The git commit hash
15 | ///
16 | public string? CommitHash { get; }
17 | ///
18 | /// The timestamp when this build was built
19 | ///
20 | public string? BuildTimestamp { get; }
21 | ///
22 | /// The unique build number.
23 | ///
24 | public string? BuildNumber { get; }
25 |
26 | ///
27 | /// Branch name this build was created from
28 | ///
29 | public string? BranchName { get; }
30 |
31 | ///
32 | /// Create an instance of
33 | ///
34 | /// The git commit hash
35 | /// The timestamp when this build was built
36 | /// The unique build number.
37 | /// Branch name this build was created from
38 | public BuildInformationAttribute(
39 | string? commitHash = null,
40 | string? buildTimestamp = null,
41 | string? buildNumber = null,
42 | string? branchName = null
43 | )
44 | {
45 | this.CommitHash = commitHash;
46 | this.BuildTimestamp = buildTimestamp;
47 | this.BuildNumber = buildNumber;
48 | this.BranchName = branchName;
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/ExcludeFromCodeCoverageAttribute.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives
6 | {
7 | [AttributeUsage(
8 | validOn: AttributeTargets.Assembly
9 | | AttributeTargets.Class
10 | | AttributeTargets.Constructor
11 | | AttributeTargets.Event
12 | | AttributeTargets.Method
13 | | AttributeTargets.Property
14 | | AttributeTargets.Struct,
15 | Inherited=false
16 | )]
17 | [ExcludeFromCodeCoverage("Internal")]
18 | internal sealed class ExcludeFromCodeCoverageAttribute : Attribute
19 | {
20 | public ExcludeFromCodeCoverageAttribute(string? reason = null) => Reason = reason;
21 | public string? Reason { get; set; }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/CharExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace NetworkPrimitives
2 | {
3 | [ExcludeFromCodeCoverage("Internal")]
4 | internal static class CharExtensions
5 | {
6 | public static bool IsHex(this char ch) => ch switch
7 | {
8 | >= '0' and <= '9' => true,
9 | >= 'a' and <= 'f' => true,
10 | >= 'A' and <= 'F' => true,
11 | _ => false,
12 | };
13 | }
14 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/IpAddressExtensions.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Net;
5 |
6 | namespace NetworkPrimitives
7 | {
8 | [ExcludeFromCodeCoverage("Internal")]
9 | internal static class IpAddressExtensions
10 | {
11 | #if !(NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER)
12 | public static bool TryWriteBytes(this IPAddress ipAddress, Span bytes, out int charsWritten)
13 | {
14 | charsWritten = default;
15 | var byteArray = ipAddress.GetAddressBytes(); // TODO: Is this the right way to do it?
16 | if (bytes.Length < byteArray.Length)
17 | return false;
18 | for (var i = 0; i < byteArray.Length; ++i)
19 | bytes[i] = byteArray[i];
20 | charsWritten = byteArray.Length;
21 | return true;
22 | }
23 | #endif
24 | }
25 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/NumericExtensions.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Buffers.Binary;
5 | using System.Numerics;
6 |
7 | namespace NetworkPrimitives
8 | {
9 | internal static class NumericExtensions
10 | {
11 | [ExcludeFromCodeCoverage("Internal")]
12 | public static uint PopCount(this uint v)
13 | {
14 | // Intentionally not using BitOperations.PopCount as its not CLS compliant.
15 | // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
16 | v = v - ((v >> 1) & 0x55555555);
17 | v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
18 | return (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
19 | }
20 |
21 | [ExcludeFromCodeCoverage("Internal")]
22 | public static byte[] ToBytesBigEndian(this uint value)
23 | {
24 | var bytes = new byte[4];
25 | value.TryWriteBigEndian(bytes, out _);
26 | return bytes;
27 | }
28 |
29 | [ExcludeFromCodeCoverage("Internal")]
30 | public static bool TryWriteBigEndian(this uint value, Span span, out int bytesWritten)
31 | {
32 | bytesWritten = default;
33 | return value.TryWriteBigEndian(ref span, ref bytesWritten);
34 | }
35 |
36 | [ExcludeFromCodeCoverage("Internal")]
37 | public static bool TryWriteBigEndian(this uint value, ref Span span, ref int bytesWritten)
38 | {
39 | if (!BinaryPrimitives.TryWriteUInt32BigEndian(span, value))
40 | return false;
41 | span = span[4..];
42 | bytesWritten += 4;
43 | return true;
44 | }
45 |
46 | [ExcludeFromCodeCoverage("Internal")]
47 | public static uint SwapEndianIfLittleEndian(this uint value)
48 | => BitConverter.IsLittleEndian ? value.SwapEndian() : value;
49 |
50 | public static uint SwapEndian(this uint value)
51 | {
52 | Span span = stackalloc byte[4];
53 | BinaryPrimitives.WriteUInt32BigEndian(span, value);
54 | return BinaryPrimitives.ReadUInt32LittleEndian(span);
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/QueueExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 |
5 | namespace NetworkPrimitives;
6 |
7 | internal static class QueueExtensions
8 | {
9 | #if !(NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_0_OR_GREATER)
10 | public static bool TryDequeue(
11 | this Queue queue,
12 | [NotNullWhen(true)] out T? item
13 | ) where T : notnull
14 | {
15 | item = default;
16 | if (queue.Count == 0)
17 | return false;
18 | try
19 | {
20 | item = queue.Dequeue();
21 | }
22 | catch
23 | {
24 | return false;
25 | }
26 | return item is not null;
27 | }
28 | #endif
29 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/SliceExtensions.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 |
6 | namespace NetworkPrimitives
7 | {
8 | [ExcludeFromCodeCoverage("Internal")]
9 | internal static class SliceExtensions
10 | {
11 | [return: NotNullIfNotNull("defaultValue")]
12 | public static TItem? SliceOrDefault(
13 | ref this TDerived span,
14 | TItem? defaultValue = default
15 | ) where TDerived : struct, ISlice
16 | {
17 | if (span.Length == 0)
18 | return defaultValue;
19 | var value = span[0];
20 | span = span[1..];
21 | return value;
22 | }
23 |
24 | public static bool TrySliceFirst(ref this TDerived span, ref int charsRead, TItem expected)
25 | where TDerived : struct, ISlice
26 | where TItem : struct
27 | => span.TrySliceFirst(ref charsRead, expected, out _);
28 |
29 | public static bool TrySliceFirst(ref this TDerived span, ref int charsRead, TItem expected, out TItem value)
30 | where TDerived : struct, ISlice
31 | where TItem : struct
32 | {
33 | if (!span.TrySliceFirst(expected, out value))
34 | return false;
35 | ++charsRead;
36 | return true;
37 | }
38 |
39 | public static bool TrySliceFirst(ref this TDerived span, ref int charsRead, out TItem value)
40 | where TDerived : struct, ISlice
41 | where TItem : struct
42 | {
43 | if (!span.TrySliceFirst(out value))
44 | return false;
45 | ++charsRead;
46 | return true;
47 | }
48 |
49 | public static bool TrySliceFirst(ref this TDerived span, TItem expected, out TItem value)
50 | where TDerived : struct, ISlice
51 | where TItem : struct
52 | {
53 | value = default;
54 | if (span.Length == 0)
55 | return false;
56 | if (!EqualityComparer.Default.Equals(span[0], expected))
57 | return false;
58 | value = span[0];
59 | span = span[1..];
60 | return true;
61 | }
62 |
63 | public static bool TrySliceFirst(ref this TDerived span, out TItem value)
64 | where TDerived : struct, ISlice
65 | where TItem : struct
66 | {
67 | value = default;
68 | if (span.Length == 0)
69 | return false;
70 | value = span[0];
71 | span = span[1..];
72 | return true;
73 | }
74 |
75 | public static bool TrySliceLast(ref this TDerived span, out TItem value)
76 | where TDerived : struct, ISlice
77 | where TItem : struct
78 | {
79 | value = default;
80 | if (span.Length == 0)
81 | return false;
82 | value = span[^1];
83 | span = span[..^1];
84 | return true;
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/SpanExtensions.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Buffers.Binary;
5 | using System.Collections.Generic;
6 |
7 | namespace NetworkPrimitives
8 | {
9 | [ExcludeFromCodeCoverage("Internal")]
10 | internal static class SpanExtensions
11 | {
12 |
13 | public static bool EqualToArray(this Span span, T[] array, IEqualityComparer? comparer = null)
14 | {
15 | comparer ??= EqualityComparer.Default;
16 | if (span.Length != array.Length) return false;
17 | for (var i = 0; i < span.Length; ++i)
18 | {
19 | if (comparer.Equals(span[i], array[i]) is false)
20 | return false;
21 | }
22 | return true;
23 | }
24 |
25 | public static string CreateString(this ReadOnlySpan span)
26 | {
27 | #if NETSTANDARD2_1_OR_GREATER
28 | return new (span);
29 | #else
30 | return new(GetArray(span.ToArray()));
31 |
32 |
33 | static char[] GetArray(char[]? arr) => arr ?? Array.Empty();
34 | #endif
35 | }
36 | public static string CreateString(this Span span)
37 | {
38 | #if NETSTANDARD2_1_OR_GREATER
39 | return new (span);
40 | #else
41 | return new(GetArray(span.ToArray()));
42 |
43 |
44 | static char[] GetArray(char[]? arr) => arr ?? Array.Empty();
45 | #endif
46 | }
47 |
48 | public static bool TryReadUInt32BigEndian(ref this ReadOnlySpan span, out uint result)
49 | {
50 | var bytesRead = 0;
51 | return span.TryReadUInt32BigEndian(ref bytesRead, out result);
52 | }
53 |
54 | public static bool TryReadUInt32BigEndian(ref this ReadOnlySpan span, ref int bytesRead, out uint result)
55 | {
56 | if (!BinaryPrimitives.TryReadUInt32BigEndian(span, out result)) return false;
57 | span = span[4..];
58 | bytesRead += 4;
59 | return true;
60 | }
61 |
62 | public static bool TryWrite(ref this Span span, T item, ref int charsWritten)
63 | {
64 | if (span.Length == 0)
65 | return false;
66 | span[0] = item;
67 | span = span[1..];
68 | ++charsWritten;
69 | return true;
70 | }
71 |
72 | public static bool TryWrite(ref this Span span, T item)
73 | {
74 | var written = 0;
75 | return span.TryWrite(item, ref written);
76 | }
77 |
78 | public static bool TryConsumeWhiteSpace(ref this ReadOnlySpan span, ref int charsRead)
79 | {
80 | var atLeastOne = false;
81 | while (!span.IsEmpty && char.IsWhiteSpace(span[0]))
82 | {
83 | span = span[1..];
84 | ++charsRead;
85 | atLeastOne = true;
86 | }
87 | return atLeastOne;
88 | }
89 |
90 | public static string GetString(this ReadOnlySpan span)
91 | {
92 | #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
93 | return new string(span);
94 | #else
95 | return new string(span.ToArray() ?? Array.Empty()); // TODO: Is this the right way to do it?
96 | #endif
97 | }
98 |
99 | public static void SplitKeepSecond(ref this ReadOnlySpan first, int length, out ReadOnlySpan second)
100 | {
101 | second = first[..length];
102 | first = first[length..];
103 | }
104 |
105 | public static bool TrySliceFirst(ref this ReadOnlySpan span, out char value)
106 | {
107 | value = default;
108 | if (span.IsEmpty)
109 | return false;
110 | value = span[0];
111 | if (span.Length == 1)
112 | {
113 | // Workaround because for some reason on .NET Framework, it turns 1..
114 | // into Slice(1, -1) when resulting length will be zero?
115 | span = string.Empty.AsSpan();
116 | return true;
117 | }
118 | span = span[1..];
119 | return true;
120 | }
121 | }
122 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/StackExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace NetworkPrimitives;
5 |
6 | internal static class StackExtensions
7 | {
8 | #if !(NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_0_OR_GREATER)
9 | public static bool TryPop(
10 | this Stack stack,
11 | [NotNullWhen(true)] out T? item
12 | ) where T : notnull
13 | {
14 | item = default;
15 | if (stack.Count == 0)
16 | return false;
17 | try
18 | {
19 | item = stack.Pop();
20 | }
21 | catch
22 | {
23 | return false;
24 | }
25 | return item is not null;
26 | }
27 | #endif
28 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives
6 | {
7 | internal static class StringExtensions
8 | {
9 | public static ReadOnlySpan GetSpan(this string? text) => (text ?? string.Empty).AsSpan();
10 | }
11 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/INetworkPrimitive.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives
6 | {
7 | internal interface INetworkPrimitive : IEquatable, ITryFormat
8 | where TDerived : INetworkPrimitive
9 | {
10 | #if STATIC_ABSTRACT
11 | public static abstract TDerived Parse(string? text);
12 | public static abstract TDerived Parse(ReadOnlySpan text);
13 | public static abstract bool TryParse(string? text, out TDerived result);
14 | public static abstract bool TryParse(string? text, out int charsRead, out TDerived result);
15 | public static abstract bool TryParse(ReadOnlySpan text, out TDerived result);
16 | public static abstract bool TryParse(ReadOnlySpan text, out int charsRead, out TDerived result);
17 | #endif
18 | }
19 | internal interface IBinaryNetworkPrimitive : INetworkPrimitive
20 | where TDerived : IBinaryNetworkPrimitive
21 | {
22 | public bool TryWriteBytes(Span destination, out int bytesWritten);
23 | public byte[] GetBytes();
24 | }
25 |
26 | internal interface IFormattableNetworkPrimitive : INetworkPrimitive, ITryFormattable
27 | where TDerived : IFormattableNetworkPrimitive
28 | {
29 | #if STATIC_ABSTRACT
30 | public static abstract TDerived ParseExact(string? text, string format);
31 | public static abstract TDerived ParseExact(ReadOnlySpan text, string format);
32 | public static abstract bool TryParseExact(string? text, string format, out TDerived result);
33 | public static abstract bool TryParseExact(string? text, string format, out int charsRead, out TDerived result);
34 | public static abstract bool TryParseExact(ReadOnlySpan text, string format, out TDerived result);
35 | public static abstract bool TryParseExact(ReadOnlySpan text, string format, out int charsRead, out TDerived result);
36 | #endif
37 | }
38 | internal interface IFormattableBinaryNetworkPrimitive : IBinaryNetworkPrimitive, ITryFormattable
39 | where TDerived : IFormattableBinaryNetworkPrimitive
40 | {
41 |
42 | }
43 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/ISlice.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives
6 | {
7 | internal interface ISlice
8 | where TDerived : ISlice
9 | {
10 | public TItem this[int index] { get; }
11 | public int Length { get; }
12 | public TDerived Slice(int start, int length);
13 | }
14 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/ITryFormat.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives
6 | {
7 | internal interface ITryFormat
8 | {
9 | public int MaximumLengthRequired { get; }
10 | public bool TryFormat(Span destination, out int charsWritten);
11 | }
12 |
13 | internal interface ITryFormattable : ITryFormat, IFormattable
14 | {
15 | public bool TryFormat(Span destination, out int charsWritten, string? format, IFormatProvider? formatProvider);
16 | }
17 |
18 |
19 | [ExcludeFromCodeCoverage("Internal")]
20 | internal static class TryFormatExtensions
21 | {
22 | private const int MAXIMUM_STACKALLOC_LENGTH = 256;
23 | internal static string GetString(
24 | this T tryFormat
25 | ) where T : ITryFormat
26 | {
27 | var charsRequired = tryFormat.MaximumLengthRequired;
28 | var chars = charsRequired >= MAXIMUM_STACKALLOC_LENGTH
29 | ? new char[charsRequired]
30 | : stackalloc char[charsRequired];
31 | _ = tryFormat.TryFormat(chars, out var charsWritten);
32 | chars = chars[..charsWritten];
33 | return chars.CreateString();
34 | }
35 |
36 | internal static string GetString(
37 | this T tryFormat,
38 | string? format,
39 | IFormatProvider? formatProvider
40 | ) where T : ITryFormattable
41 | {
42 | var charsRequired = tryFormat.MaximumLengthRequired;
43 | var chars = charsRequired >= MAXIMUM_STACKALLOC_LENGTH
44 | ? new char[charsRequired]
45 | : stackalloc char[charsRequired];
46 | _ = tryFormat.TryFormat(chars, out var charsWritten, format, formatProvider);
47 | chars = chars[..charsWritten];
48 | return chars.CreateString();
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Utilities/Formatting.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives.Utilities
6 | {
7 | [ExcludeFromCodeCoverage("Internal")]
8 | internal static class Formatting
9 | {
10 | private const int MAX_BYTE_LENGTH = 3;
11 |
12 | public static bool TryFormatTo(
13 | this byte value,
14 | ref Span destination,
15 | ref int charsWritten
16 | )
17 | {
18 | Span source = stackalloc char[Formatting.MAX_BYTE_LENGTH];
19 | if (!value.TryFormat(source, out var length))
20 | return false;
21 | source = source[..length];
22 | source.CopyTo(destination);
23 | destination = destination[length..];
24 | charsWritten += length;
25 | return true;
26 | }
27 |
28 | #if !NETSTANDARD2_1_OR_GREATER
29 | public static bool TryFormat(
30 | this byte value,
31 | Span destination,
32 | out int charsWritten
33 | )
34 | {
35 | charsWritten = default;
36 | var length = value switch
37 | {
38 | >= 100 => 3,
39 | >= 10 => 2,
40 | _ => 1,
41 | };
42 | if (destination.Length < length)
43 | return false;
44 | Span chars = stackalloc char[length];
45 | for (var i = 0; i < chars.Length; ++i)
46 | {
47 | chars[i] = (char)(value % 10 + '0');
48 | value /= 10;
49 | }
50 | chars.Reverse();
51 | if (!chars.TryCopyTo(destination))
52 | return false;
53 | charsWritten = length;
54 | return true;
55 | }
56 | #endif
57 | }
58 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Utilities/ImmutableListWrapper.cs:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | #nullable enable
4 |
5 | using System;
6 | using System.Collections;
7 | using System.Collections.Generic;
8 | using System.Collections.Immutable;
9 |
10 | namespace NetworkPrimitives.Utilities
11 | {
12 | public abstract class ImmutableListWrapperWithBuilder
13 | where TDerived : ImmutableListWrapperWithBuilder
14 | where TBuilder : ImmutableListWrapperBuilder
15 | {
16 | internal ImmutableListWrapperWithBuilder()
17 | {
18 |
19 | }
20 | //public abstract TBuilder ToBuilder();
21 | }
22 | public abstract class ImmutableListWrapperWithBuilder
23 | where TDerived : ImmutableListWrapperWithBuilder
24 | where TBuilder : ImmutableListWrapperBuilder
25 | {
26 | internal ImmutableListWrapperWithBuilder()
27 | {
28 |
29 | }
30 | //public abstract TBuilder ToBuilder();
31 | }
32 |
33 | public abstract class ImmutableListWrapper : IReadOnlyList
34 | where TDerived : ImmutableListWrapper
35 | {
36 | protected abstract IEqualityComparer EqualityComparer { get; }
37 | protected abstract TDerived CreateInstance(ImmutableList items);
38 | protected ImmutableList Items { get; }
39 | internal ImmutableListWrapper(ImmutableList items) => this.Items = items;
40 | public int Count => Items.Count;
41 | public bool IsEmpty => Count == 0;
42 | public TItem this[int index] => this.Items[index];
43 | public TDerived Add(TItem item) => CreateInstance(this.Items.Add(item));
44 | public TDerived AddRange(IEnumerable items) => CreateInstance(this.Items.AddRange(items));
45 | public TDerived Clear() => CreateInstance(ImmutableList.Empty);
46 | public TItem? Find(Predicate predicate) => this.Items.Find(predicate);
47 | public TDerived FindAll(Predicate predicate) => CreateInstance(this.Items.FindAll(predicate));
48 | public TDerived Insert(int index, TItem item) => CreateInstance(this.Items.Insert(index, item));
49 | public int IndexOf(TItem item) => this.Items.IndexOf(item, 0, Count, EqualityComparer);
50 | public TDerived InsertRange(int index, IEnumerable items) => CreateInstance(this.Items.InsertRange(index, items));
51 | public ref readonly TItem ItemRef(int index) => ref this.Items.ItemRef(index);
52 | public int LastIndexOf(TItem item) => this.Items.LastIndexOf(item, 0, Count, EqualityComparer);
53 | public TDerived Remove(TItem item) => CreateInstance(this.Items.Remove(item, EqualityComparer));
54 | public TDerived GetRange(int index, int count) => CreateInstance(this.Items.GetRange(index, count));
55 | public TDerived RemoveAll(Predicate predicate) => CreateInstance(this.Items.RemoveAll(predicate));
56 | public TDerived RemoveRange(IEnumerable items) => CreateInstance(this.Items.RemoveRange(items, EqualityComparer));
57 | public TDerived RemoveAt(int index) => CreateInstance(this.Items.RemoveAt(index));
58 | public TDerived Replace(TItem oldValue, TItem newValue) => CreateInstance(this.Items.Replace(oldValue, newValue, EqualityComparer));
59 | public TDerived Reverse() => CreateInstance(this.Items.Reverse());
60 | public TDerived Reverse(int index, int count) => CreateInstance(this.Items.Reverse(index, count));
61 | public TDerived SetItem(int index, TItem item) => CreateInstance(this.Items.SetItem(index, item));
62 | protected virtual IEnumerator GetClassEnumerator() => Items.GetEnumerator();
63 | IEnumerator IEnumerable.GetEnumerator() => GetClassEnumerator();
64 | IEnumerator IEnumerable.GetEnumerator() => GetClassEnumerator();
65 | }
66 | }
67 |
68 |
69 | */
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Utilities/ImmutableListWrapperBuilder.cs:
--------------------------------------------------------------------------------
1 | /*#nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives.Utilities
6 | {
7 | public abstract class ImmutableListWrapperBuilder
8 | where TDerived : ImmutableListWrapperBuilder
9 | {
10 | internal ImmutableListWrapperBuilder()
11 | {
12 |
13 | }
14 | //public abstract TImmutable ToImmutable();
15 | }
16 | }*/
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Utilities/Parsing.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | #nullable enable
4 |
5 | namespace NetworkPrimitives.Utilities
6 | {
7 | [ExcludeFromCodeCoverage("Internal")]
8 | internal static class Parsing
9 | {
10 |
11 | public static bool TryParseHexUshort(
12 | ref this ReadOnlySpan text,
13 | ref int charsRead,
14 | out ushort value
15 | )
16 | {
17 | var length = GetHexChars(text);
18 | length = Math.Min(4, length);
19 | value = default;
20 | for (var i = 0; i < length; ++i)
21 | {
22 | var hex = text[0];
23 | text = text[1..];
24 | value <<= 4;
25 | value |= GetValue(hex);
26 | }
27 | charsRead += length;
28 | return length > 0;
29 |
30 | static ushort GetValue(char ch) => ch switch
31 | {
32 | >= '0' and <= '9' => (ushort)(ch - '0'),
33 | >= 'a' and <= 'f' => (ushort)(ch - 'a' + 10),
34 | >= 'A' and <= 'F' => (ushort)(ch - 'A' + 10),
35 | _ => 0,
36 | };
37 | }
38 | public static bool TryParseUInt64(
39 | ref this ReadOnlySpan text,
40 | ref int charsRead,
41 | out ulong value
42 | )
43 | {
44 | value = default;
45 | var length = Parsing.GetDigitLength(text);
46 | if (length == 0) return false;
47 | text.SplitKeepSecond(length, out var remainder);
48 | #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
49 | var success = ulong.TryParse(remainder, out value);
50 | #else
51 | var success = ulong.TryParse(remainder.GetString(), out value);
52 | #endif
53 | if (!success) return false;
54 | charsRead += length;
55 | return true;
56 | }
57 |
58 | public static bool TryParseByte(
59 | ref this ReadOnlySpan text,
60 | ref int charsRead,
61 | out byte value
62 | )
63 | {
64 | /*
65 | 25[0-5]
66 | 2[0-4][0-9]
67 | 1[0-9][0-9]
68 |
69 | [1-9][0-9]
70 |
71 | [0-9]
72 | */
73 | value = default;
74 | var digitLength = text.Length switch
75 | {
76 | 0 => default,
77 | 1 => GetByteDigitLength(text[0], default, default),
78 | 2 => GetByteDigitLength(text[0], text[1], default),
79 | _ => GetByteDigitLength(text[0], text[1], text[2]),
80 | };
81 | value = 0;
82 | for (var i = 0; i < digitLength; ++i)
83 | {
84 | value *= 10;
85 | value += (byte)(text[0] - '0');
86 | text = text[1..];
87 | ++charsRead;
88 | }
89 | return digitLength > 0;
90 |
91 | static int GetByteDigitLength(char a, char b, char c) => (a, b, c) switch
92 | {
93 | (a: '2' , b: >= '0' and <= '4', c: >= '0' and <= '9') => 3,
94 | (a: '2' , b: '5' , c: >= '0' and <= '5') => 3,
95 |
96 | (a: >= '2' , b: >= '0' and <= '9', c: >= '0' and <= '9') => 0,
97 |
98 | (a: '1' , b: >= '0' and <= '9', c: >= '0' and <= '9') => 3,
99 | (a: >= '1' and <= '9', b: >= '0' and <= '9', c: _ ) => 2,
100 | (a: >= '0' and <= '9', b: _ , c: _ ) => 1,
101 | _ => 0,
102 | };
103 |
104 | }
105 |
106 | private static int GetDigitLength(ReadOnlySpan text)
107 | {
108 | var length = 0;
109 | while (text.TrySliceFirst(out var ch) && ch is >= '0' and <= '9')
110 | ++length;
111 | return length;
112 | }
113 |
114 | private static int GetHexChars(ReadOnlySpan text)
115 | {
116 | var length = 0;
117 | while (text.TrySliceFirst(out var ch) && ch.IsHex())
118 | ++length;
119 | return length;
120 | }
121 |
122 | public static bool TryReadCharacter(ref this ReadOnlySpan span, ref int charsRead, char expected)
123 | {
124 | if (!span.TrySliceFirst(out var actual) || actual != expected)
125 | return false;
126 | ++charsRead;
127 | return true;
128 | }
129 | }
130 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Common/Utilities/ReadOnlyListSpan.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace NetworkPrimitives.Utilities
6 | {
7 | [ExcludeFromCodeCoverage("Internal")]
8 | internal readonly struct ReadOnlyListSpan : IEquatable>, ISlice, T>
9 | {
10 | private readonly IReadOnlyList list;
11 | private readonly int startIndex;
12 | public ReadOnlyListSpan(IReadOnlyList? list) : this(list ?? Array.Empty(), 0, list?.Count ?? 0)
13 | {
14 | }
15 | private ReadOnlyListSpan(IReadOnlyList list, int startIndex, int length)
16 | {
17 | this.list = list;
18 | this.startIndex = startIndex;
19 | this.Length = length;
20 | }
21 | public int Length { get; }
22 | public bool IsEmpty => Length == 0;
23 | public T this[int index] => this.list[this.startIndex + index];
24 |
25 | public ReadOnlyListSpan Slice(int start, int length)
26 | {
27 | if ((uint)start > (uint)this.Length || (uint)length > (uint)(this.Length - start))
28 | throw new ArgumentOutOfRangeException();
29 | return new (this.list, this.startIndex + start, length);
30 | }
31 |
32 | public IEnumerable ToEnumerable()
33 | {
34 | for (var i = 0; i < this.Length; ++i)
35 | {
36 | yield return this[i];
37 | }
38 | }
39 |
40 | public bool Equals(ReadOnlyListSpan other) => this.list.Equals(other.list) && this.startIndex == other.startIndex && this.Length == other.Length;
41 | public override bool Equals(object? obj) => obj is ReadOnlyListSpan other && Equals(other);
42 | public override int GetHashCode() => HashCode.Combine(this.list, this.startIndex, this.Length);
43 | public static bool operator ==(ReadOnlyListSpan left, ReadOnlyListSpan right) => left.Equals(right);
44 | public static bool operator !=(ReadOnlyListSpan left, ReadOnlyListSpan right) => !left.Equals(right);
45 | }
46 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/Address/Ipv4AddressClass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NetworkPrimitives.Ipv4
4 | {
5 | ///
6 | /// In the classful networking architecture, an IP address is assigned to one of five classes.
7 | ///
8 | ///
9 | /// Classful IP addressing is obsolete. This type is provided for your information, but you should
10 | /// do your best to remove your need to use this type.
11 | ///
12 | public enum Ipv4AddressClass
13 | {
14 | ///
15 | /// IPv4 addresses between 0.0.0.0 and 127.255.255.255, with a default subnet mask of 255.0.0.0
16 | ///
17 | ClassA,
18 | ///
19 | /// IPv4 addresses between 128.0.0.0 and 191.255.255.255, with a default subnet mask of 255.255.0.0
20 | ///
21 | ClassB,
22 | ///
23 | /// IPv4 addresses between 192.0.0.0 and 223.255.255.255, with a default subnet mask of 255.255.255.0
24 | ///
25 | ClassC,
26 | ///
27 | /// IPv4 addresses between 224.0.0.0 and 239.255.255.255; Multicast.
28 | ///
29 | ClassD,
30 | ///
31 | /// IPv4 addresses between 240.0.0.0 and 255.255.255.255; Reserved.
32 | ///
33 | ClassE,
34 | }
35 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/Address/Ipv4AddressRangeType.cs:
--------------------------------------------------------------------------------
1 | namespace NetworkPrimitives.Ipv4
2 | {
3 | ///
4 | /// Indicates which special type of address (if any) that the address has.
5 | ///
6 | public enum Ipv4AddressRangeType
7 | {
8 | ///
9 | /// A normal address
10 | ///
11 | Normal,
12 |
13 | ///
14 | /// Private IP address, in the range 192.168.0.0/16
15 | ///
16 | /// RFC 1918
17 | Rfc1918,
18 |
19 | ///
20 | /// IP Range 100.64.0.0/10, used for carrier-grade NAT; Allocated by RFC 6598
21 | ///
22 | /// RFC 6598
23 | CgNat,
24 |
25 | ///
26 | /// Documentation ranges; Allocated by RFC 5737.
27 | ///
28 | /// RFC 5737
29 | Documentation,
30 |
31 | ///
32 | /// IP Range 169.254.0.0/16, used for link-local addresses[6] between two hosts on a single link
33 | /// when no IP address is otherwise specified, such as would have normally been retrieved from a DHCP server.
34 | /// Allocated by RFC 3927.
35 | ///
36 | /// RFC 3927
37 | Apipa,
38 |
39 | ///
40 | /// IP Range 127.0.0.0/8, used for loopback addresses to the host.
41 | /// Allocated by RFC 1122.
42 | ///
43 | /// RFC 1122
44 | Loopback,
45 |
46 | ///
47 | /// IP range 0.0.0.0/8
48 | ///
49 | Current,
50 |
51 | ///
52 | /// IP Address 255.255.255.255; The broadcast network
53 | ///
54 | Broadcast,
55 |
56 | ///
57 | /// IP Range 224.0.0.0/4; Multicast networks
58 | ///
59 | Multicast,
60 |
61 | ///
62 | /// IP Range 198.18.0.0/15, used for benchmark testing of inter-network communications between two separate subnets.
63 | ///
64 | Benchmark,
65 | }
66 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/Address/Ipv4WellKnownRanges.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives.Ipv4
6 | {
7 | ///
8 | /// Provides access to many well-defined Ipv4 address ranges.
9 | ///
10 | public static class Ipv4WellKnownRanges
11 | {
12 | ///
13 | /// Private IP address, in the range 10.0.0.0/8; Allocated by RFC 1918
14 | ///
15 | /// RFC 1918
16 | public static readonly Ipv4Subnet Rfc1918A = new (Ipv4Address.Parse(0x0A000000), Ipv4Cidr.Parse(8));
17 |
18 | ///
19 | /// Private IP address, in the range 172.16.0.0/12; Allocated by RFC 1918
20 | ///
21 | /// RFC 1918
22 | public static readonly Ipv4Subnet Rfc1918B = new (Ipv4Address.Parse(0xAC100000), Ipv4Cidr.Parse(12));
23 |
24 | ///
25 | /// Private IP address, in the range 192.168.0.0/16; Allocated by RFC 1918
26 | ///
27 | /// RFC 1918
28 | public static readonly Ipv4Subnet Rfc1918C = new (Ipv4Address.Parse(0xC0A80000), Ipv4Cidr.Parse(16));
29 |
30 | ///
31 | /// IP Range 100.64.0.0/10, used for carrier-grade NAT; Allocated by RFC 6598
32 | ///
33 | /// RFC 6598
34 | public static readonly Ipv4Subnet CgNat = new (Ipv4Address.Parse(0x64400000), Ipv4Cidr.Parse(10));
35 |
36 | ///
37 | /// IP Range 192.0.2.0/24, used for documentation; Allocated by RFC 5737.
38 | ///
39 | /// RFC 5737
40 | public static readonly Ipv4Subnet Doc1 = new (Ipv4Address.Parse(0xC0000200), Ipv4Cidr.Parse(24));
41 |
42 | ///
43 | /// IP Range 198.51.100.0/24, used for documentation; Allocated by RFC 5737.
44 | ///
45 | /// RFC 5737
46 | public static readonly Ipv4Subnet Doc2 = new (Ipv4Address.Parse(0xC6336400), Ipv4Cidr.Parse(24));
47 |
48 | ///
49 | /// IP Range 203.0.113.0/24, used for documentation; Allocated by RFC 5737.
50 | ///
51 | /// RFC 5737
52 | public static readonly Ipv4Subnet Doc3 = new (Ipv4Address.Parse(0xCB007100), Ipv4Cidr.Parse(24));
53 |
54 | ///
55 | /// IP Range 169.254.0.0/16, used for link-local addresses[6] between two hosts on a single link
56 | /// when no IP address is otherwise specified, such as would have normally been retrieved from a DHCP server.
57 | /// Allocated by RFC 3927.
58 | ///
59 | /// RFC 3927
60 | public static readonly Ipv4Subnet Apipa = new (Ipv4Address.Parse(0xA9FE0000), Ipv4Cidr.Parse(16));
61 |
62 | ///
63 | /// IP Range 127.0.0.0/8, used for loopback addresses to the host.
64 | /// Allocated by RFC 1122.
65 | ///
66 | /// RFC 1122
67 | public static readonly Ipv4Subnet Loopback = new (Ipv4Address.Parse(0x7F000000), Ipv4Cidr.Parse(8));
68 |
69 | ///
70 | /// IP range 0.0.0.0/8
71 | ///
72 | public static readonly Ipv4Subnet Current = new (Ipv4Address.Parse(0x00000000), Ipv4Cidr.Parse(8));
73 |
74 | ///
75 | /// IP Address 255.255.255.255; The broadcast network
76 | ///
77 | public static readonly Ipv4Subnet Broadcast = new (Ipv4Address.Parse(0xFFFFFFFF), Ipv4Cidr.Parse(32));
78 |
79 | ///
80 | /// IP Range 224.0.0.0/4; Multicast networks
81 | ///
82 | public static readonly Ipv4Subnet Multicast = new (Ipv4Address.Parse(0xE0000000), Ipv4Cidr.Parse(4));
83 |
84 | ///
85 | /// IP Range 198.18.0.0/15, used for benchmark testing of inter-network communications between two separate subnets.
86 | ///
87 | public static readonly Ipv4Subnet Benchmark = new (Ipv4Address.Parse(0xC6120000), Ipv4Cidr.Parse(15));
88 | }
89 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/Formatting/Ipv4Parsing.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Buffers.Binary;
5 | using NetworkPrimitives.Utilities;
6 |
7 | namespace NetworkPrimitives.Ipv4
8 | {
9 | [ExcludeFromCodeCoverage("Internal")]
10 | internal static class Ipv4Parsing
11 | {
12 | public static bool TryParseDottedDecimalUInt32(string? text, out uint result)
13 | {
14 | result = default;
15 | return text is not null && Ipv4Parsing.TryParseDottedDecimalUInt32(text, out var charsRead, out result) &&
16 | charsRead == text.Length;
17 | }
18 |
19 | public static bool TryParseDottedDecimalUInt32(string? text, out int charsRead, out uint result)
20 | {
21 | charsRead = default;
22 | var span = text.GetSpan();
23 | return TryParseDottedDecimalUInt32(ref span, ref charsRead, out result);
24 | }
25 |
26 | internal static bool TryParseDottedDecimalUInt32(ref ReadOnlySpan text, ref int charsRead, out uint result)
27 | {
28 | result = default;
29 | if (text.Length < Ipv4Address.MINIMUM_LENGTH)
30 | return false;
31 | Span octets = stackalloc byte[4];
32 | var textCopy = text;
33 | var charsReadCopy = charsRead;
34 | for (var i = 0; i < 4; ++i)
35 | {
36 | if (i != 0 && !textCopy.TryReadCharacter(ref charsReadCopy, '.'))
37 | return false;
38 | if (!textCopy.TryParseByte(ref charsReadCopy, out var octet))
39 | return false;
40 | octets[i] = octet;
41 | }
42 | charsRead = charsReadCopy;
43 | text = textCopy;
44 | result = BinaryPrimitives.ReadUInt32BigEndian(octets);
45 | return true;
46 | }
47 | public static bool TryParseDottedDecimalUInt32(ReadOnlySpan text, out uint result)
48 | => TryParseDottedDecimalUInt32(text, out var charsRead, out result) && charsRead == text.Length;
49 | public static bool TryParseDottedDecimalUInt32(ReadOnlySpan text, out int charsRead, out uint result)
50 | {
51 | charsRead = default;
52 | return TryParseDottedDecimalUInt32(ref text, ref charsRead, out result);
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/Ipv4Extensions.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 |
5 | namespace NetworkPrimitives.Ipv4
6 | {
7 | ///
8 | /// Utility methods for and
9 | ///
10 | public static class Ipv4Extensions
11 | {
12 | private const uint CLASS_A_MASK = 0b_10000000_00000000_00000000_00000000;
13 | private const uint CLASS_B_MASK = 0b_11000000_00000000_00000000_00000000;
14 | private const uint CLASS_C_MASK = 0b_11100000_00000000_00000000_00000000;
15 | private const uint CLASS_D_MASK = 0b_11110000_00000000_00000000_00000000;
16 | private const uint CLASS_A_VALUE = 0b_00000000_00000000_00000000_00000000;
17 | private const uint CLASS_B_VALUE = 0b_10000000_00000000_00000000_00000000;
18 | private const uint CLASS_C_VALUE = 0b_11000000_00000000_00000000_00000000;
19 | private const uint CLASS_D_VALUE = 0b_11100000_00000000_00000000_00000000;
20 |
21 | ///
22 | /// Determine which special type of address (if any) that an has.
23 | ///
24 | /// An instance of
25 | ///
26 | /// The special address type, or if the address does not have a special address type.
27 | ///
28 | public static Ipv4AddressRangeType GetRangeType(this Ipv4Address value)
29 | {
30 | return CheckAll(value, out var type) ? type : default;
31 | static bool CheckAll(Ipv4Address value, out Ipv4AddressRangeType result) =>
32 | CheckOne(Ipv4WellKnownRanges.Rfc1918A.Contains(value), Ipv4AddressRangeType.Rfc1918, out result)
33 | || CheckOne(Ipv4WellKnownRanges.Rfc1918B.Contains(value), Ipv4AddressRangeType.Rfc1918, out result)
34 | || CheckOne(Ipv4WellKnownRanges.Rfc1918C.Contains(value), Ipv4AddressRangeType.Rfc1918, out result)
35 | || CheckOne(Ipv4WellKnownRanges.CgNat.Contains(value), Ipv4AddressRangeType.CgNat, out result)
36 | || CheckOne(Ipv4WellKnownRanges.Doc1.Contains(value), Ipv4AddressRangeType.Documentation, out result)
37 | || CheckOne(Ipv4WellKnownRanges.Doc2.Contains(value), Ipv4AddressRangeType.Documentation, out result)
38 | || CheckOne(Ipv4WellKnownRanges.Doc3.Contains(value), Ipv4AddressRangeType.Documentation, out result)
39 | || CheckOne(Ipv4WellKnownRanges.Apipa.Contains(value), Ipv4AddressRangeType.Apipa, out result)
40 | || CheckOne(Ipv4WellKnownRanges.Loopback.Contains(value), Ipv4AddressRangeType.Loopback, out result)
41 | || CheckOne(Ipv4WellKnownRanges.Current.Contains(value), Ipv4AddressRangeType.Current, out result)
42 | || CheckOne(Ipv4WellKnownRanges.Broadcast.Contains(value), Ipv4AddressRangeType.Broadcast, out result)
43 | || CheckOne(Ipv4WellKnownRanges.Multicast.Contains(value), Ipv4AddressRangeType.Multicast, out result)
44 | || CheckOne(Ipv4WellKnownRanges.Benchmark.Contains(value), Ipv4AddressRangeType.Benchmark, out result)
45 | ;
46 | }
47 |
48 |
49 | ///
50 | /// Determine which special type of address (if any) that an has.
51 | ///
52 | /// An instance of
53 | ///
54 | /// The special address type, or if the subnet does not have a special address type.
55 | ///
56 | public static Ipv4AddressRangeType GetRangeType(this Ipv4Subnet value)
57 | {
58 | return CheckAll(value, out var type) ? type : default;
59 | static bool CheckAll(Ipv4Subnet value, out Ipv4AddressRangeType result) =>
60 | CheckOne(Ipv4WellKnownRanges.Rfc1918A.Contains(value), Ipv4AddressRangeType.Rfc1918, out result)
61 | || CheckOne(Ipv4WellKnownRanges.Rfc1918B.Contains(value), Ipv4AddressRangeType.Rfc1918, out result)
62 | || CheckOne(Ipv4WellKnownRanges.Rfc1918C.Contains(value), Ipv4AddressRangeType.Rfc1918, out result)
63 | || CheckOne(Ipv4WellKnownRanges.CgNat.Contains(value), Ipv4AddressRangeType.CgNat, out result)
64 | || CheckOne(Ipv4WellKnownRanges.Doc1.Contains(value), Ipv4AddressRangeType.Documentation, out result)
65 | || CheckOne(Ipv4WellKnownRanges.Doc2.Contains(value), Ipv4AddressRangeType.Documentation, out result)
66 | || CheckOne(Ipv4WellKnownRanges.Doc3.Contains(value), Ipv4AddressRangeType.Documentation, out result)
67 | || CheckOne(Ipv4WellKnownRanges.Apipa.Contains(value), Ipv4AddressRangeType.Apipa, out result)
68 | || CheckOne(Ipv4WellKnownRanges.Loopback.Contains(value), Ipv4AddressRangeType.Loopback, out result)
69 | || CheckOne(Ipv4WellKnownRanges.Current.Contains(value), Ipv4AddressRangeType.Current, out result)
70 | || CheckOne(Ipv4WellKnownRanges.Broadcast.Contains(value), Ipv4AddressRangeType.Broadcast, out result)
71 | || CheckOne(Ipv4WellKnownRanges.Multicast.Contains(value), Ipv4AddressRangeType.Multicast, out result)
72 | || CheckOne(Ipv4WellKnownRanges.Benchmark.Contains(value), Ipv4AddressRangeType.Benchmark, out result)
73 | ;
74 | }
75 |
76 | private static bool CheckOne(bool ret, Ipv4AddressRangeType type, out Ipv4AddressRangeType result)
77 | {
78 | result = ret ? type : default;
79 | return ret;
80 | }
81 |
82 | ///
83 | /// Determine which address class an is a part of.
84 | ///
85 | ///
86 | /// An instance of an
87 | ///
88 | ///
89 | /// An representing which address class is a part of.
90 | ///
91 | ///
92 | /// Classful IP addressing is obsolete. This type is provided for your information, but you should
93 | /// do your best to remove your need to use this type.
94 | ///
95 | public static Ipv4AddressClass GetAddressClass(this Ipv4Address address)
96 | {
97 | var value = address.Value;
98 | if ((value & CLASS_A_MASK) == CLASS_A_VALUE)
99 | return Ipv4AddressClass.ClassA;
100 | if ((value & CLASS_B_MASK) == CLASS_B_VALUE)
101 | return Ipv4AddressClass.ClassB;
102 | if ((value & CLASS_C_MASK) == CLASS_C_VALUE)
103 | return Ipv4AddressClass.ClassC;
104 | if ((value & CLASS_D_MASK) == CLASS_D_VALUE)
105 | return Ipv4AddressClass.ClassD;
106 | return Ipv4AddressClass.ClassE;
107 | }
108 | }
109 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/Range/Ipv4AddressRange.ClassEnumerator.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 |
7 | namespace NetworkPrimitives.Ipv4
8 | {
9 | public readonly partial struct Ipv4AddressRange
10 | {
11 | private class ClassEnumerator : IEnumerator, IEnumerable
12 | {
13 | private readonly Ipv4Address startAddress;
14 | private readonly ulong totalAddresses;
15 | private const int STATE_NOT_STARTED = 0;
16 | private const int STATE_IN_PROGRESS = 1;
17 | private const int STATE_FINISHED = 2;
18 |
19 | private int state;
20 | private uint counter;
21 | private Ipv4Address current;
22 |
23 | public ClassEnumerator(Ipv4Address startAddress, ulong totalAddresses)
24 | {
25 | this.startAddress = startAddress;
26 | this.totalAddresses = totalAddresses;
27 | this.state = default;
28 | this.current = default;
29 | this.counter = default;
30 | this.Reset();
31 | }
32 |
33 | public bool MoveNext()
34 | {
35 | switch (this.state)
36 | {
37 | case STATE_NOT_STARTED:
38 | this.current = startAddress;
39 | this.state = STATE_IN_PROGRESS;
40 | return true;
41 | case STATE_IN_PROGRESS when this.counter + 1 > totalAddresses:
42 | this.state = STATE_FINISHED;
43 | return false;
44 | case STATE_IN_PROGRESS:
45 | ++this.counter;
46 | this.current = this.startAddress.AddInternal(this.counter);
47 | return true;
48 | default:
49 | return false;
50 | }
51 | }
52 |
53 | public void Reset()
54 | {
55 | this.state = default;
56 | this.counter = default;
57 | this.current = default;
58 | }
59 |
60 | object IEnumerator.Current => this.Current;
61 |
62 | public Ipv4Address Current => state switch
63 | {
64 | 0 => throw new InvalidOperationException("Enumeration not yet started."),
65 | 1 => this.current,
66 | _ => throw new InvalidOperationException("Enumeration already finished."),
67 | };
68 | void IDisposable.Dispose()
69 | {
70 | }
71 |
72 | public IEnumerator GetEnumerator() => this;
73 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
74 | }
75 | }
76 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/Range/Ipv4AddressRange.RefStructEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NetworkPrimitives.Ipv4
4 | {
5 | ///
6 | /// Allocation-less enumerator over a range of
7 | ///
8 | public ref struct Ipv4AddressEnumerator
9 | {
10 | private readonly Ipv4Address startAddress;
11 | private readonly ulong totalAddresses;
12 | private const int STATE_NOT_STARTED = 0;
13 | private const int STATE_IN_PROGRESS = 1;
14 | private const int STATE_FINISHED = 2;
15 |
16 | private int state;
17 | private uint counter;
18 | private Ipv4Address current;
19 |
20 | internal Ipv4AddressEnumerator(Ipv4Address startAddress, ulong totalAddresses)
21 | {
22 | this.startAddress = startAddress;
23 | this.totalAddresses = totalAddresses;
24 | this.state = default;
25 | this.current = default;
26 | this.counter = default;
27 | this.Reset();
28 | }
29 |
30 | ///
31 | /// Advances the enumerator to the next address
32 | ///
33 | ///
34 | /// if the enumerator was successfully advanced to the next element;
35 | /// if the enumerator has passed the end of the range.
36 | ///
37 | public bool MoveNext()
38 | {
39 | switch (this.state)
40 | {
41 | case STATE_NOT_STARTED:
42 | this.current = startAddress;
43 | this.state = STATE_IN_PROGRESS;
44 | return true;
45 | case STATE_IN_PROGRESS when this.counter + 1 > totalAddresses:
46 | this.state = STATE_FINISHED;
47 | return false;
48 | case STATE_IN_PROGRESS:
49 | ++this.counter;
50 | this.current = this.startAddress.AddInternal(this.counter);
51 | return true;
52 | default:
53 | return false;
54 | }
55 | }
56 |
57 | ///
58 | /// Sets the enumerator to its initial position, which is before the first address in the range.
59 | ///
60 | public void Reset()
61 | {
62 | this.state = default;
63 | this.counter = default;
64 | this.current = default;
65 | }
66 |
67 | ///
68 | /// The current address
69 | ///
70 | ///
71 | /// The enumerator has not yet started, or the enumerator has already finished.
72 | ///
73 | public Ipv4Address Current => state switch
74 | {
75 | 0 => throw new InvalidOperationException("Enumeration not yet started."),
76 | 1 => this.current,
77 | _ => throw new InvalidOperationException("Enumeration already finished."),
78 | };
79 | }
80 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/RangeList/Ipv4AddressRangeListEnumerator.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using NetworkPrimitives.Utilities;
5 |
6 | namespace NetworkPrimitives.Ipv4
7 | {
8 | ///
9 | /// Allocation-less enumerator over a range of
10 | ///
11 | public ref struct Ipv4AddressRangeListEnumerator
12 | {
13 | private readonly ReadOnlyListSpan original;
14 | private ReadOnlyListSpan available;
15 | private Ipv4AddressRange current;
16 | internal Ipv4AddressRangeListEnumerator(ReadOnlyListSpan original)
17 | {
18 | this.available = this.original = original;
19 | this.current = default;
20 | }
21 |
22 |
23 | ///
24 | /// Advances the enumerator to the next range
25 | ///
26 | ///
27 | /// if the enumerator was successfully advanced to the next element;
28 | /// if the enumerator has passed the end of the range.
29 | ///
30 | public bool MoveNext() => this.available.TrySliceFirst(out this.current);
31 |
32 |
33 | ///
34 | /// The current range
35 | ///
36 | public Ipv4AddressRange Current => current;
37 |
38 |
39 | ///
40 | /// Sets the enumerator to its initial position, which is before the first range in the range.
41 | ///
42 | public void Reset()
43 | {
44 | this.available = this.original;
45 | this.current = default;
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/SubnetTree/Ipv4SubnetDictionary.Collections.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | #pragma warning disable CS1591
6 |
7 | namespace NetworkPrimitives.Ipv4;
8 |
9 | public partial class Ipv4SubnetDictionary
10 | : IReadOnlyDictionary,
11 | //IDictionary,
12 | //ICollection>,
13 |
14 | IEnumerable>>,
15 | IReadOnlyCollection>>,
16 | IReadOnlyDictionary>
17 | {
18 | private static NodeEnumerator GetNodeEnumerator(Node? node) => new (node);
19 | private NodeEnumerator GetNodeEnumerator() => GetNodeEnumerator(this.rootNode);
20 |
21 | public IEnumerable<(Ipv4Subnet Subnet, TValue? Value, Ipv4Subnet? Left, Ipv4Subnet? Right)> Nodes()
22 | => this.GetNodeEnumerator().Select(n => n.ToNodeTuple());
23 |
24 |
25 | public IEnumerable<(Ipv4Subnet Subnet, TValue Value)> SubnetsAsTuples()
26 | {
27 | using var enumerator = GetNodeEnumerator();
28 | while (enumerator.MoveNext())
29 | {
30 | if (enumerator.Current is IValueNode valueNode)
31 | yield return (valueNode.Subnet, valueNode.Value);
32 | }
33 | }
34 | private IEnumerable<(Ipv4Address Address, TValue Value)> AddressesAsTuples()
35 | {
36 | using var enumerator = GetNodeEnumerator();
37 | while (enumerator.MoveNext())
38 | {
39 | if (enumerator.Current is not IValueNode valueNode) continue;
40 | foreach (var address in valueNode.Subnet.GetAllAddresses().ToEnumerable())
41 | yield return (address, valueNode.Value);
42 | }
43 | }
44 | private IEnumerable> SubnetsAsKeyValuePairs()
45 | {
46 | using var enumerator = GetNodeEnumerator();
47 | while (enumerator.MoveNext())
48 | {
49 | if (enumerator.Current is IValueNode valueNode)
50 | yield return new(valueNode.Subnet, valueNode.Value);
51 | }
52 | }
53 | private IEnumerable> AddressesAsKeyValuePairs()
54 | {
55 | using var enumerator = GetNodeEnumerator();
56 | while (enumerator.MoveNext())
57 | {
58 | if (enumerator.Current is not IValueNode valueNode) continue;
59 | foreach (var address in valueNode.Subnet.GetAllAddresses().ToEnumerable())
60 | yield return new(address, valueNode.Value);
61 | }
62 | }
63 |
64 |
65 |
66 | #region IEnumerable
67 |
68 | IEnumerator IEnumerable.GetEnumerator()
69 | => this.SubnetsAsKeyValuePairs().GetEnumerator();
70 |
71 | #endregion IEnumerable
72 |
73 | #region IEnumerable>
74 |
75 | IEnumerator> IEnumerable>.GetEnumerator()
76 | => this.AddressesAsKeyValuePairs().GetEnumerator();
77 |
78 | #endregion IEnumerable>
79 |
80 | #region IReadOnlyDictionary
81 |
82 | IEnumerable IReadOnlyDictionary.Values
83 | => new ValueCollection(this);
84 |
85 | IEnumerable IReadOnlyDictionary.Keys
86 | => new AddressKeyCollection(this);
87 |
88 | #endregion IReadOnlyDictionary
89 |
90 |
91 | #region ICollection>
92 | /*
93 | void ICollection>.Add(KeyValuePair item)
94 | => this.Add(item.Key, item.Value);
95 |
96 | bool ICollection>.Contains(KeyValuePair item)
97 | => this.TryGetValue(item.Key, out var value) && this.valueComparer.Equals(value, item.Value);
98 |
99 | void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex)
100 | => throw new NotImplementedException();
101 |
102 | bool ICollection>.Remove(KeyValuePair item)
103 | => ((ICollection>)this).Contains(item)
104 | && this.Remove(item.Key);
105 |
106 | bool ICollection>.IsReadOnly => false;
107 | */
108 | #endregion ICollection>
109 |
110 |
111 | #region IDictionary
112 |
113 | /*
114 | bool IDictionary.Remove(Ipv4Address key)
115 | => this.Remove(key);
116 |
117 | ICollection IDictionary.Values
118 | => new ValueCollection(this);
119 |
120 | ICollection IDictionary.Keys
121 | => new AddressKeyCollection(this);
122 | */
123 | #endregion IDictionary
124 |
125 |
126 | #region IEnumerable>>
127 |
128 | IEnumerator>> IEnumerable>>.GetEnumerator()
129 | {
130 | var dict = this.Clone();
131 | return dict.GetNodeEnumerator()
132 | .OfType()
133 | .Select(ToKeyValuePair).GetEnumerator();
134 | KeyValuePair> ToKeyValuePair(IValueNode node)
135 | => new (node.Subnet, dict.Get(node.Subnet) ?? Array.Empty());
136 | }
137 |
138 | #endregion IEnumerable>>
139 |
140 |
141 | #region IReadOnlyDictionary>
142 |
143 | IEnumerable IReadOnlyDictionary>.Keys
144 | => new SubnetKeyCollection(this);
145 |
146 | IEnumerable> IReadOnlyDictionary>.Values
147 | => throw new NotImplementedException();
148 |
149 | IReadOnlyList IReadOnlyDictionary>.this[Ipv4Subnet subnet]
150 | => Get(subnet) ?? Array.Empty();
151 |
152 | #endregion IReadOnlyDictionary>
153 |
154 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/SubnetTree/Ipv4SubnetDictionary.Lookups.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Linq;
6 |
7 | #pragma warning disable CS1591
8 |
9 |
10 | namespace NetworkPrimitives.Ipv4;
11 |
12 | public partial class Ipv4SubnetDictionary
13 | {
14 | public TValue this[Ipv4Address address]
15 | {
16 | get => this.Get(address);
17 | set => this.Set(address, value);
18 | }
19 | public TValue this[Ipv4Subnet subnet]
20 | {
21 | get
22 | {
23 | if (!this.TryGetValue(subnet, out var value) || value.Count == 0)
24 | throw new KeyNotFoundException();
25 | return value[0];
26 | }
27 | set => this.Set(subnet, value);
28 | }
29 |
30 |
31 | private TValue Get(Ipv4Address address) => this.TryGetValue(address, out var value) ? value : throw new KeyNotFoundException();
32 |
33 | private IReadOnlyList? Get(Ipv4Subnet subnet)
34 | {
35 | _ = this.TryGetValue(subnet, out var value);
36 | return value;
37 | }
38 |
39 | #if NETSTANDARD2_0
40 | private bool NonNullTryGetValue(Ipv4Subnet subnet, out IReadOnlyList value)
41 | {
42 | value = Array.Empty(); // Null-forgiving operator
43 | if (!this.TryGetValue(subnet, out var nullableValue)) return false;
44 | value = nullableValue;
45 | return true;
46 | }
47 | bool IReadOnlyDictionary>.TryGetValue(Ipv4Subnet address, out IReadOnlyList value)
48 | => NonNullTryGetValue(address, out value);
49 | #endif
50 |
51 | public bool TryGetValue(Ipv4Subnet subnet, [NotNullWhen(true)] out IReadOnlyList? value)
52 | {
53 | value = default;
54 | var nodes = LocateMatchingNode(this.rootNode, subnet);
55 | if (nodes is null)
56 | return false;
57 | List? list = null;
58 | while (nodes.TryPop(out var node))
59 | {
60 | if (node is not IValueNode valueNode) continue;
61 | list ??= new ();
62 | list.Add(valueNode.Value);
63 | }
64 | value = list?.AsReadOnly();
65 | return value is not null;
66 | }
67 |
68 | #if NETSTANDARD2_0
69 | private bool NonNullTryGetValue(Ipv4Address address, out TValue value)
70 | {
71 | value = default!; // Null-forgiving operator
72 | if (!this.TryGetValue(address, out var nullableValue)) return false;
73 | value = nullableValue;
74 | return true;
75 | }
76 | bool IReadOnlyDictionary.TryGetValue(Ipv4Address address, out TValue value)
77 | => NonNullTryGetValue(address, out value);
78 | /*
79 | bool IDictionary.TryGetValue(Ipv4Address address, out TValue value)
80 | => NonNullTryGetValue(address, out value);
81 | */
82 | #endif
83 |
84 | public bool TryGetValue(Ipv4Address address, [MaybeNullWhen(false)] out TValue value)
85 | {
86 | value = default;
87 | var nodes = LocateMatchingNode(this.rootNode, new (address, Ipv4Cidr.Slash32));
88 | if (nodes is null)
89 | return false;
90 | while (nodes.TryPop(out var node))
91 | {
92 | if (node is not IValueNode valueNode) continue;
93 | value = valueNode.Value;
94 | return true;
95 | }
96 | return false;
97 | }
98 |
99 | public bool ContainsKey(Ipv4Subnet subnet) => ContainsKey(this.rootNode, subnet);
100 | public bool ContainsKey(Ipv4Address address) => ContainsKey(this.rootNode, new (address, Ipv4Cidr.Slash32));
101 |
102 | private static bool ContainsKey(Node? node, Ipv4Subnet subnet)
103 | {
104 | while (node is not null)
105 | {
106 | switch (node)
107 | {
108 | case IValueNode valueNode when valueNode.Subnet.Contains(subnet):
109 | return true;
110 | case BranchNode(_, var (leftSubnet, left), var (rightSubnet, right)) when leftSubnet.Contains(subnet):
111 | node = left;
112 | continue;
113 | case BranchNode(_, var (leftSubnet, left), var (rightSubnet, right)) when rightSubnet.Contains(subnet):
114 | node = right;
115 | continue;
116 | default:
117 | node = null;
118 | continue;
119 | }
120 | }
121 | return false;
122 | }
123 | private static Stack? LocateMatchingNode(Node? node, Ipv4Subnet address)
124 | {
125 | Stack? stack = null;
126 | while (node is not null)
127 | {
128 | stack ??= new();
129 | stack.Push(node);
130 | switch (node)
131 | {
132 | case BranchNode(_, var (leftSubnet, left), var (rightSubnet, right)) when leftSubnet.Contains(address):
133 | node = left;
134 | continue;
135 | case BranchNode(_, var (leftSubnet, left), var (rightSubnet, right)) when rightSubnet.Contains(address):
136 | node = right;
137 | continue;
138 | default:
139 | node = null;
140 | continue;
141 | }
142 | }
143 | return stack;
144 | }
145 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/SubnetTree/Ipv4SubnetDictionary.NodeEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | #pragma warning disable CS1591
6 |
7 |
8 | namespace NetworkPrimitives.Ipv4;
9 |
10 | public partial class Ipv4SubnetDictionary
11 | {
12 | private class NodeEnumerator : IEnumerable, IEnumerator
13 | {
14 | private const int STATE_NOT_STARTED = 0;
15 | private const int STATE_STARTED = 1;
16 | private const int STATE_FINISHED = 2;
17 |
18 | private readonly Node? rootNode;
19 | private readonly Queue queue = new();
20 | private int state = STATE_NOT_STARTED;
21 |
22 | public NodeEnumerator(Ipv4SubnetDictionary dictionary) : this(dictionary.rootNode)
23 | {
24 | }
25 |
26 | public NodeEnumerator(Node? rootNode) => this.rootNode = rootNode;
27 | private NodeEnumerator(Node? rootNode, IEnumerable queueNodes, int state)
28 | {
29 | this.rootNode = rootNode;
30 | this.state = state;
31 | foreach (var node in queueNodes)
32 | this.queue.Enqueue(node);
33 | }
34 |
35 | public void Reset()
36 | {
37 | this.queue.Clear();
38 | this.state = STATE_NOT_STARTED;
39 | }
40 |
41 | public bool MoveNext()
42 | {
43 | switch (this.state)
44 | {
45 | case STATE_NOT_STARTED when this.rootNode is null:
46 | this.state = STATE_FINISHED;
47 | return false;
48 | case STATE_NOT_STARTED:
49 | this.state = STATE_STARTED;
50 | this.queue.Enqueue(this.rootNode);
51 | goto case STATE_STARTED;
52 | case STATE_STARTED:
53 | this.current = HandleMoveNext();
54 | this.state = this.current is null
55 | ? STATE_FINISHED
56 | : STATE_STARTED;
57 | return this.state == STATE_STARTED;
58 | default:
59 | return false;
60 | }
61 | }
62 |
63 | private Node? HandleMoveNext()
64 | {
65 | if (!this.queue.TryDequeue(out var nextNode))
66 | {
67 | return null;
68 | }
69 | if (nextNode is not BranchNode(_, var (leftSubnet, left), var (rightSubnet, right))) return nextNode;
70 | this.QueueNode(left);
71 | this.QueueNode(right);
72 | return nextNode;
73 | }
74 |
75 | private void QueueNode(Node? node)
76 | {
77 | if (node is not null)
78 | this.queue.Enqueue(node);
79 | }
80 |
81 | public NodeEnumerator Clone() => new (this.rootNode, this.queue, this.state);
82 | private Node? current = null;
83 | public Node Current => this.current ?? throw new InvalidOperationException();
84 | public NodeEnumerator GetEnumerator() => this;
85 |
86 |
87 | object? IEnumerator.Current => this.Current;
88 | void IDisposable.Dispose()
89 | {
90 | }
91 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
92 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
93 | }
94 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/SubnetTree/Ipv4SubnetDictionary.Nodes.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Collections.Generic;
3 |
4 | #pragma warning disable CS1591
5 |
6 | namespace NetworkPrimitives.Ipv4;
7 |
8 | public static class NodeExtensions
9 | {
10 | public static void Deconstruct(
11 | this Ipv4SubnetDictionary.IValueNode node,
12 | out Ipv4Subnet subnet,
13 | out TValue value
14 | )
15 | {
16 | subnet = node.Subnet;
17 | value = node.Value;
18 | }
19 |
20 |
21 | public static void Deconstruct(
22 | this Ipv4SubnetDictionary.IBranchNode node,
23 | out Ipv4Subnet subnet,
24 | out Ipv4SubnetDictionary.Node? left,
25 | out Ipv4SubnetDictionary.Node? right
26 | )
27 | {
28 | subnet = node.Subnet;
29 | left = node.Left;
30 | right = node.Right;
31 | }
32 | }
33 |
34 | public partial class Ipv4SubnetDictionary
35 | {
36 |
37 | public readonly record struct NodeAndSubnet(Ipv4Subnet Subnet, Node? Node);
38 |
39 |
40 |
41 | public interface INode
42 | {
43 | Ipv4Subnet Subnet { get; }
44 | public (Ipv4Subnet Subnet, TValue? Value, Ipv4Subnet? Left, Ipv4Subnet? Right) ToNodeTuple();
45 | }
46 | public interface IValueNode : INode
47 | {
48 | TValue Value { get; }
49 | }
50 | public interface IBranchNode : INode
51 | {
52 | Node? Left { get; }
53 | Node? Right { get; }
54 | Ipv4Subnet LeftSubnet { get; }
55 | Ipv4Subnet RightSubnet { get; }
56 | }
57 | public interface IBranchValueNode : IBranchNode, IValueNode
58 | {
59 | }
60 |
61 | public abstract record Node(
62 | Ipv4Subnet Subnet
63 | ) : INode
64 | {
65 | public abstract (Ipv4Subnet Subnet, TValue? Value, Ipv4Subnet? Left, Ipv4Subnet? Right) ToNodeTuple();
66 | }
67 |
68 | public record LeafNode(
69 | Ipv4Subnet Subnet,
70 | TValue Value
71 | ) : Node(Subnet), IValueNode
72 | {
73 | public override (Ipv4Subnet Subnet, TValue? Value, Ipv4Subnet? Left, Ipv4Subnet? Right) ToNodeTuple()
74 | => (this.Subnet, this.Value, null, null);
75 |
76 | public BranchNode Split()
77 | {
78 | _ = this.Subnet.TrySplit(out var leftSubnet, out var rightSubnet);
79 | return BranchNode.Create(
80 | Subnet,
81 | new LeafNode(leftSubnet, this.Value),
82 | new LeafNode(rightSubnet, this.Value)
83 | );
84 | }
85 | }
86 |
87 | public record BranchNode(
88 | Ipv4Subnet Subnet,
89 | NodeAndSubnet Left,
90 | NodeAndSubnet Right
91 | ) : Node(Subnet), IBranchNode
92 | {
93 | public Ipv4SubnetMask ChildMask => Left.Subnet.Mask;
94 | public Ipv4Cidr ChildCidr => Left.Subnet.Cidr;
95 |
96 | public static BranchNode Create(
97 | Ipv4Subnet subnet,
98 | Node? left,
99 | Node? right
100 | )
101 | {
102 | _ = subnet.TrySplit(out var leftSubnet, out var rightSubnet);
103 | return new (subnet, new (leftSubnet, left), new (rightSubnet, right));
104 | }
105 | public static BranchValueNode Create(
106 | Ipv4Subnet subnet,
107 | TValue value,
108 | Node? left,
109 | Node? right
110 | )
111 | {
112 | _ = subnet.TrySplit(out var leftSubnet, out var rightSubnet);
113 | return new (subnet, value, new (leftSubnet, left), new (rightSubnet, right));
114 | }
115 |
116 | Node? IBranchNode.Left => this.Left.Node;
117 |
118 | Node? IBranchNode.Right => this.Right.Node;
119 |
120 | Ipv4Subnet IBranchNode.LeftSubnet => this.Left.Subnet;
121 |
122 | Ipv4Subnet IBranchNode.RightSubnet => this.Right.Subnet;
123 |
124 | public override (Ipv4Subnet Subnet, TValue? Value, Ipv4Subnet? Left, Ipv4Subnet? Right) ToNodeTuple()
125 | => (this.Subnet, default, this.Left.Subnet, this.Right.Subnet);
126 |
127 | public static BranchNode WithLeft(BranchNode node, Node? left)
128 | => node is BranchValueNode bvn
129 | ? bvn with { Left = bvn.Left with { Node = left } }
130 | : node with { Left = node.Left with { Node = left } };
131 |
132 | public static BranchNode WithRight(BranchNode node, Node? right)
133 | => node is BranchValueNode bvn
134 | ? bvn with { Right = bvn.Right with { Node = right } }
135 | : node with { Right = node.Right with { Node = right } };
136 | }
137 |
138 |
139 | public record BranchValueNode(
140 | Ipv4Subnet Subnet,
141 | TValue Value,
142 | NodeAndSubnet Left,
143 | NodeAndSubnet Right
144 | ) : BranchNode(Subnet, Left, Right), IBranchValueNode
145 | {
146 |
147 | public override (Ipv4Subnet Subnet, TValue? Value, Ipv4Subnet? Left, Ipv4Subnet? Right) ToNodeTuple()
148 | => (this.Subnet, this.Value, this.Left.Subnet, this.Right.Subnet);
149 |
150 | public static BranchValueNode From(BranchNode node, TValue value)
151 | => new (node.Subnet, value, node.Left, node.Right);
152 | // ReSharper disable once SuggestBaseTypeForParameter
153 | public static BranchValueNode From(LeafNode node)
154 | => Create(node.Subnet, node.Value, null, null);
155 |
156 | public BranchNode WithoutValue() => new (this.Subnet, this.Left, this.Right);
157 | }
158 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/SubnetTree/Ipv4SubnetDictionary.Remove.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable CS1591
2 | /*
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics.CodeAnalysis;
6 |
7 | namespace NetworkPrimitives.Ipv4;
8 |
9 | public partial class Ipv4SubnetDictionary
10 | {
11 | public bool Remove(Ipv4Address address)
12 | => Remove(new Ipv4Subnet(address, Ipv4Cidr.Slash32));
13 | public bool Remove(Ipv4Address address, TValue value)
14 | => Remove(new Ipv4Subnet(address, Ipv4Cidr.Slash32), value);
15 |
16 | public bool Remove(Ipv4Subnet subnet)
17 | {
18 | var newNode = Remove(this.rootNode, subnet, out var removed);
19 | this.rootNode = Consolidate(newNode);
20 | return removed;
21 | }
22 |
23 | public bool Remove(Ipv4Subnet subnet, TValue value)
24 | {
25 | var newNode = RemoveWithValue(this.rootNode, subnet, value, out var removed);
26 | this.rootNode = Consolidate(newNode);
27 | return removed;
28 | }
29 |
30 |
31 | private Node? RemoveWithValue(
32 | Node? originalNode,
33 | Ipv4Subnet subnet,
34 | TValue value,
35 | out bool removed
36 | ) => RemoveWithValue(originalNode, subnet, value, this.valueComparer, out removed);
37 |
38 | private static Node? Remove(
39 | Node? originalNode,
40 | Ipv4Subnet subnet,
41 | out bool removed
42 | )
43 | {
44 | if (originalNode is null)
45 | {
46 | removed = false;
47 | return null;
48 | }
49 | if (originalNode.Subnet == subnet)
50 | {
51 | removed = true;
52 | return null;
53 | }
54 |
55 | switch (originalNode)
56 | {
57 | case LeafNode leafNode:
58 | return Remove(leafNode.Split(), subnet, out removed);
59 | case BranchNode branch:
60 | Node? newNode;
61 | if (branch.Left.Subnet.Contains(subnet))
62 | {
63 | newNode = Remove(branch.Left.Node, subnet, out removed);
64 | return removed ? BranchNode.WithLeft(branch, newNode) : branch;
65 | }
66 | newNode = Remove(branch.Right.Node, subnet, out removed);
67 | return removed ? BranchNode.WithRight(branch, newNode) : branch;
68 | default:
69 | throw new InvalidOperationException();
70 | }
71 | }
72 |
73 |
74 | private static Node? RemoveWithValue(
75 | Node? originalNode,
76 | Ipv4Subnet subnet,
77 | TValue value,
78 | IEqualityComparer valueComparer,
79 | out bool removed
80 | )
81 | {
82 | Node? newNode;
83 | if (originalNode is null)
84 | {
85 | removed = false;
86 | return null;
87 | }
88 |
89 | if (originalNode.Subnet == subnet)
90 | {
91 | (removed, newNode) = originalNode switch
92 | {
93 | BranchValueNode node when valueComparer.Equals(node.Value, value)
94 | => (Removed: true, Node: node.WithoutValue()),
95 | LeafNode node when valueComparer.Equals(node.Value, value)
96 | => (Removed: true, Node: null),
97 | BranchValueNode or BranchNode or LeafNode
98 | => (Removed: false, Node: originalNode),
99 | _ => throw new InvalidOperationException(),
100 | };
101 | removed = originalNode is IValueNode(_, var nodeValue) && valueComparer.Equals(nodeValue, value);
102 | return newNode;
103 | }
104 |
105 | if (originalNode is not BranchNode branch)
106 | {
107 | removed = false;
108 | return originalNode;
109 | }
110 |
111 |
112 | if (branch.Left.Subnet.Contains(subnet))
113 | {
114 | newNode = RemoveWithValue(branch.Left.Node, subnet, value, valueComparer, out removed);
115 | return removed ? BranchNode.WithLeft(branch, newNode) : branch;
116 | }
117 |
118 | newNode = RemoveWithValue(branch.Right.Node, subnet, value, valueComparer, out removed);
119 | return removed ? BranchNode.WithRight(branch, newNode) : branch;
120 |
121 | }
122 | }
123 | */
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv4/SubnetTree/Ipv4SubnetDictionary.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Linq;
6 | using System.Runtime.CompilerServices;
7 | using JetBrains.Annotations;
8 |
9 | #pragma warning disable CS1591
10 |
11 | namespace NetworkPrimitives.Ipv4;
12 |
13 | public partial class Ipv4SubnetDictionary
14 | {
15 | private enum AddSetMode
16 | {
17 | Add,
18 | TryAdd,
19 | Set
20 | }
21 |
22 | [PublicAPI]
23 | public Ipv4SubnetDictionary(IEqualityComparer? valueComparer = null)
24 | : this(true, null, valueComparer)
25 | {
26 | }
27 |
28 | [PublicAPI]
29 | public Ipv4SubnetDictionary(bool consolidate, IEqualityComparer? valueComparer = null)
30 | : this(consolidate, null, valueComparer)
31 | {
32 | }
33 | private Ipv4SubnetDictionary(bool consolidate, Node? rootNode, IEqualityComparer? valueComparer)
34 | {
35 | this.valueComparer = valueComparer ?? EqualityComparer.Default;
36 | this.consolidate = consolidate;
37 | this.rootNode = rootNode;
38 | }
39 |
40 | private readonly bool consolidate;
41 | private Node? rootNode;
42 | private readonly IEqualityComparer valueComparer;
43 |
44 | [DoesNotReturn]
45 | private static Node ThrowDuplicateKey(TKey key, string parameterName)
46 | {
47 | throw new ArgumentException($"Duplicate key '{key}' added.", parameterName);
48 | }
49 |
50 | public void Clear() => this.rootNode = null;
51 |
52 | public Ipv4SubnetDictionary Clone() => new (this.consolidate, this.rootNode, this.valueComparer);
53 |
54 | public int Count => throw new NotImplementedException();
55 |
56 | public INode? RootNodeInterface => this.rootNode;
57 |
58 | [PublicAPI]
59 | public IEnumerable Addresses => new AddressKeyCollection(this);
60 |
61 | [PublicAPI]
62 | public IEnumerable Subnets => new SubnetKeyCollection(this);
63 |
64 |
65 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv6/Address/Ipv6Address.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Runtime.InteropServices;
5 | using NetworkPrimitives.Utilities;
6 |
7 | namespace NetworkPrimitives.Ipv6
8 | {
9 | public readonly struct Ipv6Address : IEquatable// , IFormattable
10 | {
11 | internal readonly ulong Low;
12 | internal readonly ulong High;
13 |
14 | private Ipv6Address(ulong low, ulong high)
15 | {
16 | this.Low = low;
17 | this.High = high;
18 | }
19 |
20 | internal void Deconstruct(out ulong low, out ulong high)
21 | {
22 | low = this.Low;
23 | high = this.High;
24 | }
25 |
26 | public static Ipv6Address operator &(Ipv6Address address, Ipv6SubnetMask mask)
27 | => new (address.Low & mask.Low, address.High & mask.High);
28 |
29 | public static Ipv6Address Parse(Ipv6SubnetMask value) => new (value.Low, value.High);
30 |
31 |
32 | public static Ipv6Address Parse(string? value)
33 | => TryParse(value, out var result) ? result : throw new FormatException();
34 | public static bool TryParse(IPAddress? ipAddress, out Ipv6Address result)
35 | {
36 | result = default;
37 | if (ipAddress is null || ipAddress.AddressFamily != AddressFamily.InterNetworkV6) return false;
38 | Span bytes = stackalloc byte[16];
39 | return ipAddress.TryWriteBytes(bytes, out _) && TryParse(bytes, out result);
40 | }
41 |
42 | public static bool TryParse(ReadOnlySpan octets, out Ipv6Address result)
43 | {
44 | result = default;
45 | if (octets.Length < 16)
46 | return false;
47 | var longs = MemoryMarshal.Cast(octets);
48 | result = new Ipv6Address(longs[0], longs[1]);
49 | return true;
50 | }
51 |
52 | public static bool TryParse(string? text, out Ipv6Address result)
53 | => TryParse(text, out _, out result);
54 |
55 | public IPAddress ToIpAddress()
56 | {
57 | Span longs = stackalloc ulong[2];
58 | longs[0] = this.High;
59 | longs[1] = this.Low;
60 | var octets = MemoryMarshal.Cast(longs);
61 | #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
62 | return new (octets);
63 | #else
64 | return new (octets.ToArray() ?? Array.Empty());
65 | #endif
66 | }
67 |
68 | public override string ToString()
69 | => Ipv6Formatting.FormatIpv6Address(this.Low, this.High);
70 |
71 | public static bool TryParse(string? text, out int charsRead, out Ipv6Address result)
72 | {
73 | var spanWrapper = text.GetSpan();
74 | return TryParse(spanWrapper, out charsRead, out result);
75 | }
76 | internal static bool TryParse(ReadOnlySpan text, out int charsRead, out Ipv6Address result)
77 | {
78 | if (Ipv6Parsing.TryParseIpv6Address(text, out charsRead, out var high, out var low))
79 | {
80 | result = new Ipv6Address(low, high);
81 | return true;
82 | }
83 | result = default;
84 | return false;
85 | }
86 |
87 | internal static bool TryParse(ref ReadOnlySpan text, ref int charsRead, out Ipv6Address result)
88 | {
89 | if (!Ipv6Address.TryParse(text, out var length, out result))
90 | return false;
91 | charsRead += length;
92 | text = text[length..];
93 | return true;
94 | }
95 |
96 |
97 |
98 | public bool Equals(Ipv6Address other) => this.Low == other.Low && this.High == other.High;
99 | public override bool Equals(object? obj) => obj is Ipv6Address other && Equals(other);
100 | public override int GetHashCode() => HashCode.Combine(this.Low, this.High);
101 |
102 | public static bool operator ==(Ipv6Address left, Ipv6Address right) => left.Equals(right);
103 | public static bool operator !=(Ipv6Address left, Ipv6Address right) => !left.Equals(right);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv6/Formatting/Ipv6FormatInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NetworkPrimitives.Ipv6
4 | {
5 | [ExcludeFromCodeCoverage("Internal")]
6 | internal readonly struct Ipv6FormatInfo : IEquatable
7 | {
8 | public bool Compress { get; }
9 | public bool CompressGroups { get; }
10 | public bool Hex { get; }
11 | public bool Upper { get; }
12 |
13 | public Ipv6FormatInfo(bool compress, bool compressGroups, bool hex, bool upper)
14 | {
15 | this.Compress = compress;
16 | this.CompressGroups = compressGroups;
17 | this.Hex = hex;
18 | this.Upper = upper;
19 | }
20 |
21 | public void Deconstruct(out bool compress, out bool compressGroups, out bool hex, out bool upper)
22 | {
23 | compress = this.Compress;
24 | compressGroups = this.CompressGroups;
25 | hex = this.Hex;
26 | upper = this.Upper;
27 | }
28 |
29 | public bool Equals(Ipv6FormatInfo other) => this.Compress == other.Compress && this.CompressGroups == other.CompressGroups && this.Hex == other.Hex && this.Upper == other.Upper;
30 | public override bool Equals(object? obj) => obj is Ipv6FormatInfo other && Equals(other);
31 | public override int GetHashCode() => HashCode.Combine(this.Compress, this.CompressGroups, this.Hex, this.Upper);
32 | public static bool operator ==(Ipv6FormatInfo left, Ipv6FormatInfo right) => left.Equals(right);
33 | public static bool operator !=(Ipv6FormatInfo left, Ipv6FormatInfo right) => !left.Equals(right);
34 | }
35 | }
--------------------------------------------------------------------------------
/src/NetworkPrimitives/Ipv6/Formatting/Ipv6Parsing.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 |
3 | using System;
4 | using System.Buffers.Binary;
5 | using System.Runtime.InteropServices;
6 | using NetworkPrimitives.Utilities;
7 |
8 | namespace NetworkPrimitives.Ipv6
9 | {
10 | [ExcludeFromCodeCoverage("Internal")]
11 | internal static class Ipv6Parsing
12 | {
13 | internal static bool TryParseIpv6Address(ReadOnlySpan