Skip to content

Commit

Permalink
expose some arithmetic methods on Inet types
Browse files Browse the repository at this point in the history
  • Loading branch information
stbuehler committed Jun 25, 2024
1 parent b7d60d9 commit 6f506ca
Showing 5 changed files with 485 additions and 112 deletions.
91 changes: 91 additions & 0 deletions src/inet/combined.rs
Original file line number Diff line number Diff line change
@@ -83,6 +83,61 @@ impl IpInet {
}
}

/// decrements host part (without changing the network part);
/// returns true on wrap around
pub fn decrement(&mut self) -> bool {
match self {
Self::V4(mut c) => c.decrement(),
Self::V6(mut c) => c.decrement(),
}
}

/// Returns previous address in network or `None` if it was the first address in the network
pub const fn previous(self) -> Option<Self> {
match self {
Self::V4(c) => match c.previous() {
Some(c) => Some(Self::V4(c)),
None => None,
},
Self::V6(c) => match c.previous() {
Some(c) => Some(Self::V6(c)),
None => None,
},
}
}

/// Find the nth host after the current one in the current network
///
/// Returned boolean indicates whether an overflow occured.
pub const fn overflowing_add(self, step: u128) -> (Self, bool) {
match self {
Self::V4(c) => {
let (c, overflow) = c.overflowing_add(step);
(Self::V4(c), overflow)
},
Self::V6(c) => {
let (c, overflow) = c.overflowing_add(step);
(Self::V6(c), overflow)
},
}
}

/// Find the nth host before the current one in the current network
///
/// Returned boolean indicates whether an overflow occured.
pub const fn overflowing_sub(self, step: u128) -> (Self, bool) {
match self {
Self::V4(c) => {
let (c, overflow) = c.overflowing_sub(step);
(Self::V4(c), overflow)
},
Self::V6(c) => {
let (c, overflow) = c.overflowing_sub(step);
(Self::V6(c), overflow)
},
}
}

/// network (i.e. drops the host information)
pub const fn network(&self) -> IpCidr {
match self {
@@ -203,6 +258,22 @@ impl Inet for IpInet {
self.next()
}

fn decrement(&mut self) -> bool {
self.decrement()
}

fn previous(self) -> Option<Self> {
self.previous()
}

fn overflowing_add(self, step: u128) -> (Self, bool) {
self.overflowing_add(step)
}

fn overflowing_sub(self, step: u128) -> (Self, bool) {
self.overflowing_sub(step)
}

fn network(&self) -> IpCidr {
self.network()
}
@@ -276,3 +347,23 @@ impl From<Ipv6Inet> for IpInet {
Self::V6(c)
}
}

impl core::ops::Add<u128> for IpInet {
type Output = IpInet;

fn add(self, step: u128) -> Self::Output {
let (result, overflow) = self.overflowing_add(step);
debug_assert!(!overflow, "{} + {} overflow", self, step);
result
}
}

impl core::ops::Sub<u128> for IpInet {
type Output = IpInet;

fn sub(self, step: u128) -> Self::Output {
let (result, overflow) = self.overflowing_sub(step);
debug_assert!(!overflow, "{} - {} overflow", self, step);
result
}
}
157 changes: 157 additions & 0 deletions src/inet/direct.rs
Original file line number Diff line number Diff line change
@@ -126,6 +126,69 @@ macro_rules! impl_inet_for {
}
}

/// decrements host part (without changing the network part);
/// returns true on wrap around
pub fn decrement(&mut self) -> bool {
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_prev(
self.address,
self.network_length,
);
self.address = address;
overflow
}

/// Returns previous address in network or `None` if it was the first address in the network
pub const fn previous(self) -> Option<Self> {
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_prev(
self.address,
self.network_length,
);
if overflow {
None
} else {
Some(Self {
address,
network_length: self.network_length,
})
}
}

/// Find the nth host after the current one in the current network
///
/// Returned boolean indicates whether an overflow occured.
pub const fn overflowing_add(self, step: u128) -> (Self, bool) {
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_inc(
self.address,
self.network_length,
step,
);
(
Self {
address,
network_length: self.network_length,
},
overflow,
)
}

/// Find the nth host before the current one in the current network
///
/// Returned boolean indicates whether an overflow occured.
pub const fn overflowing_sub(self, step: u128) -> (Self, bool) {
let (address, overflow) = <$addr as PrivUnspecAddress>::_Tools::_overflowing_dec(
self.address,
self.network_length,
step,
);
(
Self {
address,
network_length: self.network_length,
},
overflow,
)
}

/// network (i.e. drops the host information)
pub const fn network(&self) -> $cidr {
$cidr {
@@ -226,6 +289,22 @@ macro_rules! impl_inet_for {
self.next()
}

fn decrement(&mut self) -> bool {
self.decrement()
}

fn previous(self) -> Option<Self> {
self.previous()
}

fn overflowing_add(self, step: u128) -> (Self, bool) {
self.overflowing_add(step)
}

fn overflowing_sub(self, step: u128) -> (Self, bool) {
self.overflowing_sub(step)
}

fn network(&self) -> $cidr {
self.network()
}
@@ -295,8 +374,86 @@ macro_rules! impl_inet_for {
inet_from_str(s)
}
}

impl core::ops::Add<u128> for $n {
type Output = $n;

fn add(self, step: u128) -> Self::Output {
let (result, overflow) = self.overflowing_add(step);
debug_assert!(!overflow, "{} + {} overflow", self, step);
result
}
}

impl core::ops::Sub<u128> for $n {
type Output = $n;

fn sub(self, step: u128) -> Self::Output {
let (result, overflow) = self.overflowing_sub(step);
debug_assert!(!overflow, "{} - {} overflow", self, step);
result
}
}
};
}

