From e343faa1c559f5e3662e2ba349f6efae2b285369 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 21 May 2023 20:30:16 +0200 Subject: [PATCH] clippy fixes --- src/cfdp/lv.rs | 29 +++--- src/cfdp/mod.rs | 5 - src/cfdp/pdu/metadata.rs | 138 +++++++++++++++++++++++++-- src/cfdp/pdu/mod.rs | 10 ++ src/cfdp/tlv.rs | 197 ++++++++++++++++++++++++++++++++++----- 5 files changed, 332 insertions(+), 47 deletions(-) diff --git a/src/cfdp/lv.rs b/src/cfdp/lv.rs index 5867173..3ceb49a 100644 --- a/src/cfdp/lv.rs +++ b/src/cfdp/lv.rs @@ -9,10 +9,15 @@ use std::string::String; pub const MIN_LV_LEN: usize = 1; /// Generic CFDP length-value (LV) abstraction as specified in CFDP 5.1.8. +/// +/// # Lifetimes +/// * `data`: If the LV is generated from a raw bytestream, this will be the lifetime of +/// the raw bytestream. If the LV is generated from a raw slice or a similar data reference, +/// this will be the lifetime of that data reference. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Lv<'value> { - data: Option<&'value [u8]>, +pub struct Lv<'data> { + data: Option<&'data [u8]>, } pub(crate) fn generic_len_check_data_serialization( @@ -42,7 +47,7 @@ pub(crate) fn generic_len_check_deserialization( Ok(()) } -impl<'value> Lv<'value> { +impl<'data> Lv<'data> { pub fn new(data: &[u8]) -> Result { if data.len() > u8::MAX as usize { return Err(TlvLvError::DataTooLarge(data.len())); @@ -51,7 +56,7 @@ impl<'value> Lv<'value> { } /// Creates a LV with an empty value field. - pub fn new_empty() -> Lv<'value> { + pub fn new_empty() -> Lv<'data> { Lv { data: None } } @@ -64,7 +69,7 @@ impl<'value> Lv<'value> { /// 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: &'value String) -> Result, TlvLvError> { + pub fn new_from_string(string: &'data String) -> Result, TlvLvError> { Self::new(string.as_bytes()) } @@ -77,7 +82,7 @@ impl<'value> Lv<'value> { } /// Returns the full raw length, including the length byte. - pub fn len_raw(&self) -> usize { + pub fn len_full(&self) -> usize { self.len_value() + 1 } @@ -98,7 +103,7 @@ impl<'value> Lv<'value> { } /// Reads a LV from a raw buffer. - pub fn from_be_bytes(buf: &'value [u8]) -> Result, ByteConversionError> { + pub fn from_be_bytes(buf: &'data [u8]) -> Result, ByteConversionError> { generic_len_check_deserialization(buf, MIN_LV_LEN)?; Self::from_be_bytes_no_len_check(buf) } @@ -116,8 +121,8 @@ impl<'value> Lv<'value> { } pub(crate) fn from_be_bytes_no_len_check( - buf: &'value [u8], - ) -> Result, ByteConversionError> { + buf: &'data [u8], + ) -> Result, ByteConversionError> { let value_len = buf[0] as usize; generic_len_check_deserialization(buf, value_len + MIN_LV_LEN)?; let mut data = None; @@ -148,7 +153,7 @@ pub mod tests { assert_eq!(val[2], 3); assert_eq!(val[3], 4); assert!(!lv.is_empty()); - assert_eq!(lv.len_raw(), 5); + assert_eq!(lv.len_full(), 5); assert_eq!(lv.len_value(), 4); } @@ -156,7 +161,7 @@ pub mod tests { fn test_empty() { let lv_empty = Lv::new_empty(); assert_eq!(lv_empty.len_value(), 0); - assert_eq!(lv_empty.len_raw(), 1); + assert_eq!(lv_empty.len_full(), 1); assert!(lv_empty.is_empty()); assert_eq!(lv_empty.value(), None); let mut buf: [u8; 4] = [0xff; 4]; @@ -199,7 +204,7 @@ pub mod tests { assert!(!lv.is_empty()); assert!(lv.value().is_some()); assert_eq!(lv.len_value(), 4); - assert_eq!(lv.len_raw(), 5); + assert_eq!(lv.len_full(), 5); let val = lv.value().unwrap(); assert_eq!(val[0], 1); assert_eq!(val[1], 2); diff --git a/src/cfdp/mod.rs b/src/cfdp/mod.rs index 9e96088..d501b5d 100644 --- a/src/cfdp/mod.rs +++ b/src/cfdp/mod.rs @@ -140,8 +140,6 @@ pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4]; pub enum TlvLvError { DataTooLarge(usize), ByteConversionError(ByteConversionError), - /// Only relevant for TLV de-serialization. - UnknownTlvType(u8), } impl From for TlvLvError { @@ -153,9 +151,6 @@ impl From for TlvLvError { 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, diff --git a/src/cfdp/pdu/metadata.rs b/src/cfdp/pdu/metadata.rs index ed6b8ee..b0701ab 100644 --- a/src/cfdp/pdu/metadata.rs +++ b/src/cfdp/pdu/metadata.rs @@ -1,18 +1,138 @@ -#![allow(dead_code)] use crate::cfdp::lv::Lv; -use crate::cfdp::pdu::{FileDirectiveType, PduHeader}; -use crate::cfdp::ChecksumType; +use crate::cfdp::pdu::{FileDirectiveType, PduError, PduHeader}; +use crate::cfdp::tlv::Tlv; +use crate::cfdp::{ChecksumType, LargeFileFlag}; +use crate::{ByteConversionError, SizeMissmatch}; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct MetadataGenericParams { closure_requested: bool, checksum_type: ChecksumType, file_size: u64, } -pub struct MetadataPdu<'src_name, 'dest_name> { - pdu_header: PduHeader, - file_directive: FileDirectiveType, - metadata_params: MetadataGenericParams, - src_file_name: Option>, - dest_file_name: Option>, +pub fn build_metadata_opts_from_slice( + buf: &mut [u8], + tlvs: &[Tlv], +) -> Result { + let mut written = 0; + for tlv in tlvs { + written += tlv.write_to_be_bytes(buf)?; + } + Ok(written) +} + +#[cfg(feature = "alloc")] +pub fn build_metadata_opts_from_vec( + buf: &mut [u8], + tlvs: Vec, +) -> Result { + build_metadata_opts_from_slice(buf, tlvs.as_slice()) +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MetadataPdu<'src_name, 'dest_name, 'opts> { + pdu_header: PduHeader, + metadata_params: MetadataGenericParams, + #[cfg_attr(feature = "serde", serde(borrow))] + src_file_name: Lv<'src_name>, + #[cfg_attr(feature = "serde", serde(borrow))] + dest_file_name: Lv<'dest_name>, + options: Option<&'opts [u8]>, +} + +impl<'src_name, 'dest_name, 'opts> MetadataPdu<'src_name, 'dest_name, 'opts> { + pub fn new( + pdu_header: PduHeader, + metadata_params: MetadataGenericParams, + src_file_name: Lv<'src_name>, + dest_file_name: Lv<'dest_name>, + ) -> Self { + Self::new_with_opts( + pdu_header, + metadata_params, + src_file_name, + dest_file_name, + None, + ) + } + + pub fn new_with_opts( + pdu_header: PduHeader, + metadata_params: MetadataGenericParams, + src_file_name: Lv<'src_name>, + dest_file_name: Lv<'dest_name>, + options: Option<&'opts [u8]>, + ) -> Self { + Self { + pdu_header, + metadata_params, + src_file_name, + dest_file_name, + options, + } + } + + pub fn written_len(&self) -> usize { + // One directive type octet + let mut len = self.pdu_header.written_len() + 1; + if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large { + len += 8; + } else { + len += 4; + } + len += self.src_file_name.len_full(); + len += self.dest_file_name.len_full(); + if let Some(opts) = self.options { + len += opts.len(); + } + len + } + pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { + let expected_len = self.written_len(); + if buf.len() < expected_len { + return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: expected_len, + }) + .into()); + } + let mut current_idx = self.pdu_header.write_to_be_bytes(buf)?; + buf[current_idx] = FileDirectiveType::MetadataPdu as u8; + current_idx += 1; + buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 7) + | ((self.metadata_params.checksum_type as u8) << 4); + current_idx += 1; + if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large { + buf[current_idx..current_idx + core::mem::size_of::()] + .copy_from_slice(&self.metadata_params.file_size.to_be_bytes()); + current_idx += core::mem::size_of::() + } else { + if self.metadata_params.file_size > u32::MAX as u64 { + return Err(PduError::FileSizeTooLarge(self.metadata_params.file_size)); + } + buf[current_idx..current_idx + core::mem::size_of::()] + .copy_from_slice(&(self.metadata_params.file_size as u32).to_be_bytes()); + current_idx += core::mem::size_of::() + } + current_idx += self.src_file_name.write_to_be_bytes(buf)?; + current_idx += self.dest_file_name.write_to_be_bytes(buf)?; + if let Some(opts) = self.options { + buf[current_idx..current_idx + opts.len()].copy_from_slice(opts); + current_idx += opts.len(); + } + Ok(current_idx) + } +} + +#[cfg(test)] +pub mod tests { + #[test] + fn test_basic() {} } diff --git a/src/cfdp/pdu/mod.rs b/src/cfdp/pdu/mod.rs index 0f14702..408fc79 100644 --- a/src/cfdp/pdu/mod.rs +++ b/src/cfdp/pdu/mod.rs @@ -34,6 +34,7 @@ pub enum PduError { /// The first entry will be the source entity ID length, the second one the destination entity /// ID length. SourceDestIdLenMissmatch((usize, usize)), + FileSizeTooLarge(u64), } impl Display for PduError { @@ -66,6 +67,9 @@ impl Display for PduError { PduError::ByteConversionError(e) => { write!(f, "{}", e) } + PduError::FileSizeTooLarge(value) => { + write!(f, "file size value {} exceeds allowed 32 bit width", value) + } } } } @@ -210,6 +214,12 @@ impl PduHeader { } } + pub fn written_len(&self) -> usize { + FIXED_HEADER_LEN + + self.pdu_conf.source_entity_id.len() + + self.pdu_conf.transaction_seq_num.len() + + self.pdu_conf.dest_entity_id.len() + } pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { // Internal note: There is currently no way to pass a PDU configuration like this, but // this check is still kept for defensive programming. diff --git a/src/cfdp/tlv.rs b/src/cfdp/tlv.rs index 05a5c25..5f9a9c0 100644 --- a/src/cfdp/tlv.rs +++ b/src/cfdp/tlv.rs @@ -22,34 +22,68 @@ pub enum TlvType { EntityId = 0x06, } -/// Generic CFDP type-length-value (TLV) abstraction as specified in CFDP 5.1.9. -pub struct Tlv<'a> { - tlv_type: TlvType, - lv: Lv<'a>, +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum TlvTypeField { + Standard(TlvType), + Custom(u8), } -impl<'a> Tlv<'a> { - pub fn new(tlv_type: TlvType, data: &[u8]) -> Result { - if data.len() > u8::MAX as usize { - return Err(TlvLvError::DataTooLarge(data.len())); +impl From for TlvTypeField { + fn from(value: u8) -> Self { + match TlvType::try_from(value) { + Ok(tlv_type) => TlvTypeField::Standard(tlv_type), + Err(_) => TlvTypeField::Custom(value), } + } +} + +impl From for u8 { + fn from(value: TlvTypeField) -> Self { + match value { + TlvTypeField::Standard(std) => std as u8, + TlvTypeField::Custom(custom) => custom, + } + } +} + +/// Generic CFDP type-length-value (TLV) abstraction as specified in CFDP 5.1.9. +/// +/// # Lifetimes +/// * `data`: If the TLV is generated from a raw bytestream, this will be the lifetime of +/// the raw bytestream. If the TLV is generated from a raw slice or a similar data reference, +/// this will be the lifetime of that data reference. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Tlv<'data> { + tlv_type_field: TlvTypeField, + #[cfg_attr(feature = "serde", serde(borrow))] + lv: Lv<'data>, +} + +impl<'data> Tlv<'data> { + pub fn new(tlv_type: TlvType, data: &[u8]) -> Result { Ok(Tlv { - tlv_type, + tlv_type_field: TlvTypeField::Standard(tlv_type), lv: Lv::new(data)?, }) } /// Creates a TLV with an empty value field. - pub fn new_empty(tlv_type: TlvType) -> Tlv<'a> { + pub fn new_empty(tlv_type: TlvType) -> Tlv<'data> { Tlv { - tlv_type, + tlv_type_field: TlvTypeField::Standard(tlv_type), lv: Lv::new_empty(), } } + pub fn tlv_type_field(&self) -> TlvTypeField { + self.tlv_type_field + } + pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result { generic_len_check_data_serialization(buf, self.len_value(), MIN_TLV_LEN)?; - buf[0] = self.tlv_type as u8; + buf[0] = self.tlv_type_field.into(); self.lv.write_to_be_bytes_no_len_check(&mut buf[1..]); Ok(self.len_full()) } @@ -60,12 +94,12 @@ impl<'a> Tlv<'a> { /// Returns the length of the value part, not including the length byte. pub fn len_value(&self) -> usize { - self.lv.len_value() + 1 + self.lv.len_value() } /// Returns the full raw length, including the length byte. pub fn len_full(&self) -> usize { - self.lv.len_raw() + 1 + self.lv.len_full() + 1 } /// Checks whether the value field is empty. @@ -73,14 +107,14 @@ impl<'a> Tlv<'a> { self.lv.is_empty() } - pub fn from_be_bytes(buf: &'a [u8]) -> Result, TlvLvError> { + /// Creates a TLV give a raw bytestream. Please note that is is not necessary to pass the + /// bytestream with the exact size of the expected TLV. This function will take care + /// of parsing the length byte, and the length of the parsed TLV can be retrieved using + /// [len_full]. + pub fn from_be_bytes(buf: &'data [u8]) -> Result, 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(TlvLvError::UnknownTlvType(buf[1])); - } Ok(Self { - tlv_type: tlv_type_res.unwrap(), + tlv_type_field: TlvTypeField::from(buf[0]), lv: Lv::from_be_bytes(&buf[MIN_LV_LEN..])?, }) } @@ -88,6 +122,127 @@ impl<'a> Tlv<'a> { #[cfg(test)] mod tests { + use crate::cfdp::tlv::{Tlv, TlvType, TlvTypeField}; + use crate::cfdp::TlvLvError; + use crate::util::{UbfU8, UnsignedEnum}; + #[test] - fn test_basic() {} + fn test_basic() { + let entity_id = UbfU8::new(5); + let mut buf: [u8; 4] = [0; 4]; + assert!(entity_id.write_to_be_bytes(&mut buf).is_ok()); + let tlv_res = Tlv::new(TlvType::EntityId, &buf[0..1]); + assert!(tlv_res.is_ok()); + let tlv_res = tlv_res.unwrap(); + assert_eq!( + tlv_res.tlv_type_field(), + TlvTypeField::Standard(TlvType::EntityId) + ); + assert_eq!(tlv_res.len_full(), 3); + assert_eq!(tlv_res.len_value(), 1); + assert!(!tlv_res.is_empty()); + assert!(tlv_res.value().is_some()); + assert_eq!(tlv_res.value().unwrap()[0], 5); + } + + #[test] + fn test_serialization() { + let entity_id = UbfU8::new(5); + let mut buf: [u8; 4] = [0; 4]; + assert!(entity_id.write_to_be_bytes(&mut buf).is_ok()); + let tlv_res = Tlv::new(TlvType::EntityId, &buf[0..1]); + assert!(tlv_res.is_ok()); + let tlv_res = tlv_res.unwrap(); + let mut ser_buf: [u8; 4] = [0; 4]; + assert!(tlv_res.write_to_be_bytes(&mut ser_buf).is_ok()); + assert_eq!(ser_buf[0], TlvType::EntityId as u8); + assert_eq!(ser_buf[1], 1); + assert_eq!(ser_buf[2], 5); + } + + #[test] + fn test_deserialization() { + let entity_id = UbfU8::new(5); + let mut buf: [u8; 4] = [0; 4]; + assert!(entity_id.write_to_be_bytes(&mut buf[2..]).is_ok()); + buf[0] = TlvType::EntityId as u8; + buf[1] = 1; + let tlv_from_raw = Tlv::from_be_bytes(&mut buf); + assert!(tlv_from_raw.is_ok()); + let tlv_from_raw = tlv_from_raw.unwrap(); + assert_eq!( + tlv_from_raw.tlv_type_field(), + TlvTypeField::Standard(TlvType::EntityId) + ); + assert_eq!(tlv_from_raw.len_value(), 1); + assert_eq!(tlv_from_raw.len_full(), 3); + assert!(tlv_from_raw.value().is_some()); + assert_eq!(tlv_from_raw.value().unwrap()[0], 5); + } + + #[test] + fn test_empty() { + let tlv_empty = Tlv::new_empty(TlvType::MsgToUser); + assert!(tlv_empty.value().is_none()); + assert!(tlv_empty.is_empty()); + assert_eq!(tlv_empty.len_full(), 2); + assert_eq!(tlv_empty.len_value(), 0); + assert_eq!( + tlv_empty.tlv_type_field(), + TlvTypeField::Standard(TlvType::MsgToUser) + ); + } + + #[test] + fn test_empty_serialization() { + let tlv_empty = Tlv::new_empty(TlvType::MsgToUser); + let mut buf: [u8; 4] = [0; 4]; + assert!(tlv_empty.write_to_be_bytes(&mut buf).is_ok()); + assert_eq!(buf[0], TlvType::MsgToUser as u8); + assert_eq!(buf[1], 0); + } + + #[test] + fn test_empty_deserialization() { + let mut buf: [u8; 4] = [0; 4]; + buf[0] = TlvType::MsgToUser as u8; + buf[1] = 0; + let tlv_empty = Tlv::from_be_bytes(&mut buf); + assert!(tlv_empty.is_ok()); + let tlv_empty = tlv_empty.unwrap(); + assert!(tlv_empty.is_empty()); + assert!(tlv_empty.value().is_none()); + assert_eq!( + tlv_empty.tlv_type_field(), + TlvTypeField::Standard(TlvType::MsgToUser) + ); + assert_eq!(tlv_empty.len_full(), 2); + assert_eq!(tlv_empty.len_value(), 0); + } + + #[test] + fn test_buf_too_large() { + let buf_too_large: [u8; u8::MAX as usize + 1] = [0; u8::MAX as usize + 1]; + let tlv_res = Tlv::new(TlvType::MsgToUser, &buf_too_large); + assert!(tlv_res.is_err()); + let error = tlv_res.unwrap_err(); + if let TlvLvError::DataTooLarge(size) = error { + assert_eq!(size, u8::MAX as usize + 1); + } else { + panic!("unexpected error {:?}", error); + } + } + #[test] + fn test_deserialization_custom_tlv_type() { + let mut buf: [u8; 4] = [0; 4]; + buf[0] = 3; + buf[1] = 1; + buf[2] = 5; + let tlv = Tlv::from_be_bytes(&mut buf); + assert!(tlv.is_ok()); + let tlv = tlv.unwrap(); + assert_eq!(tlv.tlv_type_field(), TlvTypeField::Custom(3)); + assert_eq!(tlv.len_value(), 1); + assert_eq!(tlv.len_full(), 3); + } }