TLV and TV abstractions complete
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
Rust/spacepackets/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2023-05-18 15:01:08 +02:00
parent 0c085ef27b
commit b37d932e4f
Signed by: muellerr
GPG Key ID: A649FB78196E3849
5 changed files with 187 additions and 53 deletions

98
src/cfdp/lv.rs Normal file
View File

@ -0,0 +1,98 @@
use crate::cfdp::TlvLvError;
use crate::{ByteConversionError, SizeMissmatch};
use std::prelude::v1::String;
pub const MIN_LV_LEN: usize = 1;
/// Generic CFDP length-value (LV) abstraction.
///
/// This is just a thin wrapper around a raw slice which performs some additional error handling.
pub struct Lv<'a> {
data: &'a [u8],
}
pub(crate) fn generic_len_check_data_serialization(
buf: &[u8],
data: &[u8],
min_overhead: usize,
) -> Result<(), ByteConversionError> {
if buf.len() < data.len() + min_overhead {
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: data.len() + min_overhead,
}));
}
Ok(())
}
pub(crate) fn generic_len_check_deserialization(
buf: &[u8],
min_overheader: usize,
) -> Result<(), ByteConversionError> {
if buf.len() < min_overheader {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: MIN_LV_LEN,
}));
}
Ok(())
}
impl<'a> Lv<'a> {
pub fn new(data: &[u8]) -> Result<Lv, TlvLvError> {
if data.len() > u8::MAX as usize {
return Err(TlvLvError::DataTooLarge(data.len()));
}
Ok(Lv { data })
}
/// Helper function to build a string LV. This is especially useful for the file or directory
/// path LVs
pub fn new_from_str(str_slice: &str) -> Result<Lv, TlvLvError> {
Self::new(str_slice.as_bytes())
}
/// Helper function to build a string LV. This is especially useful for the file or directory
/// path LVs
#[cfg(feature = "std")]
pub fn new_from_string(string: &'a String) -> Result<Lv<'a>, TlvLvError> {
Self::new(string.as_bytes())
}
pub fn value(&self) -> &[u8] {
self.data
}
/// Writes the LV to a raw buffer. Please note that the first byte will contain the length
/// of the value, but the values may not exceed a length of [u8::MAX].
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
generic_len_check_data_serialization(buf, self.data, MIN_LV_LEN)?;
Ok(self.write_to_be_bytes_no_len_check(buf))
}
/// Reads a LV from a raw buffer.
pub fn from_be_bytes(buf: &'a [u8]) -> Result<Lv<'a>, TlvLvError> {
generic_len_check_deserialization(buf, MIN_LV_LEN)?;
Self::from_be_bytes_no_len_check(buf)
}
pub(crate) fn write_to_be_bytes_no_len_check(&self, buf: &mut [u8]) -> usize {
// Length check in constructor ensures the length always has a valid value.
buf[0] = self.data.len() as u8;
buf[MIN_LV_LEN..self.data.len() + MIN_LV_LEN].copy_from_slice(self.data);
MIN_LV_LEN + self.data.len()
}
pub(crate) fn from_be_bytes_no_len_check(buf: &'a [u8]) -> Result<Lv<'a>, TlvLvError> {
let value_len = buf[0] as usize;
if buf.len() < value_len + MIN_LV_LEN {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: MIN_LV_LEN + value_len,
})
.into());
}
Ok(Self {
data: &buf[MIN_LV_LEN..MIN_LV_LEN + value_len],
})
}
}

View File

@ -1,7 +1,12 @@
use crate::ByteConversionError;
use core::fmt::{Display, Formatter};
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use std::error::Error;
pub mod lv;
pub mod pdu;
pub mod tlv;
@ -127,3 +132,49 @@ pub enum ChecksumType {
}
pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4];
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TlvLvError {
DataTooLarge(usize),
ByteConversionError(ByteConversionError),
/// Only relevant for TLV de-serialization.
UnknownTlvType(u8),
}
impl From<ByteConversionError> for TlvLvError {
fn from(value: ByteConversionError) -> Self {
Self::ByteConversionError(value)
}
}
impl Display for TlvLvError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
TlvLvError::UnknownTlvType(raw_tlv_id) => {
write!(f, "unknown TLV type {raw_tlv_id}")
}
TlvLvError::DataTooLarge(data_len) => {
write!(
f,
"data with size {} larger than allowed {} bytes",
data_len,
u8::MAX
)
}
TlvLvError::ByteConversionError(e) => {
write!(f, "{}", e)
}
}
}
}
#[cfg(feature = "std")]
impl Error for TlvLvError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
TlvLvError::ByteConversionError(e) => Some(e),
_ => None,
}
}
}

