cidr_utils/combiner/
v6.rs

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