cidr_utils/separator/
v4.rs

1use std::cmp::Ordering;
2
3use cidr::Ipv4Cidr;
4
5use crate::{combiner::Ipv4CidrCombiner, iterator::Ipv4CidrIpv4AddrIterator, Ipv4CidrSize};
6
7/// To divide an IPv4 CIDR into subnetworks.
8#[derive(Debug)]
9pub struct Ipv4CidrSeparator;
10
11impl Ipv4CidrSeparator {
12    /// Evenly divide an IPv4 CIDR into a specific number of subnetworks.
13    pub fn divide_by(cidr: &Ipv4Cidr, n: usize) -> Option<Vec<Ipv4CidrCombiner>> {
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<Vec<Ipv4Cidr>> {
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}