use crate::{
core::{
convert::ToModel,
encode::{InvalidStructureError, WireEncode},
macros::impl_from,
model::Model,
},
dataplane_path::{
layout::ScionHeaderPathLayout,
onehop::model::OneHopPath,
standard::model::StandardPath,
types::{PathReverseError, PathType},
view::{ScionDpPathView, ScionDpPathViewRef},
},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[allow(clippy::large_enum_variant)]
pub enum DpPath {
Standard(StandardPath),
OneHop(OneHopPath),
Empty,
Unsupported {
path_type: PathType,
data: Vec<u8>,
},
}
impl DpPath {
#[inline]
pub fn from_view(view: &ScionDpPathViewRef) -> Self {
match *view {
ScionDpPathViewRef::Standard(standard_view) => {
DpPath::Standard(standard_view.to_model())
}
ScionDpPathViewRef::OneHop(onehop_view) => DpPath::OneHop(onehop_view.to_model()),
ScionDpPathViewRef::Empty => DpPath::Empty,
ScionDpPathViewRef::Unsupported {
path_type,
data: buf,
} => {
DpPath::Unsupported {
path_type,
data: buf.to_vec(),
}
}
}
}
#[inline]
pub fn try_encode_to_owned_view(&self) -> Result<ScionDpPathView, InvalidStructureError> {
let res = match self {
DpPath::Standard(standard_path) => {
ScionDpPathView::Standard(standard_path.try_encode_to_owned_view()?)
}
DpPath::OneHop(onehop_path) => {
ScionDpPathView::OneHop(*onehop_path.try_encode_to_owned_view()?)
}
DpPath::Empty => ScionDpPathView::Empty,
DpPath::Unsupported { path_type, data } => {
ScionDpPathView::Unsupported {
path_type: *path_type,
data: data.clone().into_boxed_slice(),
}
}
};
Ok(res)
}
}
impl DpPath {
#[inline]
pub fn path_type(&self) -> PathType {
match self {
DpPath::Standard(_) => PathType::Scion,
DpPath::OneHop(_) => PathType::OneHop,
DpPath::Empty => PathType::Empty,
DpPath::Unsupported { path_type, .. } => PathType::Other((*path_type).into()),
}
}
#[inline]
pub const fn standard(&self) -> Option<&StandardPath> {
match self {
DpPath::Standard(path) => Some(path),
_ => None,
}
}
#[inline]
pub fn try_reverse(&mut self) -> Result<(), PathReverseError> {
match self {
DpPath::Standard(path) => path.try_reverse(),
DpPath::OneHop(path) => {
let rev = path
.clone()
.try_into_reversed_standard_path()
.map_err(|(e, _)| e)?;
*self = DpPath::Standard(rev);
Ok(())
}
DpPath::Empty => Ok(()),
DpPath::Unsupported { .. } => {
Err(PathReverseError::new(
"Cannot reverse an unsupported path type",
))
}
}
}
#[allow(clippy::result_large_err)]
#[inline]
pub fn try_into_reversed(mut self) -> Result<Self, (Self, PathReverseError)> {
match self.try_reverse() {
Ok(()) => Ok(self),
Err(e) => Err((self, e)),
}
}
}
impl_from!(StandardPath, DpPath, |p| DpPath::Standard(p));
impl_from!(OneHopPath, DpPath, |p| DpPath::OneHop(p));
impl From<&ScionDpPathViewRef<'_>> for DpPath {
#[inline]
fn from(view: &ScionDpPathViewRef<'_>) -> Self {
DpPath::from_view(view)
}
}
impl WireEncode for DpPath {
#[inline]
fn required_size(&self) -> usize {
match self {
DpPath::Standard(path) => path.required_size(),
DpPath::OneHop(path) => path.required_size(),
DpPath::Unsupported { data, .. } => data.len(),
DpPath::Empty => 0,
}
}
#[inline]
fn wire_valid(&self) -> Result<(), InvalidStructureError> {
if self.required_size() > ScionHeaderPathLayout::MAX_SIZE_BYTES {
return Err("Path size exceeds maximum encodable size (984 bytes)".into());
}
match self {
Self::Standard(standard_path) => standard_path.wire_valid()?,
Self::OneHop(onehop_path) => onehop_path.wire_valid()?,
Self::Empty => {}
Self::Unsupported { path_type: _, data } => {
if !data.len().is_multiple_of(4) {
return Err("Path data must be a multiple of 4 bytes".into());
}
}
}
Ok(())
}
#[inline]
unsafe fn encode_unchecked(&self, buf: &mut [u8]) -> usize {
match self {
DpPath::Standard(path) => unsafe { path.encode_unchecked(buf) },
DpPath::OneHop(path) => unsafe { path.encode_unchecked(buf) },
DpPath::Empty => 0,
DpPath::Unsupported { data, .. } => {
let len = data.len();
unsafe {
buf.get_unchecked_mut(..len).copy_from_slice(data);
}
len
}
}
}
}
#[cfg(feature = "proptest")]
pub mod ptest {
use ::proptest::prelude::*;
use super::*;
use crate::dataplane_path::{model::DpPath, types::PathType};
#[derive(Debug, Clone)]
pub struct ArbitraryPathParams {
pub standard: u32,
pub one_hop: u32,
pub empty: u32,
pub unsupported: u32,
pub standard_params: <StandardPath as Arbitrary>::Parameters,
pub one_hop_params: <OneHopPath as Arbitrary>::Parameters,
}
impl Default for ArbitraryPathParams {
fn default() -> Self {
Self {
standard: 8,
one_hop: 2,
empty: 1,
unsupported: 1,
standard_params: Default::default(),
one_hop_params: Default::default(),
}
}
}
impl Arbitrary for DpPath {
type Parameters = ArbitraryPathParams;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
prop_oneof![
params.standard => StandardPath::arbitrary_with(params.standard_params)
.prop_map(DpPath::Standard),
params.one_hop => OneHopPath::arbitrary_with(params.one_hop_params)
.prop_map(DpPath::OneHop),
params.empty => Just(DpPath::Empty),
params.unsupported => (
any::<PathType>(),
::proptest::collection::vec(any::<u8>(), 0..512),
)
.prop_map(|(path_type, data)| {
let path_type = match path_type {
PathType::Scion|PathType::OneHop|PathType::Empty => {PathType::Other(123)}
PathType::Epic | PathType::Colibri | PathType::Other(_) => path_type,
};
let data_len = data.len() / 4 * 4; let data_truncated = &data[..data_len];
DpPath::Unsupported {
path_type,
data: data_truncated.to_vec(),
}
}),
]
.boxed()
}
}
}