cidr_utils/combiner/
v4.rs

1use core::ops::Deref;
2use std::net::Ipv4Addr;
3
4use cidr::Ipv4Cidr;
5
6use crate::Ipv4CidrSize;
7
8/// To combine multiple IPv4 CIDRs to supernetworks.
9#[derive(Debug, Clone)]
10pub struct Ipv4CidrCombiner(Vec<Ipv4Cidr>);
11
12impl Default for Ipv4CidrCombiner {
13    #[inline]
14    fn default() -> Self {
15        Ipv4CidrCombiner::new()
16    }
17}
18
19impl Deref for Ipv4CidrCombiner {
20    type Target = Vec<Ipv4Cidr>;
21
22    #[inline]
23    fn deref(&self) -> &Vec<Ipv4Cidr> {
24        &self.0
25    }
26}
27
28impl 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<Ipv4Cidr>) -> Ipv4CidrCombiner {
48        Ipv4CidrCombiner(cidr_vec)
49    }
50
51    #[inline]
52    pub fn into_ipv4_cidr_vec(self) -> Vec<Ipv4Cidr> {
53        self.0
54    }
55}
56
57impl 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}