use crate::{
core::encode::{InvalidStructureError, WireEncode},
path::{
layout::ScionHeaderPathLayout, onehop::model::OneHopPath, standard::model::StandardPath,
types::PathType, view::ScionPathView,
},
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[allow(clippy::large_enum_variant)]
pub enum Path {
Standard(StandardPath),
OneHop(OneHopPath),
Empty,
Unsupported {
path_type: PathType,
data: Vec<u8>,
},
}
impl Path {
pub fn from_view(view: &ScionPathView) -> Self {
match *view {
ScionPathView::Standard(standard_view) => {
Path::Standard(StandardPath::from_view(standard_view))
}
ScionPathView::OneHop(onehop_view) => Path::OneHop(OneHopPath::from_view(onehop_view)),
ScionPathView::Empty => Path::Empty,
ScionPathView::Unsupported {
path_type,
data: buf,
} => {
Path::Unsupported {
path_type,
data: buf.to_vec(),
}
}
}
}
}
impl Path {
pub fn path_type(&self) -> PathType {
match self {
Path::Standard(_) => PathType::Scion,
Path::OneHop(_) => PathType::OneHop,
Path::Empty => PathType::Empty,
Path::Unsupported { path_type, .. } => PathType::Other((*path_type).into()),
}
}
pub fn standard(&self) -> Option<&StandardPath> {
match self {
Path::Standard(path) => Some(path),
_ => None,
}
}
}
impl WireEncode for Path {
fn required_size(&self) -> usize {
match self {
Path::Standard(path) => path.required_size(),
Path::OneHop(path) => path.required_size(),
Path::Unsupported { data, .. } => data.len(),
Path::Empty => 0,
}
}
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(())
}
unsafe fn encode_unchecked(&self, buf: &mut [u8]) -> usize {
match self {
Path::Standard(path) => unsafe { path.encode_unchecked(buf) },
Path::OneHop(path) => unsafe { path.encode_unchecked(buf) },
Path::Empty => 0,
Path::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::path::{model::Path, 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 Path {
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(Path::Standard),
params.one_hop => OneHopPath::arbitrary_with(params.one_hop_params)
.prop_map(Path::OneHop),
params.empty => Just(Path::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];
Path::Unsupported {
path_type,
data: data_truncated.to_vec(),
}
}),
]
.boxed()
}
}
}