├── .github ├── dependabot.yml └── workflows │ ├── ci-version.yml │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── rustfmt.toml ├── src ├── combiner │ ├── fmt.rs │ ├── mod.rs │ ├── v4.rs │ └── v6.rs ├── iterator │ ├── mod.rs │ ├── v4.rs │ └── v6.rs ├── lib.rs ├── separator │ ├── mod.rs │ ├── v4.rs │ └── v6.rs └── traits.rs └── tests ├── ipv4_cidr_combiner.rs ├── ipv4_cidr_separator.rs ├── ipv6_cidr_combiner.rs └── ipv6_cidr_separator.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" -------------------------------------------------------------------------------- /.github/workflows/ci-version.yml: -------------------------------------------------------------------------------- 1 | name: CI-version 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | env: 9 | CARGO_TERM_COLOR: always 10 | 11 | jobs: 12 | tests: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - ubuntu-latest 18 | - macos-latest 19 | - windows-latest 20 | toolchain: 21 | - stable 22 | - nightly 23 | features: 24 | - 25 | - --no-default-features 26 | - --features combiner 27 | - --features separator 28 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 29 | runs-on: ${{ matrix.os }} 30 | steps: 31 | - uses: actions/checkout@v4 32 | - uses: actions-rust-lang/setup-rust-toolchain@v1 33 | with: 34 | toolchain: ${{ matrix.toolchain }} 35 | - run: cargo test --release ${{ matrix.features }} 36 | - run: cargo doc --release ${{ matrix.features }} 37 | 38 | MSRV: 39 | strategy: 40 | fail-fast: false 41 | matrix: 42 | os: 43 | - ubuntu-latest 44 | - macos-latest 45 | - windows-latest 46 | toolchain: 47 | - 1.67 48 | features: 49 | - 50 | - --no-default-features 51 | - --features combiner 52 | - --features separator 53 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 54 | runs-on: ${{ matrix.os }} 55 | steps: 56 | - uses: actions/checkout@v4 57 | - uses: actions-rust-lang/setup-rust-toolchain@v1 58 | with: 59 | toolchain: ${{ matrix.toolchain }} 60 | - run: cargo test --release --lib --bins ${{ matrix.features }} -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | rustfmt: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: actions-rust-lang/setup-rust-toolchain@v1 14 | with: 15 | toolchain: nightly 16 | components: rustfmt 17 | - uses: actions-rust-lang/rustfmt@v1 18 | 19 | clippy: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: actions-rust-lang/setup-rust-toolchain@v1 24 | with: 25 | components: clippy 26 | - run: cargo clippy --all-targets --all-features -- -D warnings 27 | 28 | tests: 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | os: 33 | - ubuntu-latest 34 | - macos-latest 35 | - windows-latest 36 | toolchain: 37 | - stable 38 | - nightly 39 | features: 40 | - 41 | - --no-default-features 42 | - --features combiner 43 | - --features separator 44 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 45 | runs-on: ${{ matrix.os }} 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: actions-rust-lang/setup-rust-toolchain@v1 49 | with: 50 | toolchain: ${{ matrix.toolchain }} 51 | - run: cargo test ${{ matrix.features }} 52 | - run: cargo doc ${{ matrix.features }} 53 | 54 | MSRV: 55 | strategy: 56 | fail-fast: false 57 | matrix: 58 | os: 59 | - ubuntu-latest 60 | - macos-latest 61 | - windows-latest 62 | toolchain: 63 | - 1.67 64 | features: 65 | - 66 | - --no-default-features 67 | - --features combiner 68 | - --features separator 69 | name: Test ${{ matrix.toolchain }} on ${{ matrix.os }} (${{ matrix.features }}) 70 | runs-on: ${{ matrix.os }} 71 | steps: 72 | - uses: actions/checkout@v4 73 | - uses: actions-rust-lang/setup-rust-toolchain@v1 74 | with: 75 | toolchain: ${{ matrix.toolchain }} 76 | - run: cargo test --lib --bins ${{ matrix.features }} 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij+all ### 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # AWS User-specific 13 | .idea/**/aws.xml 14 | 15 | # Generated files 16 | .idea/**/contentModel.xml 17 | 18 | # Sensitive or high-churn files 19 | .idea/**/dataSources/ 20 | .idea/**/dataSources.ids 21 | .idea/**/dataSources.local.xml 22 | .idea/**/sqlDataSources.xml 23 | .idea/**/dynamic.xml 24 | .idea/**/uiDesigner.xml 25 | .idea/**/dbnavigator.xml 26 | 27 | # Gradle 28 | .idea/**/gradle.xml 29 | .idea/**/libraries 30 | 31 | # Gradle and Maven with auto-import 32 | # When using Gradle or Maven with auto-import, you should exclude module files, 33 | # since they will be recreated, and may cause churn. Uncomment if using 34 | # auto-import. 35 | # .idea/artifacts 36 | # .idea/compiler.xml 37 | # .idea/jarRepositories.xml 38 | # .idea/modules.xml 39 | # .idea/*.iml 40 | # .idea/modules 41 | # *.iml 42 | # *.ipr 43 | 44 | # CMake 45 | cmake-build-*/ 46 | 47 | # Mongo Explorer plugin 48 | .idea/**/mongoSettings.xml 49 | 50 | # File-based project format 51 | *.iws 52 | 53 | # IntelliJ 54 | out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Cursive Clojure plugin 63 | .idea/replstate.xml 64 | 65 | # SonarLint plugin 66 | .idea/sonarlint/ 67 | 68 | # Crashlytics plugin (for Android Studio and IntelliJ) 69 | com_crashlytics_export_strings.xml 70 | crashlytics.properties 71 | crashlytics-build.properties 72 | fabric.properties 73 | 74 | # Editor-based Rest Client 75 | .idea/httpRequests 76 | 77 | # Android studio 3.1+ serialized cache file 78 | .idea/caches/build_file_checksums.ser 79 | 80 | ### Intellij+all Patch ### 81 | # Ignore everything but code style settings and run configurations 82 | # that are supposed to be shared within teams. 83 | 84 | .idea/* 85 | 86 | !.idea/codeStyles 87 | !.idea/runConfigurations 88 | 89 | ### Rust ### 90 | # Generated by Cargo 91 | # will have compiled files and executables 92 | debug/ 93 | target/ 94 | 95 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 96 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 97 | Cargo.lock 98 | 99 | # These are backup files generated by rustfmt 100 | **/*.rs.bk 101 | 102 | # MSVC Windows builds of rustc generate these, which store debugging information 103 | *.pdb 104 | 105 | ### Vim ### 106 | # Swap 107 | [._]*.s[a-v][a-z] 108 | !*.svg # comment out if you don't need vector files 109 | [._]*.sw[a-p] 110 | [._]s[a-rt-v][a-z] 111 | [._]ss[a-gi-z] 112 | [._]sw[a-p] 113 | 114 | # Session 115 | Session.vim 116 | Sessionx.vim 117 | 118 | # Temporary 119 | .netrwhist 120 | *~ 121 | # Auto-generated tag files 122 | tags 123 | # Persistent undo 124 | [._]*.un~ 125 | 126 | ### VisualStudioCode ### 127 | .vscode/* 128 | !.vscode/settings.json 129 | !.vscode/tasks.json 130 | !.vscode/launch.json 131 | !.vscode/extensions.json 132 | !.vscode/*.code-snippets 133 | 134 | # Local History for Visual Studio Code 135 | .history/ 136 | 137 | # Built Visual Studio Code Extensions 138 | *.vsix 139 | 140 | ### VisualStudioCode Patch ### 141 | # Ignore all local history of files 142 | .history 143 | .ionide -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cidr-utils" 3 | version = "0.6.1" 4 | authors = ["Magic Len "] 5 | edition = "2021" 6 | rust-version = "1.67" 7 | repository = "https://github.com/magiclen/cidr-utils" 8 | homepage = "https://magiclen.org/cidr-utils" 9 | keywords = ["cidr", "ip", "ipv4", "ipv6"] 10 | categories = ["network-programming"] 11 | description = "This crate provides functions for working with IPv4 CIDRs and IPv6 CIDRs." 12 | license = "MIT" 13 | include = ["src/**/*", "Cargo.toml", "README.md", "LICENSE"] 14 | 15 | [dependencies] 16 | cidr = "0.2" 17 | 18 | num-traits = "0.2" 19 | num-bigint = "0.4" 20 | 21 | [features] 22 | default = ["iterator", "combiner", "separator"] 23 | 24 | iterator = [] 25 | combiner = [] 26 | separator = ["combiner", "iterator"] 27 | 28 | [package.metadata.docs.rs] 29 | all-features = true 30 | rustdoc-args = ["--cfg", "docsrs"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 magiclen.org (Ron Li) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CIDR Utils 2 | ==================== 3 | 4 | [![CI](https://github.com/magiclen/cidr-utils/actions/workflows/ci.yml/badge.svg)](https://github.com/magiclen/cidr-utils/actions/workflows/ci.yml) 5 | 6 | This crate provides functions for working with IPv4 CIDRs and IPv6 CIDRs. 7 | 8 | ## Examples 9 | 10 | Combine subnetworks to supernetworks. 11 | 12 | ```rust 13 | use std::str::FromStr; 14 | 15 | use cidr::Ipv4Cidr; 16 | use cidr_utils::Ipv4CidrSize; 17 | use cidr_utils::combiner::Ipv4CidrCombiner; 18 | 19 | let mut combiner = Ipv4CidrCombiner::new(); 20 | 21 | combiner.push(Ipv4Cidr::from_str("192.168.51.100").unwrap()); 22 | 23 | assert_eq!(1, combiner.len()); 24 | assert_eq!("192.168.51.100".to_string(), combiner[0].to_string()); 25 | 26 | combiner.push(Ipv4Cidr::from_str("192.168.51.101").unwrap()); 27 | 28 | assert_eq!(1, combiner.len()); 29 | assert_eq!("192.168.51.100/31".to_string(), combiner[0].to_string()); 30 | 31 | combiner.push(Ipv4Cidr::from_str("192.168.51.102").unwrap()); 32 | 33 | assert_eq!(2, combiner.len()); 34 | assert_eq!("192.168.51.100/31".to_string(), combiner[0].to_string()); 35 | assert_eq!("192.168.51.102".to_string(), combiner[1].to_string()); 36 | 37 | combiner.push(Ipv4Cidr::from_str("192.168.51.103").unwrap()); 38 | 39 | assert_eq!(1, combiner.len()); 40 | assert_eq!("192.168.51.100/30".to_string(), combiner[0].to_string()); 41 | 42 | assert_eq!(true, combiner.contains(&[192, 168, 51, 102].into())); 43 | assert_eq!(false, combiner.contains(&[192, 168, 51, 105].into())); 44 | 45 | assert_eq!(4, combiner.size()); 46 | ``` 47 | 48 | Separate a network into subnetworks. 49 | 50 | ```rust 51 | use std::str::FromStr; 52 | 53 | use cidr::Ipv4Cidr; 54 | use cidr_utils::Ipv4CidrSize; 55 | use cidr_utils::separator::Ipv4CidrSeparator; 56 | 57 | let cidr = Ipv4Cidr::from_str("192.168.56.0/24").unwrap(); 58 | 59 | let result = Ipv4CidrSeparator::divide_by(&cidr, 4).unwrap(); 60 | 61 | assert_eq!(4, result.len()); 62 | assert_eq!(64, result[0].size()); 63 | assert_eq!(64, result[1].size()); 64 | assert_eq!(64, result[2].size()); 65 | assert_eq!(64, result[3].size()); 66 | 67 | assert_eq!("[192.168.56.0/26]".to_string(), result[0].to_string()); 68 | assert_eq!("[192.168.56.64/26]".to_string(), result[1].to_string()); 69 | assert_eq!("[192.168.56.128/26]".to_string(), result[2].to_string()); 70 | assert_eq!("[192.168.56.192/26]".to_string(), result[3].to_string()); 71 | 72 | let result = Ipv4CidrSeparator::divide_by(&cidr, 5).unwrap(); 73 | 74 | assert_eq!(5, result.len()); 75 | assert_eq!(51, result[0].size()); 76 | assert_eq!(51, result[1].size()); 77 | assert_eq!(51, result[2].size()); 78 | assert_eq!(51, result[3].size()); 79 | assert_eq!(52, result[4].size()); 80 | 81 | assert_eq!("[192.168.56.0/27, 192.168.56.32/28, 192.168.56.48/31, 192.168.56.50/32]".to_string(), result[0].to_string()); 82 | assert_eq!("[192.168.56.51/32, 192.168.56.52/30, 192.168.56.56/29, 192.168.56.64/27, 192.168.56.96/30, 192.168.56.100/31]".to_string(), result[1].to_string()); 83 | assert_eq!("[192.168.56.102/31, 192.168.56.104/29, 192.168.56.112/28, 192.168.56.128/28, 192.168.56.144/29, 192.168.56.152/32]".to_string(), result[2].to_string()); 84 | assert_eq!("[192.168.56.153/32, 192.168.56.154/31, 192.168.56.156/30, 192.168.56.160/27, 192.168.56.192/29, 192.168.56.200/30]".to_string(), result[3].to_string()); 85 | assert_eq!("[192.168.56.204/30, 192.168.56.208/28, 192.168.56.224/27]".to_string(), result[4].to_string()); 86 | 87 | let result = Ipv4CidrSeparator::sub_networks(&cidr, 26).unwrap(); 88 | 89 | assert_eq!(4, result.len()); 90 | assert_eq!(64, result[0].size()); 91 | assert_eq!(64, result[1].size()); 92 | assert_eq!(64, result[2].size()); 93 | assert_eq!(64, result[3].size()); 94 | 95 | assert_eq!("192.168.56.0/26".to_string(), result[0].to_string()); 96 | assert_eq!("192.168.56.64/26".to_string(), result[1].to_string()); 97 | assert_eq!("192.168.56.128/26".to_string(), result[2].to_string()); 98 | assert_eq!("192.168.56.192/26".to_string(), result[3].to_string()); 99 | ``` 100 | 101 | ## Crates.io 102 | 103 | https://crates.io/crates/cidr-utils 104 | 105 | ## Documentation 106 | 107 | https://docs.rs/cidr-utils 108 | 109 | ## License 110 | 111 | [MIT](LICENSE) -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # array_width = 60 2 | # attr_fn_like_width = 70 3 | binop_separator = "Front" 4 | blank_lines_lower_bound = 0 5 | blank_lines_upper_bound = 1 6 | brace_style = "PreferSameLine" 7 | # chain_width = 60 8 | color = "Auto" 9 | # comment_width = 100 10 | condense_wildcard_suffixes = true 11 | control_brace_style = "AlwaysSameLine" 12 | empty_item_single_line = true 13 | enum_discrim_align_threshold = 80 14 | error_on_line_overflow = false 15 | error_on_unformatted = false 16 | # fn_call_width = 60 17 | fn_params_layout = "Tall" 18 | fn_single_line = false 19 | force_explicit_abi = true 20 | force_multiline_blocks = false 21 | format_code_in_doc_comments = true 22 | doc_comment_code_block_width = 80 23 | format_generated_files = true 24 | format_macro_matchers = true 25 | format_macro_bodies = true 26 | skip_macro_invocations = [] 27 | format_strings = true 28 | hard_tabs = false 29 | hex_literal_case = "Upper" 30 | imports_indent = "Block" 31 | imports_layout = "Mixed" 32 | indent_style = "Block" 33 | inline_attribute_width = 0 34 | match_arm_blocks = true 35 | match_arm_leading_pipes = "Never" 36 | match_block_trailing_comma = true 37 | max_width = 100 38 | merge_derives = true 39 | imports_granularity = "Crate" 40 | newline_style = "Unix" 41 | normalize_comments = false 42 | normalize_doc_attributes = true 43 | overflow_delimited_expr = true 44 | remove_nested_parens = true 45 | reorder_impl_items = true 46 | reorder_imports = true 47 | group_imports = "StdExternalCrate" 48 | reorder_modules = true 49 | short_array_element_width_threshold = 10 50 | # single_line_if_else_max_width = 50 51 | space_after_colon = true 52 | space_before_colon = false 53 | spaces_around_ranges = false 54 | struct_field_align_threshold = 80 55 | struct_lit_single_line = false 56 | # struct_lit_width = 18 57 | # struct_variant_width = 35 58 | tab_spaces = 4 59 | trailing_comma = "Vertical" 60 | trailing_semicolon = true 61 | type_punctuation_density = "Wide" 62 | use_field_init_shorthand = true 63 | use_small_heuristics = "Max" 64 | use_try_shorthand = true 65 | where_single_line = false 66 | wrap_comments = false -------------------------------------------------------------------------------- /src/combiner/fmt.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{self, Display, Formatter}; 2 | use std::fmt::Write; 3 | 4 | use cidr::{Ipv4Cidr, Ipv6Cidr}; 5 | 6 | use super::{Ipv4CidrCombiner, Ipv6CidrCombiner}; 7 | 8 | pub(crate) struct DisplayIpv4Cidr<'a>(&'a Ipv4Cidr); 9 | 10 | impl<'a> Display for DisplayIpv4Cidr<'a> { 11 | #[inline] 12 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 13 | let ip = self.0.first_address(); 14 | let bits = self.0.network_length(); 15 | 16 | f.write_fmt(format_args!("{ip}/{bits}")) 17 | } 18 | } 19 | 20 | impl Display for Ipv4CidrCombiner { 21 | #[inline] 22 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 23 | f.write_char('[')?; 24 | 25 | let length = self.len(); 26 | 27 | if length > 0 { 28 | let length_dec = length - 1; 29 | 30 | for cidr in self.iter().take(length_dec) { 31 | f.write_fmt(format_args!("{}, ", DisplayIpv4Cidr(cidr)))? 32 | } 33 | 34 | f.write_fmt(format_args!("{}", DisplayIpv4Cidr(&self[length_dec])))?; 35 | } 36 | 37 | f.write_char(']') 38 | } 39 | } 40 | 41 | pub(crate) struct DisplayIpv6Cidr<'a>(&'a Ipv6Cidr); 42 | 43 | impl<'a> Display for DisplayIpv6Cidr<'a> { 44 | #[inline] 45 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 46 | let ip = self.0.first_address(); 47 | let bits = self.0.network_length(); 48 | 49 | f.write_fmt(format_args!("{ip}/{bits}")) 50 | } 51 | } 52 | 53 | impl Display for Ipv6CidrCombiner { 54 | #[inline] 55 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 56 | f.write_char('[')?; 57 | 58 | let length = self.len(); 59 | 60 | if length > 0 { 61 | let length_dec = length - 1; 62 | 63 | for cidr in self.iter().take(length_dec) { 64 | f.write_fmt(format_args!("{}, ", DisplayIpv6Cidr(cidr)))? 65 | } 66 | 67 | f.write_fmt(format_args!("{}", DisplayIpv6Cidr(&self[length_dec])))?; 68 | } 69 | 70 | f.write_char(']') 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/combiner/mod.rs: -------------------------------------------------------------------------------- 1 | mod fmt; 2 | mod v4; 3 | mod v6; 4 | 5 | pub use v4::*; 6 | pub use v6::*; 7 | -------------------------------------------------------------------------------- /src/combiner/v4.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Deref; 2 | use std::net::Ipv4Addr; 3 | 4 | use cidr::Ipv4Cidr; 5 | 6 | use crate::Ipv4CidrSize; 7 | 8 | /// To combine multiple IPv4 CIDRs to supernetworks. 9 | #[derive(Debug, Clone)] 10 | pub struct Ipv4CidrCombiner(Vec); 11 | 12 | impl Default for Ipv4CidrCombiner { 13 | #[inline] 14 | fn default() -> Self { 15 | Ipv4CidrCombiner::new() 16 | } 17 | } 18 | 19 | impl Deref for Ipv4CidrCombiner { 20 | type Target = Vec; 21 | 22 | #[inline] 23 | fn deref(&self) -> &Vec { 24 | &self.0 25 | } 26 | } 27 | 28 | impl Ipv4CidrCombiner { 29 | /// Create a new `Ipv4CidrCombiner` instance. 30 | #[inline] 31 | pub const fn new() -> Ipv4CidrCombiner { 32 | Ipv4CidrCombiner(Vec::new()) 33 | } 34 | 35 | /// Create a new `Ipv4CidrCombiner` instance with a specific capacity. 36 | #[inline] 37 | pub fn with_capacity(capacity: usize) -> Ipv4CidrCombiner { 38 | Ipv4CidrCombiner(Vec::with_capacity(capacity)) 39 | } 40 | 41 | /// Create a new `Ipv4CidrCombiner` instance with an existing array. 42 | /// 43 | /// # Safety 44 | /// 45 | /// You must ensure that the input array is ordered. 46 | #[inline] 47 | pub const unsafe fn from_ipv4_cidr_vec_unchecked(cidr_vec: Vec) -> Ipv4CidrCombiner { 48 | Ipv4CidrCombiner(cidr_vec) 49 | } 50 | 51 | #[inline] 52 | pub fn into_ipv4_cidr_vec(self) -> Vec { 53 | self.0 54 | } 55 | } 56 | 57 | impl Ipv4CidrCombiner { 58 | /// Push a CIDR into this combiner. 59 | pub fn push(&mut self, mut cidr: Ipv4Cidr) { 60 | if let Err(mut index) = self.0.binary_search(&cidr) { 61 | if self.0.is_empty() { 62 | self.0.push(cidr); 63 | } else { 64 | let pushable = if index == 0 { 65 | true 66 | } else { 67 | let previous_cidr = self.0.get(index - 1).unwrap(); 68 | 69 | !previous_cidr.contains(&cidr.first_address()) 70 | }; 71 | 72 | if pushable { 73 | loop { 74 | if index == self.0.len() { 75 | break; 76 | } 77 | 78 | let next = self.0.get(index).unwrap(); 79 | 80 | if cidr.contains(&next.first_address()) { 81 | self.0.remove(index); 82 | } else { 83 | break; 84 | } 85 | } 86 | 87 | let mut merging = true; 88 | 89 | while merging { 90 | merging = false; 91 | 92 | if index < self.0.len() { 93 | let next_cidr = self.0.get(index).unwrap(); 94 | 95 | let next_bits = next_cidr.network_length(); 96 | let bits = cidr.network_length(); 97 | 98 | if bits == next_bits { 99 | let next_prefix: u32 = next_cidr.first_address().into(); 100 | let prefix: u32 = cidr.first_address().into(); 101 | 102 | let d = next_prefix ^ prefix; 103 | 104 | if d == 1 << (32 - bits) as u32 { 105 | cidr = Ipv4Cidr::new(prefix.into(), bits - 1).unwrap(); 106 | 107 | self.0.remove(index); 108 | 109 | merging = true; 110 | } 111 | } 112 | } 113 | 114 | if index > 0 { 115 | let index_dec = index - 1; 116 | 117 | let previous_cidr = self.0.get_mut(index_dec).unwrap(); 118 | 119 | let previous_bits = previous_cidr.network_length(); 120 | let bits = cidr.network_length(); 121 | 122 | if bits == previous_bits { 123 | let previous_prefix: u32 = previous_cidr.first_address().into(); 124 | let prefix: u32 = cidr.first_address().into(); 125 | 126 | let d = prefix ^ previous_prefix; 127 | 128 | if d == 1 << (32 - bits) as u32 { 129 | self.0.remove(index_dec); 130 | 131 | index = index_dec; 132 | 133 | cidr = Ipv4Cidr::new(previous_prefix.into(), previous_bits - 1) 134 | .unwrap(); 135 | 136 | merging = true; 137 | } 138 | } 139 | } 140 | } 141 | 142 | self.0.insert(index, cidr); 143 | } 144 | } 145 | } 146 | } 147 | 148 | /// Check an IPv4 whether it is in these CIDRs. 149 | #[inline] 150 | pub fn contains(&self, ipv4: &Ipv4Addr) -> bool { 151 | for cidr in self.0.iter() { 152 | if cidr.contains(ipv4) { 153 | return true; 154 | } 155 | } 156 | 157 | false 158 | } 159 | 160 | /// Get the total size of CIDRs. 161 | #[inline] 162 | pub fn size(&self) -> u64 { 163 | let mut sum = 0; 164 | 165 | for cidr in self.0.iter() { 166 | sum += cidr.size(); 167 | } 168 | 169 | sum 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/combiner/v6.rs: -------------------------------------------------------------------------------- 1 | use core::ops::Deref; 2 | use std::net::Ipv6Addr; 3 | 4 | use cidr::Ipv6Cidr; 5 | use num_bigint::BigUint; 6 | use num_traits::Zero; 7 | 8 | use crate::Ipv6CidrSize; 9 | 10 | /// To combine multiple IPv6 CIDRs to supernetworks. 11 | #[derive(Debug, Clone)] 12 | pub struct Ipv6CidrCombiner(Vec); 13 | 14 | impl Default for Ipv6CidrCombiner { 15 | #[inline] 16 | fn default() -> Self { 17 | Ipv6CidrCombiner::new() 18 | } 19 | } 20 | 21 | impl Deref for Ipv6CidrCombiner { 22 | type Target = Vec; 23 | 24 | #[inline] 25 | fn deref(&self) -> &Vec { 26 | &self.0 27 | } 28 | } 29 | 30 | impl Ipv6CidrCombiner { 31 | /// Create a new `Ipv6CidrCombiner` instance. 32 | #[inline] 33 | pub const fn new() -> Ipv6CidrCombiner { 34 | Ipv6CidrCombiner(Vec::new()) 35 | } 36 | 37 | /// Create a new `Ipv6CidrCombiner` instance with a specific capacity. 38 | #[inline] 39 | pub fn with_capacity(capacity: usize) -> Ipv6CidrCombiner { 40 | Ipv6CidrCombiner(Vec::with_capacity(capacity)) 41 | } 42 | 43 | /// Create a new `Ipv6CidrCombiner` instance with an existing array. 44 | /// 45 | /// # Safety 46 | /// 47 | /// You must ensure that the input array is ordered. 48 | #[inline] 49 | pub const unsafe fn from_ipv6_cidr_vec_unchecked(cidr_vec: Vec) -> Ipv6CidrCombiner { 50 | Ipv6CidrCombiner(cidr_vec) 51 | } 52 | 53 | #[inline] 54 | pub fn into_ipv6_cidr_vec(self) -> Vec { 55 | self.0 56 | } 57 | } 58 | 59 | impl Ipv6CidrCombiner { 60 | /// Push a CIDR into this combiner. 61 | pub fn push(&mut self, mut cidr: Ipv6Cidr) { 62 | if let Err(mut index) = self.0.binary_search(&cidr) { 63 | if self.0.is_empty() { 64 | self.0.push(cidr); 65 | } else { 66 | let pushable = if index == 0 { 67 | true 68 | } else { 69 | let previous_cidr = self.0.get(index - 1).unwrap(); 70 | 71 | !previous_cidr.contains(&cidr.first_address()) 72 | }; 73 | 74 | if pushable { 75 | loop { 76 | if index == self.0.len() { 77 | break; 78 | } 79 | 80 | let next = self.0.get(index).unwrap(); 81 | 82 | if cidr.contains(&next.first_address()) { 83 | self.0.remove(index); 84 | } else { 85 | break; 86 | } 87 | } 88 | 89 | let mut merging = true; 90 | 91 | while merging { 92 | merging = false; 93 | 94 | if index < self.0.len() { 95 | let next_cidr = self.0.get(index).unwrap(); 96 | 97 | let next_bits = next_cidr.network_length(); 98 | let bits = cidr.network_length(); 99 | 100 | if bits == next_bits { 101 | let next_prefix: u128 = next_cidr.first_address().into(); 102 | let prefix: u128 = cidr.first_address().into(); 103 | 104 | let d = next_prefix ^ prefix; 105 | 106 | if d == 1 << (128 - bits) as u128 { 107 | cidr = Ipv6Cidr::new(prefix.into(), bits - 1).unwrap(); 108 | 109 | self.0.remove(index); 110 | 111 | merging = true; 112 | } 113 | } 114 | } 115 | 116 | if index > 0 { 117 | let index_dec = index - 1; 118 | 119 | let previous_cidr = self.0.get_mut(index_dec).unwrap(); 120 | 121 | let previous_bits = previous_cidr.network_length(); 122 | let bits = cidr.network_length(); 123 | 124 | if bits == previous_bits { 125 | let previous_prefix: u128 = previous_cidr.first_address().into(); 126 | let prefix: u128 = cidr.first_address().into(); 127 | 128 | let d = prefix ^ previous_prefix; 129 | 130 | if d == 1 << (128 - bits) as u128 { 131 | self.0.remove(index_dec); 132 | 133 | index = index_dec; 134 | 135 | cidr = Ipv6Cidr::new(previous_prefix.into(), previous_bits - 1) 136 | .unwrap(); 137 | 138 | merging = true; 139 | } 140 | } 141 | } 142 | } 143 | 144 | self.0.insert(index, cidr); 145 | } 146 | } 147 | } 148 | } 149 | 150 | /// Check an IPv6 whether it is in these CIDRs. 151 | #[inline] 152 | pub fn contains(&self, ipv6: &Ipv6Addr) -> bool { 153 | for cidr in self.0.iter() { 154 | if cidr.contains(ipv6) { 155 | return true; 156 | } 157 | } 158 | 159 | false 160 | } 161 | 162 | /// Get the total size of CIDRs. 163 | #[inline] 164 | pub fn size(&self) -> BigUint { 165 | let mut sum = BigUint::zero(); 166 | 167 | for cidr in self.0.iter() { 168 | sum += cidr.size(); 169 | } 170 | 171 | sum 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/iterator/mod.rs: -------------------------------------------------------------------------------- 1 | mod v4; 2 | mod v6; 3 | 4 | pub use v4::*; 5 | pub use v6::*; 6 | -------------------------------------------------------------------------------- /src/iterator/v4.rs: -------------------------------------------------------------------------------- 1 | use std::net::Ipv4Addr; 2 | 3 | use cidr::Ipv4Cidr; 4 | 5 | use crate::Ipv4CidrSize; 6 | 7 | // TODO: Ipv4CidrU8ArrayIterator 8 | 9 | /// To iterate IPv4 CIDRs. 10 | #[derive(Debug)] 11 | pub struct Ipv4CidrU8ArrayIterator { 12 | from: u32, 13 | size: u64, 14 | next: u64, 15 | back: u64, 16 | } 17 | 18 | impl Ipv4CidrU8ArrayIterator { 19 | #[inline] 20 | pub fn new(cidr: &Ipv4Cidr) -> Self { 21 | let from: u32 = cidr.first_address().into(); 22 | let size = cidr.size(); 23 | 24 | Self { 25 | from, 26 | size, 27 | next: 0, 28 | back: size, 29 | } 30 | } 31 | } 32 | 33 | impl Ipv4CidrU8ArrayIterator { 34 | #[inline] 35 | unsafe fn next_unchecked(&mut self) -> [u8; 4] { 36 | let p = self.from + self.next as u32; 37 | 38 | self.next += 1; 39 | 40 | p.to_be_bytes() 41 | } 42 | 43 | #[inline] 44 | unsafe fn next_back_unchecked(&mut self) -> [u8; 4] { 45 | self.back -= 1; 46 | 47 | let p = self.from + self.back as u32; 48 | 49 | p.to_be_bytes() 50 | } 51 | 52 | #[inline] 53 | pub fn nth_u64(&mut self, n: u64) -> Option<[u8; 4]> { 54 | self.next += n; 55 | 56 | if self.next < self.back { 57 | Some(unsafe { self.next_unchecked() }) 58 | } else { 59 | self.next = self.size; 60 | 61 | None 62 | } 63 | } 64 | 65 | #[inline] 66 | pub fn nth_back_u64(&mut self, n: u64) -> Option<[u8; 4]> { 67 | if self.back > n { 68 | self.back -= n; 69 | 70 | if self.next < self.back { 71 | return Some(unsafe { self.next_back_unchecked() }); 72 | } 73 | } 74 | 75 | self.next = self.size; 76 | 77 | None 78 | } 79 | } 80 | 81 | impl Iterator for Ipv4CidrU8ArrayIterator { 82 | type Item = [u8; 4]; 83 | 84 | #[inline] 85 | fn next(&mut self) -> Option { 86 | if self.next < self.back { 87 | Some(unsafe { self.next_unchecked() }) 88 | } else { 89 | None 90 | } 91 | } 92 | 93 | #[cfg(not(any( 94 | target_pointer_width = "8", 95 | target_pointer_width = "16", 96 | target_pointer_width = "32" 97 | )))] 98 | #[inline] 99 | fn size_hint(&self) -> (usize, Option) { 100 | let remaining_ips = (self.back - self.next) as usize; 101 | 102 | (remaining_ips, Some(remaining_ips)) 103 | } 104 | 105 | #[cfg(not(any( 106 | target_pointer_width = "8", 107 | target_pointer_width = "16", 108 | target_pointer_width = "32" 109 | )))] 110 | #[inline] 111 | fn count(self) -> usize 112 | where 113 | Self: Sized, { 114 | if self.next < self.back { 115 | (self.back - self.next) as usize 116 | } else { 117 | 0 118 | } 119 | } 120 | 121 | #[inline] 122 | fn last(mut self) -> Option { 123 | if self.next < self.back { 124 | self.next = self.back - 1; 125 | 126 | Some(unsafe { self.next_unchecked() }) 127 | } else { 128 | None 129 | } 130 | } 131 | 132 | #[inline] 133 | fn nth(&mut self, n: usize) -> Option { 134 | self.nth_u64(n as u64) 135 | } 136 | } 137 | 138 | impl DoubleEndedIterator for Ipv4CidrU8ArrayIterator { 139 | #[inline] 140 | fn next_back(&mut self) -> Option { 141 | if self.next < self.back { 142 | Some(unsafe { self.next_back_unchecked() }) 143 | } else { 144 | None 145 | } 146 | } 147 | 148 | #[inline] 149 | fn nth_back(&mut self, n: usize) -> Option { 150 | self.nth_back_u64(n as u64) 151 | } 152 | } 153 | 154 | // TODO: Ipv4CidrIterator 155 | 156 | /// To iterate IPv4 CIDRs. 157 | #[derive(Debug)] 158 | pub struct Ipv4CidrIterator { 159 | iter: Ipv4CidrU8ArrayIterator, 160 | } 161 | 162 | impl Ipv4CidrIterator { 163 | #[inline] 164 | pub fn new(cidr: &Ipv4Cidr) -> Self { 165 | Self { 166 | iter: Ipv4CidrU8ArrayIterator::new(cidr) 167 | } 168 | } 169 | } 170 | 171 | impl Ipv4CidrIterator { 172 | #[inline] 173 | pub fn nth_u64(&mut self, n: u64) -> Option { 174 | self.iter.nth_u64(n).map(u32::from_be_bytes) 175 | } 176 | } 177 | 178 | impl Iterator for Ipv4CidrIterator { 179 | type Item = u32; 180 | 181 | #[inline] 182 | fn next(&mut self) -> Option { 183 | self.iter.next().map(u32::from_be_bytes) 184 | } 185 | 186 | #[inline] 187 | fn last(self) -> Option { 188 | self.iter.last().map(u32::from_be_bytes) 189 | } 190 | 191 | #[inline] 192 | fn nth(&mut self, n: usize) -> Option { 193 | self.iter.nth(n).map(u32::from_be_bytes) 194 | } 195 | } 196 | 197 | impl DoubleEndedIterator for Ipv4CidrIterator { 198 | #[inline] 199 | fn next_back(&mut self) -> Option { 200 | self.iter.next_back().map(u32::from_be_bytes) 201 | } 202 | 203 | #[inline] 204 | fn nth_back(&mut self, n: usize) -> Option { 205 | self.iter.nth_back(n).map(u32::from_be_bytes) 206 | } 207 | } 208 | 209 | // TODO: Ipv4CidrIpv4AddrIterator 210 | 211 | /// To iterate IPv4 CIDRs. 212 | #[derive(Debug)] 213 | pub struct Ipv4CidrIpv4AddrIterator { 214 | iter: Ipv4CidrU8ArrayIterator, 215 | } 216 | 217 | impl Ipv4CidrIpv4AddrIterator { 218 | #[inline] 219 | pub fn new(cidr: &Ipv4Cidr) -> Self { 220 | Self { 221 | iter: Ipv4CidrU8ArrayIterator::new(cidr) 222 | } 223 | } 224 | } 225 | 226 | impl Ipv4CidrIpv4AddrIterator { 227 | #[inline] 228 | pub fn nth_u64(&mut self, n: u64) -> Option { 229 | self.iter.nth_u64(n).map(|a| a.into()) 230 | } 231 | } 232 | 233 | impl Iterator for Ipv4CidrIpv4AddrIterator { 234 | type Item = Ipv4Addr; 235 | 236 | #[inline] 237 | fn next(&mut self) -> Option { 238 | self.iter.next().map(|a| a.into()) 239 | } 240 | 241 | #[inline] 242 | fn last(self) -> Option { 243 | self.iter.last().map(|a| a.into()) 244 | } 245 | 246 | #[inline] 247 | fn nth(&mut self, n: usize) -> Option { 248 | self.iter.nth(n).map(|a| a.into()) 249 | } 250 | } 251 | 252 | impl DoubleEndedIterator for Ipv4CidrIpv4AddrIterator { 253 | #[inline] 254 | fn next_back(&mut self) -> Option { 255 | self.iter.next_back().map(|a| a.into()) 256 | } 257 | 258 | #[inline] 259 | fn nth_back(&mut self, n: usize) -> Option { 260 | self.iter.nth_back(n).map(|a| a.into()) 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/iterator/v6.rs: -------------------------------------------------------------------------------- 1 | use std::net::Ipv6Addr; 2 | 3 | use cidr::Ipv6Cidr; 4 | use num_bigint::BigUint; 5 | use num_traits::{One, ToPrimitive, Zero}; 6 | 7 | use crate::Ipv6CidrSize; 8 | 9 | // TODO: Ipv6CidrU8ArrayIterator 10 | 11 | /// To iterate IPv6 CIDRs. 12 | #[derive(Debug)] 13 | pub struct Ipv6CidrU8ArrayIterator { 14 | from: u128, 15 | size: BigUint, 16 | next: BigUint, 17 | back: BigUint, 18 | } 19 | 20 | impl Ipv6CidrU8ArrayIterator { 21 | #[inline] 22 | pub fn new(cidr: &Ipv6Cidr) -> Self { 23 | let from: u128 = cidr.first_address().into(); 24 | let size = cidr.size(); 25 | 26 | Self { 27 | from, 28 | size: size.clone(), 29 | next: BigUint::zero(), 30 | back: size, 31 | } 32 | } 33 | } 34 | 35 | impl Ipv6CidrU8ArrayIterator { 36 | #[inline] 37 | unsafe fn next_unchecked(&mut self) -> [u8; 16] { 38 | let p = self.from + self.next.to_u128().unwrap(); 39 | 40 | self.next += BigUint::one(); 41 | 42 | p.to_be_bytes() 43 | } 44 | 45 | #[inline] 46 | unsafe fn next_back_unchecked(&mut self) -> [u8; 16] { 47 | self.back -= BigUint::one(); 48 | 49 | let p = self.from + self.back.to_u128().unwrap(); 50 | 51 | p.to_be_bytes() 52 | } 53 | 54 | #[inline] 55 | pub fn nth_big_uint(&mut self, n: BigUint) -> Option<[u8; 16]> { 56 | self.next += n; 57 | 58 | if self.next < self.back { 59 | Some(unsafe { self.next_unchecked() }) 60 | } else { 61 | self.next = self.size.clone(); 62 | 63 | None 64 | } 65 | } 66 | 67 | #[inline] 68 | pub fn nth_back_big_uint(&mut self, n: BigUint) -> Option<[u8; 16]> { 69 | if self.back > n { 70 | self.back -= n; 71 | 72 | if self.next < self.back { 73 | return Some(unsafe { self.next_back_unchecked() }); 74 | } 75 | } 76 | 77 | self.next = self.size.clone(); 78 | 79 | None 80 | } 81 | } 82 | 83 | impl Iterator for Ipv6CidrU8ArrayIterator { 84 | type Item = [u8; 16]; 85 | 86 | #[inline] 87 | fn next(&mut self) -> Option { 88 | if self.next < self.back { 89 | Some(unsafe { self.next_unchecked() }) 90 | } else { 91 | None 92 | } 93 | } 94 | 95 | #[inline] 96 | fn last(mut self) -> Option { 97 | if self.next < self.back { 98 | self.next = self.back.clone() - BigUint::one(); 99 | 100 | Some(unsafe { self.next_unchecked() }) 101 | } else { 102 | None 103 | } 104 | } 105 | 106 | #[inline] 107 | fn nth(&mut self, n: usize) -> Option { 108 | self.nth_big_uint(BigUint::from(n)) 109 | } 110 | } 111 | 112 | impl DoubleEndedIterator for Ipv6CidrU8ArrayIterator { 113 | #[inline] 114 | fn next_back(&mut self) -> Option { 115 | if self.next < self.back { 116 | Some(unsafe { self.next_back_unchecked() }) 117 | } else { 118 | None 119 | } 120 | } 121 | 122 | #[inline] 123 | fn nth_back(&mut self, n: usize) -> Option { 124 | self.nth_back_big_uint(BigUint::from(n)) 125 | } 126 | } 127 | 128 | // TODO: Ipv6CidrU8ArrayIterator 129 | 130 | /// To iterate IPv6 CIDRs. 131 | #[derive(Debug)] 132 | pub struct Ipv6CidrU16ArrayIterator { 133 | from: u128, 134 | next: BigUint, 135 | back: BigUint, 136 | size: BigUint, 137 | } 138 | 139 | impl Ipv6CidrU16ArrayIterator { 140 | #[inline] 141 | pub fn new(cidr: &Ipv6Cidr) -> Self { 142 | let from: u128 = cidr.first_address().into(); 143 | let size = cidr.size(); 144 | 145 | Self { 146 | from, 147 | size: size.clone(), 148 | next: BigUint::zero(), 149 | back: size, 150 | } 151 | } 152 | } 153 | 154 | impl Ipv6CidrU16ArrayIterator { 155 | #[inline] 156 | unsafe fn next_unchecked(&mut self) -> [u16; 8] { 157 | let p = self.from + self.next.to_u128().unwrap(); 158 | 159 | self.next += BigUint::one(); 160 | 161 | u128_to_u16_array(p) 162 | } 163 | 164 | #[inline] 165 | unsafe fn next_back_unchecked(&mut self) -> [u16; 8] { 166 | self.back -= BigUint::one(); 167 | 168 | let p = self.from + self.back.to_u128().unwrap(); 169 | 170 | u128_to_u16_array(p) 171 | } 172 | 173 | #[inline] 174 | pub fn nth_big_uint(&mut self, n: BigUint) -> Option<[u16; 8]> { 175 | self.next += n; 176 | 177 | if self.next < self.back { 178 | Some(unsafe { self.next_unchecked() }) 179 | } else { 180 | self.next = self.size.clone(); 181 | 182 | None 183 | } 184 | } 185 | 186 | #[inline] 187 | pub fn nth_back_big_uint(&mut self, n: BigUint) -> Option<[u16; 8]> { 188 | if self.back > n { 189 | self.back -= n; 190 | 191 | if self.next < self.back { 192 | return Some(unsafe { self.next_back_unchecked() }); 193 | } 194 | } 195 | 196 | self.next = self.size.clone(); 197 | 198 | None 199 | } 200 | } 201 | 202 | impl Iterator for Ipv6CidrU16ArrayIterator { 203 | type Item = [u16; 8]; 204 | 205 | #[inline] 206 | fn next(&mut self) -> Option { 207 | if self.next < self.back { 208 | Some(unsafe { self.next_unchecked() }) 209 | } else { 210 | None 211 | } 212 | } 213 | 214 | #[inline] 215 | fn last(mut self) -> Option { 216 | if self.next < self.back { 217 | self.next = self.back.clone() - BigUint::one(); 218 | 219 | Some(unsafe { self.next_unchecked() }) 220 | } else { 221 | None 222 | } 223 | } 224 | 225 | #[inline] 226 | fn nth(&mut self, n: usize) -> Option { 227 | self.nth_big_uint(BigUint::from(n)) 228 | } 229 | } 230 | 231 | impl DoubleEndedIterator for Ipv6CidrU16ArrayIterator { 232 | #[inline] 233 | fn next_back(&mut self) -> Option { 234 | if self.next < self.back { 235 | Some(unsafe { self.next_back_unchecked() }) 236 | } else { 237 | None 238 | } 239 | } 240 | 241 | #[inline] 242 | fn nth_back(&mut self, n: usize) -> Option { 243 | self.nth_back_big_uint(BigUint::from(n)) 244 | } 245 | } 246 | 247 | // TODO: Ipv6CidrIterator 248 | 249 | /// To iterate IPv6 CIDRs. 250 | #[derive(Debug)] 251 | pub struct Ipv6CidrIterator { 252 | iter: Ipv6CidrU8ArrayIterator, 253 | } 254 | 255 | impl Ipv6CidrIterator { 256 | #[inline] 257 | pub fn new(cidr: &Ipv6Cidr) -> Self { 258 | Self { 259 | iter: Ipv6CidrU8ArrayIterator::new(cidr) 260 | } 261 | } 262 | } 263 | 264 | impl Ipv6CidrIterator { 265 | #[inline] 266 | pub fn nth_big_uint(&mut self, n: BigUint) -> Option { 267 | self.iter.nth_big_uint(n).map(u128::from_be_bytes) 268 | } 269 | 270 | #[inline] 271 | pub fn nth_back_big_uint(&mut self, n: BigUint) -> Option { 272 | self.iter.nth_back_big_uint(n).map(u128::from_be_bytes) 273 | } 274 | } 275 | 276 | impl Iterator for Ipv6CidrIterator { 277 | type Item = u128; 278 | 279 | #[inline] 280 | fn next(&mut self) -> Option { 281 | self.iter.next().map(u128::from_be_bytes) 282 | } 283 | 284 | #[inline] 285 | fn last(self) -> Option { 286 | self.iter.last().map(u128::from_be_bytes) 287 | } 288 | 289 | #[inline] 290 | fn nth(&mut self, n: usize) -> Option { 291 | self.iter.nth(n).map(u128::from_be_bytes) 292 | } 293 | } 294 | 295 | impl DoubleEndedIterator for Ipv6CidrIterator { 296 | #[inline] 297 | fn next_back(&mut self) -> Option { 298 | self.iter.next_back().map(u128::from_be_bytes) 299 | } 300 | 301 | #[inline] 302 | fn nth_back(&mut self, n: usize) -> Option { 303 | self.iter.nth_back(n).map(u128::from_be_bytes) 304 | } 305 | } 306 | 307 | // TODO: Ipv6CidrIpv6AddrIterator 308 | 309 | /// To iterate IPv4 CIDRs. 310 | #[derive(Debug)] 311 | pub struct Ipv6CidrIpv6AddrIterator { 312 | iter: Ipv6CidrU16ArrayIterator, 313 | } 314 | 315 | impl Ipv6CidrIpv6AddrIterator { 316 | #[inline] 317 | pub fn new(cidr: &Ipv6Cidr) -> Self { 318 | Self { 319 | iter: Ipv6CidrU16ArrayIterator::new(cidr) 320 | } 321 | } 322 | } 323 | 324 | impl Ipv6CidrIpv6AddrIterator { 325 | #[inline] 326 | pub fn nth_big_uint(&mut self, n: BigUint) -> Option { 327 | self.iter 328 | .nth_big_uint(n) 329 | .map(|a| Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 330 | } 331 | 332 | #[inline] 333 | pub fn nth_back_big_int(&mut self, n: BigUint) -> Option { 334 | self.iter 335 | .nth_back_big_uint(n) 336 | .map(|a| Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 337 | } 338 | } 339 | 340 | impl Iterator for Ipv6CidrIpv6AddrIterator { 341 | type Item = Ipv6Addr; 342 | 343 | #[inline] 344 | fn next(&mut self) -> Option { 345 | self.iter.next().map(|a| Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 346 | } 347 | 348 | #[inline] 349 | fn last(self) -> Option { 350 | self.iter.last().map(|a| Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 351 | } 352 | 353 | #[inline] 354 | fn nth(&mut self, n: usize) -> Option { 355 | self.iter.nth(n).map(|a| Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 356 | } 357 | } 358 | 359 | impl DoubleEndedIterator for Ipv6CidrIpv6AddrIterator { 360 | #[inline] 361 | fn next_back(&mut self) -> Option { 362 | self.iter.next_back().map(|a| Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 363 | } 364 | 365 | #[inline] 366 | fn nth_back(&mut self, n: usize) -> Option { 367 | self.iter.nth_back(n).map(|a| Ipv6Addr::new(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])) 368 | } 369 | } 370 | 371 | fn u128_to_u16_array(uint128: u128) -> [u16; 8] { 372 | let a = uint128.to_be_bytes(); 373 | 374 | let mut o = [0; 8]; 375 | 376 | for (i, e) in o.iter_mut().enumerate() { 377 | let ii = i * 2; 378 | 379 | *e = a[ii] as u16 * 256 + a[ii + 1] as u16; 380 | } 381 | 382 | o 383 | } 384 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | # CIDR Utils 3 | 4 | This crate provides functions for working with IPv4 CIDRs and IPv6 CIDRs. 5 | 6 | ## Examples 7 | 8 | Combine subnetworks to supernetworks. 9 | 10 | ```rust 11 | # #[cfg(feature = "combiner")] 12 | # { 13 | use std::str::FromStr; 14 | 15 | use cidr::Ipv4Cidr; 16 | use cidr_utils::Ipv4CidrSize; 17 | use cidr_utils::combiner::Ipv4CidrCombiner; 18 | 19 | let mut combiner = Ipv4CidrCombiner::new(); 20 | 21 | combiner.push(Ipv4Cidr::from_str("192.168.51.100").unwrap()); 22 | 23 | assert_eq!(1, combiner.len()); 24 | assert_eq!("192.168.51.100".to_string(), combiner[0].to_string()); 25 | 26 | combiner.push(Ipv4Cidr::from_str("192.168.51.101").unwrap()); 27 | 28 | assert_eq!(1, combiner.len()); 29 | assert_eq!("192.168.51.100/31".to_string(), combiner[0].to_string()); 30 | 31 | combiner.push(Ipv4Cidr::from_str("192.168.51.102").unwrap()); 32 | 33 | assert_eq!(2, combiner.len()); 34 | assert_eq!("192.168.51.100/31".to_string(), combiner[0].to_string()); 35 | assert_eq!("192.168.51.102".to_string(), combiner[1].to_string()); 36 | 37 | combiner.push(Ipv4Cidr::from_str("192.168.51.103").unwrap()); 38 | 39 | assert_eq!(1, combiner.len()); 40 | assert_eq!("192.168.51.100/30".to_string(), combiner[0].to_string()); 41 | 42 | assert_eq!(true, combiner.contains(&[192, 168, 51, 102].into())); 43 | assert_eq!(false, combiner.contains(&[192, 168, 51, 105].into())); 44 | 45 | assert_eq!(4, combiner.size()); 46 | # } 47 | ``` 48 | 49 | Separate a network into subnetworks. 50 | 51 | ```rust 52 | # #[cfg(feature = "separator")] 53 | # { 54 | use std::str::FromStr; 55 | 56 | use cidr::Ipv4Cidr; 57 | use cidr_utils::Ipv4CidrSize; 58 | use cidr_utils::separator::Ipv4CidrSeparator; 59 | 60 | let cidr = Ipv4Cidr::from_str("192.168.56.0/24").unwrap(); 61 | 62 | let result = Ipv4CidrSeparator::divide_by(&cidr, 4).unwrap(); 63 | 64 | assert_eq!(4, result.len()); 65 | assert_eq!(64, result[0].size()); 66 | assert_eq!(64, result[1].size()); 67 | assert_eq!(64, result[2].size()); 68 | assert_eq!(64, result[3].size()); 69 | 70 | assert_eq!("[192.168.56.0/26]".to_string(), result[0].to_string()); 71 | assert_eq!("[192.168.56.64/26]".to_string(), result[1].to_string()); 72 | assert_eq!("[192.168.56.128/26]".to_string(), result[2].to_string()); 73 | assert_eq!("[192.168.56.192/26]".to_string(), result[3].to_string()); 74 | 75 | let result = Ipv4CidrSeparator::divide_by(&cidr, 5).unwrap(); 76 | 77 | assert_eq!(5, result.len()); 78 | assert_eq!(51, result[0].size()); 79 | assert_eq!(51, result[1].size()); 80 | assert_eq!(51, result[2].size()); 81 | assert_eq!(51, result[3].size()); 82 | assert_eq!(52, result[4].size()); 83 | 84 | assert_eq!("[192.168.56.0/27, 192.168.56.32/28, 192.168.56.48/31, 192.168.56.50/32]".to_string(), result[0].to_string()); 85 | assert_eq!("[192.168.56.51/32, 192.168.56.52/30, 192.168.56.56/29, 192.168.56.64/27, 192.168.56.96/30, 192.168.56.100/31]".to_string(), result[1].to_string()); 86 | assert_eq!("[192.168.56.102/31, 192.168.56.104/29, 192.168.56.112/28, 192.168.56.128/28, 192.168.56.144/29, 192.168.56.152/32]".to_string(), result[2].to_string()); 87 | assert_eq!("[192.168.56.153/32, 192.168.56.154/31, 192.168.56.156/30, 192.168.56.160/27, 192.168.56.192/29, 192.168.56.200/30]".to_string(), result[3].to_string()); 88 | assert_eq!("[192.168.56.204/30, 192.168.56.208/28, 192.168.56.224/27]".to_string(), result[4].to_string()); 89 | 90 | let result = Ipv4CidrSeparator::sub_networks(&cidr, 26).unwrap(); 91 | 92 | assert_eq!(4, result.len()); 93 | assert_eq!(64, result[0].size()); 94 | assert_eq!(64, result[1].size()); 95 | assert_eq!(64, result[2].size()); 96 | assert_eq!(64, result[3].size()); 97 | 98 | assert_eq!("192.168.56.0/26".to_string(), result[0].to_string()); 99 | assert_eq!("192.168.56.64/26".to_string(), result[1].to_string()); 100 | assert_eq!("192.168.56.128/26".to_string(), result[2].to_string()); 101 | assert_eq!("192.168.56.192/26".to_string(), result[3].to_string()); 102 | # } 103 | ``` 104 | */ 105 | 106 | #![cfg_attr(docsrs, feature(doc_auto_cfg))] 107 | 108 | #[cfg(feature = "combiner")] 109 | pub mod combiner; 110 | #[cfg(feature = "iterator")] 111 | pub mod iterator; 112 | #[cfg(feature = "separator")] 113 | pub mod separator; 114 | mod traits; 115 | 116 | pub extern crate cidr; 117 | 118 | pub use traits::*; 119 | -------------------------------------------------------------------------------- /src/separator/mod.rs: -------------------------------------------------------------------------------- 1 | mod v4; 2 | mod v6; 3 | 4 | pub use v4::*; 5 | pub use v6::*; 6 | -------------------------------------------------------------------------------- /src/separator/v4.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | use cidr::Ipv4Cidr; 4 | 5 | use crate::{combiner::Ipv4CidrCombiner, iterator::Ipv4CidrIpv4AddrIterator, Ipv4CidrSize}; 6 | 7 | /// To divide an IPv4 CIDR into subnetworks. 8 | #[derive(Debug)] 9 | pub struct Ipv4CidrSeparator; 10 | 11 | impl Ipv4CidrSeparator { 12 | /// Evenly divide an IPv4 CIDR into a specific number of subnetworks. 13 | pub fn divide_by(cidr: &Ipv4Cidr, n: usize) -> Option> { 14 | let size = cidr.size(); 15 | 16 | let n_u64 = n as u64; 17 | 18 | if n == 0 || n_u64 > size { 19 | return None; 20 | } else if n == 1 { 21 | let mut combiner = Ipv4CidrCombiner::with_capacity(1); 22 | 23 | combiner.push(*cidr); 24 | 25 | return Some(vec![combiner]); 26 | } 27 | 28 | let mut output = Vec::with_capacity(n); 29 | 30 | let d = size / n_u64; 31 | 32 | if d * n_u64 == size { 33 | let mut iter = Ipv4CidrIpv4AddrIterator::new(cidr); 34 | 35 | let bits = cidr.network_length() + n.ilog2() as u8; 36 | 37 | let usize_max_u64 = usize::MAX as u64; 38 | 39 | if d <= usize_max_u64 { 40 | for ip in iter.step_by(d as usize) { 41 | let mut combiner = Ipv4CidrCombiner::with_capacity(1); 42 | 43 | combiner.push(Ipv4Cidr::new(ip, bits).unwrap()); 44 | 45 | output.push(combiner); 46 | } 47 | } else { 48 | let nth = d - 1; 49 | 50 | if let Some(ip) = iter.next() { 51 | let mut combiner = Ipv4CidrCombiner::with_capacity(1); 52 | 53 | combiner.push(Ipv4Cidr::new(ip, bits).unwrap()); 54 | 55 | output.push(combiner); 56 | 57 | while let Some(ip) = iter.nth_u64(nth) { 58 | let mut combiner = Ipv4CidrCombiner::with_capacity(1); 59 | 60 | combiner.push(Ipv4Cidr::new(ip, bits).unwrap()); 61 | 62 | output.push(combiner); 63 | } 64 | } 65 | } 66 | } else { 67 | let iter = Ipv4CidrIpv4AddrIterator::new(cidr); 68 | 69 | let mut current_combiner = Ipv4CidrCombiner::new(); 70 | 71 | let mut i = 1; 72 | 73 | for ip in iter { 74 | current_combiner.push(Ipv4Cidr::new(ip, 32).unwrap()); 75 | 76 | if i == d { 77 | output.push(current_combiner); 78 | 79 | current_combiner = Ipv4CidrCombiner::new(); 80 | 81 | i = 1; 82 | } else { 83 | i += 1; 84 | } 85 | } 86 | 87 | let last_combiner = output.last_mut().unwrap(); 88 | 89 | for cidr in current_combiner.into_ipv4_cidr_vec().into_iter() { 90 | last_combiner.push(cidr); 91 | } 92 | } 93 | 94 | Some(output) 95 | } 96 | 97 | /// Divide an IPv4 CIDR into subnetworks with a specific bits. 98 | pub fn sub_networks(cidr: &Ipv4Cidr, bits: u8) -> Option> { 99 | let cidr_bits = cidr.network_length(); 100 | 101 | match cidr_bits.cmp(&bits) { 102 | Ordering::Greater => return None, 103 | Ordering::Equal => return Some(vec![*cidr]), 104 | Ordering::Less => (), 105 | } 106 | 107 | let n = 2usize.pow(u32::from(bits - cidr_bits)); 108 | 109 | let n_u64 = n as u64; 110 | 111 | let mut output = Vec::with_capacity(n); 112 | 113 | let d = cidr.size() / n_u64; 114 | 115 | let mut iter = Ipv4CidrIpv4AddrIterator::new(cidr); 116 | 117 | let usize_max_u64 = usize::MAX as u64; 118 | 119 | if d <= usize_max_u64 { 120 | for ip in iter.step_by(d as usize) { 121 | output.push(Ipv4Cidr::new(ip, bits).unwrap()); 122 | } 123 | } else { 124 | let nth = d - 1; 125 | 126 | if let Some(ip) = iter.next() { 127 | output.push(Ipv4Cidr::new(ip, bits).unwrap()); 128 | 129 | while let Some(ip) = iter.nth(nth as usize) { 130 | output.push(Ipv4Cidr::new(ip, bits).unwrap()); 131 | } 132 | } 133 | } 134 | 135 | Some(output) 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/separator/v6.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | use cidr::Ipv6Cidr; 4 | use num_bigint::BigUint; 5 | use num_traits::{One, ToPrimitive}; 6 | 7 | use crate::{combiner::Ipv6CidrCombiner, iterator::Ipv6CidrIpv6AddrIterator, Ipv6CidrSize}; 8 | 9 | /// To divide an IPv6 CIDR into subnetworks. 10 | #[derive(Debug)] 11 | pub struct Ipv6CidrSeparator; 12 | 13 | impl Ipv6CidrSeparator { 14 | /// Evenly divide an IPv6 CIDR into a specific number of subnetworks. 15 | pub fn divide_by(cidr: &Ipv6Cidr, n: usize) -> Option> { 16 | let size = cidr.size(); 17 | 18 | let n_big_int = BigUint::from(n); 19 | 20 | if n == 0 || n_big_int > size { 21 | return None; 22 | } else if n == 1 { 23 | let mut combiner = Ipv6CidrCombiner::with_capacity(1); 24 | 25 | combiner.push(*cidr); 26 | 27 | return Some(vec![combiner]); 28 | } 29 | 30 | let d = size.clone() / n_big_int.clone(); 31 | 32 | let mut output = Vec::with_capacity(n); 33 | 34 | if d.clone() * n_big_int == size { 35 | let mut iter = Ipv6CidrIpv6AddrIterator::new(cidr); 36 | 37 | let bits = cidr.network_length() + n.ilog2() as u8; 38 | 39 | let usize_max_big_int = BigUint::from(usize::MAX); 40 | 41 | if d <= usize_max_big_int { 42 | for ip in iter.step_by(d.to_usize().unwrap()) { 43 | let mut combiner = Ipv6CidrCombiner::with_capacity(1); 44 | 45 | combiner.push(Ipv6Cidr::new(ip, bits).unwrap()); 46 | 47 | output.push(combiner); 48 | } 49 | } else { 50 | let nth = d - BigUint::one(); 51 | 52 | if let Some(ip) = iter.next() { 53 | let mut combiner = Ipv6CidrCombiner::with_capacity(1); 54 | 55 | combiner.push(Ipv6Cidr::new(ip, bits).unwrap()); 56 | 57 | output.push(combiner); 58 | 59 | while let Some(ip) = iter.nth_big_uint(nth.clone()) { 60 | let mut combiner = Ipv6CidrCombiner::with_capacity(1); 61 | 62 | combiner.push(Ipv6Cidr::new(ip, bits).unwrap()); 63 | 64 | output.push(combiner); 65 | } 66 | } 67 | } 68 | } else { 69 | let iter = Ipv6CidrIpv6AddrIterator::new(cidr); 70 | 71 | let mut current_combiner = Ipv6CidrCombiner::new(); 72 | 73 | let mut i = BigUint::one(); 74 | 75 | for ip in iter { 76 | current_combiner.push(Ipv6Cidr::new(ip, 128).unwrap()); 77 | 78 | if i == d { 79 | output.push(current_combiner); 80 | 81 | current_combiner = Ipv6CidrCombiner::new(); 82 | 83 | i = BigUint::one(); 84 | } else { 85 | i += BigUint::one(); 86 | } 87 | } 88 | 89 | let last_combiner = output.last_mut().unwrap(); 90 | 91 | for cidr in current_combiner.into_ipv6_cidr_vec().into_iter() { 92 | last_combiner.push(cidr); 93 | } 94 | } 95 | 96 | Some(output) 97 | } 98 | 99 | /// Divide an IPv6 CIDR into subnetworks with a specific bits. 100 | pub fn sub_networks(cidr: &Ipv6Cidr, bits: u8) -> Option> { 101 | let cidr_bits = cidr.network_length(); 102 | 103 | match cidr_bits.cmp(&bits) { 104 | Ordering::Greater => return None, 105 | Ordering::Equal => return Some(vec![*cidr]), 106 | Ordering::Less => (), 107 | } 108 | 109 | let n = 2usize.pow(u32::from(bits - cidr_bits)); 110 | 111 | let n_big_int = BigUint::from(n); 112 | 113 | let mut output = Vec::with_capacity(n); 114 | 115 | let size = cidr.size(); 116 | 117 | let d = size / n_big_int; 118 | 119 | let mut iter = Ipv6CidrIpv6AddrIterator::new(cidr); 120 | 121 | let usize_max_big_int = BigUint::from(usize::MAX); 122 | 123 | if d <= usize_max_big_int { 124 | for ip in iter.step_by(d.to_usize().unwrap()) { 125 | output.push(Ipv6Cidr::new(ip, bits).unwrap()); 126 | } 127 | } else { 128 | let nth = d - BigUint::one(); 129 | 130 | if let Some(ip) = iter.next() { 131 | output.push(Ipv6Cidr::new(ip, bits).unwrap()); 132 | 133 | while let Some(ip) = iter.nth_big_uint(nth.clone()) { 134 | output.push(Ipv6Cidr::new(ip, bits).unwrap()); 135 | } 136 | } 137 | } 138 | 139 | Some(output) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | use cidr::{Ipv4Cidr, Ipv6Cidr}; 2 | use num_bigint::BigUint; 3 | 4 | /// Get the size of an Ipv4Cidr. 5 | /// 6 | /// # Examples 7 | /// 8 | /// ``` 9 | /// use std::str::FromStr; 10 | /// 11 | /// use cidr::Ipv4Cidr; 12 | /// use cidr_utils::Ipv4CidrSize; 13 | /// 14 | /// let cidr = Ipv4Cidr::from_str("192.168.1.0/24").unwrap(); 15 | /// 16 | /// assert_eq!(256, cidr.size()); 17 | /// ``` 18 | pub trait Ipv4CidrSize { 19 | fn size(&self) -> u64; 20 | } 21 | 22 | impl Ipv4CidrSize for Ipv4Cidr { 23 | #[inline] 24 | fn size(&self) -> u64 { 25 | 2u64.pow((32 - self.network_length()) as u32) 26 | } 27 | } 28 | 29 | /// Get the size of an Ipv6Cidr. 30 | /// 31 | /// # Examples 32 | /// 33 | /// ``` 34 | /// use std::str::FromStr; 35 | /// 36 | /// use cidr::Ipv6Cidr; 37 | /// use cidr_utils::Ipv6CidrSize; 38 | /// use num_bigint::BigUint; 39 | /// 40 | /// let cidr = Ipv6Cidr::from_str("0:0:0:0:0:FFFF:FFFF:0/112").unwrap(); 41 | /// 42 | /// assert_eq!(BigUint::from(65536usize), cidr.size()); 43 | /// ``` 44 | pub trait Ipv6CidrSize { 45 | fn size(&self) -> BigUint; 46 | } 47 | 48 | impl Ipv6CidrSize for Ipv6Cidr { 49 | #[inline] 50 | fn size(&self) -> BigUint { 51 | BigUint::from(2u8).pow((128 - self.network_length()) as u32) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/ipv4_cidr_combiner.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "combiner")] 2 | 3 | use core::str::FromStr; 4 | 5 | use cidr::Ipv4Cidr; 6 | use cidr_utils::combiner::Ipv4CidrCombiner; 7 | 8 | #[test] 9 | fn push() { 10 | let mut combiner = Ipv4CidrCombiner::new(); 11 | 12 | assert_eq!(0, combiner.len()); 13 | 14 | combiner.push(Ipv4Cidr::from_str("192.168.1.0/24").unwrap()); 15 | 16 | assert_eq!(1, combiner.len()); 17 | 18 | combiner.push(Ipv4Cidr::from_str("192.168.1.0/24").unwrap()); 19 | 20 | assert_eq!(1, combiner.len()); 21 | 22 | combiner.push(Ipv4Cidr::from_str("192.168.1.52/32").unwrap()); 23 | 24 | assert_eq!(1, combiner.len()); 25 | 26 | combiner.push(Ipv4Cidr::from_str("192.168.2.0/24").unwrap()); 27 | 28 | assert_eq!(2, combiner.len()); 29 | 30 | combiner.push(Ipv4Cidr::from_str("192.168.0.0/16").unwrap()); 31 | 32 | assert_eq!(1, combiner.len()); 33 | 34 | combiner.push(Ipv4Cidr::from_str("192.168.3.0/24").unwrap()); 35 | 36 | assert_eq!(1, combiner.len()); 37 | 38 | combiner.push(Ipv4Cidr::from_str("192.171.0.0/16").unwrap()); 39 | 40 | assert_eq!(2, combiner.len()); 41 | 42 | combiner.push(Ipv4Cidr::from_str("192.170.0.0/16").unwrap()); 43 | 44 | assert_eq!(2, combiner.len()); 45 | assert_eq!("192.170.0.0/15", combiner[1].to_string()); 46 | 47 | combiner.push(Ipv4Cidr::from_str("192.169.0.0/16").unwrap()); 48 | 49 | assert_eq!(1, combiner.len()); 50 | assert_eq!("192.168.0.0/14", combiner[0].to_string()); 51 | 52 | combiner.push(Ipv4Cidr::from_str("192.167.0.0/16").unwrap()); 53 | assert_eq!(2, combiner.len()); 54 | 55 | combiner.push(Ipv4Cidr::from_str("192.166.0.0/16").unwrap()); 56 | assert_eq!(2, combiner.len()); 57 | 58 | combiner.push(Ipv4Cidr::from_str("192.166.0.0/16").unwrap()); 59 | assert_eq!(2, combiner.len()); 60 | 61 | combiner.push(Ipv4Cidr::from_str("192.164.0.0/15").unwrap()); 62 | assert_eq!(2, combiner.len()); 63 | 64 | combiner.push(Ipv4Cidr::from_str("192.172.0.0/14").unwrap()); 65 | assert_eq!(2, combiner.len()); 66 | 67 | combiner.push(Ipv4Cidr::from_str("192.160.0.0/14").unwrap()); 68 | assert_eq!(1, combiner.len()); 69 | 70 | // 192.168.1.0/24 + 192.168.1.0/24 = 192.168.1.0/24 71 | // 72 | // 192.168.1.0/24 + 192.168.2.0/24 = 192.168.1.0/24 + 192.168.2.0/24 73 | // 74 | // 192.168.1.0/24 + 192.168.2.0/24 + 192.168.0.0/16 = 192.168.0.0/16 75 | // 76 | // 192.168.0.0/16 + 192.168.3.0/24 = 192.168.0.0/16 77 | // 78 | // 192.168.0.0/16 + 192.171.0.0/16 = 192.168.0.0/16 + 192.171.0.0/16 79 | // 80 | // 192.168.0.0/16 + 192.171.0.0/16 + 192.170.0.0/16 = 192.168.0.0/16 + 192.170.0.0/15 81 | // 82 | // 192.168.0.0/16 + 192.170.0.0/15 + 192.169.0.0/16 = 192.168.0.0/15 + 192.170.0.0/15 = 192.168.0.0/14 83 | // 84 | // 192.168.0.0/14 + 192.167.0.0/16 = 192.168.0.0/14 + 192.167.0.0/16 85 | // 86 | // 192.168.0.0/14 + 192.167.0.0/16 + 192.166.0.0/16 = 192.168.0.0/14 + 192.166.0.0/15 87 | // 88 | // 192.168.0.0/14 + 192.166.0.0/15 + 192.164.0.0/15 = 192.168.0.0/14 + 192.164.0.0/14 89 | // 90 | // 192.168.0.0/14 + 192.164.0.0/14 + 192.172.0.0/14 = 192.168.0.0/14 + 192.164.0.0/13 91 | // 92 | // 192.168.0.0/14 + 192.164.0.0/13 + 192.160.0.0/14 = 192.160.0.0/13 + 192.164.0.0/13 = 192.160.0.0/12 93 | 94 | combiner.push(Ipv4Cidr::from_str("200.1.0.0/24").unwrap()); 95 | assert_eq!(2, combiner.len()); 96 | 97 | combiner.push(Ipv4Cidr::from_str("200.1.1.0/24").unwrap()); 98 | assert_eq!(2, combiner.len()); 99 | 100 | combiner.push(Ipv4Cidr::from_str("0.0.0.0/1").unwrap()); 101 | assert_eq!(3, combiner.len()); 102 | 103 | combiner.push(Ipv4Cidr::from_str("0.0.0.0/0").unwrap()); 104 | assert_eq!(1, combiner.len()); 105 | 106 | // 192.160.0.0/12 + 200.1.0.0/24 = 192.160.0.0/12 + 200.1.0.0/24 107 | // 108 | // 192.160.0.0/12 + 200.1.0.0/24 + 200.1.1.0/24 = 192.160.0.0/12 + 200.1.0.0/23 109 | // 110 | // 192.160.0.0/12 + 200.1.0.0/23 + 0.0.0.0/1 = 192.160.0.0/12 + 200.1.0.0/23 + 0.0.0.0/1 111 | // 112 | // 192.160.0.0/12 + 200.1.0.0/23 + 0.0.0.0/1 + 0.0.0.0/0 = 0.0.0.0/0 113 | } 114 | 115 | #[test] 116 | fn simple_test() { 117 | let mut combiner = Ipv4CidrCombiner::new(); 118 | 119 | combiner.push(Ipv4Cidr::from_str("192.168.1.100").unwrap()); 120 | combiner.push(Ipv4Cidr::from_str("192.168.1.101").unwrap()); 121 | combiner.push(Ipv4Cidr::from_str("192.168.1.102").unwrap()); 122 | combiner.push(Ipv4Cidr::from_str("192.168.1.103").unwrap()); 123 | 124 | assert_eq!(1, combiner.len()); 125 | assert_eq!(Ipv4Cidr::from_str("192.168.1.100/30").unwrap(), combiner[0]); 126 | } 127 | -------------------------------------------------------------------------------- /tests/ipv4_cidr_separator.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "separator")] 2 | 3 | use core::str::FromStr; 4 | 5 | use cidr::Ipv4Cidr; 6 | use cidr_utils::{separator::Ipv4CidrSeparator, Ipv4CidrSize}; 7 | 8 | #[test] 9 | fn divide_by() { 10 | let cidr = Ipv4Cidr::from_str("192.168.56.0/24").unwrap(); 11 | 12 | let result = Ipv4CidrSeparator::divide_by(&cidr, 4).unwrap(); 13 | 14 | assert_eq!(4, result.len()); 15 | assert_eq!(64, result[0].size()); 16 | assert_eq!(64, result[1].size()); 17 | assert_eq!(64, result[2].size()); 18 | assert_eq!(64, result[3].size()); 19 | 20 | let cidr = Ipv4Cidr::from_str("192.168.56.0/24").unwrap(); 21 | 22 | let result = Ipv4CidrSeparator::divide_by(&cidr, 5).unwrap(); 23 | 24 | assert_eq!(5, result.len()); 25 | assert_eq!(51, result[0].size()); 26 | assert_eq!(51, result[1].size()); 27 | assert_eq!(51, result[2].size()); 28 | assert_eq!(51, result[3].size()); 29 | assert_eq!(52, result[4].size()); 30 | 31 | let cidr = Ipv4Cidr::from_str("0.0.0.0/0").unwrap(); 32 | 33 | let result = Ipv4CidrSeparator::divide_by(&cidr, 1).unwrap(); 34 | 35 | assert_eq!(1, result.len()); 36 | assert_eq!(2u64.pow(32), result[0].size()); 37 | 38 | let result = Ipv4CidrSeparator::divide_by(&cidr, 2).unwrap(); 39 | 40 | assert_eq!(2, result.len()); 41 | assert_eq!(2u64.pow(31), result[0].size()); 42 | assert_eq!(2u64.pow(31), result[1].size()); 43 | } 44 | 45 | #[test] 46 | fn sub_networks() { 47 | let cidr = Ipv4Cidr::from_str("192.168.56.0/24").unwrap(); 48 | 49 | let result = Ipv4CidrSeparator::sub_networks(&cidr, 26).unwrap(); 50 | 51 | assert_eq!(4, result.len()); 52 | assert_eq!(64, result[0].size()); 53 | assert_eq!(64, result[1].size()); 54 | assert_eq!(64, result[2].size()); 55 | assert_eq!(64, result[3].size()); 56 | } 57 | -------------------------------------------------------------------------------- /tests/ipv6_cidr_combiner.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "combiner")] 2 | 3 | use core::str::FromStr; 4 | 5 | use cidr::Ipv6Cidr; 6 | use cidr_utils::combiner::Ipv6CidrCombiner; 7 | 8 | #[test] 9 | fn simple_test() { 10 | let mut combiner = Ipv6CidrCombiner::new(); 11 | 12 | combiner.push(Ipv6Cidr::from_str("::ffff:192.168.1.100").unwrap()); 13 | combiner.push(Ipv6Cidr::from_str("::ffff:192.168.1.101").unwrap()); 14 | combiner.push(Ipv6Cidr::from_str("::ffff:192.168.1.102").unwrap()); 15 | combiner.push(Ipv6Cidr::from_str("::ffff:192.168.1.103").unwrap()); 16 | 17 | assert_eq!(1, combiner.len()); 18 | assert_eq!("::ffff:192.168.1.100/126", combiner[0].to_string()); 19 | } 20 | 21 | #[test] 22 | fn should_combine_same_ip() { 23 | let mut combiner = Ipv6CidrCombiner::new(); 24 | 25 | combiner.push(Ipv6Cidr::from_str("::ffff:192.168.1.100").unwrap()); 26 | combiner.push(Ipv6Cidr::from_str("::ffff:192.168.1.100").unwrap()); 27 | combiner.push(Ipv6Cidr::from_str("::ffff:192.168.1.100").unwrap()); 28 | 29 | assert_eq!(1, combiner.len()); 30 | assert_eq!("::ffff:192.168.1.100", combiner[0].to_string()); 31 | } 32 | -------------------------------------------------------------------------------- /tests/ipv6_cidr_separator.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "separator")] 2 | 3 | use std::str::FromStr; 4 | 5 | use cidr::Ipv6Cidr; 6 | use cidr_utils::{separator::Ipv6CidrSeparator, Ipv6CidrSize}; 7 | use num_bigint::BigUint; 8 | 9 | #[test] 10 | fn divide_by() { 11 | let cidr = Ipv6Cidr::from_str("0:0:0:0:0:FFFF:FFFF:0/112").unwrap(); 12 | 13 | let result = Ipv6CidrSeparator::divide_by(&cidr, 4).unwrap(); 14 | 15 | assert_eq!(4, result.len()); 16 | assert_eq!(BigUint::from(16384u128), result[0].size()); 17 | assert_eq!(BigUint::from(16384u128), result[1].size()); 18 | assert_eq!(BigUint::from(16384u128), result[2].size()); 19 | assert_eq!(BigUint::from(16384u128), result[3].size()); 20 | 21 | let cidr = Ipv6Cidr::from_str("0:0:0:0:0:FFFF:FFFF:0/112").unwrap(); 22 | 23 | let result = Ipv6CidrSeparator::divide_by(&cidr, 5).unwrap(); 24 | 25 | assert_eq!(5, result.len()); 26 | assert_eq!(BigUint::from(13107u128), result[0].size()); 27 | assert_eq!(BigUint::from(13107u128), result[1].size()); 28 | assert_eq!(BigUint::from(13107u128), result[2].size()); 29 | assert_eq!(BigUint::from(13107u128), result[3].size()); 30 | assert_eq!(BigUint::from(13108u128), result[4].size()); 31 | 32 | let cidr = Ipv6Cidr::from_str("::0/0").unwrap(); 33 | 34 | let result = Ipv6CidrSeparator::divide_by(&cidr, 1).unwrap(); 35 | 36 | assert_eq!(1, result.len()); 37 | assert_eq!( 38 | BigUint::from_str("340282366920938463463374607431768211456").unwrap(), 39 | result[0].size() 40 | ); 41 | 42 | let result = Ipv6CidrSeparator::divide_by(&cidr, 2).unwrap(); 43 | 44 | assert_eq!(2, result.len()); 45 | assert_eq!(BigUint::from(2u128.pow(127)), result[0].size()); 46 | assert_eq!(BigUint::from(2u128.pow(127)), result[1].size()); 47 | } 48 | 49 | #[test] 50 | fn sub_networks() { 51 | let cidr = Ipv6Cidr::from_str("0:0:0:0:0:FFFF:FFFF:0/112").unwrap(); 52 | 53 | let result = Ipv6CidrSeparator::sub_networks(&cidr, 114).unwrap(); 54 | 55 | assert_eq!(4, result.len()); 56 | assert_eq!(BigUint::from(16384u128), result[0].size()); 57 | assert_eq!(BigUint::from(16384u128), result[1].size()); 58 | assert_eq!(BigUint::from(16384u128), result[2].size()); 59 | assert_eq!(BigUint::from(16384u128), result[3].size()); 60 | } 61 | --------------------------------------------------------------------------------