1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
//! libnetrangemege implements an algorithm that takes in a list of //! [CIDR](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) network //! ranges and produces a minimal output set of CIDR ranges where all overlapping //! and adjacent ranges have been merged. //! //! Given the list of CIDR ranges `0.0.0.0/0` and `127.0.0.0/8`, libnetrangemerge //! will produce the output CIDR range `0.0.0.0/0` since this range completely //! covers the second range. //! //! Given the list of CIDR ranges `127.0.0.0/25` and `127.0.0.128/25`, libnetrangemerge //! will produce the output CIDR range `127.0.0.0/24` since the two input ranges //! were adjacent. Not all adjacent ranges can be merged, however. For example, //! `127.0.0.1/32` and `127.0.0.2/32` are adjacent, but cannot be merged because //! there is no way to represent the range `127.0.0.1` - `127.0.0.2` in CIDR form. //! //! CIDR range merging isn't useful for all applications that operate on CIDR //! ranges. If an application wants to make different decisions depending on //! the particular range, merging those ranges into a smaller set won't be useful. //! However, for applications that treat a list of ranges equivalently, merging //! them into a minimal set can be useful. //! //! # Range Selection //! //! Every input CIDR range to libnetrangemerge must be marked with a `selected` //! flag. When two ranges are merged, if either input range is marked as `selected`, //! the output range will also be marked as `selected`; and if neither is marked //! the output range will also not be marked. The `selected` status of ranges //! has no other effect on merging. //! //! This feature can be useful in minimizing ranges in some specific cases. //! For example, if an application wants to get the set of CIDR ranges that //! are used by Amazon's EC2 servers, it can fetch the list of all of the //! Amazon IP ranges, filter out anything that isn't used by EC2, and then //! merge the resulting set. Unfortunately, not all the EC2 ranges may be //! adjacent to each other - there may be ranges in the middle that are used //! by other Amazon services. If the application wants to create the smallest //! set of ranges possible, it may be useful to use these other ranges in order //! to fill in the gaps between the EC2 ranges. The application can accomplish //! this by passing every Amazon range into libnetrangemerge, but with the //! EC2 ranges marked as `selected` and the other ranges not marked. The //! output set can then be filtered by throwing away any output range not //! marked as `selected`. The output set will no longer represent _just_ //! the EC2 ranges in this case - but if the application is ok with that, //! this can result in a smaller output set. //! //! The `selected` functionality is optional - if an application doesn't //! want to use it, the application can just mark every range as either //! `selected` or not and then not filter anything out of the result. //! //! # Usage //! //! A CIDR range is represented by a struct that implements the [`Range`] trait. //! 3 implementations of that trait are included in the library: [`IpRange`], //! [`Ipv4Range`], and [`Ipv6Range`]. An application may also implement the [`Range`] trait //! for their own types as well. //! //! The merging operation is implemented by creating a list of range values //! and then passing that list to either [`merge_ranges`] or [`merge_ranges_slice`]. //! The two functions implement the same algorithm - the difference is that the //! former function expects a `&mut Vec<Range>` which it will merge in place //! and then truncate to the size of the output set, while the latter expects //! a `&mut [Range]` which it will merge in place and then return the size //! of the output set. When using [`merge_ranges_slice`] it is the responsibility //! of the application not to access any of the ranges past the returned size - //! doing so is safe but will result in a panic. //! //! Both [`merge_ranges`] and [`merge_ranges_slice`] operate in place, do not allocate, //! and will not fail or panic, assuming that none of the methods on the [`Range`] type //! panics. //! //! # Example //! //! ``` //! use libnetrangemerge::{RangeInterest, IpRange, merge_ranges}; //! //! let mut ranges: Vec<RangeInterest<IpRange>> = vec![ //! RangeInterest::new("127.0.0.8/29".parse().unwrap(), true), //! RangeInterest::new("127.0.0.16/29".parse().unwrap(), true), //! RangeInterest::new("0.0.0.0/0".parse().unwrap(), true), //! ]; //! //! merge_ranges(&mut ranges); //! ``` //! //! # no_std Support //! //! libnetrangemerge is no_std compatible. However, in no_std mode the [`merge_ranges`] //! method is unavailable as are the built in range types. The application can implement //! its own type that implements the [`Range`] trait and pass instances of that //! type to [`merge_ranges_slice`] for merging. #![cfg_attr(not(feature = "std"), no_std)] mod merge; mod range; mod range_interest; #[cfg(feature = "std")] mod std_range; pub use merge::merge_ranges_slice; pub use range::Range; pub use range_interest::RangeInterest; #[cfg(feature = "std")] pub use std_range::{ InvalidHostAddressError, InvalidPrefixLengthError, InvalidRangeError, IpRange, Ipv4Range, Ipv6Range, RangeParseError, UnparseableRangeError, }; /// Merges all provided ranges in place. The input is truncated to the /// number of valid ranges after merging. /// /// # Example /// /// ``` /// use libnetrangemerge::{RangeInterest, IpRange, merge_ranges}; /// /// let mut ranges: Vec<RangeInterest<IpRange>> = vec![ /// RangeInterest::new("127.0.0.8/29".parse().unwrap(), true), /// RangeInterest::new("127.0.0.16/29".parse().unwrap(), true), /// RangeInterest::new("0.0.0.0/0".parse().unwrap(), true), /// ]; /// /// merge_ranges(&mut ranges); /// ``` pub fn merge_ranges<N: Range>(ranges: &mut Vec<RangeInterest<N>>) { let len = merge_ranges_slice(ranges); ranges.truncate(len) }