View File

@ -1,15 +1,17 @@
use crate::cfdp::lv::Lv;
use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
use crate::cfdp::ChecksumType;
pub struct MetadataParams {
pub struct MetadataGenericParams {
closure_requested: bool,
checksum_type: ChecksumType,
file_size: u64,
//src_file_name:
}
pub struct MetadataPdu {
pub struct MetadataPdu<'src_name, 'dest_name> {
pdu_header: PduHeader,
file_directive: FileDirectiveType,
metadata_params: MetadataParams,
metadata_params: MetadataGenericParams,
src_file_name: Option<Lv<'src_name>>,
dest_file_name: Option<Lv<'dest_name>>,
}

View File

@ -63,14 +63,21 @@ impl Display for PduError {
)
}
PduError::ByteConversionError(e) => {
write!(f, "low level byte conversion error: {e}")
write!(f, "{}", e)
}
}
}
}
#[cfg(feature = "std")]
impl Error for PduError {}
impl Error for PduError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
PduError::ByteConversionError(e) => Some(e),
_ => None,
}
}
}
impl From<ByteConversionError> for PduError {
fn from(value: ByteConversionError) -> Self {

View File

@ -1,4 +1,8 @@
use crate::{ByteConversionError, SizeMissmatch};
use crate::cfdp::lv::{
generic_len_check_data_serialization, generic_len_check_deserialization, Lv, MIN_LV_LEN,
};
use crate::cfdp::TlvLvError;
use crate::ByteConversionError;
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
@ -19,68 +23,40 @@ pub enum TlvType {
pub struct Tlv<'a> {
tlv_type: TlvType,
data: &'a [u8],
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TlvError {
DataTooLarge(usize),
ByteConversionError(ByteConversionError),
UnknownTlvType(u8),
}
impl From<ByteConversionError> for TlvError {
fn from(value: ByteConversionError) -> Self {
Self::ByteConversionError(value)
}
lv: Lv<'a>,
}
impl<'a> Tlv<'a> {
pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv, TlvError> {
pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv, TlvLvError> {
if data.len() > u8::MAX as usize {
return Err(TlvError::DataTooLarge(data.len()));
return Err(TlvLvError::DataTooLarge(data.len()));
}
Ok(Tlv { tlv_type, data })
Ok(Tlv {
tlv_type,
lv: Lv::new(data)?,
})
}
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
if buf.len() < self.data.len() + MIN_TLV_LEN {
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: self.data.len() + MIN_TLV_LEN,
}));
}
generic_len_check_data_serialization(buf, self.value(), MIN_TLV_LEN)?;
buf[0] = self.tlv_type as u8;
// Length check in constructor ensures the length always has a valid value.
buf[1] = self.data.len() as u8;
buf[MIN_TLV_LEN..self.data.len() + MIN_TLV_LEN].copy_from_slice(self.data);
Ok(MIN_TLV_LEN + self.data.len())
self.lv.write_to_be_bytes_no_len_check(&mut buf[1..]);
Ok(MIN_TLV_LEN + self.value().len())
}
pub fn from_be_bytes(buf: &'a [u8]) -> Result<Tlv<'a>, TlvError> {
if buf.len() < MIN_TLV_LEN {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: MIN_TLV_LEN,
})
.into());
}
pub fn value(&self) -> &[u8] {
self.lv.value()
}
pub fn from_be_bytes(buf: &'a [u8]) -> Result<Tlv<'a>, TlvLvError> {
generic_len_check_deserialization(buf, MIN_TLV_LEN)?;
let tlv_type_res = TlvType::try_from(buf[0]);
if tlv_type_res.is_err() {
return Err(TlvError::UnknownTlvType(buf[1]));
}
let value_len = buf[1] as usize;
if buf.len() < value_len {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: MIN_TLV_LEN + value_len,
})
.into());
return Err(TlvLvError::UnknownTlvType(buf[1]));
}
Ok(Self {
tlv_type: tlv_type_res.unwrap(),
data: &buf[MIN_TLV_LEN..MIN_TLV_LEN + value_len],
lv: Lv::from_be_bytes(&buf[MIN_LV_LEN..])?,
})
}
}