Skip to content

Commit 0deb2ab

Browse files
Add ip_mask_to_prefix_checked (#214)
* Add `ipv4_mask_to_prefix_checked` * Add `ipv6_mask_to_prefix_checked` * Add `ip_mask_to_prefix_checked`
1 parent 170fc4c commit 0deb2ab

3 files changed

Lines changed: 50 additions & 13 deletions

File tree

src/ipv4.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -382,13 +382,24 @@ impl IntoIterator for &'_ Ipv4Network {
382382
///
383383
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
384384
pub fn ipv4_mask_to_prefix(mask: Ipv4Addr) -> Result<u8, IpNetworkError> {
385-
let mask = u32::from(mask);
385+
match ipv4_mask_to_prefix_checked(mask) {
386+
Some(prefix) => Ok(prefix),
387+
None => Err(IpNetworkError::InvalidPrefix),
388+
}
389+
}
390+
391+
/// Converts a `Ipv4Addr` network mask into a prefix.
392+
///
393+
/// If the mask is invalid this will return `None`. This is useful in const contexts where
394+
/// [`Option::unwrap`] may be called to trigger a compile-time error if the prefix is invalid.
395+
pub const fn ipv4_mask_to_prefix_checked(mask: Ipv4Addr) -> Option<u8> {
396+
let mask = mask.to_bits();
386397

387398
let prefix = (!mask).leading_zeros() as u8;
388-
if (u64::from(mask) << prefix) & 0xffff_ffff != 0 {
389-
Err(IpNetworkError::InvalidPrefix)
399+
if ((mask as u64) << prefix) & 0xffff_ffff != 0 {
400+
None
390401
} else {
391-
Ok(prefix)
402+
Some(prefix)
392403
}
393404
}
394405

src/ipv6.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -391,12 +391,25 @@ impl fmt::Display for Ipv6Network {
391391
/// Converts a `Ipv6Addr` network mask into a prefix.
392392
/// If the mask is invalid this will return an `IpNetworkError::InvalidPrefix`.
393393
pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
394+
match ipv6_mask_to_prefix_checked(mask) {
395+
Some(prefix) => Ok(prefix),
396+
None => Err(IpNetworkError::InvalidPrefix),
397+
}
398+
}
399+
400+
/// Converts a `Ipv6Addr` network mask into a prefix.
401+
///
402+
/// If the mask is invalid this will return `None`. This is useful in const contexts where
403+
/// [`Option::unwrap`] may be called to trigger a compile-time error if the prefix is invalid.
404+
pub const fn ipv6_mask_to_prefix_checked(mask: Ipv6Addr) -> Option<u8> {
394405
let mask = mask.segments();
395-
let mut mask_iter = mask.iter();
396406

397407
// Count the number of set bits from the start of the address
398408
let mut prefix = 0;
399-
for &segment in &mut mask_iter {
409+
let mut i = 0;
410+
while i < mask.len() {
411+
let segment = mask[i];
412+
i += 1;
400413
if segment == 0xffff {
401414
prefix += IPV6_SEGMENT_BITS;
402415
} else if segment == 0 {
@@ -406,21 +419,23 @@ pub fn ipv6_mask_to_prefix(mask: Ipv6Addr) -> Result<u8, IpNetworkError> {
406419
let prefix_bits = (!segment).leading_zeros() as u8;
407420
// Check that the remainder of the bits are all unset
408421
if segment << prefix_bits != 0 {
409-
return Err(IpNetworkError::InvalidPrefix);
422+
return None;
410423
}
411424
prefix += prefix_bits;
412425
break;
413426
}
414427
}
415428

416429
// Now check all the remaining bits are unset
417-
for &segment in mask_iter {
430+
while i < mask.len() {
431+
let segment = mask[i];
432+
i += 1;
418433
if segment != 0 {
419-
return Err(IpNetworkError::InvalidPrefix);
434+
return None;
420435
}
421436
}
422437

423-
Ok(prefix)
438+
Some(prefix)
424439
}
425440

426441
#[cfg(test)]

src/lib.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ mod ipv6;
1616
mod parse;
1717
mod size;
1818

19-
pub use crate::error::{NetworkSizeError, IpNetworkError};
19+
pub use crate::error::{IpNetworkError, NetworkSizeError};
2020
pub use crate::ipv4::Ipv4NetworkIterator;
21-
pub use crate::ipv4::{ipv4_mask_to_prefix, Ipv4Network};
21+
pub use crate::ipv4::{ipv4_mask_to_prefix, ipv4_mask_to_prefix_checked, Ipv4Network};
2222
pub use crate::ipv6::Ipv6NetworkIterator;
23-
pub use crate::ipv6::{ipv6_mask_to_prefix, Ipv6Network};
23+
pub use crate::ipv6::{ipv6_mask_to_prefix, ipv6_mask_to_prefix_checked, Ipv6Network};
2424
pub use crate::size::NetworkSize;
2525

2626
/// Represents a generic network range. This type can have two variants:
@@ -433,6 +433,17 @@ pub fn ip_mask_to_prefix(mask: IpAddr) -> Result<u8, IpNetworkError> {
433433
}
434434
}
435435

436+
/// Converts a `IpAddr` network mask into a prefix.
437+
///
438+
/// If the mask is invalid this will return `None`. This is useful in const contexts where
439+
/// [`Option::unwrap`] may be called to trigger a compile-time error if the prefix is invalid.
440+
pub const fn ip_mask_to_prefix_checked(mask: IpAddr) -> Option<u8> {
441+
match mask {
442+
IpAddr::V4(mask) => ipv4_mask_to_prefix_checked(mask),
443+
IpAddr::V6(mask) => ipv6_mask_to_prefix_checked(mask),
444+
}
445+
}
446+
436447
#[cfg(test)]
437448
mod test {
438449
#[test]

0 commit comments

Comments
 (0)