use tinyvec::{array_vec, tiny_vec};
use crate::{
core::encode::WireEncode,
path::{
onehop::layout::OneHopPathLayout,
standard::{
mac::{ForwardingKey, algo::mac_beta_step},
model::{HopField, InfoField, Segment, StandardPath},
types::{HopFieldFlags, HopFieldMac, InfoFieldFlags},
},
},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct OneHopPath {
pub info: InfoField,
pub hops: [HopField; 2],
}
impl OneHopPath {
pub fn new_from_parts(info: InfoField, hops: [HopField; 2]) -> Self {
Self { info, hops }
}
pub fn new(
egress_interface: u16,
segment_id: u16,
timestamp: u32,
forwarding_key: ForwardingKey,
expiration_units: u8,
) -> Self {
let info = InfoField {
flags: InfoFieldFlags::CONS_DIR,
segment_id,
timestamp,
};
let hop1 = HopField {
flags: HopFieldFlags::empty(),
cons_ingress: 0,
cons_egress: egress_interface,
expiration_units,
mac: HopFieldMac([0u8; 6]),
}
.with_calculated_mac(info.segment_id, info.timestamp, &forwarding_key);
let hop2 = HopField::empty();
Self {
info,
hops: [hop1, hop2],
}
}
pub fn set_second_hop(
&mut self,
ingress_interface: u16,
forwarding_key: ForwardingKey,
segment_id_was_advanced: bool,
) {
let beta = if segment_id_was_advanced {
self.info.segment_id
} else {
mac_beta_step(self.info.segment_id, self.hops[0].mac.into())
};
self.hops[1] = HopField {
flags: HopFieldFlags::empty(),
cons_ingress: ingress_interface,
cons_egress: 0,
expiration_units: 0,
mac: HopFieldMac([0u8; 6]),
}
.with_calculated_mac(beta, self.info.timestamp, &forwarding_key);
}
pub fn into_reversed_standard_path(self) -> Result<StandardPath, Self> {
if self.hops[1].cons_ingress == 0 {
return Err(self);
}
let OneHopPath {
mut info,
hops: [hop1, hop2],
} = self;
info.flags ^= InfoFieldFlags::CONS_DIR;
let standard_path = StandardPath {
current_info_field: 0,
current_hop_field: 0,
segments: array_vec!(Segment {
info_field: info,
hop_fields: tiny_vec!(hop2, hop1),
}),
};
Ok(standard_path)
}
}
impl OneHopPath {
pub fn from_view(view: &crate::proto::path::onehop::view::OneHopPathView) -> Self {
let info: InfoField = InfoField::from_view(view.info_field());
let [hop1, hop2] = view.hop_fields();
let hops = [HopField::from_view(hop1), HopField::from_view(hop2)];
Self { info, hops }
}
}
impl WireEncode for OneHopPath {
fn required_size(&self) -> usize {
OneHopPathLayout::SIZE_BYTES
}
fn wire_valid(&self) -> Result<(), crate::core::encode::InvalidStructureError> {
Ok(())
}
unsafe fn encode_unchecked(&self, buf: &mut [u8]) -> usize {
unsafe {
use OneHopPathLayout as OHPL;
self.info
.encode_unchecked(&mut buf[OHPL::INFO_FIELD.aligned_byte_range()]);
self.hops[0].encode_unchecked(&mut buf[OHPL::HOP_FIELD_1.aligned_byte_range()]);
self.hops[1].encode_unchecked(&mut buf[OHPL::HOP_FIELD_2.aligned_byte_range()]);
}
OneHopPathLayout::SIZE_BYTES
}
}
#[cfg(feature = "proptest")]
pub mod ptest {
use ::proptest::prelude::*;
use super::*;
#[derive(Debug, Clone, Default)]
pub struct ArbitraryOneHopPathContext {
}
impl Arbitrary for OneHopPath {
type Parameters = ArbitraryOneHopPathContext;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(_ctx: Self::Parameters) -> Self::Strategy {
(
any::<InfoField>(),
any::<HopField>(),
any::<Option<HopField>>(),
)
.prop_map(|(info, hop1, hop2)| {
Self {
info,
hops: [hop1, hop2.unwrap_or_else(HopField::empty)],
}
})
.boxed()
}
}
}