1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use std::cmp::Ordering;

use crate::{cidr::Ipv4Cidr, utils::Ipv4CidrCombiner};

/// To divide an IPv4 CIDR into subnetworks.
#[derive(Debug)]
pub struct Ipv4CidrSeparator;

impl Ipv4CidrSeparator {
    /// Evenly divide an IPv4 CIDR into a specific number of subnetworks.
    pub fn divide_by(cidr: &Ipv4Cidr, n: usize) -> Option<Vec<Ipv4CidrCombiner>> {
        let size = cidr.size();

        let n_u64 = n as u64;

        if n == 0 || n_u64 > size {
            return None;
        } else if n == 1 {
            let mut combiner = Ipv4CidrCombiner::with_capacity(1);

            combiner.push(*cidr);

            return Some(vec![combiner]);
        }

        let mut output = Vec::with_capacity(n);

        let d = size / n_u64;

        if d * n_u64 == size {
            let mut iter = cidr.iter();

            let bits = cidr.get_bits() + (n as f64).log2() as u8;

            let usize_max_u64 = usize::MAX as u64;

            if d <= usize_max_u64 {
                for ip in iter.step_by(d as usize) {
                    let mut combiner = Ipv4CidrCombiner::with_capacity(1);

                    combiner.push(Ipv4Cidr::from_prefix_and_bits(ip, bits).unwrap());

                    output.push(combiner);
                }
            } else {
                let nth = d - 1;

                if let Some(ip) = iter.next() {
                    let mut combiner = Ipv4CidrCombiner::with_capacity(1);

                    combiner.push(Ipv4Cidr::from_prefix_and_bits(ip, bits).unwrap());

                    output.push(combiner);

                    while let Some(ip) = iter.nth_u64(nth) {
                        let mut combiner = Ipv4CidrCombiner::with_capacity(1);

                        combiner.push(Ipv4Cidr::from_prefix_and_bits(ip, bits).unwrap());

                        output.push(combiner);
                    }
                }
            }
        } else {
            let iter = cidr.iter();

            let mut current_combiner = Ipv4CidrCombiner::new();

            let mut i = 1;

            for ip in iter {
                current_combiner.push(Ipv4Cidr::from_prefix_and_bits(ip, 32).unwrap());

                if i == d {
                    output.push(current_combiner);

                    current_combiner = Ipv4CidrCombiner::new();

                    i = 1;
                } else {
                    i += 1;
                }
            }

            let last_combiner = output.last_mut().unwrap();

            for cidr in current_combiner.into_ipv4_cidr_vec().into_iter() {
                last_combiner.push(cidr);
            }
        }

        Some(output)
    }

    /// Divide an IPv4 CIDR into subnetworks with a specific bits.
    pub fn sub_networks(cidr: &Ipv4Cidr, bits: u8) -> Option<Vec<Ipv4Cidr>> {
        let cidr_bits = cidr.get_bits();

        match cidr_bits.cmp(&bits) {
            Ordering::Greater => return None,
            Ordering::Equal => return Some(vec![*cidr]),
            Ordering::Less => (),
        }

        let n = 2usize.pow(u32::from(bits - cidr_bits));

        let n_u64 = n as u64;

        let mut output = Vec::with_capacity(n);

        let d = cidr.size() / n_u64;

        let mut iter = cidr.iter();

        let usize_max_u64 = usize::MAX as u64;

        if d <= usize_max_u64 {
            for ip in iter.step_by(d as usize) {
                output.push(Ipv4Cidr::from_prefix_and_bits(ip, bits).unwrap());
            }
        } else {
            let nth = d - 1;

            if let Some(ip) = iter.next() {
                output.push(Ipv4Cidr::from_prefix_and_bits(ip, bits).unwrap());

                while let Some(ip) = iter.nth_u64(nth) {
                    output.push(Ipv4Cidr::from_prefix_and_bits(ip, bits).unwrap());
                }
            }
        }

        Some(output)
    }
}