Skip to main content

bgpkit_parser/models/bgp/attributes/
aspath.rs

1use crate::models::*;
2use itertools::Itertools;
3use smallvec::SmallVec;
4use std::borrow::Cow;
5use std::fmt::{Display, Formatter};
6use std::hash::{Hash, Hasher};
7use std::iter::FromIterator;
8use std::marker::PhantomData;
9use std::mem::discriminant;
10
11/// Enum of AS path segment.
12///
13/// Uses SmallVec for inline storage of ASNs to avoid heap allocations for common cases:
14/// - AsSequence/ConfedSequence: 91% of segments have ≤6 ASNs (per RIB analysis)
15/// - AsSet/ConfedSet: Sets are typically small (1-3 ASNs)
16#[derive(Debug, Clone)]
17pub enum AsPathSegment {
18    /// AS_SEQUENCE with inline storage for up to 6 ASNs (91% zero-alloc coverage)
19    AsSequence(SmallVec<[Asn; 6]>),
20    /// AS_SET with inline storage for up to 6 ASNs
21    AsSet(SmallVec<[Asn; 6]>),
22    /// AS_CONFED_SEQUENCE (rarely used, same size for pattern matching compatibility)
23    ConfedSequence(SmallVec<[Asn; 6]>),
24    /// AS_CONFED_SET (rarely used, same size for pattern matching compatibility)
25    ConfedSet(SmallVec<[Asn; 6]>),
26}
27
28impl AsPathSegment {
29    /// Shorthand for creating an `AsSequence` segment.
30    pub fn sequence<S: AsRef<[u32]>>(seq: S) -> Self {
31        AsPathSegment::AsSequence(seq.as_ref().iter().copied().map_into().collect())
32    }
33
34    /// Shorthand for creating an `AsSet` segment.
35    pub fn set<S: AsRef<[u32]>>(seq: S) -> Self {
36        AsPathSegment::AsSet(seq.as_ref().iter().copied().map_into().collect())
37    }
38
39    /// Get the number of ASNs this segment adds to the route. For the number of ASNs within the
40    /// segment use [AsPathSegment::len] instead.
41    pub fn route_len(&self) -> usize {
42        match self {
43            AsPathSegment::AsSequence(v) => v.len(),
44            AsPathSegment::AsSet(_) => 1,
45            AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_) => 0,
46        }
47    }
48
49    /// Ge the total number of ASNs within this segment. For the number of ASNs this segment adds to
50    /// a packet's route, use [AsPathSegment::route_len] instead.
51    pub fn len(&self) -> usize {
52        self.as_ref().len()
53    }
54
55    /// Returns true if this segment has a length of 0.
56    pub fn is_empty(&self) -> bool {
57        self.as_ref().is_empty()
58    }
59
60    /// Get an iterator over the ASNs within this path segment
61    pub fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
62        self.into_iter()
63    }
64
65    /// Get a mutable iterator over the ASNs within this path segment
66    pub fn iter_mut(&mut self) -> <&'_ mut Self as IntoIterator>::IntoIter {
67        self.into_iter()
68    }
69
70    /// Gets if a segment represents the local members of an autonomous system confederation.
71    /// Shorthand for `matches!(x, AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_))`.
72    ///
73    /// <https://datatracker.ietf.org/doc/html/rfc3065#section-5>
74    pub fn is_confed(&self) -> bool {
75        matches!(
76            self,
77            AsPathSegment::ConfedSequence(_) | AsPathSegment::ConfedSet(_)
78        )
79    }
80
81    /// Merge two [AsPathSegment]s in place and return if the merge was successful.
82    ///
83    /// See [AsPath::coalesce] for more information.
84    fn merge_in_place(&mut self, other: &mut Self) -> bool {
85        use AsPathSegment::*;
86
87        match (self, other) {
88            (AsSequence(x), AsSequence(y)) | (ConfedSequence(x), ConfedSequence(y)) => {
89                x.extend_from_slice(y);
90                true
91            }
92            (x @ (AsSequence(_) | ConfedSequence(_)), y) if x.is_empty() => {
93                std::mem::swap(x, y);
94                true
95            }
96            (_, AsSequence(y) | ConfedSequence(y)) if y.is_empty() => true,
97            _ => false,
98        }
99    }
100
101    /// A much more aggressive version of [AsPathSegment::merge_in_place] which de-duplicates and
102    /// converts sets with only 1 ASN to sequences.
103    ///
104    /// See [AsPath::dedup_coalesce] for more information.
105    fn dedup_merge_in_place(&mut self, other: &mut Self) -> bool {
106        use AsPathSegment::*;
107
108        other.dedup();
109        match (self, other) {
110            (AsSequence(x), AsSequence(y)) | (ConfedSequence(x), ConfedSequence(y)) => {
111                x.extend_from_slice(y);
112                x.dedup();
113                true
114            }
115            (x @ (AsSequence(_) | ConfedSequence(_)), y) if x.is_empty() => {
116                std::mem::swap(x, y);
117                true
118            }
119            (_, AsSequence(y) | ConfedSequence(y)) if y.is_empty() => true,
120            _ => false,
121        }
122    }
123
124    /// Deduplicate ASNs in this path segment. Additionally, sets are sorted and may be converted to
125    /// sequences if they only have a single element.
126    ///
127    /// See [AsPath::dedup_coalesce] for more information.
128    fn dedup(&mut self) {
129        match self {
130            AsPathSegment::AsSequence(x) | AsPathSegment::ConfedSequence(x) => x.dedup(),
131            AsPathSegment::AsSet(x) => {
132                x.sort_unstable();
133                x.dedup();
134                if x.len() == 1 {
135                    *self = AsPathSegment::AsSequence(std::mem::take(x));
136                }
137            }
138            AsPathSegment::ConfedSet(x) => {
139                x.sort_unstable();
140                x.dedup();
141                if x.len() == 1 {
142                    *self = AsPathSegment::ConfedSequence(std::mem::take(x));
143                }
144            }
145        }
146    }
147
148    pub fn to_u32_vec_opt(&self, dedup: bool) -> Option<Vec<u32>> {
149        match self {
150            AsPathSegment::AsSequence(v) => {
151                let mut p: Vec<u32> = v.iter().map(|asn| (*asn).into()).collect();
152                if dedup {
153                    p.dedup();
154                }
155                Some(p)
156            }
157            AsPathSegment::AsSet(v) => {
158                if v.len() == 1 {
159                    // if the segment is an AS_SET and the length is 1, we can consider this a
160                    // single ASN path vector
161                    Some(vec![v[0].into()])
162                } else {
163                    None
164                }
165            }
166            _ => None,
167        }
168    }
169}
170
171impl IntoIterator for AsPathSegment {
172    type Item = Asn;
173    type IntoIter = smallvec::IntoIter<[Asn; 6]>;
174
175    fn into_iter(self) -> Self::IntoIter {
176        let (AsPathSegment::AsSequence(x)
177        | AsPathSegment::AsSet(x)
178        | AsPathSegment::ConfedSequence(x)
179        | AsPathSegment::ConfedSet(x)) = self;
180        x.into_iter()
181    }
182}
183
184impl<'a> IntoIterator for &'a AsPathSegment {
185    type Item = &'a Asn;
186    type IntoIter = std::slice::Iter<'a, Asn>;
187
188    fn into_iter(self) -> Self::IntoIter {
189        let (AsPathSegment::AsSequence(x)
190        | AsPathSegment::AsSet(x)
191        | AsPathSegment::ConfedSequence(x)
192        | AsPathSegment::ConfedSet(x)) = self;
193        x.iter()
194    }
195}
196
197impl<'a> IntoIterator for &'a mut AsPathSegment {
198    type Item = &'a mut Asn;
199    type IntoIter = std::slice::IterMut<'a, Asn>;
200
201    fn into_iter(self) -> Self::IntoIter {
202        let (AsPathSegment::AsSequence(x)
203        | AsPathSegment::AsSet(x)
204        | AsPathSegment::ConfedSequence(x)
205        | AsPathSegment::ConfedSet(x)) = self;
206        x.iter_mut()
207    }
208}
209
210impl AsRef<[Asn]> for AsPathSegment {
211    fn as_ref(&self) -> &[Asn] {
212        let (AsPathSegment::AsSequence(x)
213        | AsPathSegment::AsSet(x)
214        | AsPathSegment::ConfedSequence(x)
215        | AsPathSegment::ConfedSet(x)) = self;
216        x
217    }
218}
219
220impl Hash for AsPathSegment {
221    fn hash<H: Hasher>(&self, state: &mut H) {
222        // Hash the discriminant since we do not differentiate between confederation segments
223        discriminant(self).hash(state);
224
225        let set = match self {
226            AsPathSegment::AsSequence(x) | AsPathSegment::ConfedSequence(x) => {
227                return x.hash(state)
228            }
229            AsPathSegment::AsSet(x) | AsPathSegment::ConfedSet(x) => x,
230        };
231
232        // Most sets arrive in ascending order (the AS_SET wire form is written that way),
233        // so only copy and sort when the order actually differs.
234        if set.is_sorted() {
235            Asn::hash_slice(set, state);
236            return;
237        }
238
239        if set.len() <= 32 {
240            let mut buffer = [Asn::new_32bit(0); 32];
241            set.iter()
242                .zip(&mut buffer)
243                .for_each(|(asn, buffer)| *buffer = *asn);
244
245            let slice = &mut buffer[..set.len()];
246            slice.sort_unstable();
247            Asn::hash_slice(slice, state);
248            return;
249        }
250
251        // Fallback to allocating a Vec on the heap to sort
252        set.iter().sorted().for_each(|x| x.hash(state));
253    }
254}
255
256/// Check for equality of two path segments.
257/// ```rust
258/// # use bgpkit_parser::models::AsPathSegment;
259/// let a = AsPathSegment::sequence([1, 2, 3]);
260/// let b = AsPathSegment::set([1, 2, 3]);
261///
262/// // Sequences must be identical to be considered equivalent
263/// assert_eq!(a, AsPathSegment::sequence([1, 2, 3]));
264/// assert_ne!(a, AsPathSegment::sequence([1, 2, 3, 3]));
265///
266/// // Sets may be reordered, but must contain exactly the same ASNs.
267/// assert_eq!(b, AsPathSegment::set([3, 1, 2]));
268/// assert_ne!(b, AsPathSegment::set([1, 2, 3, 3]));
269/// ```
270impl PartialEq for AsPathSegment {
271    fn eq(&self, other: &Self) -> bool {
272        let (x, y) = match (self, other) {
273            (AsPathSegment::AsSequence(x), AsPathSegment::AsSequence(y))
274            | (AsPathSegment::ConfedSequence(x), AsPathSegment::ConfedSequence(y)) => {
275                return x == y
276            }
277            (AsPathSegment::AsSet(x), AsPathSegment::AsSet(y))
278            | (AsPathSegment::ConfedSet(x), AsPathSegment::ConfedSet(y)) => (x, y),
279            _ => return false,
280        };
281
282        // Attempt to exit early
283        if x.len() != y.len() {
284            return false;
285        } else if x == y {
286            return true;
287        }
288
289        if x.len() <= 32 {
290            let mut x_buffer = [Asn::new_32bit(0); 32];
291            let mut y_buffer = [Asn::new_32bit(0); 32];
292            x.iter()
293                .zip(&mut x_buffer)
294                .for_each(|(asn, buffer)| *buffer = *asn);
295            y.iter()
296                .zip(&mut y_buffer)
297                .for_each(|(asn, buffer)| *buffer = *asn);
298
299            x_buffer[..x.len()].sort_unstable();
300            y_buffer[..y.len()].sort_unstable();
301            return x_buffer[..x.len()] == y_buffer[..y.len()];
302        }
303
304        x.iter()
305            .sorted()
306            .zip(y.iter().sorted())
307            .all(|(a, b)| a == b)
308    }
309}
310
311impl Eq for AsPathSegment {}
312
313/// This is not a perfect solution since it is theoretically possible that a path could be created
314/// with more variations than a u64. That being said, the chances of such a thing occurring are
315/// essentially non-existent unless a BGP peer begins announcing maliciously constructed paths.
316struct AsPathNumberedRouteIter<'a> {
317    path: &'a [AsPathSegment],
318    index: usize,
319    route_num: u64,
320}
321
322impl Iterator for AsPathNumberedRouteIter<'_> {
323    type Item = Asn;
324
325    fn next(&mut self) -> Option<Self::Item> {
326        loop {
327            match self.path.first()? {
328                AsPathSegment::AsSequence(x) => match x.get(self.index) {
329                    None => {
330                        self.index = 0;
331                        self.path = &self.path[1..];
332                    }
333                    Some(asn) => {
334                        self.index += 1;
335                        return Some(*asn);
336                    }
337                },
338                AsPathSegment::AsSet(x) => {
339                    self.path = &self.path[1..];
340                    if x.is_empty() {
341                        return Some(Asn::RESERVED);
342                    }
343
344                    let asn = x[(self.route_num % x.len() as u64) as usize];
345                    self.route_num /= x.len() as u64;
346                    return Some(asn);
347                }
348                _ => self.path = &self.path[1..],
349            }
350        }
351    }
352}
353
354pub struct AsPathRouteIter<'a, D> {
355    path: Cow<'a, [AsPathSegment]>,
356    route_num: u64,
357    total_routes: u64,
358    _phantom: PhantomData<D>,
359}
360
361impl<D> Iterator for AsPathRouteIter<'_, D>
362where
363    D: FromIterator<Asn>,
364{
365    type Item = D;
366
367    fn next(&mut self) -> Option<Self::Item> {
368        if self.route_num >= self.total_routes {
369            return None;
370        }
371
372        // Attempt to speed up what is by far the most common case (a path of a single sequence)
373        if self.route_num == 0 && self.path.len() == 1 {
374            if let AsPathSegment::AsSequence(sequence) = &self.path[0] {
375                let route = D::from_iter(sequence.iter().copied());
376                self.route_num += 1;
377                return Some(route);
378            }
379        }
380
381        let route_asn_iter = AsPathNumberedRouteIter {
382            path: self.path.as_ref(),
383            index: 0,
384            route_num: self.route_num,
385        };
386
387        self.route_num += 1;
388        Some(D::from_iter(route_asn_iter))
389    }
390}
391
392/// AS Path representation.
393///
394/// Uses SmallVec for inline storage of segments to avoid heap allocations.
395/// Per RIB analysis: 99.99% of AS paths have exactly 1 segment.
396#[derive(Debug, PartialEq, Clone, Eq, Default, Hash)]
397pub struct AsPath {
398    /// Inline storage for up to 1 segment (99.99% zero-alloc coverage)
399    pub segments: SmallVec<[AsPathSegment; 1]>,
400}
401
402/// TypeScript-only marker describing the wire/serde encoding of [`AsPath`]:
403/// a flat array where numbers are AS_SEQUENCE ASNs, nested arrays are AS_SET
404/// members, and confederation segments fall back to `{ ty, values }` objects.
405/// [`AsPath`] itself has a manual serde impl, so it cannot carry the ts-rs
406/// derive; this marker stands in for it in generated bindings.
407#[cfg(feature = "ts-rs")]
408#[derive(ts_rs::TS)]
409#[ts(
410    export,
411    type = "(number | number[] | { ty: \"AS_SET\" | \"AS_SEQUENCE\" | \"AS_CONFED_SEQUENCE\" | \"AS_CONFED_SET\", values: number[] })[]"
412)]
413pub struct AsPathWire;
414
415// Define iterator type aliases. The storage mechanism and by extension the iterator types may
416// change later, but these types should remain consistent.
417pub type SegmentIter<'a> = std::slice::Iter<'a, AsPathSegment>;
418pub type SegmentIterMut<'a> = std::slice::IterMut<'a, AsPathSegment>;
419pub type SegmentIntoIter = smallvec::IntoIter<[AsPathSegment; 1]>;
420
421impl AsPath {
422    pub fn new() -> AsPath {
423        AsPath {
424            segments: SmallVec::new(),
425        }
426    }
427
428    /// Shorthand for creating an `AsPath` consisting of a single `AsSequence` segment.
429    pub fn from_sequence<S: AsRef<[u32]>>(seq: S) -> Self {
430        let segment = AsPathSegment::AsSequence(seq.as_ref().iter().copied().map_into().collect());
431
432        AsPath {
433            segments: SmallVec::from_buf([segment]),
434        }
435    }
436
437    pub fn from_segments<S: Into<SmallVec<[AsPathSegment; 1]>>>(segments: S) -> AsPath {
438        AsPath {
439            segments: segments.into(),
440        }
441    }
442
443    /// Adds a new segment to the end of the path. This will change the origin of the path. No
444    /// validation or merging the segment is performed during this step.
445    pub fn append_segment(&mut self, segment: AsPathSegment) {
446        self.segments.push(segment);
447    }
448
449    /// Check if the path is empty. Note that a non-empty path may have a route length of 0 due to
450    /// empty segments or confederation segments.
451    pub fn is_empty(&self) -> bool {
452        self.segments.is_empty()
453    }
454
455    /// Get the total length of the routes this path represents. For example, if this route
456    /// contained a sequence of 5 ASNs followed by a set of 3 ASNs, the total route length would be
457    /// 6.
458    ///
459    /// Confederation segments do not count towards the total route length. This means it is
460    /// possible to have a non-empty AsPath with a length of 0.
461    pub fn route_len(&self) -> usize {
462        self.segments.iter().map(AsPathSegment::route_len).sum()
463    }
464
465    /// Get the number of segments that make up this path. For the number of ASNs in routes
466    /// represented by this path, use [AsPath::route_len].
467    pub fn len(&self) -> usize {
468        self.segments.len()
469    }
470
471    /// Get the total number of routes this path represents. This function assumes the total number
472    /// of route variations can be represented by a u64.
473    pub fn num_route_variations(&self) -> u64 {
474        let mut variations: u64 = 1;
475
476        for segment in &self.segments {
477            if let AsPathSegment::AsSet(x) = segment {
478                variations *= x.len() as u64;
479            }
480        }
481
482        variations
483    }
484
485    /// Checks if any segments of this [AsPath] contain the following ASN.
486    pub fn contains_asn(&self, x: Asn) -> bool {
487        self.iter_segments().flatten().contains(&x)
488    }
489
490    /// Coalesce this [AsPath] into the minimum number of segments required without changing the
491    /// values along the path. This can be helpful as some BGP servers will prepend additional
492    /// segments without coalescing sequences. For de-duplicating see [AsPath::dedup_coalesce].
493    ///
494    /// Changes applied by this function:
495    ///  - Merge adjacent AS_SEQUENCE segments
496    ///  - Merge adjacent AS_CONFED_SEQUENCE segments
497    ///  - Removing empty AS_SEQUENCE and AS_CONFED_SEQUENCE segments
498    ///
499    /// ```rust
500    /// # use bgpkit_parser::models::{AsPath, AsPathSegment};
501    /// let mut a = AsPath::from_segments(vec![
502    ///     AsPathSegment::sequence([]),
503    ///     AsPathSegment::sequence([1, 2]),
504    ///     AsPathSegment::sequence([]),
505    ///     AsPathSegment::sequence([2]),
506    ///     AsPathSegment::set([2]),
507    ///     AsPathSegment::set([5, 3, 3, 2]),
508    /// ]);
509    ///
510    /// let expected = AsPath::from_segments(vec![
511    ///     AsPathSegment::sequence([1, 2, 2]),
512    ///     AsPathSegment::set([2]),
513    ///     AsPathSegment::set([5, 3, 3, 2]),
514    /// ]);
515    ///
516    /// a.coalesce();
517    /// assert_eq!(a, expected);
518    /// ```
519    /// If there is only one segment, no changes will occur. This function will not attempt to
520    /// deduplicate sequences or alter sets.
521    pub fn coalesce(&mut self) {
522        let mut end_index = 0;
523        let mut scan_index = 1;
524
525        while scan_index < self.segments.len() {
526            let (a, b) = self.segments.split_at_mut(scan_index);
527            if !AsPathSegment::merge_in_place(&mut a[end_index], &mut b[0]) {
528                end_index += 1;
529                self.segments.swap(end_index, scan_index);
530            }
531            scan_index += 1;
532        }
533
534        self.segments.truncate(end_index + 1);
535    }
536
537    /// A more aggressive version of [AsPath::coalesce] which also de-duplicates ASNs within this
538    /// path and converts sets of a single ASN to sequences. Some BGP servers will prepend their own
539    /// ASN multiple times when announcing a path to artificially increase the route length and make
540    /// the route seem less less desirable to peers.This function is best suited for use-cases which
541    /// only care about transitions between ASes along the path.
542    ///
543    /// Changes applied by this function:
544    ///  - Merge adjacent AS_SEQUENCE segments
545    ///  - Merge adjacent AS_CONFED_SEQUENCE segments
546    ///  - Removing empty AS_SEQUENCE and AS_CONFED_SEQUENCE segments
547    ///  - De-duplicate ASNs in AS_SEQUENCE and AS_CONFED_SEQUENCE segments
548    ///  - Sort and de-duplicate ASNs in AS_SET and AS_CONFED_SET segments
549    ///  - Convert AS_SET and AS_CONFED_SET segments with exactly 1 element to sequences
550    ///
551    /// ```rust
552    /// # use bgpkit_parser::models::{AsPath, AsPathSegment};
553    /// let mut a = AsPath::from_segments(vec![
554    ///     AsPathSegment::sequence([1, 2]),
555    ///     AsPathSegment::sequence([]),
556    ///     AsPathSegment::sequence([2]),
557    ///     AsPathSegment::set([2]),
558    ///     AsPathSegment::set([5, 3, 3, 2]),
559    /// ]);
560    ///
561    /// let expected = AsPath::from_segments(vec![
562    ///     AsPathSegment::sequence([1, 2]),
563    ///     AsPathSegment::set([2, 3, 5]),
564    /// ]);
565    ///
566    /// a.dedup_coalesce();
567    /// assert_eq!(a, expected);
568    /// ```
569    pub fn dedup_coalesce(&mut self) {
570        if !self.segments.is_empty() {
571            self.segments[0].dedup();
572        }
573        let mut end_index = 0;
574        let mut scan_index = 1;
575
576        while scan_index < self.segments.len() {
577            let (a, b) = self.segments.split_at_mut(scan_index);
578            if !AsPathSegment::dedup_merge_in_place(&mut a[end_index], &mut b[0]) {
579                end_index += 1;
580                self.segments.swap(end_index, scan_index);
581            }
582            scan_index += 1;
583        }
584
585        self.segments.truncate(end_index + 1);
586    }
587
588    /// Checks if two paths correspond to equivalent routes. Unlike `a == b`, this function will
589    /// ignore duplicate ASNs by comparing the coalesced versions of each path.
590    ///
591    /// This is equivalent to [AsPath::eq] after calling [AsPath::dedup_coalesce] on both paths.
592    pub fn has_equivalent_routing(&self, other: &Self) -> bool {
593        let mut a = self.to_owned();
594        let mut b = other.to_owned();
595
596        a.dedup_coalesce();
597        b.dedup_coalesce();
598
599        a == b
600    }
601
602    /// Get the length of ASN required to store all of the ASNs within this path
603    pub fn required_asn_length(&self) -> AsnLength {
604        self.iter_segments().flatten().map(Asn::required_len).fold(
605            AsnLength::Bits16,
606            |a, b| match (a, b) {
607                (AsnLength::Bits16, AsnLength::Bits16) => AsnLength::Bits16,
608                _ => AsnLength::Bits32,
609            },
610        )
611    }
612
613    pub fn iter_segments(&self) -> SegmentIter<'_> {
614        self.segments.iter()
615    }
616
617    pub fn iter_segments_mut(&mut self) -> SegmentIterMut<'_> {
618        self.segments.iter_mut()
619    }
620
621    pub fn into_segments_iter(self) -> SegmentIntoIter {
622        self.segments.into_iter()
623    }
624
625    /// Gets an iterator over all possible routes this path represents.
626    pub fn iter_routes<D>(&self) -> AsPathRouteIter<'_, D>
627    where
628        D: FromIterator<Asn>,
629    {
630        AsPathRouteIter {
631            path: Cow::Borrowed(&self.segments),
632            route_num: 0,
633            total_routes: self.num_route_variations(),
634            _phantom: PhantomData,
635        }
636    }
637
638    /// Construct AsPath from AS_PATH and AS4_PATH
639    ///
640    /// <https://datatracker.ietf.org/doc/html/rfc6793#section-4.2.3>
641    ///
642    /// ```text
643    ///    If the number of AS numbers in the AS_PATH attribute is less than the
644    ///    number of AS numbers in the AS4_PATH attribute, then the AS4_PATH
645    ///    attribute SHALL be ignored, and the AS_PATH attribute SHALL be taken
646    ///    as the AS path information.
647    ///
648    ///    If the number of AS numbers in the AS_PATH attribute is larger than
649    ///    or equal to the number of AS numbers in the AS4_PATH attribute, then
650    ///    the AS path information SHALL be constructed by taking as many AS
651    ///    numbers and path segments as necessary from the leading part of the
652    ///    AS_PATH attribute, and then prepending them to the AS4_PATH attribute
653    ///    so that the AS path information has a number of AS numbers identical
654    ///    to that of the AS_PATH attribute.  Note that a valid
655    ///    AS_CONFED_SEQUENCE or AS_CONFED_SET path segment SHALL be prepended
656    ///    if it is either the leading path segment or is adjacent to a path
657    ///    segment that is prepended.
658    /// ```
659    pub fn merge_aspath_as4path(aspath: &AsPath, as4path: &AsPath) -> AsPath {
660        if aspath.route_len() < as4path.route_len() {
661            // Per RFC6793, if 2-byte AS path is shorter than 4-byte AS path, ignore 4-byte AS path
662            return aspath.clone();
663        }
664
665        // Keep route_len(AS_PATH) - route_len(AS4_PATH) AS numbers from the
666        // leading part of the AS_PATH and prepend them to the AS4_PATH. The
667        // leading count is trimmed across segment boundaries, not per segment:
668        // whole segments are kept while they fit, and the segment straddling
669        // the boundary is cut after the remaining number of AS numbers.
670        let mut leading = aspath.route_len() - as4path.route_len();
671        let mut new_segs: Vec<AsPathSegment> = vec![];
672        for seg in &aspath.segments {
673            if leading == 0 {
674                break;
675            }
676            match seg.route_len() {
677                // Confederation segments contribute no AS numbers to the
678                // route and stay adjacent to the prepended part
679                // (RFC 6793 §4.2.3); empty segments carry no information.
680                0 => new_segs.push(seg.clone()),
681                n if n <= leading => {
682                    new_segs.push(seg.clone());
683                    leading -= n;
684                }
685                _ => {
686                    // Only an AS_SEQUENCE can straddle the boundary: an AS_SET
687                    // counts as a single route ASN and confederation segments
688                    // as none.
689                    let AsPathSegment::AsSequence(v) = seg else {
690                        unreachable!("only an AS_SEQUENCE can exceed the leading count");
691                    };
692                    new_segs.push(AsPathSegment::AsSequence(
693                        v.iter().take(leading).copied().collect(),
694                    ));
695                    leading = 0;
696                }
697            }
698        }
699        new_segs.extend(as4path.segments.iter().cloned());
700
701        let mut merged = AsPath {
702            segments: new_segs.into(),
703        };
704        merged.coalesce();
705        merged
706    }
707
708    /// Iterate through the originating ASNs of this path. This functionality is provided for
709    /// completeness, but in almost all cases this iterator should only contain a single element.
710    pub fn iter_origins(&self) -> impl '_ + Iterator<Item = Asn> {
711        let origin_slice = match self.segments.last() {
712            Some(AsPathSegment::AsSequence(v)) => v.last().map(std::slice::from_ref).unwrap_or(&[]),
713            Some(AsPathSegment::AsSet(v)) => v.as_ref(),
714            _ => &[],
715        };
716
717        origin_slice.iter().copied()
718    }
719
720    /// This function serves as a alternative to [AsPath::iter_origins] which attempts to make the
721    /// assumption that a path can only have exactly one origin. If a path does not have exactly 1
722    /// origin (such as when empty or ending in a set), then `None` will be returned instead.
723    pub fn get_origin_opt(&self) -> Option<Asn> {
724        match self.segments.last() {
725            Some(AsPathSegment::AsSequence(v)) => v.last().copied(),
726            Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
727            _ => None,
728        }
729    }
730
731    /// This function optionally returns the first hop in the AS hop, which is considered as the
732    /// collector ASN of the message.
733    pub fn get_collector_opt(&self) -> Option<Asn> {
734        match self.segments.first() {
735            Some(AsPathSegment::AsSequence(v)) => v.first().copied(),
736            Some(AsPathSegment::AsSet(v)) if v.len() == 1 => Some(v[0]),
737            _ => None,
738        }
739    }
740
741    pub fn to_u32_vec_opt(&self, dedup: bool) -> Option<Vec<u32>> {
742        let mut path = vec![];
743
744        // Iterate over the segments in reverse order
745        for seg in self.segments.iter().rev() {
746            // If a segment cannot be converted to a u32, the path is not a simple sequence of
747            // ASNs, and returning a partial AS path would be misleading.
748            let p = seg.to_u32_vec_opt(dedup)?;
749            // For each segment, reverse the ASN order so `path` is stored reversed.
750            path.extend(p.iter().rev());
751        }
752
753        match path.is_empty() {
754            true => {
755                // empty path is not a valid AS path
756                None
757            }
758            false => {
759                // reverse the order of ASNs to get the original path
760                path.reverse();
761                Some(path)
762            }
763        }
764    }
765}
766
767/// Iterates over all route variations the given `AsPath` represents.
768impl<'a> IntoIterator for &'a AsPath {
769    type Item = Vec<Asn>;
770    type IntoIter = AsPathRouteIter<'a, Vec<Asn>>;
771
772    fn into_iter(self) -> Self::IntoIter {
773        self.iter_routes()
774    }
775}
776
777/// Iterates over all route variations the given `AsPath` represents.
778impl IntoIterator for AsPath {
779    type Item = Vec<Asn>;
780    type IntoIter = AsPathRouteIter<'static, Vec<Asn>>;
781
782    fn into_iter(self) -> Self::IntoIter {
783        AsPathRouteIter {
784            total_routes: self.num_route_variations(),
785            path: Cow::Owned(self.segments.into_vec()),
786            route_num: 0,
787            _phantom: PhantomData,
788        }
789    }
790}
791
792impl Display for AsPath {
793    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
794        for (index, segment) in self.iter_segments().enumerate() {
795            if index != 0 {
796                write!(f, " ")?;
797            }
798
799            match segment {
800                AsPathSegment::AsSequence(v) | AsPathSegment::ConfedSequence(v) => {
801                    let mut asn_iter = v.iter();
802                    if let Some(first_element) = asn_iter.next() {
803                        write!(f, "{first_element}")?;
804
805                        for asn in asn_iter {
806                            write!(f, " {asn}")?;
807                        }
808                    }
809                }
810                AsPathSegment::AsSet(v) | AsPathSegment::ConfedSet(v) => {
811                    write!(f, "{{")?;
812                    let mut asn_iter = v.iter();
813                    if let Some(first_element) = asn_iter.next() {
814                        write!(f, "{first_element}")?;
815
816                        for asn in asn_iter {
817                            write!(f, ",{asn}")?;
818                        }
819                    }
820                    write!(f, "}}")?;
821                }
822            }
823        }
824
825        Ok(())
826    }
827}
828
829#[cfg(feature = "serde")]
830mod serde_impl {
831    use super::*;
832    use serde::de::{SeqAccess, Visitor};
833    use serde::ser::SerializeSeq;
834    use serde::{Deserialize, Deserializer, Serialize, Serializer};
835    use std::borrow::Cow;
836
837    /// Segment type names using names from RFC3065.
838    ///
839    /// <https://datatracker.ietf.org/doc/html/rfc3065#section-5>
840    #[allow(non_camel_case_types)]
841    #[derive(Serialize, Deserialize)]
842    enum SegmentType {
843        AS_SET,
844        AS_SEQUENCE,
845        AS_CONFED_SEQUENCE,
846        AS_CONFED_SET,
847    }
848
849    #[derive(Serialize, Deserialize)]
850    struct VerboseSegment<'s> {
851        ty: SegmentType,
852        values: Cow<'s, [Asn]>,
853    }
854
855    impl Serialize for AsPathSegment {
856        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
857        where
858            S: Serializer,
859        {
860            let (ty, elements) = match self {
861                AsPathSegment::AsSequence(x) => (SegmentType::AS_SEQUENCE, x.as_ref()),
862                AsPathSegment::AsSet(x) => (SegmentType::AS_SET, x.as_ref()),
863                AsPathSegment::ConfedSequence(x) => (SegmentType::AS_CONFED_SEQUENCE, x.as_ref()),
864                AsPathSegment::ConfedSet(x) => (SegmentType::AS_CONFED_SET, x.as_ref()),
865            };
866
867            let verbose = VerboseSegment {
868                ty,
869                values: Cow::Borrowed(elements),
870            };
871
872            verbose.serialize(serializer)
873        }
874    }
875
876    impl<'de> Deserialize<'de> for AsPathSegment {
877        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
878        where
879            D: Deserializer<'de>,
880        {
881            let verbose = VerboseSegment::deserialize(deserializer)?;
882
883            let values: SmallVec<[Asn; 6]> = verbose.values.into_owned().into();
884            match verbose.ty {
885                SegmentType::AS_SET => Ok(AsPathSegment::AsSet(values)),
886                SegmentType::AS_SEQUENCE => Ok(AsPathSegment::AsSequence(values)),
887                SegmentType::AS_CONFED_SEQUENCE => Ok(AsPathSegment::ConfedSequence(values)),
888                SegmentType::AS_CONFED_SET => Ok(AsPathSegment::ConfedSet(values)),
889            }
890        }
891    }
892
893    /// Check if we can serialize an `AsPath` using the simplified format and get the number of
894    /// elements to do so. The ambiguities that could prevent us from doing so are confederation
895    /// segments and adjacent sequence segments.
896    fn simplified_format_len(segments: &[AsPathSegment]) -> Option<usize> {
897        let mut elements = 0;
898        let mut prev_was_sequence = false;
899        for segment in segments {
900            match segment {
901                AsPathSegment::AsSequence(seq) if !prev_was_sequence => {
902                    prev_was_sequence = true;
903                    elements += seq.len();
904                }
905                AsPathSegment::AsSet(_) => {
906                    prev_was_sequence = false;
907                    elements += 1;
908                }
909                _ => return None,
910            }
911        }
912
913        Some(elements)
914    }
915
916    /// # Serialization format
917    /// For the sake of readability and ease of use within other applications, there are verbose and
918    /// simplified variants for serialization.
919    ///
920    /// ## Simplified format
921    /// The simplified format is the default preferred serialization format. This format does not
922    /// cover confederation segments and involves a single list of ASNs within the path sequence.
923    /// For sets, a list of set members is used in place of an ASN.
924    /// ```rust
925    /// # use bgpkit_parser::models::{Asn, AsPath};
926    /// # use bgpkit_parser::models::AsPathSegment::*;
927    ///
928    /// let a: AsPath = serde_json::from_str("[123, 942, 102]").unwrap();
929    /// let b: AsPath = serde_json::from_str("[231, 432, [643, 836], 352]").unwrap();
930    ///
931    /// assert_eq!(&a.segments[..], &[
932    ///     AsSequence(vec![Asn::from(123), Asn::from(942), Asn::from(102)].into())
933    /// ][..]);
934    /// assert_eq!(&b.segments[..], &[
935    ///     AsSequence(vec![Asn::from(231), Asn::from(432)].into()),
936    ///     AsSet(vec![Asn::from(643), Asn::from(836)].into()),
937    ///     AsSequence(vec![Asn::from(352)].into())
938    /// ][..]);
939    /// ```
940    ///
941    /// ## Verbose format
942    /// The verbose format serves as the fallback format for when the simplified format can not be
943    /// used due to ambiguity. This happens when confederation segments are present, or multiple
944    /// sequences occur back to back. In this format, segments are explicitly seperated and labeled.
945    /// Segment types, denoted by the `ty` field, correspond to the names used within RFC3065
946    /// (`AS_SET`, `AS_SEQUENCE`, `AS_CONFED_SEQUENCE`, `AS_CONFED_SET`).
947    /// ```rust
948    /// # use bgpkit_parser::models::{Asn, AsPath};
949    /// # use bgpkit_parser::models::AsPathSegment::*;
950    ///
951    /// let a = r#"[
952    ///     { "ty": "AS_CONFED_SEQUENCE", "values": [123, 942] },
953    ///     { "ty": "AS_SEQUENCE", "values": [773] },
954    ///     { "ty": "AS_SEQUENCE", "values": [382, 293] }
955    /// ]"#;
956    ///
957    /// let parsed: AsPath = serde_json::from_str(a).unwrap();
958    /// assert_eq!(&parsed.segments[..], &[
959    ///     ConfedSequence(vec![Asn::from(123), Asn::from(942)].into()),
960    ///     AsSequence(vec![Asn::from(773)].into()),
961    ///     AsSequence(vec![Asn::from(382), Asn::from(293)].into())
962    /// ][..]);
963    /// ```
964    impl Serialize for AsPath {
965        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
966        where
967            S: Serializer,
968        {
969            if let Some(num_elements) = simplified_format_len(&self.segments) {
970                // Serialize simplified format
971                let mut seq_serializer = serializer.serialize_seq(Some(num_elements))?;
972
973                for segment in &self.segments {
974                    match segment {
975                        AsPathSegment::AsSequence(elements) => {
976                            elements
977                                .iter()
978                                .try_for_each(|x| seq_serializer.serialize_element(x))?;
979                        }
980                        AsPathSegment::AsSet(x) => seq_serializer.serialize_element(x)?,
981                        _ => unreachable!("simplified_format_len checked for confed segments"),
982                    }
983                }
984                return seq_serializer.end();
985            }
986
987            // Serialize verbose format
988            serializer.collect_seq(&self.segments)
989        }
990    }
991
992    struct AsPathVisitor;
993
994    impl<'de> Visitor<'de> for AsPathVisitor {
995        type Value = AsPath;
996
997        fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
998            formatter.write_str("list of AS_PATH segments")
999        }
1000
1001        fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
1002        where
1003            A: SeqAccess<'de>,
1004        {
1005            // Technically, we can handle an input that mixes the simplified and verbose formats,
1006            // but we do not want to document this behavior as it may change in future updates.
1007            #[derive(Deserialize)]
1008            #[serde(untagged)]
1009            enum PathElement {
1010                SequenceElement(Asn),
1011                Set(Vec<Asn>),
1012                Verbose(AsPathSegment),
1013            }
1014
1015            let mut append_new_sequence = false;
1016            let mut segments: SmallVec<[AsPathSegment; 1]> = SmallVec::new();
1017            while let Some(element) = seq.next_element()? {
1018                match element {
1019                    PathElement::SequenceElement(x) => {
1020                        if append_new_sequence {
1021                            // If the input is mixed between verbose and regular segments, this flag
1022                            // is used to prevent appending to a verbose sequence.
1023                            append_new_sequence = false;
1024                            segments.push(AsPathSegment::AsSequence(SmallVec::new()));
1025                        }
1026
1027                        if let Some(AsPathSegment::AsSequence(last_sequence)) = segments.last_mut()
1028                        {
1029                            last_sequence.push(x);
1030                        } else {
1031                            let mut new_seq: SmallVec<[Asn; 6]> = SmallVec::new();
1032                            new_seq.push(x);
1033                            segments.push(AsPathSegment::AsSequence(new_seq));
1034                        }
1035                    }
1036                    PathElement::Set(values) => {
1037                        segments.push(AsPathSegment::AsSet(values.into()));
1038                    }
1039                    PathElement::Verbose(verbose) => {
1040                        segments.push(verbose);
1041                    }
1042                }
1043            }
1044
1045            Ok(AsPath { segments })
1046        }
1047    }
1048
1049    impl<'de> Deserialize<'de> for AsPath {
1050        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1051        where
1052            D: Deserializer<'de>,
1053        {
1054            deserializer.deserialize_seq(AsPathVisitor)
1055        }
1056    }
1057}
1058
1059#[cfg(test)]
1060mod tests {
1061    use crate::models::*;
1062    use itertools::Itertools;
1063    use std::collections::hash_map::DefaultHasher;
1064    use std::collections::HashSet;
1065    use std::hash::{Hash, Hasher};
1066
1067    #[test]
1068    fn test_aspath_as4path_merge() {
1069        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1070        let as4path = AsPath::from_sequence([2, 3, 7]);
1071        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1072        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 3, 7]));
1073
1074        let aspath = AsPath::from_sequence([1, 2]);
1075        let as4path = AsPath::from_sequence([2, 3, 7]);
1076        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1077        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2]));
1078
1079        // when the sequence length is the same, the as4path should be used
1080        let aspath = AsPath::from_sequence([1, 2]);
1081        let as4path = AsPath::from_sequence([3, 4]);
1082        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1083        assert_eq!(newpath.segments[0], AsPathSegment::sequence([3, 4]));
1084
1085        let aspath = AsPath::from_segments(vec![
1086            AsPathSegment::sequence([1, 2, 3, 5]),
1087            AsPathSegment::set([7, 8]),
1088        ]);
1089        let as4path = AsPath::from_sequence([6, 7, 8]);
1090        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1091        // route_len 5 - 3 = 2 leading AS numbers, then the whole AS4_PATH;
1092        // the AS_PATH trailing set is the stale 2-octet mirror and is dropped.
1093        assert_eq!(newpath.segments.len(), 1);
1094        assert_eq!(
1095            newpath.segments[0],
1096            AsPathSegment::sequence([1, 2, 6, 7, 8])
1097        );
1098
1099        let aspath = AsPath::from_segments(vec![
1100            AsPathSegment::sequence([1, 2]),
1101            AsPathSegment::sequence([3, 5]),
1102            AsPathSegment::set([13, 14]),
1103        ]);
1104        let as4path = AsPath::from_segments(vec![
1105            AsPathSegment::sequence([8, 4, 6]),
1106            AsPathSegment::set([11, 12]),
1107        ]);
1108        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1109        // route_len 5 - 4 = 1 leading AS number cut across segment boundaries.
1110        assert_eq!(newpath.segments.len(), 2);
1111        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 8, 4, 6]));
1112        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1113
1114        let aspath = AsPath::from_segments(vec![
1115            AsPathSegment::sequence([1, 2, 3]),
1116            AsPathSegment::sequence([5]),
1117            AsPathSegment::set([13, 14]),
1118        ]);
1119        let as4path = AsPath::from_segments(vec![
1120            AsPathSegment::sequence([7, 8]),
1121            AsPathSegment::set([11, 12]),
1122        ]);
1123        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1124        // route_len 5 - 3 = 2 leading AS numbers cut across segment boundaries.
1125        assert_eq!(newpath.segments.len(), 2);
1126        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 7, 8]));
1127        assert_eq!(newpath.segments[1], AsPathSegment::set([11, 12]));
1128
1129        // Misaligned segment boundaries: the leading count must be trimmed
1130        // across segments, not aligned per segment pair (PR #330 review).
1131        let aspath = AsPath::from_segments(vec![
1132            AsPathSegment::sequence([1, 2]),
1133            AsPathSegment::sequence([3, 4]),
1134        ]);
1135        let as4path = AsPath::from_sequence([9, 10]);
1136        let newpath = AsPath::merge_aspath_as4path(&aspath, &as4path);
1137        assert_eq!(newpath.segments.len(), 1);
1138        assert_eq!(newpath.segments[0], AsPathSegment::sequence([1, 2, 9, 10]));
1139    }
1140
1141    #[test]
1142    fn test_get_origin() {
1143        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1144        let origin = aspath.get_origin_opt();
1145        assert_eq!(origin.unwrap(), 5);
1146
1147        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1, 2, 3, 5])]);
1148        let origin = aspath.get_origin_opt();
1149        assert!(origin.is_none());
1150
1151        let aspath = AsPath::from_segments(vec![AsPathSegment::set([1])]);
1152        let origin = aspath.get_origin_opt();
1153        assert_eq!(origin.unwrap(), 1);
1154
1155        let aspath = AsPath::from_segments(vec![
1156            AsPathSegment::sequence([1, 2, 3, 5]),
1157            AsPathSegment::set([7, 8]),
1158        ]);
1159        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1160        assert_eq!(origins, vec![7, 8]);
1161
1162        let aspath = AsPath::from_segments(vec![
1163            AsPathSegment::sequence([1, 2, 3, 5]),
1164            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
1165        ]);
1166        let origins = aspath.iter_origins().map_into::<u32>().collect::<Vec<_>>();
1167        assert_eq!(origins, Vec::<u32>::new());
1168    }
1169
1170    #[test]
1171    fn test_get_collector() {
1172        let aspath = AsPath::from_sequence([1, 2, 3, 5]);
1173        let collector = aspath.get_collector_opt();
1174        assert_eq!(collector.unwrap(), 1);
1175
1176        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7])]);
1177        let collector = aspath.get_collector_opt();
1178        assert_eq!(collector.unwrap(), 7);
1179
1180        let aspath = AsPath::from_segments(vec![AsPathSegment::set([7, 8])]);
1181        let collector = aspath.get_collector_opt();
1182        assert!(collector.is_none());
1183    }
1184
1185    #[test]
1186    fn test_aspath_route_iter() {
1187        let path = AsPath::from_segments(vec![AsPathSegment::sequence([3, 4])]);
1188        let mut routes = HashSet::new();
1189        for route in &path {
1190            assert!(routes.insert(route));
1191        }
1192        assert_eq!(1, routes.len());
1193
1194        let path = AsPath::from_segments(vec![
1195            AsPathSegment::set([3, 4]),
1196            AsPathSegment::set([5, 6]),
1197            AsPathSegment::sequence([7, 8]),
1198            AsPathSegment::ConfedSet(vec![Asn::new_32bit(9)].into()),
1199            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(9)].into()),
1200        ]);
1201        assert_eq!(path.route_len(), 4);
1202
1203        let mut routes = HashSet::new();
1204        for route in &path {
1205            assert!(routes.insert(route));
1206        }
1207
1208        assert_eq!(routes.len(), 4);
1209        assert!(routes.contains(&vec![
1210            Asn::from(3),
1211            Asn::from(5),
1212            Asn::from(7),
1213            Asn::from(8)
1214        ]));
1215        assert!(routes.contains(&vec![
1216            Asn::from(3),
1217            Asn::from(6),
1218            Asn::from(7),
1219            Asn::from(8)
1220        ]));
1221        assert!(routes.contains(&vec![
1222            Asn::from(4),
1223            Asn::from(5),
1224            Asn::from(7),
1225            Asn::from(8)
1226        ]));
1227        assert!(routes.contains(&vec![
1228            Asn::from(4),
1229            Asn::from(6),
1230            Asn::from(7),
1231            Asn::from(8)
1232        ]));
1233    }
1234
1235    #[test]
1236    fn test_segment() {
1237        let path_segment = AsPathSegment::sequence([1, 2, 3, 4]);
1238        assert_eq!(path_segment.len(), 4);
1239
1240        // test iter
1241        let mut iter = path_segment.iter();
1242        assert_eq!(iter.next(), Some(&Asn::new_32bit(1)));
1243        assert_eq!(iter.next(), Some(&Asn::new_32bit(2)));
1244        assert_eq!(iter.next(), Some(&Asn::new_32bit(3)));
1245        assert_eq!(iter.next(), Some(&Asn::new_32bit(4)));
1246        assert_eq!(iter.next(), None);
1247
1248        // test iter_mut
1249        let mut path_segment = AsPathSegment::sequence([1]);
1250        let mut iter_mut = path_segment.iter_mut();
1251        assert_eq!(iter_mut.next(), Some(&mut Asn::new_32bit(1)));
1252        assert_eq!(iter_mut.next(), None);
1253
1254        // test is_confed
1255        assert!(AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into()).is_confed());
1256        assert!(AsPathSegment::ConfedSet(vec![Asn::new_32bit(1)].into()).is_confed());
1257    }
1258
1259    #[test]
1260    fn test_coalesce() {
1261        let mut a = AsPath::from_segments(vec![
1262            AsPathSegment::sequence([]),
1263            AsPathSegment::sequence([1, 2]),
1264            AsPathSegment::sequence([]),
1265            AsPathSegment::sequence([2]),
1266            AsPathSegment::set([2]),
1267            AsPathSegment::set([5, 3, 3, 2]),
1268        ]);
1269
1270        let expected = AsPath::from_segments(vec![
1271            AsPathSegment::sequence([1, 2, 2]),
1272            AsPathSegment::set([2]),
1273            AsPathSegment::set([5, 3, 3, 2]),
1274        ]);
1275
1276        a.coalesce();
1277        assert_eq!(a, expected);
1278    }
1279
1280    #[test]
1281    fn test_confed_set_dedup() {
1282        let mut path_segment =
1283            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(1)].into());
1284        path_segment.dedup();
1285        assert_eq!(
1286            path_segment,
1287            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1)].into())
1288        );
1289
1290        let mut path_segment = AsPathSegment::ConfedSet(
1291            vec![Asn::new_32bit(1), Asn::new_32bit(2), Asn::new_32bit(2)].into(),
1292        );
1293        path_segment.dedup();
1294        assert_eq!(
1295            path_segment,
1296            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into())
1297        );
1298    }
1299
1300    #[test]
1301    fn test_path_to_u32() {
1302        // regular sequence
1303        let path_segment = AsPathSegment::sequence([1, 2, 3, 3]);
1304        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1, 2, 3, 3]));
1305        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1, 2, 3]));
1306
1307        // regular set: should return None
1308        let path_segment = AsPathSegment::set([1, 2, 3, 3]);
1309        assert_eq!(path_segment.to_u32_vec_opt(false), None);
1310        assert_eq!(path_segment.to_u32_vec_opt(true), None);
1311
1312        // singular set: should be converted to singleton vector
1313        let path_segment = AsPathSegment::set([1]);
1314        assert_eq!(path_segment.to_u32_vec_opt(false), Some(vec![1]));
1315        assert_eq!(path_segment.to_u32_vec_opt(true), Some(vec![1]));
1316
1317        // combination of a sequence and a few singleton sets, should be merged into a single vector
1318        let as_path = AsPath::from_segments(vec![
1319            AsPathSegment::set([4]),
1320            AsPathSegment::sequence([2, 3, 3]),
1321            AsPathSegment::set([1]),
1322        ]);
1323        assert_eq!(as_path.to_u32_vec_opt(false), Some(vec![4, 2, 3, 3, 1]));
1324        assert_eq!(as_path.to_u32_vec_opt(true), Some(vec![4, 2, 3, 1]));
1325
1326        // should the path containing any non-convertible segments, return None
1327        let as_path = AsPath::from_segments(vec![
1328            AsPathSegment::set([4, 2]),
1329            AsPathSegment::sequence([2, 3, 3]),
1330            AsPathSegment::set([1]),
1331        ]);
1332        assert_eq!(as_path.to_u32_vec_opt(false), None);
1333        assert_eq!(as_path.to_u32_vec_opt(true), None);
1334
1335        // other corner cases
1336
1337        // empty path
1338        let as_path = AsPath::from_segments(vec![]);
1339        assert_eq!(as_path.to_u32_vec_opt(false), None);
1340        assert_eq!(as_path.to_u32_vec_opt(true), None);
1341
1342        // path with federation segments
1343        let as_path = AsPath::from_segments(vec![
1344            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into()),
1345            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(3), Asn::new_32bit(4)].into()),
1346        ]);
1347        assert_eq!(as_path.to_u32_vec_opt(false), None);
1348        assert_eq!(as_path.to_u32_vec_opt(true), None);
1349    }
1350
1351    #[test]
1352    fn test_as_ref() {
1353        let path_segment = AsPathSegment::sequence([1, 2]);
1354        assert_eq!(
1355            path_segment.as_ref(),
1356            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1357        );
1358
1359        let path_segment = AsPathSegment::set([1, 2]);
1360        assert_eq!(
1361            path_segment.as_ref(),
1362            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1363        );
1364
1365        let path_segment =
1366            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
1367        assert_eq!(
1368            path_segment.as_ref(),
1369            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1370        );
1371
1372        let path_segment =
1373            AsPathSegment::ConfedSet(vec![Asn::new_32bit(1), Asn::new_32bit(2)].into());
1374        assert_eq!(
1375            path_segment.as_ref(),
1376            &[Asn::new_32bit(1), Asn::new_32bit(2)]
1377        );
1378    }
1379
1380    #[test]
1381    fn test_hashing() {
1382        let path_segment = AsPathSegment::sequence([1, 2]);
1383        let path_segment2 = AsPathSegment::sequence([1, 2]);
1384
1385        let hashset = std::iter::once(path_segment).collect::<HashSet<_>>();
1386        assert!(hashset.contains(&path_segment2));
1387    }
1388
1389    #[test]
1390    fn test_set_hashing_is_order_independent() {
1391        fn hash_of(segment: &AsPathSegment) -> u64 {
1392            let mut hasher = DefaultHasher::new();
1393            segment.hash(&mut hasher);
1394            hasher.finish()
1395        }
1396
1397        // sets hash by sorted contents, whichever path the implementation takes
1398        let sorted = AsPathSegment::set([1, 2, 3]);
1399        let unsorted = AsPathSegment::set([3, 1, 2]);
1400        assert_eq!(hash_of(&sorted), hash_of(&unsorted));
1401
1402        // ... including the heap fallback for sets larger than the stack buffer
1403        let big_sorted = AsPathSegment::set((1u32..40).collect::<Vec<_>>());
1404        let big_unsorted = AsPathSegment::set((1u32..40).rev().collect::<Vec<_>>());
1405        assert_eq!(hash_of(&big_sorted), hash_of(&big_unsorted));
1406    }
1407
1408    #[test]
1409    fn test_equality() {
1410        let path_segment = AsPathSegment::sequence([1, 2]);
1411        let path_segment2 = AsPathSegment::sequence([1, 2]);
1412
1413        assert_eq!(path_segment, path_segment2);
1414
1415        let path_segment = AsPathSegment::sequence([1, 2]);
1416        let path_segment2 = AsPathSegment::set([1, 2, 3]);
1417        assert_ne!(path_segment, path_segment2);
1418
1419        // test equality of AS path longer than 32 ASNs
1420        let path_segment = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1421        let path_segment2 = AsPathSegment::sequence((1..33).collect::<Vec<_>>());
1422        assert_eq!(path_segment, path_segment2);
1423    }
1424
1425    #[test]
1426    fn test_as_path_display() {
1427        let path = AsPath::from_segments(vec![
1428            AsPathSegment::sequence([1, 2]),
1429            AsPathSegment::set([3, 4]),
1430            AsPathSegment::sequence([5, 6]),
1431            AsPathSegment::ConfedSet(vec![Asn::new_32bit(7)].into()),
1432            AsPathSegment::ConfedSequence(vec![Asn::new_32bit(8)].into()),
1433        ]);
1434
1435        assert_eq!(path.to_string(), "1 2 {3,4} 5 6 {7} 8");
1436    }
1437}