use std::fmt::Display;
use std::mem;
use std::net::IpAddr;
use std::sync::LazyLock;
use num_traits::PrimInt;
use crate::{aggregator::Aggregator, EitherIpRange, Ipv4Range, Ipv6Range};
pub trait MathLog2 {
fn log2(self) -> u32;
fn checked_log2(self) -> Option<u32>;
}
impl<T: PrimInt> MathLog2 for T {
fn log2(self) -> u32 {
std::mem::size_of::<Self>() as u32 * 8 - self.leading_zeros() - 1
}
fn checked_log2(self) -> Option<u32> {
if self.count_ones() == 1 {
Some(self.log2())
} else {
None
}
}
}
#[allow(dead_code)]
pub fn to_string_overflow<T: PrimInt + Display>(num: T, zero_as_overflow: bool) -> String {
if zero_as_overflow && num == T::zero() {
if mem::size_of::<T>() * 8 == 32 {
String::from("4294967296")
} else if mem::size_of::<T>() * 8 == 128 {
String::from("340282366920938463463374607431768211456")
} else {
unimplemented!()
}
} else {
num.to_string()
}
}
pub fn ip_addr_to_bit_length(ipa: IpAddr) -> u32 {
if ipa.is_ipv4() {
32
} else if ipa.is_ipv6() {
128
} else {
unimplemented!()
}
}
pub fn ip_addr_trailing_zeros(ipa: IpAddr) -> u32 {
match ipa {
IpAddr::V4(ip) => u32::from(ip).trailing_zeros(),
IpAddr::V6(ip) => u128::from(ip).trailing_zeros(),
}
}
pub static IPV4_RESERVED: LazyLock<Vec<Ipv4Range>> = LazyLock::new(|| {
[
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
"192.0.0.0/24",
"192.0.2.0/24",
"192.88.99.0/24",
"192.168.0.0/16",
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"224.0.0.0/4",
"233.252.0.0/24",
"240.0.0.0/4",
"255.255.255.255/32",
]
.iter()
.cloned()
.map(|s| s.parse::<EitherIpRange>().unwrap().into_v4().unwrap())
.collect::<Vec<Ipv4Range>>()
.aggregated()
});
pub static IPV6_RESERVED: LazyLock<Vec<Ipv6Range>> = LazyLock::new(|| {
[
"::/128",
"::1/128",
"::ffff:0:0/96",
"::ffff:0:0:0/96",
"64:ff9b::/96",
"100::/64",
"2001:0000::/32",
"2001:20::/28",
"2001:db8::/32",
"2002::/16",
"fc00::/7",
"fe80::/10",
"ff00::/8",
]
.iter()
.cloned()
.map(|s| s.parse::<EitherIpRange>().unwrap().into_v6().unwrap())
.collect::<Vec<Ipv6Range>>()
.aggregated()
});
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
#[test]
fn log2_powers_of_two() {
assert_eq!(1u32.log2(), 0);
assert_eq!(2u32.log2(), 1);
assert_eq!(4u32.log2(), 2);
assert_eq!(8u32.log2(), 3);
assert_eq!(256u32.log2(), 8);
assert_eq!(65536u64.log2(), 16);
}
#[test]
fn log2_non_power_of_two() {
assert_eq!(3u32.log2(), 1); assert_eq!(5u32.log2(), 2);
assert_eq!(7u32.log2(), 2);
}
#[test]
fn checked_log2_power_of_two() {
assert_eq!(1u32.checked_log2(), Some(0));
assert_eq!(2u32.checked_log2(), Some(1));
assert_eq!(256u32.checked_log2(), Some(8));
}
#[test]
fn checked_log2_non_power_of_two() {
assert_eq!(3u32.checked_log2(), None);
assert_eq!(6u32.checked_log2(), None);
assert_eq!(0u32.checked_log2(), None);
}
#[test]
fn ip_addr_to_bit_length_v4() {
assert_eq!(
ip_addr_to_bit_length(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
32
);
}
#[test]
fn ip_addr_to_bit_length_v6() {
assert_eq!(ip_addr_to_bit_length(IpAddr::V6(Ipv6Addr::LOCALHOST)), 128);
}
#[test]
fn trailing_zeros_v4() {
assert_eq!(
ip_addr_trailing_zeros(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 0))),
8
);
assert_eq!(
ip_addr_trailing_zeros(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 0))),
25
);
}
#[test]
fn trailing_zeros_v6() {
assert_eq!(
ip_addr_trailing_zeros(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
0
);
assert_eq!(
ip_addr_trailing_zeros(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0))),
128
);
}
#[test]
fn to_string_overflow_normal() {
assert_eq!(to_string_overflow(256u32, false), "256");
assert_eq!(to_string_overflow(0u32, false), "0");
}
#[test]
fn to_string_overflow_v4_full() {
assert_eq!(to_string_overflow(0u32, true), "4294967296");
}
#[test]
fn to_string_overflow_v6_full() {
assert_eq!(
to_string_overflow(0u128, true),
"340282366920938463463374607431768211456"
);
}
}