use std::{
fmt::{Debug, Display},
mem::transmute,
};
use crate::{
core::view::{View, ViewConversionError},
dataplane_path::{
onehop::layout::OneHopPathLayout,
standard::{
mac::{ForwardingKey, HopMacCalculate, algo::mac_beta_step},
types::{InfoFieldFlags, exp_time_to_duration},
view::{HopFieldView, InfoFieldView},
},
types::PathReverseError,
},
};
#[repr(transparent)]
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct OneHopPathView([u8; OneHopPathLayout::SIZE_BYTES]);
impl OneHopPathView {
#[inline]
pub fn info_field(&self) -> &InfoFieldView {
unsafe {
InfoFieldView::from_slice_unchecked(
&self.0[OneHopPathLayout::INFO_FIELD.aligned_byte_range()],
)
}
}
#[inline]
pub fn info_field_mut(&mut self) -> &mut InfoFieldView {
unsafe {
InfoFieldView::from_mut_slice_unchecked(
&mut self.0[OneHopPathLayout::INFO_FIELD.aligned_byte_range()],
)
}
}
#[inline]
pub fn hop_fields(&self) -> [&HopFieldView; 2] {
unsafe {
[
HopFieldView::from_slice_unchecked(
&self.0[OneHopPathLayout::HOP_FIELD_1.aligned_byte_range()],
),
HopFieldView::from_slice_unchecked(
&self.0[OneHopPathLayout::HOP_FIELD_2.aligned_byte_range()],
),
]
}
}
#[inline]
pub fn mut_hop_fields(&mut self) -> [&mut HopFieldView; 2] {
unsafe {
let [hop1, hop2] = self.0.get_disjoint_unchecked_mut([
OneHopPathLayout::HOP_FIELD_1.aligned_byte_range(),
OneHopPathLayout::HOP_FIELD_2.aligned_byte_range(),
]);
[
HopFieldView::from_mut_slice_unchecked(hop1),
HopFieldView::from_mut_slice_unchecked(hop2),
]
}
}
}
impl OneHopPathView {
#[inline]
pub fn set_second_hop(
&mut self,
ingress_interface: u16,
forwarding_key: ForwardingKey,
segment_id_was_advanced: bool,
) {
let (beta, timestamp) = {
let info = self.info_field();
let beta = if segment_id_was_advanced {
info.segment_id()
} else {
mac_beta_step(info.segment_id(), self.hop_fields()[0].mac().into())
};
(beta, info.timestamp())
};
let [hop1, hop2] = self.mut_hop_fields();
hop2.set_cons_ingress(ingress_interface);
hop2.set_cons_ingress(ingress_interface);
hop2.set_cons_egress(0);
hop2.set_cons_ingress(ingress_interface);
hop2.set_exp_time(hop1.exp_time());
let mac = hop2.calculate_mac(beta, timestamp, &forwarding_key);
hop2.set_mac(mac);
}
#[inline]
pub fn try_reverse(&mut self) -> Result<(), PathReverseError> {
if self.hop_fields()[1].cons_ingress() == 0 {
return Err(PathReverseError::new(
"Cannot reverse a one-hop path whose second hop has not been set yet",
));
}
let [hop1, hop2] = self.mut_hop_fields();
std::mem::swap(hop1, hop2);
let mut flags = self.info_field().flags();
flags.toggle(InfoFieldFlags::CONS_DIR);
self.info_field_mut().set_flags(flags);
Ok(())
}
}
impl OneHopPathView {
#[inline]
pub fn expiration(&self) -> u32 {
let base = self.info_field().timestamp();
let min_exp = self
.hop_fields()
.iter()
.map(|hop| hop.exp_time())
.min()
.unwrap_or(0);
base + exp_time_to_duration(min_exp).as_secs() as u32
}
}
impl Debug for OneHopPathView {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OneHopPathView")
.field("info_field", &self.info_field())
.field("hop_fields", &self.hop_fields())
.finish()
}
}
impl Display for OneHopPathView {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let cons_dir = self.info_field().flags().cons_dir();
let [hop1, hop2] = self.hop_fields();
match cons_dir {
true => {
write!(
f,
"[onehop] cons_dir: {cons_dir} hops: [{},{}; {},{}]",
hop1.cons_ingress(),
hop1.cons_egress(),
hop2.cons_ingress(),
hop2.cons_egress()
)
}
false => {
write!(
f,
"[onehop] cons_dir: {cons_dir} hops: [{},{}: {},{}]",
hop2.cons_egress(),
hop2.cons_ingress(),
hop1.cons_egress(),
hop1.cons_ingress(),
)
}
}
}
}
impl View for OneHopPathView {
#[inline]
fn has_required_size(buf: &[u8]) -> Result<usize, ViewConversionError> {
use crate::core::layout::Layout;
let layout = OneHopPathLayout::try_from(buf)?;
Ok(layout.size_bytes())
}
#[inline]
fn as_slice(&self) -> &[u8] {
&self.0
}
#[inline]
unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
&mut self.0
}
#[inline]
fn as_slice_boxed(self: Box<Self>) -> Box<[u8]> {
let sized: Box<[u8; OneHopPathLayout::SIZE_BYTES]> = unsafe { transmute(self) };
sized
}
#[inline]
unsafe fn from_slice_unchecked(buf: &[u8]) -> &Self {
let sized: &[u8; OneHopPathLayout::SIZE_BYTES] =
unsafe { buf.try_into().unwrap_unchecked() };
unsafe { transmute(sized) }
}
#[inline]
unsafe fn from_mut_slice_unchecked(buf: &mut [u8]) -> &mut Self {
let sized: &mut [u8; OneHopPathLayout::SIZE_BYTES] =
unsafe { buf.try_into().unwrap_unchecked() };
unsafe { transmute(sized) }
}
#[inline]
unsafe fn from_boxed_unchecked(buf: Box<[u8]>) -> Box<Self> {
let sized: Box<[u8; OneHopPathLayout::SIZE_BYTES]> =
unsafe { buf.try_into().unwrap_unchecked() };
unsafe { transmute(sized) }
}
}