impl_inet_for! {Ipv4Inet : cidr Ipv4Cidr : addr Ipv4Addr : pair Ipv4InetPair : family Family::Ipv4}
impl_inet_for! {Ipv6Inet : cidr Ipv6Cidr : addr Ipv6Addr : pair Ipv6InetPair : family Family::Ipv6}

impl Ipv4Inet {
/// Find the nth host after the current one in the current network (32-bit IPv4 variant)
///
/// Returned boolean indicates whether an overflow occured.
pub const fn overflowing_add_u32(self, step: u32) -> (Self, bool) {
let (address, overflow) = <Ipv4Addr as PrivUnspecAddress>::_Tools::_overflowing_inc_u32(
self.address,
self.network_length,
step,
);
(
Self {
address,
network_length: self.network_length,
},
overflow,
)
}

/// Find the nth host before the current one in the current network (32-bit IPv4 variant)
///
/// Returned boolean indicates whether an overflow occured.
pub const fn overflowing_sub_u32(self, step: u32) -> (Self, bool) {
let (address, overflow) = <Ipv4Addr as PrivUnspecAddress>::_Tools::_overflowing_dec_u32(
self.address,
self.network_length,
step,
);
(
Self {
address,
network_length: self.network_length,
},
overflow,
)
}
}

impl core::ops::Add<u32> for Ipv4Inet {
type Output = Ipv4Inet;

fn add(self, step: u32) -> Self::Output {
let (result, overflow) = self.overflowing_add_u32(step);
debug_assert!(!overflow, "{} + {} overflow", self, step);
result
}
}

impl core::ops::Sub<u32> for Ipv4Inet {
type Output = Ipv4Inet;

fn sub(self, step: u32) -> Self::Output {
let (result, overflow) = self.overflowing_sub_u32(step);
debug_assert!(!overflow, "{} - {} overflow", self, step);
result
}
}
71 changes: 70 additions & 1 deletion src/inet/tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use core::cmp::Ordering;
use core::{
cmp::Ordering,
str::FromStr,
};
use std::net::{
IpAddr,
Ipv4Addr,
Ipv6Addr,
};

use crate::{
errors::NetworkParseError,
Inet,
IpInet,
Ipv4Inet,
Ipv6Inet,
@@ -963,3 +968,67 @@ fn order_v6() {
fn order() {
test_order(Ordering::Less, "192.0.2.0/24", "2001:DB8:1234:5678::/64");
}

fn test_nth<I>(a: &str, b: &str, step: u128, overflow: bool)
where
I: Inet
+ FromStr<Err = NetworkParseError>
+ core::ops::Add<u128, Output = I>
+ core::ops::Sub<u128, Output = I>,
{
let a = a.parse::<I>().unwrap();
let b = b.parse::<I>().unwrap();

assert_eq!(a.overflowing_add(step), (b, overflow));
assert_eq!(b.overflowing_sub(step), (a, overflow));

if !overflow {
// overflow would trigger debug asserts here
assert_eq!(a + step, b);
assert_eq!(b - step, a);
}
}

#[test]
fn test_nth_v4() {
test_nth::<Ipv4Inet>("255.255.255.255/0", "0.0.0.0/0", 1, true);
test_nth::<IpInet>("255.255.255.255/0", "0.0.0.0/0", 1, true);
test_nth::<Ipv4Inet>("255.255.255.255/0", "127.0.0.1/0", 0x7f000002, true);
test_nth::<IpInet>("255.255.255.255/0", "127.0.0.1/0", 0x7f000002, true);

test_nth::<Ipv4Inet>("192.0.2.23/24", "192.0.2.42/24", 19, false);
test_nth::<IpInet>("192.0.2.23/24", "192.0.2.42/24", 19, false);
test_nth::<Ipv4Inet>("192.0.2.42/24", "192.0.2.23/24", 237, true);
test_nth::<IpInet>("192.0.2.42/24", "192.0.2.23/24", 237, true);
}

#[test]
fn test_nth_v6() {
test_nth::<Ipv6Inet>("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", "::/0", 1, true);
test_nth::<IpInet>("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0", "::/0", 1, true);

test_nth::<Ipv6Inet>(
"2001:DB8:1234:5678::23/64",
"2001:DB8:1234:5678::42/64",
0x1f,
false,
);
test_nth::<IpInet>(
"2001:DB8:1234:5678::23/64",
"2001:DB8:1234:5678::42/64",
0x1f,
false,
);
test_nth::<Ipv6Inet>(
"2001:DB8:1234:5678::42/64",
"2001:DB8:1234:5678::23/64",
(1 << 64) - 0x1f,
true,
);
test_nth::<IpInet>(
"2001:DB8:1234:5678::42/64",
"2001:DB8:1234:5678::23/64",
(1 << 64) - 0x1f,
true,
);
}
Loading

0 comments on commit 6f506ca

Please sign in to comment.