use crate::models::*;
use itertools::Itertools;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::mem::discriminant;
#[derive(Debug, Clone)]
pub enum AsPathSegment {
AsSequence(SmallVec<[Asn; 6]>),
AsSet(SmallVec<[Asn; 6]>),
ConfedSequence(SmallVec<[Asn; 6]>),
ConfedSet(SmallVec<[Asn; 6]>),
}
impl AsPathSegment {
pub fn sequence<S: AsRef<[u32]>>(seq: S) -> Self {
AsPathSegment::AsSequence(seq.as_ref().iter().copied().map_into().collect())
}
pub fn set<S: AsRef<[u32]>>(seq: S) -> Self {
AsPathSegment::AsSet(seq.as_ref().iter().copied().map_into().collect())
}
pub fn route_len(&self) -> usize {
match self {
AsPathSegment::AsSequence(v) => v.len(),
AsPathSegment::AsSet(_) => 1,
AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_) => 0,
}
}
pub fn len(&self) -> usize {
self.as_ref().len()
}
pub fn is_empty(&self) -> bool {
self.as_ref().is_empty()
}
pub fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
self.into_iter()
}
pub fn iter_mut(&mut self) -> <&'_ mut Self as IntoIterator>::IntoIter {
self.into_iter()
}
pub fn is_confed(&self) -> bool {
matches!(
self,
AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_)
)
}
fn merge_in_place(&mut self, other: &mut Self) -> bool {
use AsPathSegment::*;
match (self, other) {
(AsSequence(x), AsSequence(y)) | (ConfedSequence(x), ConfedSequence(y)) => {
x.extend_from_slice(y);
true
}
(x @ (AsSequence(_) | ConfedSequence(_)), y) if x.is_empty() => {
std::mem::swap(x, y);
true
}
(_, AsSequence(y) | ConfedSequence(y)) if y.is_empty() => true,
_ => false,
}
}
fn dedup_merge_in_place(&mut self, other: &mut Self) -> bool {
use AsPathSegment::*;
other.dedup();
match (self, other) {
(AsSequence(x), AsSequence(y)) | (ConfedSequence(x), ConfedSequence(y)) => {
x.extend_from_slice(y);
x.dedup();
true
}
(x @ (AsSequence(_) | ConfedSequence(_)), y) if x.is_empty() => {
std::mem::swap(x, y);
true
}
(_, AsSequence(y) | ConfedSequence(y)) if y.is_empty() => true,
_ => false,
}
}
fn dedup(&mut self) {
match self {
AsPathSegment::AsSequence(x) | AsPathSegment::ConfedSequence(x) => x.dedup(),
AsPathSegment::AsSet(x) => {
x.sort_unstable();
x.dedup();
if x.len() == 1 {
*self = AsPathSegment::AsSequence(std::mem::take(x));
}
}
AsPathSegment::ConfedSet(x) => {
x.sort_unstable();
x.dedup();
if x.len() == 1 {
*self = AsPathSegment::ConfedSequence(std::mem::take(x));
}
}
}
}
pub fn to_u32_vec_opt(&self, dedup: bool) -> Option<Vec<u32>> {
match self {
AsPathSegment::AsSequence(v) => {
let mut p: Vec<u32> = v.iter().map(|asn| (*asn).into()).collect();
if dedup {
p.dedup();
}
Some(p)
}
AsPathSegment::AsSet(v) => {
if v.len() == 1 {
Some(vec![v[0].into()])
} else {
None
}
}
_ => None,
}
}
}
impl IntoIterator for AsPathSegment {
type Item = Asn;
type IntoIter = smallvec::IntoIter<[Asn; 6]>;
fn into_iter(self) -> Self::IntoIter {
let (AsPathSegment::AsSequence(x)
| AsPathSegment::AsSet(x)
| AsPathSegment::ConfedSequence(x)
| AsPathSegment::ConfedSet(x)) = self;
x.into_iter()
}
}
impl<'a> IntoIterator for &'a AsPathSegment {
type Item = &'a Asn;
type IntoIter = std::slice::Iter<'a, Asn>;
fn into_iter(self) -> Self::IntoIter {
let (AsPathSegment::AsSequence(x)
| AsPathSegment::AsSet(x)
| AsPathSegment::ConfedSequence(x)
| AsPathSegment::ConfedSet(x)) = self;
x.iter()
}
}
impl<'a> IntoIterator for &'a mut AsPathSegment {
type Item = &'a mut Asn;
type IntoIter = std::slice::IterMut<'a, Asn>;
fn into_iter(self) -> Self::IntoIter {
let (AsPathSegment::AsSequence(x)
| AsPathSegment::AsSet(x)
| AsPathSegment::ConfedSequence(x)
| AsPathSegment::ConfedSet(x)) = self;
x.iter_mut()
}
}
impl AsRef<[Asn]> for AsPathSegment {
fn as_ref(&self) -> &[Asn] {
let (AsPathSegment::AsSequence(x)
| AsPathSegment::AsSet(x)
| AsPathSegment::ConfedSequence(x)
| AsPathSegment::ConfedSet(x)) = self;
x
}
}
impl Hash for AsPathSegment {
fn hash<H: Hasher>(&self, state: &mut H) {
discriminant(self).hash(state);
let set = match self {
AsPathSegment::AsSequence(x) | AsPathSegment::ConfedSequence(x) => {
return x.hash(state)
}
AsPathSegment::AsSet(x) | AsPathSegment::ConfedSet(x) => x,
};
if set.len() <= 32 {
let mut buffer = [Asn::new_32bit(0); 32];
set.iter()
.zip(&mut buffer)
.for_each(|(asn, buffer)| *buffer = *asn);
let slice = &mut buffer[..set.len()];
slice.sort_unstable();
Asn::hash_slice(slice, state);
return;
}
set.iter().sorted().for_each(|x| x.hash(state));
}
}
impl PartialEq for AsPathSegment {
fn eq(&self, other: &Self) -> bool {
let (x, y) = match (self, other) {
(AsPathSegment::AsSequence(x), AsPathSegment::AsSequence(y))
| (AsPathSegment::ConfedSequence(x), AsPathSegment::ConfedSequence(y)) => {
return x == y
}
(AsPathSegment::AsSet(x), AsPathSegment::AsSet(y))
| (AsPathSegment::ConfedSet(x), AsPathSegment::ConfedSet(y)) => (x, y),
_ => return false,
};
if x.len() != y.len() {
return false;
} else if x == y {
return true;
}
if x.len() <= 32 {
let mut x_buffer = [Asn::new_32bit(0); 32];
let mut y_buffer = [Asn::new_32bit(0); 32];
x.iter()
.zip(&mut x_buffer)
.for_each(|(asn, buffer)| *buffer = *asn);
y.iter()
.zip(&mut y_buffer)
.for_each(|(asn, buffer)| *buffer = *asn);
x_buffer[..x.len()].sort_unstable();
y_buffer[..y.len()].sort_unstable();
return x_buffer[..x.len()] == y_buffer[..y.len()];
}
x.iter()
.sorted()
.zip(y.iter().sorted())
.all(|(a, b)| a == b)
}
}
impl Eq for AsPathSegment {}
struct AsPathNumberedRouteIter<'a> {
path: &'a [AsPathSegment],
index: usize,
route_num: u64,
}
impl Iterator for AsPathNumberedRouteIter<'_> {
type Item = Asn;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.path.first()? {
AsPathSegment::AsSequence(x) => match x.get(self.index) {
None => {
self.index = 0;
self.path = &self.path[1..];
}
Some(asn) => {
self.index += 1;
return Some(*asn);
}
},
AsPathSegment::AsSet(x) => {
self.path = &self.path[1..];
if x.is_empty() {
return Some(Asn::RESERVED);
}
let asn = x[(self.route_num % x.len() as u64) as usize];
self.route_num /= x.len() as u64;
return Some(asn);
}
_ => self.path = &self.path[1..],
}
}
}
}
pub struct AsPathRouteIter<'a, D> {
path: Cow<'a, [AsPathSegment]>,
route_num: u64,
total_routes: u64,
_phantom: PhantomData<D>,
}
impl<D> Iterator for AsPathRouteIter<'_, D>
where
D: FromIterator<Asn>,
{
type Item = D;
fn next(&mut self) -> Option<Self::Item> {
if self.route_num >= self.total_routes {
return None;
}
if self.route_num == 0 && self.path.len() == 1 {
if let AsPathSegment::AsSequence(sequence) = &self.path[0] {
let route = D::from_iter(sequence.iter().copied());
self.route_num += 1;
return Some(route);
}
}
let route_asn_iter = AsPathNumberedRouteIter {
path: self.path.as_ref(),
index: 0,
route_num: self.route_num,
};
self.route_num += 1;
Some(D::from_iter(route_asn_iter))
}
}
#[derive(Debug, PartialEq, Clone, Eq, Default, Hash)]
pub struct AsPath {
pub segments: SmallVec<[AsPathSegment; 1]>,
}
#[cfg(feature = "ts-rs")]
#[derive(ts_rs::TS)]
#[ts(
export,
type = "(number | number[] | { ty: \"AS_SET\" | \"AS_SEQUENCE\" | \"AS_CONFED_SEQUENCE\" | \"AS_CONFED_SET\", values: number[] })[]"
)]
pub struct AsPathWire;
pub type SegmentIter<'a> = std::slice::Iter<'a, AsPathSegment>;
pub type SegmentIterMut<'a> = std::slice::IterMut<'a, AsPathSegment>;
pub type SegmentIntoIter = smallvec::IntoIter<[AsPathSegment; 1]>;
impl AsPath {
pub fn new() -> AsPath {
AsPath {
segments: SmallVec::new(),
}
}
pub fn from_sequence<S: AsRef<[u32]>>(seq: S) -> Self {
let segment = AsPathSegment::AsSequence(seq.as_ref().iter().copied().map_into().collect());
AsPath {
segments: SmallVec::from_buf([segment]),
}
}
pub fn from_segments<S: Into<SmallVec<[AsPathSegment; 1]>>>(segments: S) -> AsPath {
AsPath {
segments: segments.into(),
}
}
pub fn append_segment(&mut self, segment: AsPathSegment) {
self.segments.push(segment);
}
pub fn is_empty(&self) -> bool {
self.segments.is_empty()
}
pub fn route_len(&self) -> usize {
self.segments.iter().map(AsPathSegment::route_len).sum()
}
pub fn len(&self) -> usize {
self.segments.len()
}
pub fn num_route_variations(&self) -> u64 {
let mut variations: u64 = 1;
for segment in &self.segments {
if let AsPathSegment::AsSet(x) = segment {
variations *= x.len() as u64;
}
}
variations
}
pub fn contains_asn(&self, x: Asn) -> bool {
self.iter_segments().flatten().contains(&x)
}
pub fn coalesce(&mut self) {
let mut end_index = 0;
let mut scan_index = 1;
while scan_index < self.segments.len() {
let (a, b) = self.segments.split_at_mut(scan_index);
if !AsPathSegment::merge_in_place(&mut a[end_index], &mut b[0]) {
end_index += 1;
self.segments.swap(end_index, scan_index);
}
scan_index += 1;
}
self.segments.truncate(end_index + 1);
}
pub fn dedup_coalesce(&mut self) {
if !self.segments.is_empty() {
self.segments[0].dedup();
}
let mut end_index = 0;
let mut scan_index = 1;
while scan_index < self.segments.len() {
let (a, b) = self.segments.split_at_mut(scan_index);
if !AsPathSegment::dedup_merge_in_place(&mut a[end_index], &mut b[0]) {
end_index += 1;
self.segments.swap(end_index, scan_index);
}
scan_index += 1;
}
self.segments.truncate(end_index + 1);
}
pub fn has_equivalent_routing(&self, other: &Self) -> bool {
let mut a = self.to_owned();
let mut b = other.to_owned();
a.dedup_coalesce();
b.dedup_coalesce();
a == b
}
pub fn required_asn_length(&self) -> AsnLength {
self.iter_segments().flatten().map(Asn::required_len).fold(
AsnLength::Bits16,
|a, b| match (a, b) {
(AsnLength::Bits16, AsnLength::Bits16) => AsnLength::Bits16,
_ => AsnLength::Bits32,
},
)
}
pub fn iter_segments(&self) -> SegmentIter<'_> {
self.segments.iter()
}
pub fn iter_segments_mut(&mut self) -> SegmentIterMut<'_> {
self.segments.iter_mut()
}
pub fn into_segments_iter(self) -> SegmentIntoIter {
self.segments.into_iter()
}
pub fn iter_routes<D>(&self) -> AsPathRouteIter<'_, D>
where
D: FromIterator<Asn>,
{
AsPathRouteIter {
path: Cow::Borrowed(&self.segments),
route_num: 0,
total_routes: self.num_route_variations(),
_phantom: PhantomData,
}
}
pub fn merge_aspath_as4path(aspath: &AsPath, as4path: &AsPath) -> AsPath {
if aspath.route_len() < as4path.route_len() {
return aspath.clone();
}
let mut leading = aspath.route_len() - as4path.route_len();
let mut new_segs: Vec<AsPathSegment> = vec![];
for seg in &aspath.segments {
if leading == 0 {
break;
}
match seg.route_len() {
0 => new_segs.push(seg.clone()),
n if n <= leading => {
new_segs.push(seg.clone());
leading -= n;
}
_ => {
let AsPathSegment::AsSequence(v) = seg else {
unreachable!("only an AS_SEQUENCE can exceed the leading count");
};
new_segs.push(AsPathSegment::AsSequence(
v.iter().take(leading).copied().collect(),
));
leading = 0;
}
}
}
new_segs.extend(as4path.segments.iter().cloned());
let mut merged = AsPath {
segments: new_segs.into(),
};
merged.coalesce();
merged
}
pub fn iter_origins(&self) -> impl '_ + Iterator<Item = Asn> {
let origin_slice = match self.segments.last() {
Some(AsPathSegment::AsSequence(v)) => v.last().map(std::slice::from_ref).unwrap_or(&[]),
Some(AsPathSegment::AsSet(v)) => v.as_ref(),
_ => &[],
};
origin_slice.iter().copied()
}
pub fn get_origin_opt(&self) -> Option<Asn> {
match self.segments.last() {
Some(AsPathSegment::AsSequence(v)) => v.last().copied(),
Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
_ => None,
}
}
pub fn get_collector_opt(&self) -> Option<Asn> {
match self.segments.first() {
Some(AsPathSegment::AsSequence(v)) => v.first().copied(),
Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
_ => None,
}
}
pub fn to_u32_vec_opt(&self, dedup: bool) -> Option<Vec<u32>> {
let mut path = vec![];
for seg in self.segments.iter().rev() {
let p = seg.to_u32_vec_opt(dedup)?;
path.extend(p.iter().rev());
}
match path.is_empty() {
true => {
None
}
false => {
path.reverse();
Some(path)
}
}
}
}
impl<'a> IntoIterator for &'a AsPath {
type Item = Vec<Asn>;
type IntoIter = AsPathRouteIter<'a, Vec<Asn>>;
fn into_iter(self) -> Self::IntoIter {
self.iter_routes()
}
}
impl IntoIterator for AsPath {
type Item = Vec<Asn>;
type IntoIter = AsPathRouteIter<'static, Vec<Asn>>;
fn into_iter(self) -> Self::IntoIter {
AsPathRouteIter {
total_routes: self.num_route_variations(),
path: Cow::Owned(self.segments.into_vec()),
route_num: 0,
_phantom: PhantomData,
}
}
}
impl Display for AsPath {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
for (index, segment) in self.iter_segments().enumerate() {
if index != 0 {
write!(f, " ")?;
}
match segment {
AsPathSegment::AsSequence(v) | AsPathSegment::ConfedSequence(v) => {
let mut asn_iter = v.iter();
if let Some(first_element) = asn_iter.next() {
write!(f, "{first_element}")?;
for asn in asn_iter {
write!(f, " {asn}")?;
}
}
}
AsPathSegment::AsSet(v) | AsPathSegment::ConfedSet(v) => {
write!(f, "{{")?;
let mut asn_iter = v.iter();
if let Some(first_element) = asn_iter.next() {
write!(f, "{first_element}")?;
for asn in asn_iter {
write!(f, ",{asn}")?;
}
}
write!(f, "}}")?;
}
}
}
Ok(())
}
}
#[cfg(feature = "serde")]
mod serde_impl {
use super::*;
use serde::de::{SeqAccess, Visitor};
use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::borrow::Cow;
#[allow(non_camel_case_types)]
#[derive(Serialize, Deserialize)]
enum SegmentType {
AS_SET,
AS_SEQUENCE,
AS_CONFED_SEQUENCE,
AS_CONFED_SET,
}
#[derive(Serialize, Deserialize)]
struct VerboseSegment<'s> {
ty: SegmentType,
values: Cow<'s, [Asn]>,
}
impl Serialize for AsPathSegment {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let (ty, elements) = match self {
AsPathSegment::AsSequence(x) => (SegmentType::AS_SEQUENCE, x.as_ref()),
AsPathSegment::AsSet(x) => (SegmentType::AS_SET, x.as_ref()),
AsPathSegment::ConfedSequence(x) => (SegmentType::AS_CONFED_SEQUENCE, x.as_ref()),
AsPathSegment::ConfedSet(x) => (SegmentType::AS_CONFED_SET, x.as_ref()),
};
let verbose = VerboseSegment {
ty,
values: Cow::Borrowed(elements),
};
verbose.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for AsPathSegment {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let verbose = VerboseSegment::deserialize(deserializer)?;
let values: SmallVec<[Asn; 6]> = verbose.values.into_owned().into();
match verbose.ty {
SegmentType::AS_SET => Ok(AsPathSegment::AsSet(values)),
SegmentType::AS_SEQUENCE => Ok(AsPathSegment::AsSequence(values)),
SegmentType::AS_CONFED_SEQUENCE => Ok(AsPathSegment::ConfedSequence(values)),
SegmentType::AS_CONFED_SET => Ok(AsPathSegment::ConfedSet(values)),
}
}
}
fn simplified_format_len(segments: &[AsPathSegment]) -> Option<usize> {
let mut elements = 0;
let mut prev_was_sequence = false;
for segment in segments {
match segment {
AsPathSegment::AsSequence(seq) if !prev_was_sequence => {
prev_was_sequence = true;
elements += seq.len();
}
AsPathSegment::AsSet(_) => {
prev_was_sequence = false;
elements += 1;
}
_ => return None,
}
}
Some(elements)
}
impl Serialize for AsPath {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if let Some(num_elements) = simplified_format_len(&self.segments) {
let mut seq_serializer = serializer.serialize_seq(Some(num_elements))?;
for segment in &self.segments {
match segment {
AsPathSegment::AsSequence(elements) => {
elements
.iter()
.try_for_each(|x| seq_serializer.serialize_element(x))?;
}
AsPathSegment::AsSet(x) => seq_serializer.serialize_element(x)?,
_ => unreachable!("simplified_format_len checked for confed segments"),
}
}
return seq_serializer.end();
}
serializer.collect_seq(&self.segments)
}
}
struct AsPathVisitor;
impl<'de> Visitor<'de> for AsPathVisitor {
type Value = AsPath;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("list of AS_PATH segments")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum PathElement {
SequenceElement(Asn),
Set(Vec<Asn>),
Verbose(AsPathSegment),
}
let mut append_new_sequence = false;
let mut segments: SmallVec<[AsPathSegment; 1]> = SmallVec::new();
while let Some(element) = seq.next_element()? {
match element {
PathElement::SequenceElement(x) => {
if append_new_sequence {
append_new_sequence = false;
segments.push(AsPathSegment::AsSequence(SmallVec::new()));
}
if let Some(AsPathSegment::AsSequence(last_sequence)) = segments.last_mut()
{
last_sequence.push(x);
} else {
let mut new_seq: SmallVec<[Asn; 6]> = SmallVec::new();
new_seq.push(x);
segments.push(AsPathSegment::AsSequence(new_seq));
}
}
PathElement::Set(values) => {
segments.push(AsPathSegment::AsSet(values.into()));
}
PathElement::Verbose(verbose) => {
segments.push(verbose);
}
}
}
Ok(AsPath { segments })
}
}
impl<'de> Deserialize<'de> for AsPath {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_seq(AsPathVisitor)
}
}
}
#[cfg(test)]
mod tests {
use crate::models::*;
use itertools::Itertools;
use std::collections::HashSet;
#[test]
fn test_aspath_as4path_merge() {
let aspath = AsPath::from_sequence([1, 2, 3, 5]);
let as4path = AsPath::from_sequence([2, 3, 7]);
let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 3, 7]));
let aspath = AsPath::from_sequence([1, 2]);
let as4path = AsPath::from_sequence([2, 3, 7]);
let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2]));
let aspath = AsPath::from_sequence([1, 2]);
let as4path = AsPath::from_sequence([3, 4]);
let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
assert_eq!(newpath.segments[0], AsPathSegment::sequence([3, 4]));
let aspath = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2, 3, 5]),
AsPathSegment::set([7, 8]),
]);
let as4path = AsPath::from_sequence([6, 7, 8]);
let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
assert_eq!(newpath.segments.len(), 1);
assert_eq!(
newpath.segments[0],
AsPathSegment::sequence([1, 2, 6, 7, 8])
);
let aspath = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2]),
AsPathSegment::sequence([3, 5]),
AsPathSegment::set([13, 14]),
]);
let as4path = AsPath::from_segments(vec![
AsPathSegment::sequence([8, 4, 6]),
AsPathSegment::set([11, 12]),
]);
let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
assert_eq!(newpath.segments.len(), 2);
assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 8, 4, 6]));
assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
let aspath = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2, 3]),
AsPathSegment::sequence([5]),
AsPathSegment::set([13, 14]),
]);
let as4path = AsPath::from_segments(vec![
AsPathSegment::sequence([7, 8]),
AsPathSegment::set([11, 12]),
]);
let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
assert_eq!(newpath.segments.len(), 2);
assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 7, 8]));
assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
let aspath = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2]),
AsPathSegment::sequence([3, 4]),
]);
let as4path = AsPath::from_sequence([9, 10]);
let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
assert_eq!(newpath.segments.len(), 1);
assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 9, 10]));
}
#[test]
fn test_get_origin() {
let aspath = AsPath::from_sequence([1, 2, 3, 5]);
let origin = aspath.get_origin_opt();
assert_eq!(origin.unwrap(), 5);
let aspath = AsPath::from_segments(vec![AsPathSegment::set([1, 2, 3, 5])]);
let origin = aspath.get_origin_opt();
assert!(origin.is_none());
let aspath = AsPath::from_segments(vec![AsPathSegment::set([1])]);
let origin = aspath.get_origin_opt();
assert_eq!(origin.unwrap(), 1);
let aspath = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2, 3, 5]),
AsPathSegment::set([7, 8]),
]);
let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
assert_eq!(origins, vec![7, 8]);
let aspath = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2, 3, 5]),
AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
]);
let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
assert_eq!(origins, Vec::<u32>::new());
}
#[test]
fn test_get_collector() {
let aspath = AsPath::from_sequence([1, 2, 3, 5]);
let collector = aspath.get_collector_opt();
assert_eq!(collector.unwrap(), 1);
let aspath = AsPath::from_segments(vec![AsPathSegment::set([7])]);
let collector = aspath.get_collector_opt();
assert_eq!(collector.unwrap(), 7);
let aspath = AsPath::from_segments(vec![AsPathSegment::set([7, 8])]);
let collector = aspath.get_collector_opt();
assert!(collector.is_none());
}
#[test]
fn test_aspath_route_iter() {
let path = AsPath::from_segments(vec![AsPathSegment::sequence([3, 4])]);
let mut routes = HashSet::new();
for route in &path {
assert!(routes.insert(route));
}
assert_eq!(1, routes.len());
let path = AsPath::from_segments(vec![
AsPathSegment::set([3, 4]),
AsPathSegment::set([5, 6]),
AsPathSegment::sequence([7, 8]),
AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
AsPathSegment::ConfedSequence(vec![Asn::new_32bit(9)].into()),
]);
assert_eq!(path.route_len(), 4);
let mut routes = HashSet::new();
for route in &path {
assert!(routes.insert(route));
}
assert_eq!(routes.len(), 4);
assert!(routes.contains(&vec![
Asn::from(3),
Asn::from(5),
Asn::from(7),
Asn::from(8)
]));
assert!(routes.contains(&vec![
Asn::from(3),
Asn::from(6),
Asn::from(7),
Asn::from(8)
]));
assert!(routes.contains(&vec![
Asn::from(4),
Asn::from(5),
Asn::from(7),
Asn::from(8)
]));
assert!(routes.contains(&vec![
Asn::from(4),
Asn::from(6),
Asn::from(7),
Asn::from(8)
]));
}
#[test]
fn test_segment() {
let path_segment = AsPathSegment::sequence([1, 2, 3, 4]);
assert_eq!(path_segment.len(), 4);
let mut iter = path_segment.iter();
assert_eq!(iter.next(), Some(&Asn::new_32bit(1)));
assert_eq!(iter.next(), Some(&Asn::new_32bit(2)));
assert_eq!(iter.next(), Some(&Asn::new_32bit(3)));
assert_eq!(iter.next(), Some(&Asn::new_32bit(4)));
assert_eq!(iter.next(), None);
let mut path_segment = AsPathSegment::sequence([1]);
let mut iter_mut = path_segment.iter_mut();
assert_eq!(iter_mut.next(), Some(&mut Asn::new_32bit(1)));
assert_eq!(iter_mut.next(), None);
assert!(AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into()).is_confed());
assert!(AsPathSegment::ConfedSet(vec![Asn::new_32bit(1)].into()).is_confed());
}
#[test]
fn test_coalesce() {
let mut a = AsPath::from_segments(vec![
AsPathSegment::sequence([]),
AsPathSegment::sequence([1, 2]),
AsPathSegment::sequence([]),
AsPathSegment::sequence([2]),
AsPathSegment::set([2]),
AsPathSegment::set([5, 3, 3, 2]),
]);
let expected = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2, 2]),
AsPathSegment::set([2]),
AsPathSegment::set([5, 3, 3, 2]),
]);
a.coalesce();
assert_eq!(a, expected);
}
#[test]
fn test_confed_set_dedup() {
let mut path_segment =
AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(1)].into());
path_segment.dedup();
assert_eq!(
path_segment,
AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into())
);
let mut path_segment = AsPathSegment::ConfedSet(
vec![Asn::new_32bit(1), Asn::new_32bit(2), Asn::new_32bit(2)].into(),
);
path_segment.dedup();
assert_eq!(
path_segment,
AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into())
);
}
#[test]
fn test_path_to_u32() {
let path_segment = AsPathSegment::sequence([1, 2, 3, 3]);
assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1, 2, 3, 3]));
assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1, 2, 3]));
let path_segment = AsPathSegment::set([1, 2, 3, 3]);
assert_eq!(path_segment.to_u32_vec_opt(false), None);
assert_eq!(path_segment.to_u32_vec_opt(true), None);
let path_segment = AsPathSegment::set([1]);
assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1]));
assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1]));
let as_path = AsPath::from_segments(vec![
AsPathSegment::set([4]),
AsPathSegment::sequence([2, 3, 3]),
AsPathSegment::set([1]),
]);
assert_eq!(as_path.to_u32_vec_opt(false), Some(vec![4, 2, 3, 3, 1]));
assert_eq!(as_path.to_u32_vec_opt(true), Some(vec![4, 2, 3, 1]));
let as_path = AsPath::from_segments(vec![
AsPathSegment::set([4, 2]),
AsPathSegment::sequence([2, 3, 3]),
AsPathSegment::set([1]),
]);
assert_eq!(as_path.to_u32_vec_opt(false), None);
assert_eq!(as_path.to_u32_vec_opt(true), None);
let as_path = AsPath::from_segments(vec![]);
assert_eq!(as_path.to_u32_vec_opt(false), None);
assert_eq!(as_path.to_u32_vec_opt(true), None);
let as_path = AsPath::from_segments(vec![
AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into()),
AsPathSegment::ConfedSequence(vec![Asn::new_32bit(3), Asn::new_32bit(4)].into()),
]);
assert_eq!(as_path.to_u32_vec_opt(false), None);
assert_eq!(as_path.to_u32_vec_opt(true), None);
}
#[test]
fn test_as_ref() {
let path_segment = AsPathSegment::sequence([1, 2]);
assert_eq!(
path_segment.as_ref(),
&[Asn::new_32bit(1), Asn::new_32bit(2)]
);
let path_segment = AsPathSegment::set([1, 2]);
assert_eq!(
path_segment.as_ref(),
&[Asn::new_32bit(1), Asn::new_32bit(2)]
);
let path_segment =
AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
assert_eq!(
path_segment.as_ref(),
&[Asn::new_32bit(1), Asn::new_32bit(2)]
);
let path_segment =
AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
assert_eq!(
path_segment.as_ref(),
&[Asn::new_32bit(1), Asn::new_32bit(2)]
);
}
#[test]
fn test_hashing() {
let path_segment = AsPathSegment::sequence([1, 2]);
let path_segment2 = AsPathSegment::sequence([1, 2]);
let hashset = std::iter::once(path_segment).collect::<HashSet<_>>();
assert!(hashset.contains(&path_segment2));
}
#[test]
fn test_equality() {
let path_segment = AsPathSegment::sequence([1, 2]);
let path_segment2 = AsPathSegment::sequence([1, 2]);
assert_eq!(path_segment, path_segment2);
let path_segment = AsPathSegment::sequence([1, 2]);
let path_segment2 = AsPathSegment::set([1, 2, 3]);
assert_ne!(path_segment, path_segment2);
let path_segment = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
let path_segment2 = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
assert_eq!(path_segment, path_segment2);
}
#[test]
fn test_as_path_display() {
let path = AsPath::from_segments(vec![
AsPathSegment::sequence([1, 2]),
AsPathSegment::set([3, 4]),
AsPathSegment::sequence([5, 6]),
AsPathSegment::ConfedSet(vec![Asn::new_32bit(7)].into()),
AsPathSegment::ConfedSequence(vec![Asn::new_32bit(8)].into()),
]);
assert_eq!(path.to_string(), "1 2 {3,4} 5 6 {7} 8");
}
}