From 3761380f38f873b7b982d007ba716cab9bb6f7bd Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 17 Jun 2024 15:27:18 +0200 Subject: [PATCH 01/25] Add definition for file transfer packages --- README.md | 1 + src/ft/dlt_ft_data_pkg.rs | 20 +++++++++ src/ft/dlt_ft_end_pkg.rs | 13 ++++++ src/ft/dlt_ft_error_code.rs | 5 +++ src/ft/dlt_ft_error_pkg.rs | 35 +++++++++++++++ src/ft/dlt_ft_file_not_exist_error_pkg.rs | 20 +++++++++ src/ft/dlt_ft_header_pkg.rs | 28 ++++++++++++ src/ft/dlt_ft_info_pkg.rs | 28 ++++++++++++ src/ft/dlt_ft_int.rs | 52 +++++++++++++++++++++++ src/ft/dlt_ft_pkg.rs | 20 +++++++++ src/ft/dlt_ft_uint.rs | 52 +++++++++++++++++++++++ src/ft/mod.rs | 29 +++++++++++++ src/lib.rs | 3 ++ 13 files changed, 306 insertions(+) create mode 100644 src/ft/dlt_ft_data_pkg.rs create mode 100644 src/ft/dlt_ft_end_pkg.rs create mode 100644 src/ft/dlt_ft_error_code.rs create mode 100644 src/ft/dlt_ft_error_pkg.rs create mode 100644 src/ft/dlt_ft_file_not_exist_error_pkg.rs create mode 100644 src/ft/dlt_ft_header_pkg.rs create mode 100644 src/ft/dlt_ft_info_pkg.rs create mode 100644 src/ft/dlt_ft_int.rs create mode 100644 src/ft/dlt_ft_pkg.rs create mode 100644 src/ft/dlt_ft_uint.rs create mode 100644 src/ft/mod.rs diff --git a/README.md b/README.md index 7c66983..f4937a1 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,7 @@ An complete example which includes the parsing of the ethernet & udp headers can ## References * [Log and Trace Protocol Specification](https://www.autosar.org/fileadmin/standards/foundation/1-3/AUTOSAR_PRS_LogAndTraceProtocol.pdf) +* [COVESA DLT Filetransfer](https://github.com/COVESA/dlt-daemon/blob/603f0e4bb87478f7d3e95c89b37790e55ff1e4e5/doc/dlt_filetransfer.md) ## License Licensed under either of Apache License, Version 2.0 or MIT license at your option. The corresponding license texts can be found in the LICENSE-APACHE file and the LICENSE-MIT file. diff --git a/src/ft/dlt_ft_data_pkg.rs b/src/ft/dlt_ft_data_pkg.rs new file mode 100644 index 0000000..07ad6b2 --- /dev/null +++ b/src/ft/dlt_ft_data_pkg.rs @@ -0,0 +1,20 @@ +use super::*; + +/// Package containing a chunk of data of a file. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct DltFtDataPkg<'a> { + /// File serial number (usually inode). + pub file_serial_number: DltFtUInt, + + /// Transfered package number. + pub package_nr: DltFtUInt, + + /// Transfered data. + pub data: &'a [u8], +} + + +impl<'a> DltFtDataPkg<'a> { + /// Verbose string at the start and end of the "DLT File Transfer Data" package. + pub const PKG_FLAG: &'static str = "FLDA"; +} diff --git a/src/ft/dlt_ft_end_pkg.rs b/src/ft/dlt_ft_end_pkg.rs new file mode 100644 index 0000000..5916c86 --- /dev/null +++ b/src/ft/dlt_ft_end_pkg.rs @@ -0,0 +1,13 @@ +use super::*; + +/// Package sent after a file transfer is complete. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct DltFtEndPkg { + /// File serial number (usually inode). + pub file_serial_number: DltFtUInt, +} + +impl DltFtEndPkg { + /// Verbose string at the start and end of the "DLT File Transfer End" package. + pub const PKG_FLAG: &'static str = "FLFI"; +} diff --git a/src/ft/dlt_ft_error_code.rs b/src/ft/dlt_ft_error_code.rs new file mode 100644 index 0000000..dc20d96 --- /dev/null +++ b/src/ft/dlt_ft_error_code.rs @@ -0,0 +1,5 @@ +use super::*; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct DltFtErrorCode(pub DltFtInt); + diff --git a/src/ft/dlt_ft_error_pkg.rs b/src/ft/dlt_ft_error_pkg.rs new file mode 100644 index 0000000..7d4c76c --- /dev/null +++ b/src/ft/dlt_ft_error_pkg.rs @@ -0,0 +1,35 @@ +use super::*; + +/// Error package sent when an error occured with an +/// existing file. +/// +/// If a files does not exist +/// [`crate::ft::DltFileNotExistErrorPkg`] is sent instead. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct DltFtErrorPkg<'a, 'b> { + /// Error code. + pub error_code: DltFtErrorCode, + + /// Standard linux error code. + pub linux_error_code: DltFtInt, + + /// File serial number (usually inode). + pub file_serial_number: DltFtUInt, + + /// Absolute path to the file. + pub file_name: &'a str, + + /// Size of the file. + pub file_size: DltFtUInt, + + /// File creaton date. + pub creation_date: &'b str, + + /// Number of packages that will be used to transfer the file. + pub number_of_packages: DltFtUInt, +} + +impl<'a, 'b> DltFtErrorPkg<'a, 'b> { + /// Verbose string at the start and end of the "DLT File Transfer Error" package. + pub const PKG_FLAG: &'static str = "FLER"; +} diff --git a/src/ft/dlt_ft_file_not_exist_error_pkg.rs b/src/ft/dlt_ft_file_not_exist_error_pkg.rs new file mode 100644 index 0000000..76375c0 --- /dev/null +++ b/src/ft/dlt_ft_file_not_exist_error_pkg.rs @@ -0,0 +1,20 @@ +use super::*; + +/// Error package sent if a file that should have been +/// transfered does not exists. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct DltFtFileNotExistErrorPkg<'a> { + /// Error code. + pub error_code: DltFtErrorCode, + + /// Standard linux error code. + pub linux_error_code: DltFtInt, + + /// Absolute path to the file. + pub file_name: &'a str, +} + +impl<'a> DltFtFileNotExistErrorPkg<'a> { + /// Verbose string at the start and end of the "DLT File Transfer Error" package. + pub const PKG_FLAG: &'static str = "FLER"; +} diff --git a/src/ft/dlt_ft_header_pkg.rs b/src/ft/dlt_ft_header_pkg.rs new file mode 100644 index 0000000..29b19aa --- /dev/null +++ b/src/ft/dlt_ft_header_pkg.rs @@ -0,0 +1,28 @@ +use super::*; + +/// Packet sent at the start of a file transfer. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct DltFtHeaderPkg<'a, 'b> { + /// File serial number (usually inode). + pub file_serial_number: DltFtUInt, + + /// Absolute path to the file. + pub file_name: &'a str, + + /// Size of the file. + pub file_size: DltFtUInt, + + /// File creaton date. + pub creation_date: &'b str, + + /// Number of packages that will be used to transfer the file. + pub number_of_packages: DltFtUInt, + + /// Needed buffer size to reconsturct the file. + pub buffer_size: DltFtUInt, +} + +impl<'a, 'b> DltFtHeaderPkg<'a, 'b> { + /// Verbose string at the start and end of the "DLT File Transfer Header" package. + pub const PKG_FLAG: &'static str = "FLST"; +} diff --git a/src/ft/dlt_ft_info_pkg.rs b/src/ft/dlt_ft_info_pkg.rs new file mode 100644 index 0000000..21298c3 --- /dev/null +++ b/src/ft/dlt_ft_info_pkg.rs @@ -0,0 +1,28 @@ +use super::*; + +/// Info packet for a file if only metadat is sent. +/// +/// This packet is sent if only informations about a file +/// are sent without the file contents. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub struct DltFtInfoPkg<'a, 'b> { + /// File serial number (usually inode). + pub file_serial_number: DltFtUInt, + + /// Absolute path to the file. + pub file_name: &'a str, + + /// Size of the file. + pub file_size: DltFtUInt, + + /// File creaton date. + pub creation_date: &'b str, + + /// Number of packages that will be used to transfer the file. + pub number_of_packages: DltFtUInt, +} + +impl<'a, 'b> DltFtInfoPkg<'a, 'b> { + /// Verbose string at the start and end of the "DLT File Transfer Info" package. + pub const PKG_FLAG: &'static str = "FLIF"; +} diff --git a/src/ft/dlt_ft_int.rs b/src/ft/dlt_ft_int.rs new file mode 100644 index 0000000..0e26e27 --- /dev/null +++ b/src/ft/dlt_ft_int.rs @@ -0,0 +1,52 @@ + +/// Signed integer (either 32 or 64 bit). +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum DltFtInt { + I32(i32), + I64(i64), +} + +impl From for DltFtInt { + fn from(value: i32) -> Self { + DltFtInt::I32(value) + } +} + +impl From for DltFtInt { + fn from(value: i64) -> Self { + DltFtInt::I64(value) + } +} + +#[cfg(target_pointer_width = "32")] +impl From for DltFtInt { + fn from(value: isize) -> Self { + DltFtInt::I32(value as u32) + } +} + +#[cfg(target_pointer_width = "64")] +impl From for DltFtInt { + fn from(value: isize) -> Self { + DltFtInt::I64(value as i64) + } +} + +#[cfg(target_pointer_width = "64")] +impl From for isize { + fn from(value: DltFtInt) -> Self { + match value { + DltFtInt::I32(v) => v as isize, + DltFtInt::I64(v) => v as isize, + } + } +} + +impl From for i64 { + fn from(value: DltFtInt) -> Self { + match value { + DltFtInt::I32(v) => v as i64, + DltFtInt::I64(v) => v, + } + } +} diff --git a/src/ft/dlt_ft_pkg.rs b/src/ft/dlt_ft_pkg.rs new file mode 100644 index 0000000..9eed1c8 --- /dev/null +++ b/src/ft/dlt_ft_pkg.rs @@ -0,0 +1,20 @@ +use super::*; + +/// DLT file transfer package. +#[derive(Debug, Clone, Eq, PartialEq, Hash)] +pub enum DltFtPkg<'a, 'b> { + /// Packet sent at the start of a file transfer. + Header(DltFtHeaderPkg<'a, 'b>), + /// Package containing a chunk of data of a file. + Data(DltFtDataPkg<'a>), + /// Package sent after a file transfer is complete. + End(DltFtEndPkg), + /// Info packet for a file if only metadat is sent. + Info(DltFtInfoPkg<'a, 'b>), + /// Error package sent when an error occured with an + /// existing file. + Error(DltFtErrorPkg<'a, 'b>), + /// Error package sent if a file that should have been + /// transfered does not exists. + FileNotExistsError(DltFtFileNotExistErrorPkg<'a>), +} diff --git a/src/ft/dlt_ft_uint.rs b/src/ft/dlt_ft_uint.rs new file mode 100644 index 0000000..8977d41 --- /dev/null +++ b/src/ft/dlt_ft_uint.rs @@ -0,0 +1,52 @@ + +/// Unsigned integer (either 32 or 64 bit). +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum DltFtUInt { + U32(u32), + U64(u64), +} + +impl From for DltFtUInt { + fn from(value: u32) -> Self { + DltFtUInt::U32(value) + } +} + +impl From for DltFtUInt { + fn from(value: u64) -> Self { + DltFtUInt::U64(value) + } +} + +#[cfg(target_pointer_width = "32")] +impl From for DltFtUInt { + fn from(value: usize) -> Self { + DltFtUInt::U32(value as u32) + } +} + +#[cfg(target_pointer_width = "64")] +impl From for DltFtUInt { + fn from(value: usize) -> Self { + DltFtUInt::U64(value as u64) + } +} + +#[cfg(target_pointer_width = "64")] +impl From for usize { + fn from(value: DltFtUInt) -> Self { + match value { + DltFtUInt::U32(v) => v as usize, + DltFtUInt::U64(v) => v as usize, + } + } +} + +impl From for u64 { + fn from(value: DltFtUInt) -> Self { + match value { + DltFtUInt::U32(v) => v as u64, + DltFtUInt::U64(v) => v, + } + } +} diff --git a/src/ft/mod.rs b/src/ft/mod.rs new file mode 100644 index 0000000..826939a --- /dev/null +++ b/src/ft/mod.rs @@ -0,0 +1,29 @@ +mod dlt_ft_data_pkg; +pub use dlt_ft_data_pkg::*; + +mod dlt_ft_end_pkg; +pub use dlt_ft_end_pkg::*; + +mod dlt_ft_error_code; +pub use dlt_ft_error_code::*; + +mod dlt_ft_error_pkg; +pub use dlt_ft_error_pkg::*; + +mod dlt_ft_file_not_exist_error_pkg; +pub use dlt_ft_file_not_exist_error_pkg::*; + +mod dlt_ft_header_pkg; +pub use dlt_ft_header_pkg::*; + +mod dlt_ft_info_pkg; +pub use dlt_ft_info_pkg::*; + +mod dlt_ft_int; +pub use dlt_ft_int::*; + +mod dlt_ft_pkg; +pub use dlt_ft_pkg::*; + +mod dlt_ft_uint; +pub use dlt_ft_uint::*; diff --git a/src/lib.rs b/src/lib.rs index 1edcf96..1f82d12 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,6 +227,9 @@ pub mod control; /// Errors that can be returned by functions in dlt_parse. pub mod error; +/// DLT file transfer related structs & functions (used to tranfer files via DLT). +pub mod ft; + /// Module containing "verbose DLT" encoding & decoding structs & functions. pub mod verbose; From 2870542a0dd768cdf33a0a7d31243a35ad485932 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 17 Jun 2024 15:31:30 +0200 Subject: [PATCH 02/25] Correct 32bit implementation --- src/ft/dlt_ft_int.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ft/dlt_ft_int.rs b/src/ft/dlt_ft_int.rs index 0e26e27..6d16cb1 100644 --- a/src/ft/dlt_ft_int.rs +++ b/src/ft/dlt_ft_int.rs @@ -21,7 +21,7 @@ impl From for DltFtInt { #[cfg(target_pointer_width = "32")] impl From for DltFtInt { fn from(value: isize) -> Self { - DltFtInt::I32(value as u32) + DltFtInt::I32(value as i32) } } From 81dc7b6c149432e307e7447a552bb12af801a8df Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 17 Jun 2024 15:34:46 +0200 Subject: [PATCH 03/25] Apply cargo fmt --- src/control/mod.rs | 5 ++--- src/dlt_typed_payload.rs | 2 +- src/ft/dlt_ft_data_pkg.rs | 1 - src/ft/dlt_ft_error_code.rs | 1 - src/ft/dlt_ft_error_pkg.rs | 8 ++++---- src/ft/dlt_ft_header_pkg.rs | 8 ++++---- src/ft/dlt_ft_info_pkg.rs | 8 ++++---- src/ft/dlt_ft_int.rs | 1 - src/ft/dlt_ft_uint.rs | 1 - src/nv_payload.rs | 28 ++++++++++++++++++++-------- 10 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/control/mod.rs b/src/control/mod.rs index 0e97297..bdc079a 100644 --- a/src/control/mod.rs +++ b/src/control/mod.rs @@ -1,4 +1,3 @@ - /// "Set Log Level" service id pub const CMD_ID_SET_LOG_LEVEL: u32 = 0x01; /// "Set Log Level" name @@ -119,7 +118,7 @@ mod test { use super::*; use proptest::prelude::*; - proptest!{ + proptest! { #[test] fn test_get_control_command_name( unknown_id in 0x24..0xFFFu32, @@ -169,4 +168,4 @@ mod test { assert_eq!(Some("CallSWCInjection"), get_control_command_name(sw_injections_id)); } } -} \ No newline at end of file +} diff --git a/src/dlt_typed_payload.rs b/src/dlt_typed_payload.rs index 5bb8e94..87f70ab 100644 --- a/src/dlt_typed_payload.rs +++ b/src/dlt_typed_payload.rs @@ -1,4 +1,4 @@ -use crate::{*, verbose::VerboseIter}; +use crate::{verbose::VerboseIter, *}; /// Typed payload of a DLT log message based on the message info in the DLT /// extended header. diff --git a/src/ft/dlt_ft_data_pkg.rs b/src/ft/dlt_ft_data_pkg.rs index 07ad6b2..6e8dd98 100644 --- a/src/ft/dlt_ft_data_pkg.rs +++ b/src/ft/dlt_ft_data_pkg.rs @@ -13,7 +13,6 @@ pub struct DltFtDataPkg<'a> { pub data: &'a [u8], } - impl<'a> DltFtDataPkg<'a> { /// Verbose string at the start and end of the "DLT File Transfer Data" package. pub const PKG_FLAG: &'static str = "FLDA"; diff --git a/src/ft/dlt_ft_error_code.rs b/src/ft/dlt_ft_error_code.rs index dc20d96..dfbb2e9 100644 --- a/src/ft/dlt_ft_error_code.rs +++ b/src/ft/dlt_ft_error_code.rs @@ -2,4 +2,3 @@ use super::*; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct DltFtErrorCode(pub DltFtInt); - diff --git a/src/ft/dlt_ft_error_pkg.rs b/src/ft/dlt_ft_error_pkg.rs index 7d4c76c..dd40ff7 100644 --- a/src/ft/dlt_ft_error_pkg.rs +++ b/src/ft/dlt_ft_error_pkg.rs @@ -2,7 +2,7 @@ use super::*; /// Error package sent when an error occured with an /// existing file. -/// +/// /// If a files does not exist /// [`crate::ft::DltFileNotExistErrorPkg`] is sent instead. #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -18,13 +18,13 @@ pub struct DltFtErrorPkg<'a, 'b> { /// Absolute path to the file. pub file_name: &'a str, - + /// Size of the file. pub file_size: DltFtUInt, - + /// File creaton date. pub creation_date: &'b str, - + /// Number of packages that will be used to transfer the file. pub number_of_packages: DltFtUInt, } diff --git a/src/ft/dlt_ft_header_pkg.rs b/src/ft/dlt_ft_header_pkg.rs index 29b19aa..22488a8 100644 --- a/src/ft/dlt_ft_header_pkg.rs +++ b/src/ft/dlt_ft_header_pkg.rs @@ -8,16 +8,16 @@ pub struct DltFtHeaderPkg<'a, 'b> { /// Absolute path to the file. pub file_name: &'a str, - + /// Size of the file. pub file_size: DltFtUInt, - + /// File creaton date. pub creation_date: &'b str, - + /// Number of packages that will be used to transfer the file. pub number_of_packages: DltFtUInt, - + /// Needed buffer size to reconsturct the file. pub buffer_size: DltFtUInt, } diff --git a/src/ft/dlt_ft_info_pkg.rs b/src/ft/dlt_ft_info_pkg.rs index 21298c3..faf9b7d 100644 --- a/src/ft/dlt_ft_info_pkg.rs +++ b/src/ft/dlt_ft_info_pkg.rs @@ -1,7 +1,7 @@ use super::*; /// Info packet for a file if only metadat is sent. -/// +/// /// This packet is sent if only informations about a file /// are sent without the file contents. #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -11,13 +11,13 @@ pub struct DltFtInfoPkg<'a, 'b> { /// Absolute path to the file. pub file_name: &'a str, - + /// Size of the file. pub file_size: DltFtUInt, - + /// File creaton date. pub creation_date: &'b str, - + /// Number of packages that will be used to transfer the file. pub number_of_packages: DltFtUInt, } diff --git a/src/ft/dlt_ft_int.rs b/src/ft/dlt_ft_int.rs index 6d16cb1..5b40da0 100644 --- a/src/ft/dlt_ft_int.rs +++ b/src/ft/dlt_ft_int.rs @@ -1,4 +1,3 @@ - /// Signed integer (either 32 or 64 bit). #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum DltFtInt { diff --git a/src/ft/dlt_ft_uint.rs b/src/ft/dlt_ft_uint.rs index 8977d41..7ad3b7c 100644 --- a/src/ft/dlt_ft_uint.rs +++ b/src/ft/dlt_ft_uint.rs @@ -1,4 +1,3 @@ - /// Unsigned integer (either 32 or 64 bit). #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum DltFtUInt { diff --git a/src/nv_payload.rs b/src/nv_payload.rs index ece183a..b1cef08 100644 --- a/src/nv_payload.rs +++ b/src/nv_payload.rs @@ -47,26 +47,38 @@ mod tests { #[test] fn from() { - let data = [5,6,7,8]; + let data = [5, 6, 7, 8]; let msg_id = 1234_5678u32; let payload = &data; // LogNvPayload assert_eq!( - NvPayload::from(LogNvPayload{ msg_id, payload, log_level: DltLogLevel::Info }), - NvPayload{ msg_id, payload } + NvPayload::from(LogNvPayload { + msg_id, + payload, + log_level: DltLogLevel::Info + }), + NvPayload { msg_id, payload } ); // TraceNvPayload assert_eq!( - NvPayload::from(TraceNvPayload{ msg_id, payload, trace_type: DltTraceType::State }), - NvPayload{ msg_id, payload } + NvPayload::from(TraceNvPayload { + msg_id, + payload, + trace_type: DltTraceType::State + }), + NvPayload { msg_id, payload } ); // TraceNvPayload assert_eq!( - NvPayload::from(NetworkNvPayload{msg_id,payload, net_type: DltNetworkType::Flexray }), - NvPayload{ msg_id, payload } + NvPayload::from(NetworkNvPayload { + msg_id, + payload, + net_type: DltNetworkType::Flexray + }), + NvPayload { msg_id, payload } ); } -} \ No newline at end of file +} From dd49f46218b08b5c8dc062adfba455d773828e02 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 18 Jun 2024 08:40:38 +0200 Subject: [PATCH 04/25] Add parsing code for DltFtPkg --- src/ft/dlt_ft_int.rs | 18 ++++++ src/ft/dlt_ft_pkg.rs | 141 ++++++++++++++++++++++++++++++++++++++++++ src/ft/dlt_ft_uint.rs | 18 ++++++ 3 files changed, 177 insertions(+) diff --git a/src/ft/dlt_ft_int.rs b/src/ft/dlt_ft_int.rs index 5b40da0..f34ac88 100644 --- a/src/ft/dlt_ft_int.rs +++ b/src/ft/dlt_ft_int.rs @@ -1,3 +1,5 @@ +use crate::verbose::*; + /// Signed integer (either 32 or 64 bit). #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum DltFtInt { @@ -5,6 +7,22 @@ pub enum DltFtInt { I64(i64), } +impl DltFtInt { + pub fn try_take_from_iter(iter: &mut VerboseIter) -> Option { + let Some(Ok(value)) = iter.next() else { + return None; + }; + if value.name().is_some() { + return None; + } + match value { + VerboseValue::I32(v) => Some(DltFtInt::I32(v.value)), + VerboseValue::I64(v) => Some(DltFtInt::I64(v.value)), + _ => None, + } + } +} + impl From for DltFtInt { fn from(value: i32) -> Self { DltFtInt::I32(value) diff --git a/src/ft/dlt_ft_pkg.rs b/src/ft/dlt_ft_pkg.rs index 9eed1c8..a818df7 100644 --- a/src/ft/dlt_ft_pkg.rs +++ b/src/ft/dlt_ft_pkg.rs @@ -1,3 +1,4 @@ +use crate::verbose::*; use super::*; /// DLT file transfer package. @@ -18,3 +19,143 @@ pub enum DltFtPkg<'a, 'b> { /// transfered does not exists. FileNotExistsError(DltFtFileNotExistErrorPkg<'a>), } + +impl<'a, 'b> DltFtPkg<'a, 'b> { + + /// Checks if the verbose iterator contains a DLT file transfer package + /// and returns the package if so. + pub fn try_from(mut iter: VerboseIter<'a>) -> Option> { + + match iter.number_of_arguments() { + 3 => { + Self::check_for_str(DltFtEndPkg::PKG_FLAG, &mut iter)?; + let file_serial_number = DltFtUInt::try_take_from_iter(&mut iter)?; + Self::check_for_str(DltFtEndPkg::PKG_FLAG, &mut iter)?; + + Some(DltFtPkg::End(DltFtEndPkg { file_serial_number })) + } + 5 => { + let first = Self::try_take_str_from_iter(&mut iter)?; + match first { + DltFtDataPkg::PKG_FLAG => { + let file_serial_number = DltFtUInt::try_take_from_iter(&mut iter)?; + let package_nr = DltFtUInt::try_take_from_iter(&mut iter)?; + let data = Self::try_take_raw_from_iter(&mut iter)?; + Self::check_for_str(DltFtDataPkg::PKG_FLAG, &mut iter)?; + + Some(DltFtPkg::Data(DltFtDataPkg{ + file_serial_number, + package_nr, + data, + })) + }, + DltFtFileNotExistErrorPkg::PKG_FLAG => { + let error_code = DltFtInt::try_take_from_iter(&mut iter)?; + let linux_error_code = DltFtInt::try_take_from_iter(&mut iter)?; + let file_name = Self::try_take_str_from_iter(&mut iter)?; + Self::check_for_str(DltFtFileNotExistErrorPkg::PKG_FLAG, &mut iter)?; + + Some(DltFtPkg::FileNotExistsError(DltFtFileNotExistErrorPkg{ + error_code: DltFtErrorCode(error_code), + linux_error_code, + file_name, + })) + }, + _ => None, + } + } + 7 => { + Self::check_for_str(DltFtInfoPkg::PKG_FLAG, &mut iter)?; + let file_serial_number = DltFtUInt::try_take_from_iter(&mut iter)?; + let file_name = Self::try_take_str_from_iter(&mut iter)?; + let file_size = DltFtUInt::try_take_from_iter(&mut iter)?; + let creation_date = Self::try_take_str_from_iter(&mut iter)?; + let number_of_packages = DltFtUInt::try_take_from_iter(&mut iter)?; + Self::check_for_str(DltFtInfoPkg::PKG_FLAG, &mut iter)?; + + Some(DltFtPkg::Info(DltFtInfoPkg{ + file_serial_number, + file_name, + file_size, + creation_date, + number_of_packages, + })) + } + 8 => { + Self::check_for_str(DltFtHeaderPkg::PKG_FLAG, &mut iter)?; + let file_serial_number = DltFtUInt::try_take_from_iter(&mut iter)?; + let file_name = Self::try_take_str_from_iter(&mut iter)?; + let file_size = DltFtUInt::try_take_from_iter(&mut iter)?; + let creation_date = Self::try_take_str_from_iter(&mut iter)?; + let number_of_packages = DltFtUInt::try_take_from_iter(&mut iter)?; + let buffer_size = DltFtUInt::try_take_from_iter(&mut iter)?; + Self::check_for_str(DltFtHeaderPkg::PKG_FLAG, &mut iter)?; + + Some(DltFtPkg::Header(DltFtHeaderPkg{ + file_serial_number, + file_name, + file_size, + creation_date, + number_of_packages, + buffer_size, + })) + } + 9 => { + Self::check_for_str(DltFtErrorPkg::PKG_FLAG, &mut iter)?; + let error_code = DltFtInt::try_take_from_iter(&mut iter)?; + let file_serial_number = DltFtUInt::try_take_from_iter(&mut iter)?; + let linux_error_code = DltFtInt::try_take_from_iter(&mut iter)?; + let file_name = Self::try_take_str_from_iter(&mut iter)?; + let file_size = DltFtUInt::try_take_from_iter(&mut iter)?; + let creation_date = Self::try_take_str_from_iter(&mut iter)?; + let number_of_packages = DltFtUInt::try_take_from_iter(&mut iter)?; + Self::check_for_str(DltFtErrorPkg::PKG_FLAG, &mut iter)?; + + Some(DltFtPkg::Error(DltFtErrorPkg{ + error_code: DltFtErrorCode(error_code), + linux_error_code, + file_serial_number, + file_name, + file_size, + creation_date, + number_of_packages, + })) + } + _ => None, + } + } + + fn try_take_str_from_iter<'c>(iter: &mut VerboseIter<'c>) -> Option<&'c str> { + let Some(Ok(VerboseValue::Str(result))) = iter.next() else { + return None; + }; + if result.name.is_some() { + return None; + } + Some(result.value) + } + + fn try_take_raw_from_iter<'c>(iter: &mut VerboseIter<'c>) -> Option<&'c [u8]> { + let Some(Ok(VerboseValue::Raw(result))) = iter.next() else { + return None; + }; + if result.name.is_some() { + return None; + } + Some(result.data) + } + + fn check_for_str<'c>(expected: &str, iter: &mut VerboseIter<'_>) -> Option<()> { + let Some(Ok(VerboseValue::Str(result))) = iter.next() else { + return None; + }; + if result.name.is_some() { + return None; + } + if result.value != expected { + return None; + } + Some(()) + } + +} diff --git a/src/ft/dlt_ft_uint.rs b/src/ft/dlt_ft_uint.rs index 7ad3b7c..b16c6fe 100644 --- a/src/ft/dlt_ft_uint.rs +++ b/src/ft/dlt_ft_uint.rs @@ -1,3 +1,5 @@ +use crate::verbose::*; + /// Unsigned integer (either 32 or 64 bit). #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum DltFtUInt { @@ -5,6 +7,22 @@ pub enum DltFtUInt { U64(u64), } +impl DltFtUInt { + pub fn try_take_from_iter(iter: &mut VerboseIter) -> Option { + let Some(Ok(value)) = iter.next() else { + return None; + }; + if value.name().is_some() { + return None; + } + match value { + VerboseValue::U32(v) => Some(DltFtUInt::U32(v.value)), + VerboseValue::U64(v) => Some(DltFtUInt::U64(v.value)), + _ => None, + } + } +} + impl From for DltFtUInt { fn from(value: u32) -> Self { DltFtUInt::U32(value) From aa0889a28bf08e346746c6d8763fb8205c76a91d Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 18 Jun 2024 11:08:11 +0200 Subject: [PATCH 05/25] Add uint16 to file transfer unsigned int --- src/ft/dlt_ft_uint.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ft/dlt_ft_uint.rs b/src/ft/dlt_ft_uint.rs index b16c6fe..491ce87 100644 --- a/src/ft/dlt_ft_uint.rs +++ b/src/ft/dlt_ft_uint.rs @@ -3,6 +3,7 @@ use crate::verbose::*; /// Unsigned integer (either 32 or 64 bit). #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum DltFtUInt { + U16(u16), U32(u32), U64(u64), } @@ -16,6 +17,7 @@ impl DltFtUInt { return None; } match value { + VerboseValue::U16(v) => Some(DltFtUInt::U16(v.value)), VerboseValue::U32(v) => Some(DltFtUInt::U32(v.value)), VerboseValue::U64(v) => Some(DltFtUInt::U64(v.value)), _ => None, @@ -53,6 +55,7 @@ impl From for DltFtUInt { impl From for usize { fn from(value: DltFtUInt) -> Self { match value { + DltFtUInt::U16(v) => usize::from(v), DltFtUInt::U32(v) => v as usize, DltFtUInt::U64(v) => v as usize, } @@ -62,7 +65,8 @@ impl From for usize { impl From for u64 { fn from(value: DltFtUInt) -> Self { match value { - DltFtUInt::U32(v) => v as u64, + DltFtUInt::U16(v) => u64::from(v), + DltFtUInt::U32(v) => u64::from(v), DltFtUInt::U64(v) => v, } } From cb52b9734d31f9036d7c50a0ff5dc3bf3697c003 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Wed, 19 Jun 2024 11:21:11 +0200 Subject: [PATCH 06/25] Add reconstruction buffer for dlt file transfer streams --- src/error/ft_reassemble_error.rs | 97 ++++++ src/error/mod.rs | 3 + src/ft/dlt_ft_buffer.rs | 499 +++++++++++++++++++++++++++++++ src/ft/dlt_ft_range.rs | 108 +++++++ src/ft/mod.rs | 6 + 5 files changed, 713 insertions(+) create mode 100644 src/error/ft_reassemble_error.rs create mode 100644 src/ft/dlt_ft_buffer.rs create mode 100644 src/ft/dlt_ft_range.rs diff --git a/src/error/ft_reassemble_error.rs b/src/error/ft_reassemble_error.rs new file mode 100644 index 0000000..c4d8acc --- /dev/null +++ b/src/error/ft_reassemble_error.rs @@ -0,0 +1,97 @@ +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum FtReassembleError { + + /// Error if the number of bytes of data in a "DLT file transfer" packet + /// is not matching the original in the header specified buffer size. + DataLenNotMatchingBufferSize { + header_buffer_len: u64, + data_pkt_len: u64, + data_pkt_nr: u64, + number_of_packages: u64, + }, + + /// Error if the number of packages & buffer len do not match the file_size. + InconsitantHeaderLenValues { + file_size: u64, + number_of_packages: u64, + buffer_len: u64 + }, + + /// Error if a data package with an unexpected package nr is received. + UnexpectedPackageNrInDataPkg { + expected_nr_of_packages: u64, + package_nr: u64, + }, + + /// Error if not enough memory could be allocated to store the file in memory. + AllocationFailure { len: usize }, +} + +impl core::fmt::Display for FtReassembleError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FtReassembleError::*; + match self { + DataLenNotMatchingBufferSize{ header_buffer_len, data_pkt_len, data_pkt_nr, number_of_packages } => write!(f, "Payload length {data_pkt_len} of DLT file transfer data packet (nr {data_pkt_nr} of {number_of_packages}) is not matching the buffer len {header_buffer_len} set by the header packet."), + InconsitantHeaderLenValues{ file_size, number_of_packages, buffer_len } => write!(f, "DLT file transfer header packet 'file size' {file_size} is inconsistant with the 'buffer size' {buffer_len} and 'number of packages' {number_of_packages}"), + UnexpectedPackageNrInDataPkg { expected_nr_of_packages, package_nr } => write!(f, "Received a DLT file transfer data packet with the unexpected package number {package_nr} (expected number of packages based on header package is {expected_nr_of_packages})."), + AllocationFailure { len } => write!(f, "Failed to allocate {len} bytes of memory to reconstruct the SOMEIP TP packets."), + } + } +} + +impl std::error::Error for FtReassembleError {} + +#[cfg(test)] +mod tests { + /*use super::FtReassembleError::*; + + + #[test] + fn debug() { + let err = AllocationFailure { len: 0 }; + let _ = format!("{err:?}"); + } + + #[test] + fn clone_eq_hash_ord() { + use core::cmp::Ordering; + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let err = AllocationFailure { len: 0 }; + assert_eq!(err, err.clone()); + let hash_a = { + let mut hasher = DefaultHasher::new(); + err.hash(&mut hasher); + hasher.finish() + }; + let hash_b = { + let mut hasher = DefaultHasher::new(); + err.clone().hash(&mut hasher); + hasher.finish() + }; + assert_eq!(hash_a, hash_b); + assert_eq!(Ordering::Equal, err.cmp(&err)); + assert_eq!(Some(Ordering::Equal), err.partial_cmp(&err)); + } + + #[test] + fn fmt() { + let tests = [ + (UnalignedTpPayloadLen { offset: 1, payload_len: 2 }, "Payload length 2 of SOMEIP TP segment (offset 1) is not a multiple of 16. This is only allowed for TP packets where the 'more segements' flag is not set."), + (SegmentTooBig { offset: 1, payload_len: 2, max: 3, }, "Overall length of TP segment (offset 1, payload len: 2) bigger then the maximum allowed size of 3."), + (ConflictingEnd { previous_end: 1, conflicting_end: 2, }, "Received a TP package (offset + len: 2) which conflicts a package that previously set the end to 1."), + (AllocationFailure { len: 0 }, "Faield to allocate 0 bytes of memory to reconstruct the SOMEIP TP packets."), + ]; + for test in tests { + assert_eq!(format!("{}", test.0), test.1); + } + } + + #[test] + fn source() { + use std::error::Error; + assert!(AllocationFailure { len: 0 }.source().is_none()); + } + */ +} \ No newline at end of file diff --git a/src/error/mod.rs b/src/error/mod.rs index ea2d7c1..6a74d70 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,6 +1,9 @@ mod dlt_message_length_too_small_error; pub use dlt_message_length_too_small_error::*; +mod ft_reassemble_error; +pub use ft_reassemble_error::*; + mod layer; pub use layer::*; diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs new file mode 100644 index 0000000..59fb573 --- /dev/null +++ b/src/ft/dlt_ft_buffer.rs @@ -0,0 +1,499 @@ +#[cfg(feature = "std")] +use crate::{ft::*, error::FtReassembleError}; +#[cfg(feature = "std")] +use std::vec::Vec; +#[cfg(feature = "std")] +use std::string::String; + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct DltFtBuffer { + /// Buffer containing the recostructed data + data: Vec, + + /// Contains the ranges filled with data. + sections: Vec, + + /// Set to the extended end size. + file_size: u64, + + /// Number of expected packets. + number_of_packets: u64, + + /// Buffer size. + buffer_size: u64, + + /// File serial number (usually inode). + file_serial_number: DltFtUInt, + + /// Absolute path to the file. + file_name: String, + + /// File creaton date. + creation_date: String, +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl DltFtBuffer { + + /// File serial number (usually inode). + #[inline] + pub fn file_serial_number(&self) -> DltFtUInt { + self.file_serial_number + } + + /// Absolute path to the file. + #[inline] + pub fn file_name(&self) -> &str { + &self.file_name + } + + /// File creaton date. + #[inline] + pub fn creation_date(&self) -> &str { + &self.creation_date + } + + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub fn new(header: &DltFtHeaderPkg) -> Result { + let mut result = DltFtBuffer { + data: Vec::new(), + sections: Vec::with_capacity(4), + file_size: 0, + number_of_packets: 0, + buffer_size: 0, + file_serial_number: header.file_serial_number, + file_name: String::with_capacity(header.file_name.len()), + creation_date: String::with_capacity(header.creation_date.len()), + }; + result.reinit_from_header_pkg(header)?; + Ok(result) + } + + /// Reset buffer to starting state. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + pub fn clear(&mut self) { + self.data.clear(); + self.sections.clear(); + self.file_size = 0; + self.number_of_packets = 0; + self.buffer_size = 0; + } + + /// Setup all the buffers based on a received header package. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] + #[cfg(any(target_pointer_width = "64"))] + pub fn reinit_from_header_pkg(&mut self, header: &DltFtHeaderPkg) -> Result<(), FtReassembleError> { + + // validate that the header is consistant + use core::ops::RangeInclusive; + let value_err = FtReassembleError::InconsitantHeaderLenValues { + file_size: header.file_size.into(), + number_of_packages: header.number_of_packages.into(), + buffer_len: header.buffer_size.into() + }; + if u64::from(header.number_of_packages) == 0 { + return Err(value_err.clone()); + } + let max_expected_size = u64::from(header.buffer_size) + .checked_mul(header.number_of_packages.into()) + .ok_or_else(|| value_err.clone())?; + let min_expected_size = max_expected_size + .checked_sub(header.buffer_size.into()) + .ok_or_else(|| value_err.clone())? + .checked_add(1) + .ok_or_else(|| value_err.clone())?; + + if !RangeInclusive::new(min_expected_size, max_expected_size).contains(&header.file_size.into()) { + return Err(value_err); + } + + // reset the buffer + self.data.clear(); + // TODO fix for 32 bit systems + self.data.try_reserve(header.file_size.into()) + .map_err(|_| FtReassembleError::AllocationFailure { len: header.file_size.into() })?; + self.sections.clear(); + + // set values + self.file_size = header.file_size.into(); + self.number_of_packets = header.number_of_packages.into(); + self.buffer_size = header.buffer_size.into(); + + Ok(()) + } + + /// Consume a DLT file transfer data package, the caller is responsible to ensure the + /// [`DltFtDataPkg::file_serial_number`] of the data package match the + /// [`Self::file_serial_number`] of the buffer. + #[cfg(any(target_pointer_width = "64"))] + pub fn consume_data_pkg( + &mut self, + data: &DltFtDataPkg, + ) -> Result<(), FtReassembleError> { + + // validate the package number + let package_nr: u64 = data.package_nr.into(); + if package_nr == 0 || package_nr > self.number_of_packets { + return Err(FtReassembleError::UnexpectedPackageNrInDataPkg{ + expected_nr_of_packages: self.number_of_packets, + package_nr, + }); + } + + // determine insertion start + let insertion_start: usize = (package_nr as usize - 1)*(self.buffer_size as usize); + + // validate the data len of the package + let expected_len = if package_nr < self.number_of_packets { + self.buffer_size + } else { + // the last package only contains the left overs + let rest = self.file_size % self.buffer_size; + if rest > 0 { + rest + } else { + self.buffer_size + } + }; + + if (data.data.len() as u64) != expected_len { + return Err(FtReassembleError::DataLenNotMatchingBufferSize { + header_buffer_len: self.buffer_size, + data_pkt_len: data.data.len() as u64, + data_pkt_nr: package_nr, + number_of_packages: self.number_of_packets, + }); + } + + // insert the data + // check if it is possible to grow & append the data + let insert_end = insertion_start + (expected_len as usize); + if self.data.len() <= insertion_start { + // fill until insertion point if needed + if self.data.len() < insertion_start { + self.data.resize(insertion_start, 0); + } + // add data + self.data.extend_from_slice(data.data); + } else { + + // overwrite the existing data + let overwrite_end = std::cmp::min(self.data.len(), insert_end); + let overwrite_len = overwrite_end - insertion_start; + (&mut self.data.as_mut_slice()[insertion_start..overwrite_end]).clone_from_slice(&data.data[..overwrite_len]); + + // in case some data still needs to appended do that as well + if overwrite_end < insert_end { + self.data.extend_from_slice(&data.data[overwrite_len..]); + } + } + + // update sections + let mut new_section = DltFtRange { + start: insertion_start as u64, + end: insert_end as u64, + }; + + // merge overlapping section into new section and remove them + self.sections.retain(|it| -> bool { + if let Some(merged) = new_section.merge(*it) { + new_section = merged; + false + } else { + true + } + }); + self.sections.push(new_section); + + Ok(()) + } + + /// Returns true if the data + pub fn is_complete(&self) -> bool { + 1 == self.sections.len() && 0 == self.sections[0].start && self.sections[0].end == self.file_size + } + + /// Try finalizing the reconstructed file data and return a reference to it + /// if the stream reconstruction was completed. + pub fn try_finalize(&mut self) -> Option<&[u8]> { + if false == self.is_complete() { + return None; + } else { + Some(&self.data) + } + } +} + +#[cfg(test)] +mod test { +/* + use crate::*; + + #[test] + fn debug_clone_eq() { + let buf = DltFtBuffer::new(Default::default()); + let _ = format!("{:?}", buf); + assert_eq!(buf, buf.clone()); + assert_eq!(buf.cmp(&buf), core::cmp::Ordering::Equal); + assert_eq!(buf.partial_cmp(&buf), Some(core::cmp::Ordering::Equal)); + + use core::hash::{Hash, Hasher}; + use std::collections::hash_map::DefaultHasher; + let h1 = { + let mut h = DefaultHasher::new(); + buf.hash(&mut h); + h.finish() + }; + let h2 = { + let mut h = DefaultHasher::new(); + buf.clone().hash(&mut h); + h.finish() + }; + assert_eq!(h1, h2); + } + + struct TestPacket { + offset: u32, + more_segments: bool, + payload: Vec, + } + + impl TestPacket { + fn new(offset: u32, more_segments: bool, payload: &[u8]) -> TestPacket { + TestPacket { + offset, + more_segments, + payload: payload.iter().copied().collect(), + } + } + + fn send_to_buffer(&self, buffer: &mut DltFtBuffer) -> Result<(), err::TpReassembleError> { + let packet = self.to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + buffer.consume_tp(slice) + } + + fn to_vec(&self) -> Vec { + let header = SomeipHeader { + message_id: 1234, + length: 8 + 4 + self.payload.len() as u32, + request_id: 23, + interface_version: 1, + message_type: MessageType::Notification, + return_code: 0, + tp_header: { + let mut tp = TpHeader::new(self.more_segments); + tp.set_offset(self.offset).unwrap(); + Some(tp) + }, + }; + let mut result = Vec::with_capacity(SOMEIP_HEADER_LENGTH + 4 + self.payload.len()); + result.extend_from_slice(&header.base_to_bytes()); + result.extend_from_slice(&header.tp_header.as_ref().unwrap().to_bytes()); + result.extend_from_slice(&self.payload); + result + } + + fn result_header(payload_length: u32) -> SomeipHeader { + SomeipHeader { + message_id: 1234, + length: payload_length + 8, + request_id: 23, + interface_version: 1, + message_type: MessageType::Notification, + return_code: 0, + tp_header: None, + } + } + } + + #[test] + fn new() { + let actual = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); + assert!(actual.data.is_empty()); + assert!(actual.sections.is_empty()); + assert!(actual.end.is_none()); + assert_eq!(1024, actual.config.tp_buffer_start_payload_alloc_len); + assert_eq!(2048, actual.config.tp_max_payload_len()); + } + + #[test] + fn clear() { + let mut actual = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); + + actual.data.push(1); + actual.sections.push(TpRange { start: 2, end: 3 }); + actual.end = Some(123); + + actual.clear(); + + assert!(actual.data.is_empty()); + assert!(actual.sections.is_empty()); + assert!(actual.end.is_none()); + assert_eq!(1024, actual.config.tp_buffer_start_payload_alloc_len); + assert_eq!(2048, actual.config.tp_max_payload_len()); + } + + /// Returns a u8 vec counting up from "start" until len is reached (truncating bits greater then u8). + fn sequence(start: usize, len: usize) -> Vec { + let mut result = Vec::with_capacity(len); + for i in start..start + len { + result.push((i & 0xff) as u8); + } + result + } + + #[rustfmt::skip] + #[test] + fn consume() { + use err::TpReassembleError::*; + + // normal reconstruction + { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); + + let actions = [ + (false, TestPacket::new(0, true, &sequence(0,16))), + (false, TestPacket::new(16, true, &sequence(16,32))), + (true, TestPacket::new(48, false, &sequence(48,16))), + ]; + for a in actions { + a.1.send_to_buffer(&mut buffer).unwrap(); + assert_eq!(a.0, buffer.is_complete()); + } + let result = buffer.try_finalize().unwrap(); + assert_eq!(result.to_header(), TestPacket::result_header(16*4)); + assert_eq!(result.payload(), &sequence(0,16*4)); + } + + // overlapping reconstruction + { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); + + let actions = [ + (false, TestPacket::new(0, true, &sequence(0,16))), + // will be overwritten + (false, TestPacket::new(32, true, &sequence(0,16))), + // overwrites + (false, TestPacket::new(32, false, &sequence(32,16))), + // completes + (true, TestPacket::new(16, true, &sequence(16,16))), + ]; + for a in actions { + a.1.send_to_buffer(&mut buffer).unwrap(); + assert_eq!(a.0, buffer.is_complete()); + } + let result = buffer.try_finalize().unwrap(); + assert_eq!(result.to_header(), TestPacket::result_header(16*3)); + assert_eq!(result.payload(), &sequence(0,16*3)); + } + + // reverse order + { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); + + let actions = [ + (false, TestPacket::new(48, false, &sequence(48,16))), + (false, TestPacket::new(16, true, &sequence(16,32))), + (true, TestPacket::new(0, true, &sequence(0,16))), + ]; + for a in actions { + a.1.send_to_buffer(&mut buffer).unwrap(); + assert_eq!(a.0, buffer.is_complete()); + } + let result = buffer.try_finalize().unwrap(); + assert_eq!(result.to_header(), TestPacket::result_header(16*4)); + assert_eq!(result.payload(), &sequence(0,16*4)); + } + + // error tp packet bigger then max (offset only) + { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(32, 32).unwrap()); + assert_eq!( + SegmentTooBig { offset: 32 + 16, payload_len: 16, max: 32 }, + TestPacket::new(32 + 16, true, &sequence(0,16)).send_to_buffer(&mut buffer).unwrap_err() + ); + } + + // error tp packet bigger then max (offset + payload) + { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(32, 32).unwrap()); + assert_eq!( + SegmentTooBig { offset: 16, payload_len: 32, max: 32 }, + TestPacket::new(16, true, &sequence(0,32)).send_to_buffer(&mut buffer).unwrap_err() + ); + } + + // check packets that fill exactly to the max work + { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(32, 32).unwrap()); + let test_packet = TestPacket::new(16, false, &sequence(0,16)); + + let packet = test_packet.to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + + assert_eq!(Ok(()), buffer.consume_tp(slice)); + } + + // packets conflicting with previously seen end + for bad_offset in 1..16 { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(16*100, 16*100).unwrap()); + let test_packet = TestPacket::new(48, true, &sequence(0,32 + bad_offset)); + + let packet = test_packet.to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + + assert_eq!( + UnalignedTpPayloadLen { offset: 48, payload_len: 32 + bad_offset }, + buffer.consume_tp(slice).unwrap_err() + ); + } + + // test that conflicting ends trigger errors (received a different end) + { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); + + // setup an end (aka no more segements) + TestPacket::new(32, false, &sequence(32,16)).send_to_buffer(&mut buffer).unwrap(); + + // test that a "non end" going over the end package triggers an error + assert_eq!( + ConflictingEnd { previous_end: 32 + 16, conflicting_end: 48 + 16 }, + TestPacket::new(48, true, &sequence(48,16)).send_to_buffer(&mut buffer).unwrap_err() + ); + + // test that a new end at an earlier position triggers an error + assert_eq!( + ConflictingEnd { previous_end: 32 + 16, conflicting_end: 16 + 16 }, + TestPacket::new(16, false, &sequence(16,16)).send_to_buffer(&mut buffer).unwrap_err() + ); + } + } + + #[test] + fn try_finalize() { + let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); + + // not ended + assert_eq!(buffer.try_finalize(), None); + TestPacket::new(0, true, &sequence(0, 16)) + .send_to_buffer(&mut buffer) + .unwrap(); + assert_eq!(buffer.try_finalize(), None); + + // ended + TestPacket::new(16, false, &sequence(16, 16)) + .send_to_buffer(&mut buffer) + .unwrap(); + let result = buffer.try_finalize().unwrap(); + assert_eq!(result.to_header(), TestPacket::result_header(16 * 2)); + assert_eq!(result.payload(), &sequence(0, 16 * 2)); + } + */ +} diff --git a/src/ft/dlt_ft_range.rs b/src/ft/dlt_ft_range.rs new file mode 100644 index 0000000..e0c603d --- /dev/null +++ b/src/ft/dlt_ft_range.rs @@ -0,0 +1,108 @@ +/// Describing the range of received data in an DLT file transfer stream. +#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq, Ord, PartialOrd)] +pub struct DltFtRange { + /// Offset of section + pub start: u64, + /// Offset + length of section + pub end: u64, +} + +impl DltFtRange { + /// Return if the value is contained within the section. + fn is_value_connected(&self, value: u64) -> bool { + self.start <= value && self.end >= value + } + + /// Combine both sections if possible. + pub fn merge(&self, other: DltFtRange) -> Option { + if self.is_value_connected(other.start) + || self.is_value_connected(other.end) + || other.is_value_connected(self.start) + || other.is_value_connected(self.end) + { + Some(DltFtRange { + start: core::cmp::min(self.start, other.start), + end: core::cmp::max(self.end, other.end), + }) + } else { + None + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn debug_clone_eq() { + use alloc::format; + let section = DltFtRange { start: 1, end: 2 }; + let _ = format!("{:?}", section); + assert_eq!(section, section.clone()); + assert_eq!(section.cmp(§ion), core::cmp::Ordering::Equal); + assert_eq!( + section.partial_cmp(§ion), + Some(core::cmp::Ordering::Equal) + ); + + use core::hash::{Hash, Hasher}; + use std::collections::hash_map::DefaultHasher; + let h1 = { + let mut h = DefaultHasher::new(); + section.hash(&mut h); + h.finish() + }; + let h2 = { + let mut h = DefaultHasher::new(); + section.clone().hash(&mut h); + h.finish() + }; + assert_eq!(h1, h2); + } + + #[test] + fn is_value_connected() { + let s = DltFtRange { start: 5, end: 9 }; + assert_eq!(false, s.is_value_connected(3)); + assert_eq!(false, s.is_value_connected(4)); + assert!(s.is_value_connected(5)); + assert!(s.is_value_connected(6)); + assert!(s.is_value_connected(7)); + assert!(s.is_value_connected(8)); + assert!(s.is_value_connected(9)); + assert_eq!(false, s.is_value_connected(10)); + assert_eq!(false, s.is_value_connected(11)); + } + + #[test] + fn merge() { + let tests = [ + ((0, 1), (1, 2), Some((0, 2))), + ((0, 1), (2, 3), None), + ((3, 7), (1, 2), None), + ((3, 7), (1, 3), Some((1, 7))), + ((3, 7), (1, 4), Some((1, 7))), + ((3, 7), (1, 5), Some((1, 7))), + ((3, 7), (1, 6), Some((1, 7))), + ((3, 7), (1, 7), Some((1, 7))), + ((3, 7), (1, 8), Some((1, 8))), + ]; + for t in tests { + let a = DltFtRange { + start: t.0 .0, + end: t.0 .1, + }; + let b = DltFtRange { + start: t.1 .0, + end: t.1 .1, + }; + let expected = t.2.map(|v| DltFtRange { + start: v.0, + end: v.1, + }); + assert_eq!(a.merge(b), expected); + assert_eq!(b.merge(a), expected); + } + } +} \ No newline at end of file diff --git a/src/ft/mod.rs b/src/ft/mod.rs index 826939a..75cf60b 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -1,3 +1,6 @@ +mod dlt_ft_buffer; +pub use dlt_ft_buffer::*; + mod dlt_ft_data_pkg; pub use dlt_ft_data_pkg::*; @@ -25,5 +28,8 @@ pub use dlt_ft_int::*; mod dlt_ft_pkg; pub use dlt_ft_pkg::*; +mod dlt_ft_range; +pub use dlt_ft_range::*; + mod dlt_ft_uint; pub use dlt_ft_uint::*; From 6d375a6c018faf78c79eca07a5a9154fd4a9d882 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Wed, 19 Jun 2024 14:53:53 +0200 Subject: [PATCH 07/25] Added filetransfer buffer --- src/error/ft_pool_error.rs | 91 ++++++ src/error/mod.rs | 3 + src/ft/dlt_ft_buffer.rs | 39 ++- src/ft/dlt_ft_complete_in_mem_file.rs | 16 + src/ft/dlt_ft_pool.rs | 443 ++++++++++++++++++++++++++ src/ft/mod.rs | 6 + 6 files changed, 593 insertions(+), 5 deletions(-) create mode 100644 src/error/ft_pool_error.rs create mode 100644 src/ft/dlt_ft_complete_in_mem_file.rs create mode 100644 src/ft/dlt_ft_pool.rs diff --git a/src/error/ft_pool_error.rs b/src/error/ft_pool_error.rs new file mode 100644 index 0000000..bd35321 --- /dev/null +++ b/src/error/ft_pool_error.rs @@ -0,0 +1,91 @@ +use super::*; +use crate::ft::DltFtUInt; + +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum FtPoolError { + /// Error re-assembling the stream. + FtReassemble(FtReassembleError), + + /// Error if a data packet for an unknown stream is received. + DataForUnknownStream{ + file_serial_number: DltFtUInt + }, + + /// Error if a end packet for an unknown stream is received. + EndForUnknownStream{ + file_serial_number: DltFtUInt + }, +} + +impl core::fmt::Display for FtPoolError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use FtPoolError::*; + match self { + FtReassemble(err) => err.fmt(f), + DataForUnknownStream{ file_serial_number } => write!(f, "Received a DLT file transfer 'data' packet from an unknown stream with file_serial_number {file_serial_number:?}."), + EndForUnknownStream{ file_serial_number } => write!(f, "Received a DLT file transfer 'end' packet from an unknown stream with file_serial_number {file_serial_number:?}."), + } + } +} + +impl std::error::Error for FtPoolError {} + +impl From for FtPoolError { + fn from(value: FtReassembleError) -> Self { + FtPoolError::FtReassemble(value) + } +} + +#[cfg(test)] +mod tests { + /*use super::FtPoolError::*; + + #[test] + fn debug() { + let err = AllocationFailure { len: 0 }; + let _ = format!("{err:?}"); + } + + #[test] + fn clone_eq_hash_ord() { + use core::cmp::Ordering; + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let err = AllocationFailure { len: 0 }; + assert_eq!(err, err.clone()); + let hash_a = { + let mut hasher = DefaultHasher::new(); + err.hash(&mut hasher); + hasher.finish() + }; + let hash_b = { + let mut hasher = DefaultHasher::new(); + err.clone().hash(&mut hasher); + hasher.finish() + }; + assert_eq!(hash_a, hash_b); + assert_eq!(Ordering::Equal, err.cmp(&err)); + assert_eq!(Some(Ordering::Equal), err.partial_cmp(&err)); + } + + #[test] + fn fmt() { + let tests = [ + (UnalignedTpPayloadLen { offset: 1, payload_len: 2 }, "Payload length 2 of SOMEIP TP segment (offset 1) is not a multiple of 16. This is only allowed for TP packets where the 'more segements' flag is not set."), + (SegmentTooBig { offset: 1, payload_len: 2, max: 3, }, "Overall length of TP segment (offset 1, payload len: 2) bigger then the maximum allowed size of 3."), + (ConflictingEnd { previous_end: 1, conflicting_end: 2, }, "Received a TP package (offset + len: 2) which conflicts a package that previously set the end to 1."), + (AllocationFailure { len: 0 }, "Faield to allocate 0 bytes of memory to reconstruct the SOMEIP TP packets."), + ]; + for test in tests { + assert_eq!(format!("{}", test.0), test.1); + } + } + + #[test] + fn source() { + use std::error::Error; + assert!(AllocationFailure { len: 0 }.source().is_none()); + } + */ +} diff --git a/src/error/mod.rs b/src/error/mod.rs index 6a74d70..6c64ed6 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -1,6 +1,9 @@ mod dlt_message_length_too_small_error; pub use dlt_message_length_too_small_error::*; +mod ft_pool_error; +pub use ft_pool_error::*; + mod ft_reassemble_error; pub use ft_reassemble_error::*; diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 59fb573..3b533b6 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -32,6 +32,9 @@ pub struct DltFtBuffer { /// File creaton date. creation_date: String, + + /// True if an end packet was received. + end_received: bool, } #[cfg(feature = "std")] @@ -56,6 +59,12 @@ impl DltFtBuffer { &self.creation_date } + /// True if an end packet was received. + #[inline] + pub fn end_received(&self) -> bool { + self.end_received + } + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn new(header: &DltFtHeaderPkg) -> Result { @@ -68,6 +77,7 @@ impl DltFtBuffer { file_serial_number: header.file_serial_number, file_name: String::with_capacity(header.file_name.len()), creation_date: String::with_capacity(header.creation_date.len()), + end_received: false, }; result.reinit_from_header_pkg(header)?; Ok(result) @@ -82,6 +92,10 @@ impl DltFtBuffer { self.file_size = 0; self.number_of_packets = 0; self.buffer_size = 0; + self.file_serial_number = DltFtUInt::U64(0); + self.file_name.clear(); + self.creation_date.clear(); + self.end_received = false; } /// Setup all the buffers based on a received header package. @@ -115,7 +129,7 @@ impl DltFtBuffer { // reset the buffer self.data.clear(); - // TODO fix for 32 bit systems + // TODO implement for 32 bit systems self.data.try_reserve(header.file_size.into()) .map_err(|_| FtReassembleError::AllocationFailure { len: header.file_size.into() })?; self.sections.clear(); @@ -124,10 +138,20 @@ impl DltFtBuffer { self.file_size = header.file_size.into(); self.number_of_packets = header.number_of_packages.into(); self.buffer_size = header.buffer_size.into(); + self.file_name.clear(); + self.file_name.push_str(header.file_name); + self.creation_date.clear(); + self.creation_date.push_str(header.creation_date); + self.end_received = false; Ok(()) } + /// Sets that the end packet was received. + pub fn set_end_received(&mut self) { + self.end_received = true; + } + /// Consume a DLT file transfer data package, the caller is responsible to ensure the /// [`DltFtDataPkg::file_serial_number`] of the data package match the /// [`Self::file_serial_number`] of the buffer. @@ -214,18 +238,23 @@ impl DltFtBuffer { Ok(()) } - /// Returns true if the data + /// Returns true if the data has been completed and the end received. pub fn is_complete(&self) -> bool { - 1 == self.sections.len() && 0 == self.sections[0].start && self.sections[0].end == self.file_size + self.end_received && 1 == self.sections.len() && 0 == self.sections[0].start && self.sections[0].end == self.file_size } /// Try finalizing the reconstructed file data and return a reference to it /// if the stream reconstruction was completed. - pub fn try_finalize(&mut self) -> Option<&[u8]> { + pub fn try_finalize<'a>(&'a self) -> Option> { if false == self.is_complete() { return None; } else { - Some(&self.data) + Some(DltFtCompleteInMemFile{ + file_serial_number: self.file_serial_number, + file_name: &self.file_name, + creation_date: &self.creation_date, + data: &self.data, + }) } } } diff --git a/src/ft/dlt_ft_complete_in_mem_file.rs b/src/ft/dlt_ft_complete_in_mem_file.rs new file mode 100644 index 0000000..5ad3b34 --- /dev/null +++ b/src/ft/dlt_ft_complete_in_mem_file.rs @@ -0,0 +1,16 @@ +use super::*; + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct DltFtCompleteInMemFile<'a> { + /// File serial number (usually inode). + pub file_serial_number: DltFtUInt, + + /// Absolute path to the file. + pub file_name: &'a str, + + /// File creaton date. + pub creation_date: &'a str, + + /// Slice containing the complete file data. + pub data: &'a [u8], +} diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs new file mode 100644 index 0000000..2d59720 --- /dev/null +++ b/src/ft/dlt_ft_pool.rs @@ -0,0 +1,443 @@ +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +use crate::{error::*, ft::*}; +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +use core::hash::Hash; +#[cfg(feature = "std")] +use std::{vec::Vec, collections::HashMap}; + +/// Pool of buffers to reconstruct multiple DLT file transfer packet streams in +/// parallel (re-uses buffers to minimize allocations). +/// +/// # This implementation is NOT safe against "Out of Memory" attacks +/// +/// If you use the [`DltFtPool`] in an untrusted environment an attacker could +/// cause an "out of memory error" by opening up multiple parallel file transfer streams, +/// never ending them and filling them up with as much data as possible. +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[derive(Debug, Clone)] +pub struct DltFtPool +where + ChannelId: Hash + Eq + PartialEq + Clone + Sized, + Timestamp: Sized + core::fmt::Debug + Clone, +{ + /// Currently reconstructing file transfer streams. + active: HashMap<(ChannelId, DltFtUInt), (DltFtBuffer, Timestamp)>, + + /// Buffers that have finished receiving data and can be re-used. + finished: Vec, +} + +impl DltFtPool +where + ChannelId: Hash + Eq + PartialEq + Clone + Sized, + Timestamp: core::fmt::Debug + Clone + Sized, +{ + pub fn new() -> DltFtPool { + DltFtPool { + active: HashMap::new(), + finished: Vec::new(), + } + } + + #[inline] + pub fn active_bufs(&self) -> &HashMap<(ChannelId, DltFtUInt), (DltFtBuffer, Timestamp)> { + &self.active + } + + #[inline] + pub fn finished_bufs(&self) -> &Vec { + &self.finished + } + + pub fn consume<'a: 'c, 'b: 'c, 'c: 'a + 'b>( + &'a mut self, + id: ChannelId, + timestamp: Timestamp, + pkg: &DltFtPkg, + ) -> Result>, FtPoolError> { + use std::collections::hash_map::Entry::*; + + match pkg { + DltFtPkg::Header(header_pkg) => { + match self.active.entry((id, header_pkg.file_serial_number)) { + Occupied(mut entry) => { + let m = entry.get_mut(); + m.0.reinit_from_header_pkg(header_pkg)?; + m.1 = timestamp; + + // TODO add error + Ok(None) + }, + Vacant(vac) => { + vac.insert((DltFtBuffer::new(header_pkg)?, timestamp)); + Ok(None) + }, + } + }, + DltFtPkg::Data(data_pkg) => { + match self.active.entry((id, data_pkg.file_serial_number)) { + Occupied(mut entry) => { + + // inject the new data & process + let (buffer, last_ts) = entry.get_mut(); + *last_ts = timestamp; + buffer.consume_data_pkg(data_pkg)?; + + // check if the data is complete + if buffer.is_complete() { + // take out the buffer + let (buf, _) = entry.remove(); + self.finished.push(buf); + Ok(Some( + self.finished.last().unwrap().try_finalize().unwrap() + )) + } else { + Ok(None) + } + }, + Vacant(_) => { + return Err(FtPoolError::DataForUnknownStream { file_serial_number: data_pkg.file_serial_number }); + }, + } + }, + DltFtPkg::End(end_pkg) => { + match self.active.entry((id, end_pkg.file_serial_number)) { + Occupied(mut entry) => { + + // inject the new data & process + let (buffer, last_ts) = entry.get_mut(); + *last_ts = timestamp; + buffer.set_end_received(); + + // check if the data is complete + if buffer.is_complete() { + // take out the buffer + let (buf, _) = entry.remove(); + self.finished.push(buf); + Ok(Some( + self.finished.last().unwrap().try_finalize().unwrap() + )) + } else { + Ok(None) + } + }, + Vacant(_) => { + return Err(FtPoolError::EndForUnknownStream { file_serial_number: end_pkg.file_serial_number }); + }, + } + }, + + DltFtPkg::Error(err) => { + match self.active.entry((id, err.file_serial_number)) { + Occupied(entry) => { + // take out the buffer + let (buf, _) = entry.remove(); + self.finished.push(buf); + Ok(None) + }, + Vacant(_) => Ok(None), + } + }, + DltFtPkg::FileNotExistsError(_) => Ok(None), + DltFtPkg::Info(_) => Ok(None), + } + } + + /// Retains only the elements specified by the predicate. + pub fn retain(&mut self, f: F) + where + F: Fn(&Timestamp) -> bool, + { + // check if any entry has to be removed + if self.active.iter().any(|(_, (_, t))| false == f(t)) { + self.active = self + .active + .drain() + .filter_map(|(k, v)| { + if f(&v.1) { + Some((k, v)) + } else { + self.finished.push(v.0); + None + } + }) + .collect(); + } + } +} + +impl PartialEq for DltFtPool +where + ChannelId: Hash + Eq + PartialEq + Clone + Sized, + Timestamp: core::fmt::Debug + Clone + Sized + PartialEq, +{ + fn eq(&self, other: &Self) -> bool { + self.active == other.active + && self.finished == other.finished + } +} + +impl Eq for DltFtPool +where + ChannelId: Hash + Eq + PartialEq + Clone + Sized, + Timestamp: core::fmt::Debug + Clone + Sized + PartialEq + Eq, +{ +} + +#[cfg(test)] +mod tests { + use super::*; + + /* + #[test] + fn debug_clone_eq() { + let pool: DltFtPool<(), ()> = DltFtPool::new(Default::default()); + let _ = format!("{:?}", pool); + assert_eq!(pool, pool.clone()); + assert_eq!(pool.buf_config(), &TpBufConfig::default()); + } + + #[test] + fn with_capacity() { + let pool = DltFtPool::<(), ()>::with_capacity(Default::default(), 3); + assert_eq!(3, pool.finished_bufs().len()); + assert!(pool.active.capacity() >= 3); + } + + #[test] + fn reserve() { + let mut pool = DltFtPool::<(), ()>::new(Default::default()); + pool.reserve(2); + assert_eq!(2, pool.finished_bufs().len()); + assert!(pool.active.capacity() >= 2); + pool.reserve(3); + assert_eq!(5, pool.finished_bufs().len()); + assert!(pool.active.capacity() >= 5); + } + + struct TestPacket { + request_id: u32, + offset: u32, + more_segments: bool, + payload: Vec, + } + + impl TestPacket { + fn new(request_id: u32, offset: u32, more_segments: bool, payload: &[u8]) -> TestPacket { + TestPacket { + request_id, + offset, + more_segments, + payload: payload.iter().copied().collect(), + } + } + + fn to_vec(&self) -> Vec { + let header = SomeipHeader { + message_id: 1234, + length: 8 + 4 + self.payload.len() as u32, + request_id: self.request_id, + interface_version: 1, + message_type: MessageType::Notification, + return_code: 0, + tp_header: { + let mut tp = TpHeader::new(self.more_segments); + tp.set_offset(self.offset).unwrap(); + Some(tp) + }, + }; + let mut result = Vec::with_capacity(SOMEIP_HEADER_LENGTH + 4 + self.payload.len()); + result.extend_from_slice(&header.base_to_bytes()); + result.extend_from_slice(&header.tp_header.as_ref().unwrap().to_bytes()); + result.extend_from_slice(&self.payload); + result + } + + fn result_header(&self, payload_length: u32) -> SomeipHeader { + SomeipHeader { + message_id: 1234, + length: payload_length + 8, + request_id: self.request_id, + interface_version: 1, + message_type: MessageType::Notification, + return_code: 0, + tp_header: None, + } + } + } + + /// Returns a u8 vec counting up from "start" until len is reached (truncating bits greater then u8). + fn sequence(start: usize, len: usize) -> Vec { + let mut result = Vec::with_capacity(len); + for i in start..start + len { + result.push((i & 0xff) as u8); + } + result + } + + #[rustfmt::skip] + #[test] + fn consume() { + use err::TpReassembleError::*; + + // simple packet forwarding (without TP effect) + { + // build a non tp packet + let header = SomeipHeader { + message_id: 1234, + length: 8 + 8 as u32, + request_id: 234, + interface_version: 1, + message_type: MessageType::Notification, + return_code: 0, + // no tp header + tp_header: None, + }; + let mut result = Vec::with_capacity(SOMEIP_HEADER_LENGTH + 8); + result.extend_from_slice(&header.base_to_bytes()); + result.extend_from_slice(&[0;8]); + + let someip_slice = SomeipMsgSlice::from_slice(&result).unwrap(); + + let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); + let result = pool.consume((), (), someip_slice.clone()).unwrap(); + assert_eq!(Some(someip_slice), result); + } + + // normal reconstruction (without additional id) + { + let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); + + let actions = [ + // start two streams in parallel + (TestPacket::new(1, 0, true, &sequence(1,16)), None, 1, 0), + (TestPacket::new(2, 0, true, &sequence(2,32)), None, 2, 0), + // stream 1 ends + (TestPacket::new(1, 16, false, &sequence(1 + 16,16)), Some(sequence(1,32)), 1, 1), + // stream 3 which imidiatly ends + (TestPacket::new(3, 0, false, &sequence(3,16*4)), Some(sequence(3, 16*4)), 1, 1), + // end stream 2 + (TestPacket::new(2, 32, true, &sequence(32 + 2,16*4)), None, 1, 1), + (TestPacket::new(2, 16*6, false, &sequence(16*6 + 2,16*3)), Some(sequence(2, 16*9)), 0, 2), + ]; + for a in actions { + let packet = a.0.to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + let result = pool.consume((), (), slice).unwrap(); + if let Some(expected_payload) = a.1 { + let msg = result.unwrap(); + assert_eq!(msg.to_header(), a.0.result_header(expected_payload.len() as u32)); + assert_eq!(msg.payload(), expected_payload); + } else { + assert!(result.is_none()); + } + assert_eq!(a.2, pool.active_bufs().len()); + assert_eq!(a.3, pool.finished_bufs().len()); + } + } + + // normal reconstruction (with additional id) + { + let mut pool: DltFtPool = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); + + // all actions have the same request id have differing id's + let actions = [ + // start two streams in parallel + (123, TestPacket::new(1, 0, true, &sequence(1,16)), None), + (234, TestPacket::new(1, 0, true, &sequence(2,32)), None), + // stream 1 ends + (123, TestPacket::new(1, 16, false, &sequence(1 + 16,16)), Some(sequence(1,32))), + // stream 3 which imidiatly ends + (345, TestPacket::new(1, 0, false, &sequence(3,16*4)), Some(sequence(3, 16*4))), + // end stream 2 + (234, TestPacket::new(1, 32, true, &sequence(32 + 2,16*4)), None), + (234, TestPacket::new(1, 16*6, false, &sequence(16*6 + 2,16*3)), Some(sequence(2, 16*9))), + ]; + for a in actions { + let packet = a.1.to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + let result = pool.consume(a.0.clone(), (), slice).unwrap(); + if let Some(expected_payload) = a.2 { + let msg = result.unwrap(); + assert_eq!(msg.to_header(), a.1.result_header(expected_payload.len() as u32)); + assert_eq!(msg.payload(), expected_payload); + } else { + assert!(result.is_none()); + } + } + } + + // error during reconstruction (at start) + { + let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); + + // should trigger an error as the payload is not a multiple of 1 + let packet = TestPacket::new(1, 0, true, &sequence(1,15)).to_vec(); + let someip_slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + assert_eq!( + pool.consume((), (), someip_slice).unwrap_err(), + UnalignedTpPayloadLen { offset: 0, payload_len: 15 } + ); + } + + // error during reconstruction (after start) + { + let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); + + { + let packet = TestPacket::new(1, 0, true, &sequence(1,16)).to_vec(); + let someip_slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + pool.consume((), (), someip_slice).unwrap(); + } + + // should trigger an error as the payload is not a multiple of 1 + let packet = TestPacket::new(1, 16, true, &sequence(1,15)).to_vec(); + let someip_slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + assert_eq!( + pool.consume((), (), someip_slice).unwrap_err(), + UnalignedTpPayloadLen { offset: 16, payload_len: 15 } + ); + } + + } + + #[test] + fn retain() { + let mut pool: DltFtPool = DltFtPool::new(Default::default()); + // request id 1, channel id 2, timestamp 123 + { + let packet = TestPacket::new(1, 0, true, &sequence(1, 16)).to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + let result = pool.consume(2u16, 123u32, slice).unwrap(); + assert!(result.is_none()); + assert_eq!(123, pool.active_bufs().get(&(2u16, 1u32)).unwrap().1); + } + // request id 1, channel id 2, timestamp 124 + { + let packet = TestPacket::new(1, 16, true, &sequence(16, 16)).to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + let result = pool.consume(2u16, 124u32, slice).unwrap(); + assert!(result.is_none()); + // check the timestamp was overwritten by the newer packet + assert_eq!(124, pool.active_bufs().get(&(2u16, 1u32)).unwrap().1); + } + // request id 1, channel id 3, timestamp 125 + { + let packet = TestPacket::new(1, 16, true, &sequence(16, 16)).to_vec(); + let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + let result = pool.consume(3u16, 125u32, slice).unwrap(); + assert!(result.is_none()); + } + + // discard streams with a timestamp smaller then 125 + pool.retain(|timestamp| *timestamp >= 125); + + assert_eq!(1, pool.active.len()); + assert_eq!(1, pool.finished.len()); + assert_eq!(125, pool.active_bufs().get(&(3u16, 1u32)).unwrap().1); + } + */ +} \ No newline at end of file diff --git a/src/ft/mod.rs b/src/ft/mod.rs index 75cf60b..4677073 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -1,6 +1,9 @@ mod dlt_ft_buffer; pub use dlt_ft_buffer::*; +mod dlt_ft_complete_in_mem_file; +pub use dlt_ft_complete_in_mem_file::*; + mod dlt_ft_data_pkg; pub use dlt_ft_data_pkg::*; @@ -22,6 +25,9 @@ pub use dlt_ft_header_pkg::*; mod dlt_ft_info_pkg; pub use dlt_ft_info_pkg::*; +mod dlt_ft_pool; +pub use dlt_ft_pool::*; + mod dlt_ft_int; pub use dlt_ft_int::*; From 821cbd10ae14794ba17a613e0ca3aada7a30b89d Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 24 Jun 2024 21:01:51 +0200 Subject: [PATCH 08/25] Resolved clippy warnings --- src/error/ft_pool_error.rs | 8 +--- src/error/ft_reassemble_error.rs | 7 ++- src/ft/dlt_ft_buffer.rs | 77 +++++++++++++++++++------------- src/ft/dlt_ft_pkg.rs | 21 ++++----- src/ft/dlt_ft_pool.rs | 63 ++++++++++++-------------- src/ft/dlt_ft_range.rs | 2 +- 6 files changed, 88 insertions(+), 90 deletions(-) diff --git a/src/error/ft_pool_error.rs b/src/error/ft_pool_error.rs index bd35321..2858912 100644 --- a/src/error/ft_pool_error.rs +++ b/src/error/ft_pool_error.rs @@ -7,14 +7,10 @@ pub enum FtPoolError { FtReassemble(FtReassembleError), /// Error if a data packet for an unknown stream is received. - DataForUnknownStream{ - file_serial_number: DltFtUInt - }, + DataForUnknownStream { file_serial_number: DltFtUInt }, /// Error if a end packet for an unknown stream is received. - EndForUnknownStream{ - file_serial_number: DltFtUInt - }, + EndForUnknownStream { file_serial_number: DltFtUInt }, } impl core::fmt::Display for FtPoolError { diff --git a/src/error/ft_reassemble_error.rs b/src/error/ft_reassemble_error.rs index c4d8acc..8773715 100644 --- a/src/error/ft_reassemble_error.rs +++ b/src/error/ft_reassemble_error.rs @@ -1,6 +1,5 @@ #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum FtReassembleError { - /// Error if the number of bytes of data in a "DLT file transfer" packet /// is not matching the original in the header specified buffer size. DataLenNotMatchingBufferSize { @@ -14,7 +13,7 @@ pub enum FtReassembleError { InconsitantHeaderLenValues { file_size: u64, number_of_packages: u64, - buffer_len: u64 + buffer_len: u64, }, /// Error if a data package with an unexpected package nr is received. @@ -45,7 +44,7 @@ impl std::error::Error for FtReassembleError {} mod tests { /*use super::FtReassembleError::*; - + #[test] fn debug() { let err = AllocationFailure { len: 0 }; @@ -94,4 +93,4 @@ mod tests { assert!(AllocationFailure { len: 0 }.source().is_none()); } */ -} \ No newline at end of file +} diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 3b533b6..e7c0346 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -1,9 +1,9 @@ #[cfg(feature = "std")] -use crate::{ft::*, error::FtReassembleError}; -#[cfg(feature = "std")] -use std::vec::Vec; +use crate::{error::FtReassembleError, ft::*}; #[cfg(feature = "std")] use std::string::String; +#[cfg(feature = "std")] +use std::vec::Vec; #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] @@ -40,7 +40,6 @@ pub struct DltFtBuffer { #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl DltFtBuffer { - /// File serial number (usually inode). #[inline] pub fn file_serial_number(&self) -> DltFtUInt { @@ -101,15 +100,17 @@ impl DltFtBuffer { /// Setup all the buffers based on a received header package. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] - #[cfg(any(target_pointer_width = "64"))] - pub fn reinit_from_header_pkg(&mut self, header: &DltFtHeaderPkg) -> Result<(), FtReassembleError> { - + #[cfg(target_pointer_width = "64")] + pub fn reinit_from_header_pkg( + &mut self, + header: &DltFtHeaderPkg, + ) -> Result<(), FtReassembleError> { // validate that the header is consistant use core::ops::RangeInclusive; let value_err = FtReassembleError::InconsitantHeaderLenValues { file_size: header.file_size.into(), number_of_packages: header.number_of_packages.into(), - buffer_len: header.buffer_size.into() + buffer_len: header.buffer_size.into(), }; if u64::from(header.number_of_packages) == 0 { return Err(value_err.clone()); @@ -123,15 +124,20 @@ impl DltFtBuffer { .checked_add(1) .ok_or_else(|| value_err.clone())?; - if !RangeInclusive::new(min_expected_size, max_expected_size).contains(&header.file_size.into()) { + if !RangeInclusive::new(min_expected_size, max_expected_size) + .contains(&header.file_size.into()) + { return Err(value_err); } // reset the buffer self.data.clear(); // TODO implement for 32 bit systems - self.data.try_reserve(header.file_size.into()) - .map_err(|_| FtReassembleError::AllocationFailure { len: header.file_size.into() })?; + self.data + .try_reserve(header.file_size.into()) + .map_err(|_| FtReassembleError::AllocationFailure { + len: header.file_size.into(), + })?; self.sections.clear(); // set values @@ -155,23 +161,19 @@ impl DltFtBuffer { /// Consume a DLT file transfer data package, the caller is responsible to ensure the /// [`DltFtDataPkg::file_serial_number`] of the data package match the /// [`Self::file_serial_number`] of the buffer. - #[cfg(any(target_pointer_width = "64"))] - pub fn consume_data_pkg( - &mut self, - data: &DltFtDataPkg, - ) -> Result<(), FtReassembleError> { - + #[cfg(target_pointer_width = "64")] + pub fn consume_data_pkg(&mut self, data: &DltFtDataPkg) -> Result<(), FtReassembleError> { // validate the package number let package_nr: u64 = data.package_nr.into(); if package_nr == 0 || package_nr > self.number_of_packets { - return Err(FtReassembleError::UnexpectedPackageNrInDataPkg{ + return Err(FtReassembleError::UnexpectedPackageNrInDataPkg { expected_nr_of_packages: self.number_of_packets, package_nr, }); } // determine insertion start - let insertion_start: usize = (package_nr as usize - 1)*(self.buffer_size as usize); + let insertion_start: usize = (package_nr as usize - 1) * (self.buffer_size as usize); // validate the data len of the package let expected_len = if package_nr < self.number_of_packets { @@ -206,12 +208,12 @@ impl DltFtBuffer { // add data self.data.extend_from_slice(data.data); } else { - // overwrite the existing data let overwrite_end = std::cmp::min(self.data.len(), insert_end); let overwrite_len = overwrite_end - insertion_start; - (&mut self.data.as_mut_slice()[insertion_start..overwrite_end]).clone_from_slice(&data.data[..overwrite_len]); - + self.data.as_mut_slice()[insertion_start..overwrite_end] + .clone_from_slice(&data.data[..overwrite_len]); + // in case some data still needs to appended do that as well if overwrite_end < insert_end { self.data.extend_from_slice(&data.data[overwrite_len..]); @@ -240,16 +242,19 @@ impl DltFtBuffer { /// Returns true if the data has been completed and the end received. pub fn is_complete(&self) -> bool { - self.end_received && 1 == self.sections.len() && 0 == self.sections[0].start && self.sections[0].end == self.file_size + self.end_received + && 1 == self.sections.len() + && 0 == self.sections[0].start + && self.sections[0].end == self.file_size } /// Try finalizing the reconstructed file data and return a reference to it /// if the stream reconstruction was completed. - pub fn try_finalize<'a>(&'a self) -> Option> { + pub fn try_finalize(&self) -> Option> { if false == self.is_complete() { - return None; + None } else { - Some(DltFtCompleteInMemFile{ + Some(DltFtCompleteInMemFile { file_serial_number: self.file_serial_number, file_name: &self.file_name, creation_date: &self.creation_date, @@ -261,12 +266,20 @@ impl DltFtBuffer { #[cfg(test)] mod test { -/* - use crate::*; + + use crate::ft::*; + use alloc::format; #[test] fn debug_clone_eq() { - let buf = DltFtBuffer::new(Default::default()); + let buf = DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(0), + file_name: "a.txt", + file_size: DltFtUInt::U32(0), + creation_date: "", + number_of_packages: DltFtUInt::U32(0), + buffer_size: DltFtUInt::U32(0), + }); let _ = format!("{:?}", buf); assert_eq!(buf, buf.clone()); assert_eq!(buf.cmp(&buf), core::cmp::Ordering::Equal); @@ -286,7 +299,7 @@ mod test { }; assert_eq!(h1, h2); } - + /* struct TestPacket { offset: u32, more_segments: bool, @@ -466,7 +479,7 @@ mod test { let packet = test_packet.to_vec(); let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - + assert_eq!(Ok(()), buffer.consume_tp(slice)); } @@ -477,7 +490,7 @@ mod test { let packet = test_packet.to_vec(); let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - + assert_eq!( UnalignedTpPayloadLen { offset: 48, payload_len: 32 + bad_offset }, buffer.consume_tp(slice).unwrap_err() diff --git a/src/ft/dlt_ft_pkg.rs b/src/ft/dlt_ft_pkg.rs index a818df7..ec12b3e 100644 --- a/src/ft/dlt_ft_pkg.rs +++ b/src/ft/dlt_ft_pkg.rs @@ -1,5 +1,5 @@ -use crate::verbose::*; use super::*; +use crate::verbose::*; /// DLT file transfer package. #[derive(Debug, Clone, Eq, PartialEq, Hash)] @@ -21,11 +21,9 @@ pub enum DltFtPkg<'a, 'b> { } impl<'a, 'b> DltFtPkg<'a, 'b> { - /// Checks if the verbose iterator contains a DLT file transfer package /// and returns the package if so. pub fn try_from(mut iter: VerboseIter<'a>) -> Option> { - match iter.number_of_arguments() { 3 => { Self::check_for_str(DltFtEndPkg::PKG_FLAG, &mut iter)?; @@ -43,24 +41,24 @@ impl<'a, 'b> DltFtPkg<'a, 'b> { let data = Self::try_take_raw_from_iter(&mut iter)?; Self::check_for_str(DltFtDataPkg::PKG_FLAG, &mut iter)?; - Some(DltFtPkg::Data(DltFtDataPkg{ + Some(DltFtPkg::Data(DltFtDataPkg { file_serial_number, package_nr, data, })) - }, + } DltFtFileNotExistErrorPkg::PKG_FLAG => { let error_code = DltFtInt::try_take_from_iter(&mut iter)?; let linux_error_code = DltFtInt::try_take_from_iter(&mut iter)?; let file_name = Self::try_take_str_from_iter(&mut iter)?; Self::check_for_str(DltFtFileNotExistErrorPkg::PKG_FLAG, &mut iter)?; - Some(DltFtPkg::FileNotExistsError(DltFtFileNotExistErrorPkg{ + Some(DltFtPkg::FileNotExistsError(DltFtFileNotExistErrorPkg { error_code: DltFtErrorCode(error_code), linux_error_code, file_name, })) - }, + } _ => None, } } @@ -73,7 +71,7 @@ impl<'a, 'b> DltFtPkg<'a, 'b> { let number_of_packages = DltFtUInt::try_take_from_iter(&mut iter)?; Self::check_for_str(DltFtInfoPkg::PKG_FLAG, &mut iter)?; - Some(DltFtPkg::Info(DltFtInfoPkg{ + Some(DltFtPkg::Info(DltFtInfoPkg { file_serial_number, file_name, file_size, @@ -91,7 +89,7 @@ impl<'a, 'b> DltFtPkg<'a, 'b> { let buffer_size = DltFtUInt::try_take_from_iter(&mut iter)?; Self::check_for_str(DltFtHeaderPkg::PKG_FLAG, &mut iter)?; - Some(DltFtPkg::Header(DltFtHeaderPkg{ + Some(DltFtPkg::Header(DltFtHeaderPkg { file_serial_number, file_name, file_size, @@ -111,7 +109,7 @@ impl<'a, 'b> DltFtPkg<'a, 'b> { let number_of_packages = DltFtUInt::try_take_from_iter(&mut iter)?; Self::check_for_str(DltFtErrorPkg::PKG_FLAG, &mut iter)?; - Some(DltFtPkg::Error(DltFtErrorPkg{ + Some(DltFtPkg::Error(DltFtErrorPkg { error_code: DltFtErrorCode(error_code), linux_error_code, file_serial_number, @@ -145,7 +143,7 @@ impl<'a, 'b> DltFtPkg<'a, 'b> { Some(result.data) } - fn check_for_str<'c>(expected: &str, iter: &mut VerboseIter<'_>) -> Option<()> { + fn check_for_str(expected: &str, iter: &mut VerboseIter<'_>) -> Option<()> { let Some(Ok(VerboseValue::Str(result))) = iter.next() else { return None; }; @@ -157,5 +155,4 @@ impl<'a, 'b> DltFtPkg<'a, 'b> { } Some(()) } - } diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index 2d59720..d40b8d9 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -5,7 +5,7 @@ use crate::{error::*, ft::*}; #[cfg_attr(docsrs, doc(cfg(feature = "std")))] use core::hash::Hash; #[cfg(feature = "std")] -use std::{vec::Vec, collections::HashMap}; +use std::{collections::HashMap, vec::Vec}; /// Pool of buffers to reconstruct multiple DLT file transfer packet streams in /// parallel (re-uses buffers to minimize allocations). @@ -17,7 +17,7 @@ use std::{vec::Vec, collections::HashMap}; /// never ending them and filling them up with as much data as possible. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone)] pub struct DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -70,18 +70,17 @@ where // TODO add error Ok(None) - }, + } Vacant(vac) => { vac.insert((DltFtBuffer::new(header_pkg)?, timestamp)); Ok(None) - }, + } } - }, + } DltFtPkg::Data(data_pkg) => { match self.active.entry((id, data_pkg.file_serial_number)) { Occupied(mut entry) => { - - // inject the new data & process + // inject the new data & process let (buffer, last_ts) = entry.get_mut(); *last_ts = timestamp; buffer.consume_data_pkg(data_pkg)?; @@ -91,23 +90,20 @@ where // take out the buffer let (buf, _) = entry.remove(); self.finished.push(buf); - Ok(Some( - self.finished.last().unwrap().try_finalize().unwrap() - )) + Ok(Some(self.finished.last().unwrap().try_finalize().unwrap())) } else { Ok(None) } - }, - Vacant(_) => { - return Err(FtPoolError::DataForUnknownStream { file_serial_number: data_pkg.file_serial_number }); - }, + } + Vacant(_) => Err(FtPoolError::DataForUnknownStream { + file_serial_number: data_pkg.file_serial_number, + }), } - }, + } DltFtPkg::End(end_pkg) => { match self.active.entry((id, end_pkg.file_serial_number)) { Occupied(mut entry) => { - - // inject the new data & process + // inject the new data & process let (buffer, last_ts) = entry.get_mut(); *last_ts = timestamp; buffer.set_end_received(); @@ -117,19 +113,17 @@ where // take out the buffer let (buf, _) = entry.remove(); self.finished.push(buf); - Ok(Some( - self.finished.last().unwrap().try_finalize().unwrap() - )) + Ok(Some(self.finished.last().unwrap().try_finalize().unwrap())) } else { Ok(None) } - }, - Vacant(_) => { - return Err(FtPoolError::EndForUnknownStream { file_serial_number: end_pkg.file_serial_number }); - }, + } + Vacant(_) => Err(FtPoolError::EndForUnknownStream { + file_serial_number: end_pkg.file_serial_number, + }), } - }, - + } + DltFtPkg::Error(err) => { match self.active.entry((id, err.file_serial_number)) { Occupied(entry) => { @@ -137,10 +131,10 @@ where let (buf, _) = entry.remove(); self.finished.push(buf); Ok(None) - }, + } Vacant(_) => Ok(None), } - }, + } DltFtPkg::FileNotExistsError(_) => Ok(None), DltFtPkg::Info(_) => Ok(None), } @@ -175,8 +169,7 @@ where Timestamp: core::fmt::Debug + Clone + Sized + PartialEq, { fn eq(&self, other: &Self) -> bool { - self.active == other.active - && self.finished == other.finished + self.active == other.active && self.finished == other.finished } } @@ -190,16 +183,16 @@ where #[cfg(test)] mod tests { use super::*; + use alloc::format; - /* #[test] fn debug_clone_eq() { - let pool: DltFtPool<(), ()> = DltFtPool::new(Default::default()); + let pool: DltFtPool<(), ()> = Default::default(); let _ = format!("{:?}", pool); assert_eq!(pool, pool.clone()); - assert_eq!(pool.buf_config(), &TpBufConfig::default()); } + /* #[test] fn with_capacity() { let pool = DltFtPool::<(), ()>::with_capacity(Default::default(), 3); @@ -299,7 +292,7 @@ mod tests { let mut result = Vec::with_capacity(SOMEIP_HEADER_LENGTH + 8); result.extend_from_slice(&header.base_to_bytes()); result.extend_from_slice(&[0;8]); - + let someip_slice = SomeipMsgSlice::from_slice(&result).unwrap(); let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); @@ -440,4 +433,4 @@ mod tests { assert_eq!(125, pool.active_bufs().get(&(3u16, 1u32)).unwrap().1); } */ -} \ No newline at end of file +} diff --git a/src/ft/dlt_ft_range.rs b/src/ft/dlt_ft_range.rs index e0c603d..e4e11e4 100644 --- a/src/ft/dlt_ft_range.rs +++ b/src/ft/dlt_ft_range.rs @@ -105,4 +105,4 @@ mod test { assert_eq!(b.merge(a), expected); } } -} \ No newline at end of file +} From 27ed6a2286c44985c70bd19ae6dafd90bb03eda9 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 24 Jun 2024 21:25:32 +0200 Subject: [PATCH 09/25] Resolved no-std errors & add doc tags for no-std --- src/dlt_header.rs | 2 ++ .../dlt_message_length_too_small_error.rs | 1 + src/error/ft_pool_error.rs | 4 +++- src/error/ft_reassemble_error.rs | 4 +++- src/error/mod.rs | 1 + src/error/packet_slice_error.rs | 1 + src/error/range_error.rs | 1 + src/error/read_error.rs | 7 ++++++ .../storage_header_start_pattern_error.rs | 1 + src/error/typed_payload_error.rs | 1 + src/error/unexpected_end_of_slice_error.rs | 1 + src/error/unsupported_dlt_version_error.rs | 1 + src/error/verbose_decode_error.rs | 1 + src/ft/dlt_ft_error_pkg.rs | 2 +- src/ft/dlt_ft_pool.rs | 22 +++++-------------- src/storage/dlt_storage_reader.rs | 2 ++ src/storage/dlt_storage_writer.rs | 2 ++ src/storage/storage_header.rs | 2 ++ 18 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/dlt_header.rs b/src/dlt_header.rs index 41c7cbd..88c1e89 100644 --- a/src/dlt_header.rs +++ b/src/dlt_header.rs @@ -307,6 +307,7 @@ impl DltHeader { ///Deserialize a DltHeader & TpHeader from the given reader. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn read(reader: &mut T) -> Result { use crate::error::UnsupportedDltVersionError; @@ -382,6 +383,7 @@ impl DltHeader { ///Serializes the header to the given writer. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { { let length_be = self.length.to_be_bytes(); diff --git a/src/error/dlt_message_length_too_small_error.rs b/src/error/dlt_message_length_too_small_error.rs index 4aaee75..1ccbdb4 100644 --- a/src/error/dlt_message_length_too_small_error.rs +++ b/src/error/dlt_message_length_too_small_error.rs @@ -18,6 +18,7 @@ impl core::fmt::Display for DltMessageLengthTooSmallError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for DltMessageLengthTooSmallError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None diff --git a/src/error/ft_pool_error.rs b/src/error/ft_pool_error.rs index 2858912..62d839b 100644 --- a/src/error/ft_pool_error.rs +++ b/src/error/ft_pool_error.rs @@ -14,7 +14,7 @@ pub enum FtPoolError { } impl core::fmt::Display for FtPoolError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use FtPoolError::*; match self { FtReassemble(err) => err.fmt(f), @@ -24,6 +24,8 @@ impl core::fmt::Display for FtPoolError { } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for FtPoolError {} impl From for FtPoolError { diff --git a/src/error/ft_reassemble_error.rs b/src/error/ft_reassemble_error.rs index 8773715..d4be7a2 100644 --- a/src/error/ft_reassemble_error.rs +++ b/src/error/ft_reassemble_error.rs @@ -27,7 +27,7 @@ pub enum FtReassembleError { } impl core::fmt::Display for FtReassembleError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use FtReassembleError::*; match self { DataLenNotMatchingBufferSize{ header_buffer_len, data_pkt_len, data_pkt_nr, number_of_packages } => write!(f, "Payload length {data_pkt_len} of DLT file transfer data packet (nr {data_pkt_nr} of {number_of_packages}) is not matching the buffer len {header_buffer_len} set by the header packet."), @@ -38,6 +38,8 @@ impl core::fmt::Display for FtReassembleError { } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for FtReassembleError {} #[cfg(test)] diff --git a/src/error/mod.rs b/src/error/mod.rs index 6c64ed6..4ed277b 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -17,6 +17,7 @@ mod range_error; pub use range_error::*; mod read_error; + pub use read_error::*; mod storage_header_start_pattern_error; diff --git a/src/error/packet_slice_error.rs b/src/error/packet_slice_error.rs index 7ed14d1..092d2ce 100644 --- a/src/error/packet_slice_error.rs +++ b/src/error/packet_slice_error.rs @@ -27,6 +27,7 @@ impl core::fmt::Display for PacketSliceError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for PacketSliceError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use PacketSliceError::*; diff --git a/src/error/range_error.rs b/src/error/range_error.rs index b39ee80..1fe72cc 100644 --- a/src/error/range_error.rs +++ b/src/error/range_error.rs @@ -6,6 +6,7 @@ pub enum RangeError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for RangeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None diff --git a/src/error/read_error.rs b/src/error/read_error.rs index 810fe0d..e649c90 100644 --- a/src/error/read_error.rs +++ b/src/error/read_error.rs @@ -1,7 +1,9 @@ +#[cfg(feature = "std")] use super::*; ///Errors that can occure on reading a dlt header. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[derive(Debug)] pub enum ReadError { /// Error if the slice is smaller then dlt length field or minimal size. @@ -22,6 +24,7 @@ pub enum ReadError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for ReadError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use ReadError::*; @@ -36,6 +39,7 @@ impl std::error::Error for ReadError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl core::fmt::Display for ReadError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { use ReadError::*; @@ -53,6 +57,7 @@ impl core::fmt::Display for ReadError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From for ReadError { fn from(err: StorageHeaderStartPatternError) -> ReadError { ReadError::StorageHeaderStartPattern(err) @@ -60,6 +65,7 @@ impl From for ReadError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From for ReadError { fn from(err: PacketSliceError) -> ReadError { use PacketSliceError as I; @@ -72,6 +78,7 @@ impl From for ReadError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl From for ReadError { fn from(err: std::io::Error) -> ReadError { ReadError::IoError(err) diff --git a/src/error/storage_header_start_pattern_error.rs b/src/error/storage_header_start_pattern_error.rs index 11ef040..fe76e3c 100644 --- a/src/error/storage_header_start_pattern_error.rs +++ b/src/error/storage_header_start_pattern_error.rs @@ -19,6 +19,7 @@ impl core::fmt::Display for StorageHeaderStartPatternError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for StorageHeaderStartPatternError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None diff --git a/src/error/typed_payload_error.rs b/src/error/typed_payload_error.rs index 7c79335..54989b5 100644 --- a/src/error/typed_payload_error.rs +++ b/src/error/typed_payload_error.rs @@ -15,6 +15,7 @@ pub enum TypedPayloadError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for TypedPayloadError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None diff --git a/src/error/unexpected_end_of_slice_error.rs b/src/error/unexpected_end_of_slice_error.rs index c89eb05..ae90026 100644 --- a/src/error/unexpected_end_of_slice_error.rs +++ b/src/error/unexpected_end_of_slice_error.rs @@ -26,6 +26,7 @@ impl core::fmt::Display for UnexpectedEndOfSliceError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for UnexpectedEndOfSliceError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None diff --git a/src/error/unsupported_dlt_version_error.rs b/src/error/unsupported_dlt_version_error.rs index 8b598ef..8acca57 100644 --- a/src/error/unsupported_dlt_version_error.rs +++ b/src/error/unsupported_dlt_version_error.rs @@ -20,6 +20,7 @@ impl core::fmt::Display for UnsupportedDltVersionError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for UnsupportedDltVersionError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None diff --git a/src/error/verbose_decode_error.rs b/src/error/verbose_decode_error.rs index 361f05c..46bdc1b 100644 --- a/src/error/verbose_decode_error.rs +++ b/src/error/verbose_decode_error.rs @@ -59,6 +59,7 @@ impl core::fmt::Display for VerboseDecodeError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for VerboseDecodeError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use VerboseDecodeError::*; diff --git a/src/ft/dlt_ft_error_pkg.rs b/src/ft/dlt_ft_error_pkg.rs index dd40ff7..5cf4d3d 100644 --- a/src/ft/dlt_ft_error_pkg.rs +++ b/src/ft/dlt_ft_error_pkg.rs @@ -4,7 +4,7 @@ use super::*; /// existing file. /// /// If a files does not exist -/// [`crate::ft::DltFileNotExistErrorPkg`] is sent instead. +/// [`crate::ft::DltFtFileNotExistErrorPkg`] is sent instead. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct DltFtErrorPkg<'a, 'b> { /// Error code. diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index d40b8d9..0578bb5 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -30,6 +30,8 @@ where finished: Vec, } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -163,6 +165,8 @@ where } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl PartialEq for DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -173,6 +177,8 @@ where } } +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Eq for DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -193,23 +199,7 @@ mod tests { } /* - #[test] - fn with_capacity() { - let pool = DltFtPool::<(), ()>::with_capacity(Default::default(), 3); - assert_eq!(3, pool.finished_bufs().len()); - assert!(pool.active.capacity() >= 3); - } - #[test] - fn reserve() { - let mut pool = DltFtPool::<(), ()>::new(Default::default()); - pool.reserve(2); - assert_eq!(2, pool.finished_bufs().len()); - assert!(pool.active.capacity() >= 2); - pool.reserve(3); - assert_eq!(5, pool.finished_bufs().len()); - assert!(pool.active.capacity() >= 5); - } struct TestPacket { request_id: u32, diff --git a/src/storage/dlt_storage_reader.rs b/src/storage/dlt_storage_reader.rs index ee3a7a5..37ded19 100644 --- a/src/storage/dlt_storage_reader.rs +++ b/src/storage/dlt_storage_reader.rs @@ -30,6 +30,7 @@ use super::StorageSlice; /// } /// ``` #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[derive(Debug)] pub struct DltStorageReader { reader: R, @@ -42,6 +43,7 @@ pub struct DltStorageReader { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl DltStorageReader { /// Creates a new reader. pub fn new(reader: R) -> DltStorageReader { diff --git a/src/storage/dlt_storage_writer.rs b/src/storage/dlt_storage_writer.rs index 6f93812..b765711 100644 --- a/src/storage/dlt_storage_writer.rs +++ b/src/storage/dlt_storage_writer.rs @@ -49,12 +49,14 @@ use crate::{storage::StorageHeader, DltPacketSlice}; /// ).expect("failed to write dlt packet"); /// ``` #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[derive(Debug)] pub struct DltStorageWriter { writer: W, } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl DltStorageWriter { /// Creates a new writer that allows writing dlt packets to a storage file. pub fn new(writer: W) -> DltStorageWriter { diff --git a/src/storage/storage_header.rs b/src/storage/storage_header.rs index d337364..760a1ac 100644 --- a/src/storage/storage_header.rs +++ b/src/storage/storage_header.rs @@ -63,6 +63,7 @@ impl StorageHeader { ///Deserialize a DltHeader & TpHeader from the given reader. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn read(reader: &mut T) -> Result { let mut bytes: [u8; 16] = [0; 16]; reader.read_exact(&mut bytes)?; @@ -71,6 +72,7 @@ impl StorageHeader { ///Serializes the header to the given writer. #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { writer.write_all(&self.to_bytes())?; Ok(()) From fff5e849763afeb06823a89e0f685676cd17f0ab Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 24 Jun 2024 21:37:43 +0200 Subject: [PATCH 10/25] Resolved no std test errors --- src/error/mod.rs | 2 +- src/ft/dlt_ft_buffer.rs | 8 +------- src/ft/dlt_ft_pool.rs | 2 +- src/ft/mod.rs | 2 ++ 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/error/mod.rs b/src/error/mod.rs index 4ed277b..e934461 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -17,7 +17,7 @@ mod range_error; pub use range_error::*; mod read_error; - +#[cfg(feature = "std")] pub use read_error::*; mod storage_header_start_pattern_error; diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index e7c0346..928b6a0 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -64,8 +64,6 @@ impl DltFtBuffer { self.end_received } - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn new(header: &DltFtHeaderPkg) -> Result { let mut result = DltFtBuffer { data: Vec::new(), @@ -83,8 +81,6 @@ impl DltFtBuffer { } /// Reset buffer to starting state. - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn clear(&mut self) { self.data.clear(); self.sections.clear(); @@ -98,8 +94,6 @@ impl DltFtBuffer { } /// Setup all the buffers based on a received header package. - #[cfg(feature = "std")] - #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[cfg(target_pointer_width = "64")] pub fn reinit_from_header_pkg( &mut self, @@ -264,7 +258,7 @@ impl DltFtBuffer { } } -#[cfg(test)] +#[cfg(all(feature = "std",test))] mod test { use crate::ft::*; diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index 0578bb5..7cbbb5d 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -186,7 +186,7 @@ where { } -#[cfg(test)] +#[cfg(all(feature = "std", test))] mod tests { use super::*; use alloc::format; diff --git a/src/ft/mod.rs b/src/ft/mod.rs index 4677073..64e5e11 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -1,4 +1,5 @@ mod dlt_ft_buffer; +#[cfg(feature = "std")] pub use dlt_ft_buffer::*; mod dlt_ft_complete_in_mem_file; @@ -26,6 +27,7 @@ mod dlt_ft_info_pkg; pub use dlt_ft_info_pkg::*; mod dlt_ft_pool; +#[cfg(feature = "std")] pub use dlt_ft_pool::*; mod dlt_ft_int; From 3353c87d9ff977433ac146579ccda1c5cfc61afc Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 24 Jun 2024 21:49:48 +0200 Subject: [PATCH 11/25] Resolved 32 bit building errors --- src/ft/dlt_ft_buffer.rs | 4 ++++ src/ft/dlt_ft_int.rs | 2 ++ src/ft/dlt_ft_pool.rs | 2 -- src/ft/dlt_ft_uint.rs | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 928b6a0..8c4c189 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -64,6 +64,8 @@ impl DltFtBuffer { self.end_received } + #[cfg(target_pointer_width = "64")] + #[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] pub fn new(header: &DltFtHeaderPkg) -> Result { let mut result = DltFtBuffer { data: Vec::new(), @@ -95,6 +97,7 @@ impl DltFtBuffer { /// Setup all the buffers based on a received header package. #[cfg(target_pointer_width = "64")] + #[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] pub fn reinit_from_header_pkg( &mut self, header: &DltFtHeaderPkg, @@ -156,6 +159,7 @@ impl DltFtBuffer { /// [`DltFtDataPkg::file_serial_number`] of the data package match the /// [`Self::file_serial_number`] of the buffer. #[cfg(target_pointer_width = "64")] + #[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] pub fn consume_data_pkg(&mut self, data: &DltFtDataPkg) -> Result<(), FtReassembleError> { // validate the package number let package_nr: u64 = data.package_nr.into(); diff --git a/src/ft/dlt_ft_int.rs b/src/ft/dlt_ft_int.rs index f34ac88..797a955 100644 --- a/src/ft/dlt_ft_int.rs +++ b/src/ft/dlt_ft_int.rs @@ -43,6 +43,7 @@ impl From for DltFtInt { } #[cfg(target_pointer_width = "64")] +#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] impl From for DltFtInt { fn from(value: isize) -> Self { DltFtInt::I64(value as i64) @@ -50,6 +51,7 @@ impl From for DltFtInt { } #[cfg(target_pointer_width = "64")] +#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] impl From for isize { fn from(value: DltFtInt) -> Self { match value { diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index 7cbbb5d..9cea73f 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -1,8 +1,6 @@ #[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] use crate::{error::*, ft::*}; #[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] use core::hash::Hash; #[cfg(feature = "std")] use std::{collections::HashMap, vec::Vec}; diff --git a/src/ft/dlt_ft_uint.rs b/src/ft/dlt_ft_uint.rs index 491ce87..a6e200b 100644 --- a/src/ft/dlt_ft_uint.rs +++ b/src/ft/dlt_ft_uint.rs @@ -45,6 +45,7 @@ impl From for DltFtUInt { } #[cfg(target_pointer_width = "64")] +#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] impl From for DltFtUInt { fn from(value: usize) -> Self { DltFtUInt::U64(value as u64) @@ -52,6 +53,7 @@ impl From for DltFtUInt { } #[cfg(target_pointer_width = "64")] +#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] impl From for usize { fn from(value: DltFtUInt) -> Self { match value { From 8ba64c8da6a6048ae932d9a90bf9620bda41e6f5 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 24 Jun 2024 21:55:20 +0200 Subject: [PATCH 12/25] Restricted DltFtBuffer to 64bit systems --- src/ft/dlt_ft_buffer.rs | 2 +- src/ft/dlt_ft_pool.rs | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 8c4c189..e5ec46a 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -262,7 +262,7 @@ impl DltFtBuffer { } } -#[cfg(all(feature = "std",test))] +#[cfg(all(feature = "std", test))] mod test { use crate::ft::*; diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index 9cea73f..c36970f 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -13,8 +13,8 @@ use std::{collections::HashMap, vec::Vec}; /// If you use the [`DltFtPool`] in an untrusted environment an attacker could /// cause an "out of memory error" by opening up multiple parallel file transfer streams, /// never ending them and filling them up with as much data as possible. -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] #[derive(Debug, Default, Clone)] pub struct DltFtPool where @@ -28,8 +28,8 @@ where finished: Vec, } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] impl DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -163,8 +163,8 @@ where } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] impl PartialEq for DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -175,8 +175,8 @@ where } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] impl Eq for DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -184,7 +184,7 @@ where { } -#[cfg(all(feature = "std", test))] +#[cfg(all(feature = "std", target_pointer_width = "64", test))] mod tests { use super::*; use alloc::format; From 6fbe755c3a53a0d6a11afb1a82d989d09794944a Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 06:28:59 +0200 Subject: [PATCH 13/25] Resolved warnings on 32bit systems --- src/ft/dlt_ft_buffer.rs | 2 +- src/ft/dlt_ft_pool.rs | 6 +++--- src/ft/mod.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index e5ec46a..8964a09 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -1,4 +1,4 @@ -#[cfg(feature = "std")] +#[cfg(all(feature = "std", target_pointer_width = "64"))] use crate::{error::FtReassembleError, ft::*}; #[cfg(feature = "std")] use std::string::String; diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index c36970f..986b26e 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -1,8 +1,8 @@ -#[cfg(feature = "std")] +#[cfg(all(feature = "std", target_pointer_width = "64"))] use crate::{error::*, ft::*}; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", target_pointer_width = "64"))] use core::hash::Hash; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", target_pointer_width = "64"))] use std::{collections::HashMap, vec::Vec}; /// Pool of buffers to reconstruct multiple DLT file transfer packet streams in diff --git a/src/ft/mod.rs b/src/ft/mod.rs index 64e5e11..3ea7bd0 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -27,7 +27,7 @@ mod dlt_ft_info_pkg; pub use dlt_ft_info_pkg::*; mod dlt_ft_pool; -#[cfg(feature = "std")] +#[cfg(all(feature = "std", target_pointer_width = "64"))] pub use dlt_ft_pool::*; mod dlt_ft_int; From 48aeaec85ef77d0037f208703b8f98c8702c6a1e Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 06:32:29 +0200 Subject: [PATCH 14/25] Resolved errors on 32bit systems --- src/ft/dlt_ft_buffer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 8964a09..ce81bfc 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -1,5 +1,7 @@ +#[cfg(feature = "std")] +use crate::ft::*; #[cfg(all(feature = "std", target_pointer_width = "64"))] -use crate::{error::FtReassembleError, ft::*}; +use crate::error::FtReassembleError; #[cfg(feature = "std")] use std::string::String; #[cfg(feature = "std")] From 82a46f03dc1eed96e88aa362adfce60fbfa29b97 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 07:01:05 +0200 Subject: [PATCH 15/25] Added support for 32bit systems to file transfer --- src/error/ft_reassemble_error.rs | 12 +++++++++-- src/ft/dlt_ft_buffer.rs | 37 ++++++++++++++++++-------------- src/ft/dlt_ft_pool.rs | 24 ++++++++++----------- src/ft/mod.rs | 2 +- 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/src/error/ft_reassemble_error.rs b/src/error/ft_reassemble_error.rs index d4be7a2..c41f4ff 100644 --- a/src/error/ft_reassemble_error.rs +++ b/src/error/ft_reassemble_error.rs @@ -13,7 +13,7 @@ pub enum FtReassembleError { InconsitantHeaderLenValues { file_size: u64, number_of_packages: u64, - buffer_len: u64, + buffer_size: u64, }, /// Error if a data package with an unexpected package nr is received. @@ -22,6 +22,13 @@ pub enum FtReassembleError { package_nr: u64, }, + /// File transfer is too big to be stored in memory (if the file size + /// exceeds the platform pointer width). + FileSizeTooBig { + file_size: u64, + max_allowed: u64, + }, + /// Error if not enough memory could be allocated to store the file in memory. AllocationFailure { len: usize }, } @@ -31,8 +38,9 @@ impl core::fmt::Display for FtReassembleError { use FtReassembleError::*; match self { DataLenNotMatchingBufferSize{ header_buffer_len, data_pkt_len, data_pkt_nr, number_of_packages } => write!(f, "Payload length {data_pkt_len} of DLT file transfer data packet (nr {data_pkt_nr} of {number_of_packages}) is not matching the buffer len {header_buffer_len} set by the header packet."), - InconsitantHeaderLenValues{ file_size, number_of_packages, buffer_len } => write!(f, "DLT file transfer header packet 'file size' {file_size} is inconsistant with the 'buffer size' {buffer_len} and 'number of packages' {number_of_packages}"), + InconsitantHeaderLenValues{ file_size, number_of_packages, buffer_size: buffer_len } => write!(f, "DLT file transfer header packet 'file size' {file_size} is inconsistant with the 'buffer size' {buffer_len} and 'number of packages' {number_of_packages}"), UnexpectedPackageNrInDataPkg { expected_nr_of_packages, package_nr } => write!(f, "Received a DLT file transfer data packet with the unexpected package number {package_nr} (expected number of packages based on header package is {expected_nr_of_packages})."), + FileSizeTooBig { file_size, max_allowed } => write!(f, "DLT filetransfer file size {file_size} exceeds the supported maximum of {max_allowed}."), AllocationFailure { len } => write!(f, "Failed to allocate {len} bytes of memory to reconstruct the SOMEIP TP packets."), } } diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index ce81bfc..d8ed2e0 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -1,7 +1,5 @@ #[cfg(feature = "std")] -use crate::ft::*; -#[cfg(all(feature = "std", target_pointer_width = "64"))] -use crate::error::FtReassembleError; +use crate::{ft::*, error::FtReassembleError}; #[cfg(feature = "std")] use std::string::String; #[cfg(feature = "std")] @@ -66,8 +64,6 @@ impl DltFtBuffer { self.end_received } - #[cfg(target_pointer_width = "64")] - #[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] pub fn new(header: &DltFtHeaderPkg) -> Result { let mut result = DltFtBuffer { data: Vec::new(), @@ -98,30 +94,42 @@ impl DltFtBuffer { } /// Setup all the buffers based on a received header package. - #[cfg(target_pointer_width = "64")] - #[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] pub fn reinit_from_header_pkg( &mut self, header: &DltFtHeaderPkg, ) -> Result<(), FtReassembleError> { + // validate that the file is not too big + let file_size: u64 = header.file_size.into(); + { + let max_allowed: u64 = usize::MAX as u64; + if file_size > max_allowed { + return Err(FtReassembleError::FileSizeTooBig { file_size, max_allowed }); + } + } // validate that the header is consistant + let number_of_packages: u64 = header.number_of_packages.into(); + let buffer_size: u64 = header.buffer_size.into(); use core::ops::RangeInclusive; let value_err = FtReassembleError::InconsitantHeaderLenValues { file_size: header.file_size.into(), - number_of_packages: header.number_of_packages.into(), - buffer_len: header.buffer_size.into(), + number_of_packages, + buffer_size, }; - if u64::from(header.number_of_packages) == 0 { + if (number_of_packages == 0 || buffer_size == 0) && file_size != 0 { return Err(value_err.clone()); } let max_expected_size = u64::from(header.buffer_size) .checked_mul(header.number_of_packages.into()) .ok_or_else(|| value_err.clone())?; - let min_expected_size = max_expected_size + let min_expected_size = if number_of_packages > 0 { + max_expected_size .checked_sub(header.buffer_size.into()) .ok_or_else(|| value_err.clone())? .checked_add(1) - .ok_or_else(|| value_err.clone())?; + .ok_or_else(|| value_err.clone())? + } else { + 0 + }; if !RangeInclusive::new(min_expected_size, max_expected_size) .contains(&header.file_size.into()) @@ -131,9 +139,8 @@ impl DltFtBuffer { // reset the buffer self.data.clear(); - // TODO implement for 32 bit systems self.data - .try_reserve(header.file_size.into()) + .try_reserve(u64::from(header.file_size) as usize) .map_err(|_| FtReassembleError::AllocationFailure { len: header.file_size.into(), })?; @@ -160,8 +167,6 @@ impl DltFtBuffer { /// Consume a DLT file transfer data package, the caller is responsible to ensure the /// [`DltFtDataPkg::file_serial_number`] of the data package match the /// [`Self::file_serial_number`] of the buffer. - #[cfg(target_pointer_width = "64")] - #[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] pub fn consume_data_pkg(&mut self, data: &DltFtDataPkg) -> Result<(), FtReassembleError> { // validate the package number let package_nr: u64 = data.package_nr.into(); diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index 986b26e..9cea73f 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -1,8 +1,8 @@ -#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg(feature = "std")] use crate::{error::*, ft::*}; -#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg(feature = "std")] use core::hash::Hash; -#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg(feature = "std")] use std::{collections::HashMap, vec::Vec}; /// Pool of buffers to reconstruct multiple DLT file transfer packet streams in @@ -13,8 +13,8 @@ use std::{collections::HashMap, vec::Vec}; /// If you use the [`DltFtPool`] in an untrusted environment an attacker could /// cause an "out of memory error" by opening up multiple parallel file transfer streams, /// never ending them and filling them up with as much data as possible. -#[cfg(all(feature = "std", target_pointer_width = "64"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[derive(Debug, Default, Clone)] pub struct DltFtPool where @@ -28,8 +28,8 @@ where finished: Vec, } -#[cfg(all(feature = "std", target_pointer_width = "64"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -163,8 +163,8 @@ where } } -#[cfg(all(feature = "std", target_pointer_width = "64"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl PartialEq for DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -175,8 +175,8 @@ where } } -#[cfg(all(feature = "std", target_pointer_width = "64"))] -#[cfg_attr(docsrs, doc(cfg(all(feature = "std", target_pointer_width = "64"))))] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Eq for DltFtPool where ChannelId: Hash + Eq + PartialEq + Clone + Sized, @@ -184,7 +184,7 @@ where { } -#[cfg(all(feature = "std", target_pointer_width = "64", test))] +#[cfg(all(feature = "std", test))] mod tests { use super::*; use alloc::format; diff --git a/src/ft/mod.rs b/src/ft/mod.rs index 3ea7bd0..64e5e11 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -27,7 +27,7 @@ mod dlt_ft_info_pkg; pub use dlt_ft_info_pkg::*; mod dlt_ft_pool; -#[cfg(all(feature = "std", target_pointer_width = "64"))] +#[cfg(feature = "std")] pub use dlt_ft_pool::*; mod dlt_ft_int; From ad743e62f04a53a2263acb3123d836ce87bbf3e5 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 07:03:57 +0200 Subject: [PATCH 16/25] Resolved 32bit system compilation issue --- src/ft/dlt_ft_buffer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index d8ed2e0..afec023 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -140,9 +140,9 @@ impl DltFtBuffer { // reset the buffer self.data.clear(); self.data - .try_reserve(u64::from(header.file_size) as usize) + .try_reserve(file_size as usize) .map_err(|_| FtReassembleError::AllocationFailure { - len: header.file_size.into(), + len: file_size as usize, })?; self.sections.clear(); From 583fa99c064c8a70355166a18466e4600c10c3a9 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 08:14:38 +0200 Subject: [PATCH 17/25] Tests for dlt ft buffer new & bugfix --- src/ft/dlt_ft_buffer.rs | 109 +++++++++++++++++++++++++++++++++++++++- src/ft/dlt_ft_int.rs | 26 ---------- src/ft/dlt_ft_uint.rs | 27 ---------- 3 files changed, 107 insertions(+), 55 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index afec023..82c5ed9 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -150,6 +150,7 @@ impl DltFtBuffer { self.file_size = header.file_size.into(); self.number_of_packets = header.number_of_packages.into(); self.buffer_size = header.buffer_size.into(); + self.file_serial_number = header.file_serial_number; self.file_name.clear(); self.file_name.push_str(header.file_name); self.creation_date.clear(); @@ -272,8 +273,8 @@ impl DltFtBuffer { #[cfg(all(feature = "std", test))] mod test { - use crate::ft::*; - use alloc::format; + use crate::{ft::*, error::FtReassembleError}; + use alloc::{borrow::ToOwned, vec::Vec, format}; #[test] fn debug_clone_eq() { @@ -304,6 +305,110 @@ mod test { }; assert_eq!(h1, h2); } + + #[test] + fn new() { + // ok case + { + let buf = DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(1234), + file_name: "a.txt", + file_size: DltFtUInt::U32(20), + creation_date: "2024-06-25", + number_of_packages: DltFtUInt::U32(2), + buffer_size: DltFtUInt::U32(10), + }); + assert_eq!( + DltFtBuffer { + data: Vec::new(), + sections: Vec::new(), + file_size: 20, + number_of_packets: 2, + buffer_size: 10, + file_serial_number: DltFtUInt::U32(1234), + file_name: "a.txt".to_owned(), + creation_date: "2024-06-25".to_owned(), + end_received: false, + }, + buf.unwrap() + ); + } + // error case + { + let buf = DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(1234), + file_name: "a.txt", + file_size: DltFtUInt::U32(20), + creation_date: "2024-06-25", + // bad number of packages + number_of_packages: DltFtUInt::U32(0), + buffer_size: DltFtUInt::U32(10), + }); + assert_eq!( + FtReassembleError::InconsitantHeaderLenValues { + file_size: 20, + number_of_packages: 0, + buffer_size: 10, + }, + buf.unwrap_err() + ); + } + } + + #[cfg(target_pointer_width = "32")] + #[test] + fn reinit_from_header_pkg() { + + } + + #[test] + fn reinit_from_header_pkg() { + let base = { + let mut base = DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(0), + file_name: "base.txt", + file_size: DltFtUInt::U32(4), + creation_date: "0-0-0", + number_of_packages: DltFtUInt::U32(1), + buffer_size: DltFtUInt::U32(4), + }).unwrap(); + base.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U32(1), + data: &[1,2,3,4], + }).unwrap(); + base.set_end_received(); + base + }; + + // ok init + { + let mut buf = base.clone(); + buf.reinit_from_header_pkg(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt", + file_size: DltFtUInt::U32(10), + creation_date: "2024-06-25", + number_of_packages: DltFtUInt::U32(2), + buffer_size: DltFtUInt::U32(5), + }).unwrap(); + assert_eq!( + DltFtBuffer { + data: Vec::new(), + sections: Vec::new(), + file_size: 10, + number_of_packets: 2, + buffer_size: 5, + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt".to_owned(), + creation_date: "2024-06-25".to_owned(), + end_received: false, + }, + buf + ); + } + } + /* struct TestPacket { offset: u32, diff --git a/src/ft/dlt_ft_int.rs b/src/ft/dlt_ft_int.rs index 797a955..ca6f8ef 100644 --- a/src/ft/dlt_ft_int.rs +++ b/src/ft/dlt_ft_int.rs @@ -35,32 +35,6 @@ impl From for DltFtInt { } } -#[cfg(target_pointer_width = "32")] -impl From for DltFtInt { - fn from(value: isize) -> Self { - DltFtInt::I32(value as i32) - } -} - -#[cfg(target_pointer_width = "64")] -#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] -impl From for DltFtInt { - fn from(value: isize) -> Self { - DltFtInt::I64(value as i64) - } -} - -#[cfg(target_pointer_width = "64")] -#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] -impl From for isize { - fn from(value: DltFtInt) -> Self { - match value { - DltFtInt::I32(v) => v as isize, - DltFtInt::I64(v) => v as isize, - } - } -} - impl From for i64 { fn from(value: DltFtInt) -> Self { match value { diff --git a/src/ft/dlt_ft_uint.rs b/src/ft/dlt_ft_uint.rs index a6e200b..eef06b7 100644 --- a/src/ft/dlt_ft_uint.rs +++ b/src/ft/dlt_ft_uint.rs @@ -37,33 +37,6 @@ impl From for DltFtUInt { } } -#[cfg(target_pointer_width = "32")] -impl From for DltFtUInt { - fn from(value: usize) -> Self { - DltFtUInt::U32(value as u32) - } -} - -#[cfg(target_pointer_width = "64")] -#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] -impl From for DltFtUInt { - fn from(value: usize) -> Self { - DltFtUInt::U64(value as u64) - } -} - -#[cfg(target_pointer_width = "64")] -#[cfg_attr(docsrs, doc(cfg(target_pointer_width = "64")))] -impl From for usize { - fn from(value: DltFtUInt) -> Self { - match value { - DltFtUInt::U16(v) => usize::from(v), - DltFtUInt::U32(v) => v as usize, - DltFtUInt::U64(v) => v as usize, - } - } -} - impl From for u64 { fn from(value: DltFtUInt) -> Self { match value { From 49af07a44c23d6b889a86969d721e25f20eb81f2 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 09:42:32 +0200 Subject: [PATCH 18/25] Further tests & reworks --- src/ft/dlt_ft_buffer.rs | 105 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 82c5ed9..021072d 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -122,11 +122,7 @@ impl DltFtBuffer { .checked_mul(header.number_of_packages.into()) .ok_or_else(|| value_err.clone())?; let min_expected_size = if number_of_packages > 0 { - max_expected_size - .checked_sub(header.buffer_size.into()) - .ok_or_else(|| value_err.clone())? - .checked_add(1) - .ok_or_else(|| value_err.clone())? + (max_expected_size - buffer_size) + 1 } else { 0 }; @@ -357,8 +353,33 @@ mod test { #[cfg(target_pointer_width = "32")] #[test] - fn reinit_from_header_pkg() { - + fn reinit_from_header_pkg_size_err_32() { + // setup base + let mut base = DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(0), + file_name: "base.txt", + file_size: DltFtUInt::U32(4), + creation_date: "0-0-0", + number_of_packages: DltFtUInt::U32(1), + buffer_size: DltFtUInt::U32(4), + }).unwrap(); + + // check for error if the file is bigger then representable + // in 32 bits + assert_eq!( + FtReassembleError::FileSizeTooBig { + file_size: usize::MAX as u64 + 1, + max_allowed: usize::MAX as u64, + }, + buf.reinit_from_header_pkg(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt", + file_size: DltFtUInt::U64(usize::MAX as u64 + 1), + creation_date: "2024-06-25", + number_of_packages: DltFtUInt::U32(2), + buffer_size: DltFtUInt::U32(5), + }).unwrap_err() + ); } #[test] @@ -407,6 +428,76 @@ mod test { buf ); } + + // empty file + { + let mut buf = base.clone(); + buf.reinit_from_header_pkg(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt", + file_size: DltFtUInt::U32(0), + creation_date: "2024-06-25", + number_of_packages: DltFtUInt::U32(0), + buffer_size: DltFtUInt::U32(0), + }).unwrap(); + assert_eq!( + DltFtBuffer { + data: Vec::new(), + sections: Vec::new(), + file_size: 0, + number_of_packets: 0, + buffer_size: 0, + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt".to_owned(), + creation_date: "2024-06-25".to_owned(), + end_received: false, + }, + buf + ); + } + + // tests for consistency error checks + { + use FtReassembleError::*; + let tests = [ + // file_size, number_of_packages, buffer_size + // basic checks for zero values + (1, 0, 0), + (1, 1, 0), + (1, 0, 1), + // out of range errors + (5, 1, 4), + (3, 2, 4), + (9, 2, 4), + // overflow errors + (9, u64::MAX, 2), + (9, 2, u64::MAX), + ]; + for (file_size, number_of_packages, buffer_size) in tests { + let mut buf = base.clone(); + let err = buf.reinit_from_header_pkg(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt", + file_size: DltFtUInt::U64(file_size), + creation_date: "2024-06-25", + number_of_packages: DltFtUInt::U64(number_of_packages), + buffer_size: DltFtUInt::U64(buffer_size), + }).unwrap_err(); + assert_eq!( + err, + InconsitantHeaderLenValues { + file_size, + number_of_packages, + buffer_size, + } + ); + } + } + + // test allocation error + { + // TODO + } } /* From 4cb8949ed5bc6078ecfd67a6ae4706022651af33 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 09:44:14 +0200 Subject: [PATCH 19/25] Add test for allocation failure --- src/ft/dlt_ft_buffer.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 021072d..1459a89 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -497,6 +497,21 @@ mod test { // test allocation error { // TODO + let mut buf = base.clone(); + let err = buf.reinit_from_header_pkg(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt", + file_size: DltFtUInt::U64(usize::MAX as u64), + creation_date: "2024-06-25", + number_of_packages: DltFtUInt::U64(1), + buffer_size: DltFtUInt::U64(usize::MAX as u64), + }).unwrap_err(); + assert_eq!( + err, + FtReassembleError::AllocationFailure { + len: usize::MAX + } + ); } } From a02c26950ecb52085e8a7789d12c232e52c8d24f Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Tue, 25 Jun 2024 09:45:09 +0200 Subject: [PATCH 20/25] Correct 32bit test --- src/ft/dlt_ft_buffer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 1459a89..cecc154 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -355,7 +355,7 @@ mod test { #[test] fn reinit_from_header_pkg_size_err_32() { // setup base - let mut base = DltFtBuffer::new(&DltFtHeaderPkg { + let mut buf = DltFtBuffer::new(&DltFtHeaderPkg { file_serial_number: DltFtUInt::U32(0), file_name: "base.txt", file_size: DltFtUInt::U32(4), From e03f3eafc3a33e418addb13cf8acd53470221e0f Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Wed, 26 Jun 2024 08:04:43 +0200 Subject: [PATCH 21/25] Addition tests and fix for empty files --- src/ft/dlt_ft_buffer.rs | 291 +++++++++++++++++++++++++++++++++++----- 1 file changed, 254 insertions(+), 37 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index cecc154..39e1d43 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -19,7 +19,7 @@ pub struct DltFtBuffer { file_size: u64, /// Number of expected packets. - number_of_packets: u64, + number_of_packages: u64, /// Buffer size. buffer_size: u64, @@ -40,6 +40,13 @@ pub struct DltFtBuffer { #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl DltFtBuffer { + + /// Returns the reconstructed section ranges. + #[inline] + pub fn sections(&self) -> &Vec { + &self.sections + } + /// File serial number (usually inode). #[inline] pub fn file_serial_number(&self) -> DltFtUInt { @@ -69,7 +76,7 @@ impl DltFtBuffer { data: Vec::new(), sections: Vec::with_capacity(4), file_size: 0, - number_of_packets: 0, + number_of_packages: 0, buffer_size: 0, file_serial_number: header.file_serial_number, file_name: String::with_capacity(header.file_name.len()), @@ -85,7 +92,7 @@ impl DltFtBuffer { self.data.clear(); self.sections.clear(); self.file_size = 0; - self.number_of_packets = 0; + self.number_of_packages = 0; self.buffer_size = 0; self.file_serial_number = DltFtUInt::U64(0); self.file_name.clear(); @@ -144,7 +151,7 @@ impl DltFtBuffer { // set values self.file_size = header.file_size.into(); - self.number_of_packets = header.number_of_packages.into(); + self.number_of_packages = header.number_of_packages.into(); self.buffer_size = header.buffer_size.into(); self.file_serial_number = header.file_serial_number; self.file_name.clear(); @@ -167,9 +174,9 @@ impl DltFtBuffer { pub fn consume_data_pkg(&mut self, data: &DltFtDataPkg) -> Result<(), FtReassembleError> { // validate the package number let package_nr: u64 = data.package_nr.into(); - if package_nr == 0 || package_nr > self.number_of_packets { + if package_nr == 0 || package_nr > self.number_of_packages { return Err(FtReassembleError::UnexpectedPackageNrInDataPkg { - expected_nr_of_packages: self.number_of_packets, + expected_nr_of_packages: self.number_of_packages, package_nr, }); } @@ -178,7 +185,7 @@ impl DltFtBuffer { let insertion_start: usize = (package_nr as usize - 1) * (self.buffer_size as usize); // validate the data len of the package - let expected_len = if package_nr < self.number_of_packets { + let expected_len = if package_nr < self.number_of_packages { self.buffer_size } else { // the last package only contains the left overs @@ -195,7 +202,7 @@ impl DltFtBuffer { header_buffer_len: self.buffer_size, data_pkt_len: data.data.len() as u64, data_pkt_nr: package_nr, - number_of_packages: self.number_of_packets, + number_of_packages: self.number_of_packages, }); } @@ -244,10 +251,16 @@ impl DltFtBuffer { /// Returns true if the data has been completed and the end received. pub fn is_complete(&self) -> bool { - self.end_received - && 1 == self.sections.len() - && 0 == self.sections[0].start - && self.sections[0].end == self.file_size + self.end_received && ( + ( + 1 == self.sections.len() + && 0 == self.sections[0].start + && self.sections[0].end == self.file_size + ) || ( + 0 == self.file_size && + 0 == self.sections.len() + ) + ) } /// Try finalizing the reconstructed file data and return a reference to it @@ -272,6 +285,26 @@ mod test { use crate::{ft::*, error::FtReassembleError}; use alloc::{borrow::ToOwned, vec::Vec, format}; + #[test] + fn getters() { + let buf = DltFtBuffer{ + data: Vec::new(), + sections: Vec::new(), + file_size: 1, + number_of_packages: 2, + buffer_size: 3, + file_serial_number: DltFtUInt::U32(4), + file_name: "5".to_owned(), + creation_date: "6".to_owned(), + end_received: true, + }; + assert_eq!(buf.sections(), &Vec::new()); + assert_eq!(buf.file_serial_number(), DltFtUInt::U32(4)); + assert_eq!(buf.file_name(), "5".to_owned()); + assert_eq!(buf.creation_date(), "6".to_owned()); + assert_eq!(buf.end_received(), true); + } + #[test] fn debug_clone_eq() { let buf = DltFtBuffer::new(&DltFtHeaderPkg { @@ -319,7 +352,7 @@ mod test { data: Vec::new(), sections: Vec::new(), file_size: 20, - number_of_packets: 2, + number_of_packages: 2, buffer_size: 10, file_serial_number: DltFtUInt::U32(1234), file_name: "a.txt".to_owned(), @@ -404,29 +437,49 @@ mod test { // ok init { - let mut buf = base.clone(); - buf.reinit_from_header_pkg(&DltFtHeaderPkg { - file_serial_number: DltFtUInt::U32(1234), - file_name: "file.txt", - file_size: DltFtUInt::U32(10), - creation_date: "2024-06-25", - number_of_packages: DltFtUInt::U32(2), - buffer_size: DltFtUInt::U32(5), - }).unwrap(); - assert_eq!( - DltFtBuffer { - data: Vec::new(), - sections: Vec::new(), - file_size: 10, - number_of_packets: 2, - buffer_size: 5, + let tests = [ + // file_size, number_of_packages, buffer_size + (0, 0, 0), + (0, 0, 4), + (1, 1, 4), + (2, 1, 4), + (3, 1, 4), + (4, 1, 4), + (5, 2, 4), + (6, 2, 4), + (7, 2, 4), + (8, 2, 4), + (9, 3, 4), + (10, 3, 4), + (11, 3, 4), + (12, 3, 4), + (13, 4, 4), + ]; + for (file_size, number_of_packages, buffer_size) in tests { + let mut buf = base.clone(); + buf.reinit_from_header_pkg(&DltFtHeaderPkg { file_serial_number: DltFtUInt::U32(1234), - file_name: "file.txt".to_owned(), - creation_date: "2024-06-25".to_owned(), - end_received: false, - }, - buf - ); + file_name: "file.txt", + file_size: DltFtUInt::U64(file_size), + creation_date: "2024-06-25", + number_of_packages: DltFtUInt::U64(number_of_packages), + buffer_size: DltFtUInt::U64(buffer_size), + }).unwrap(); + assert_eq!( + DltFtBuffer { + data: Vec::new(), + sections: Vec::new(), + file_size, + number_of_packages, + buffer_size, + file_serial_number: DltFtUInt::U32(1234), + file_name: "file.txt".to_owned(), + creation_date: "2024-06-25".to_owned(), + end_received: false, + }, + buf + ); + } } // empty file @@ -445,7 +498,7 @@ mod test { data: Vec::new(), sections: Vec::new(), file_size: 0, - number_of_packets: 0, + number_of_packages: 0, buffer_size: 0, file_serial_number: DltFtUInt::U32(1234), file_name: "file.txt".to_owned(), @@ -496,7 +549,6 @@ mod test { // test allocation error { - // TODO let mut buf = base.clone(); let err = buf.reinit_from_header_pkg(&DltFtHeaderPkg { file_serial_number: DltFtUInt::U32(1234), @@ -515,6 +567,171 @@ mod test { } } + #[test] + fn set_end_received() { + let mut buf = DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(0), + file_name: "base.txt", + file_size: DltFtUInt::U32(4), + creation_date: "0-0-0", + number_of_packages: DltFtUInt::U32(1), + buffer_size: DltFtUInt::U32(4), + }).unwrap(); + buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U32(1), + data: &[1,2,3,4], + }).unwrap(); + + assert_eq!(false, buf.end_received()); + buf.set_end_received(); + assert!(buf.end_received()); + } + + #[test] + #[rustfmt::skip] + fn consume_data_pkg() { + let new_buf = |file_size: u64, number_of_packages: u64, buffer_size: u64| -> DltFtBuffer { + DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(0), + file_name: "base.txt", + file_size: DltFtUInt::U64(file_size), + creation_date: "0-0-0", + number_of_packages: DltFtUInt::U64(number_of_packages), + buffer_size: DltFtUInt::U64(buffer_size), + }).unwrap() + }; + let assert_sequence = |buf: &DltFtBuffer| { + assert!(buf.is_complete()); + let data = buf.try_finalize().unwrap(); + assert_eq!(data.file_serial_number, buf.file_serial_number); + assert_eq!(data.file_name, buf.file_name); + assert_eq!(data.creation_date, buf.creation_date); + for (i, value) in data.data.iter().enumerate() { + assert_eq!(i as u8, *value); + } + }; + + // ok reconstruction + { + let tests: [((u64, u64, u64), &'static [(u64, &'static [u8])]);12] = [ + ((0, 0, 4), &[]), + ( + (1, 1, 4), + &[(1, &[0]),] + ), + ( + (2, 1, 4), + &[(1, &[0,1]),] + ), + ( + (3, 1, 4), + &[(1, &[0,1,2]),] + ), + ( + (4, 1, 4), + &[(1, &[0,1,2,3]),] + ), + ( + (5, 2, 4), + &[ + (1, &[0,1,2,3]), + (2, &[4]), + ] + ), + ( + (7, 2, 4), + &[ + (1, &[0,1,2,3]), + (2, &[4,5,6]), + ] + ), + ( + (8, 2, 4), + &[ + (1, &[0,1,2,3]), + (2, &[4,5,6,7]), + ] + ), + ( + (9, 3, 4), + &[ + (1, &[0,1,2,3]), + (2, &[4,5,6,7]), + (3, &[8]), + ] + ), + ( + (10, 3, 4), + &[ + (1, &[0,1,2,3]), + (2, &[4,5,6,7]), + (3, &[8,9]), + ] + ), + // out of order + ( + (10, 3, 4), + &[ + (3, &[8,9]), + (1, &[0,1,2,3]), + (2, &[4,5,6,7]), + ] + ), + ( + (10, 3, 4), + &[ + (1, &[0,1,2,3]), + (3, &[8,9]), + (2, &[4,5,6,7]), + ] + ), + ]; + + // run with end at the end + for ((file_size, number_of_packages, buffer_size), consumes) in tests { + let mut buf = new_buf(file_size, number_of_packages, buffer_size); + for (package_nr, data) in consumes { + assert_eq!(false, buf.is_complete()); + assert_eq!(None, buf.try_finalize()); + buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(*package_nr), + data, + }).unwrap(); + } + buf.set_end_received(); + assert_sequence(&buf); + } + + // end at the start + for ((file_size, number_of_packages, buffer_size), consumes) in tests { + let mut buf = new_buf(file_size, number_of_packages, buffer_size); + buf.set_end_received(); + for (package_nr, data) in consumes { + assert_eq!(false, buf.is_complete()); + assert_eq!(None, buf.try_finalize()); + buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(*package_nr), + data, + }).unwrap(); + } + assert_sequence(&buf); + } + } + + // package number error + { + // TODO + } + + // data len error + { + // TODO + } + } + /* struct TestPacket { offset: u32, From ce387e45890a8b9b4834ca3f1814f88f27ddde06 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Fri, 28 Jun 2024 18:37:08 +0200 Subject: [PATCH 22/25] Completed tests for dlt ft buffer --- src/ft/dlt_ft_buffer.rs | 384 +++++++++++++++------------------------- 1 file changed, 143 insertions(+), 241 deletions(-) diff --git a/src/ft/dlt_ft_buffer.rs b/src/ft/dlt_ft_buffer.rs index 39e1d43..fc1ed94 100644 --- a/src/ft/dlt_ft_buffer.rs +++ b/src/ft/dlt_ft_buffer.rs @@ -223,10 +223,9 @@ impl DltFtBuffer { self.data.as_mut_slice()[insertion_start..overwrite_end] .clone_from_slice(&data.data[..overwrite_len]); - // in case some data still needs to appended do that as well - if overwrite_end < insert_end { - self.data.extend_from_slice(&data.data[overwrite_len..]); - } + // there should not be anything left to overwrite + // as all packets have a fixed size + debug_assert!(overwrite_end >= insert_end); } // update sections @@ -588,6 +587,46 @@ mod test { assert!(buf.end_received()); } + #[test] + fn clear() { + let mut buf = DltFtBuffer::new(&DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "base.txt", + file_size: DltFtUInt::U64(20*12), + creation_date: "0-0-0", + number_of_packages: DltFtUInt::U64(12), + buffer_size: DltFtUInt::U64(20), + }).unwrap(); + buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U64(1), + data: &[0u8;20], + }).unwrap(); + buf.set_end_received(); + + assert_eq!(20, buf.data.len()); + assert_eq!(1, buf.sections.len()); + assert_eq!(20*12, buf.file_size); + assert_eq!(12, buf.number_of_packages); + assert_eq!(20, buf.buffer_size); + assert_eq!(DltFtUInt::U32(123), buf.file_serial_number); + assert_eq!("base.txt", buf.file_name.as_str()); + assert_eq!("0-0-0", buf.creation_date.as_str()); + assert!(buf.end_received); + + buf.clear(); + + assert_eq!(0, buf.data.len()); + assert_eq!(0, buf.sections.len()); + assert_eq!(0, buf.file_size); + assert_eq!(0, buf.number_of_packages); + assert_eq!(0, buf.buffer_size); + assert_eq!(DltFtUInt::U64(0), buf.file_serial_number); + assert_eq!("", buf.file_name.as_str()); + assert_eq!("", buf.creation_date.as_str()); + assert_eq!(false, buf.end_received); + } + #[test] #[rustfmt::skip] fn consume_data_pkg() { @@ -723,252 +762,115 @@ mod test { // package number error { - // TODO - } - - // data len error - { - // TODO - } - } - - /* - struct TestPacket { - offset: u32, - more_segments: bool, - payload: Vec, - } - - impl TestPacket { - fn new(offset: u32, more_segments: bool, payload: &[u8]) -> TestPacket { - TestPacket { - offset, - more_segments, - payload: payload.iter().copied().collect(), + // zero + { + let mut buf = new_buf(12*20, 12, 20); + let err = buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(0), + data: &[0u8;20], + }); + assert_eq!( + Err(FtReassembleError::UnexpectedPackageNrInDataPkg { + expected_nr_of_packages: 12, + package_nr: 0 + }), + err + ); } - } - - fn send_to_buffer(&self, buffer: &mut DltFtBuffer) -> Result<(), err::TpReassembleError> { - let packet = self.to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - buffer.consume_tp(slice) - } - fn to_vec(&self) -> Vec { - let header = SomeipHeader { - message_id: 1234, - length: 8 + 4 + self.payload.len() as u32, - request_id: 23, - interface_version: 1, - message_type: MessageType::Notification, - return_code: 0, - tp_header: { - let mut tp = TpHeader::new(self.more_segments); - tp.set_offset(self.offset).unwrap(); - Some(tp) - }, - }; - let mut result = Vec::with_capacity(SOMEIP_HEADER_LENGTH + 4 + self.payload.len()); - result.extend_from_slice(&header.base_to_bytes()); - result.extend_from_slice(&header.tp_header.as_ref().unwrap().to_bytes()); - result.extend_from_slice(&self.payload); - result - } - - fn result_header(payload_length: u32) -> SomeipHeader { - SomeipHeader { - message_id: 1234, - length: payload_length + 8, - request_id: 23, - interface_version: 1, - message_type: MessageType::Notification, - return_code: 0, - tp_header: None, + // above start + { + let mut buf = new_buf(12*20, 12, 20); + let err = buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(13), + data: &[0u8;20], + }); + assert_eq!( + Err(FtReassembleError::UnexpectedPackageNrInDataPkg { + expected_nr_of_packages: 12, + package_nr: 13 + }), + err + ); } } - } - - #[test] - fn new() { - let actual = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); - assert!(actual.data.is_empty()); - assert!(actual.sections.is_empty()); - assert!(actual.end.is_none()); - assert_eq!(1024, actual.config.tp_buffer_start_payload_alloc_len); - assert_eq!(2048, actual.config.tp_max_payload_len()); - } - - #[test] - fn clear() { - let mut actual = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); - - actual.data.push(1); - actual.sections.push(TpRange { start: 2, end: 3 }); - actual.end = Some(123); - - actual.clear(); - - assert!(actual.data.is_empty()); - assert!(actual.sections.is_empty()); - assert!(actual.end.is_none()); - assert_eq!(1024, actual.config.tp_buffer_start_payload_alloc_len); - assert_eq!(2048, actual.config.tp_max_payload_len()); - } - - /// Returns a u8 vec counting up from "start" until len is reached (truncating bits greater then u8). - fn sequence(start: usize, len: usize) -> Vec { - let mut result = Vec::with_capacity(len); - for i in start..start + len { - result.push((i & 0xff) as u8); - } - result - } - #[rustfmt::skip] - #[test] - fn consume() { - use err::TpReassembleError::*; - - // normal reconstruction + // data len error { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); - - let actions = [ - (false, TestPacket::new(0, true, &sequence(0,16))), - (false, TestPacket::new(16, true, &sequence(16,32))), - (true, TestPacket::new(48, false, &sequence(48,16))), - ]; - for a in actions { - a.1.send_to_buffer(&mut buffer).unwrap(); - assert_eq!(a.0, buffer.is_complete()); + // middle package not matching buffer size (too small) + { + let mut buf = new_buf(12*20, 12, 20); + let err = buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(1), + data: &[0u8;19], + }); + assert_eq!( + Err(FtReassembleError::DataLenNotMatchingBufferSize { + header_buffer_len: 20, + data_pkt_len: 19, + data_pkt_nr: 1, + number_of_packages: 12 + }), + err + ); } - let result = buffer.try_finalize().unwrap(); - assert_eq!(result.to_header(), TestPacket::result_header(16*4)); - assert_eq!(result.payload(), &sequence(0,16*4)); - } - - // overlapping reconstruction - { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); - - let actions = [ - (false, TestPacket::new(0, true, &sequence(0,16))), - // will be overwritten - (false, TestPacket::new(32, true, &sequence(0,16))), - // overwrites - (false, TestPacket::new(32, false, &sequence(32,16))), - // completes - (true, TestPacket::new(16, true, &sequence(16,16))), - ]; - for a in actions { - a.1.send_to_buffer(&mut buffer).unwrap(); - assert_eq!(a.0, buffer.is_complete()); + // middle package not matching buffer size (too big) + { + let mut buf = new_buf(12*20, 12, 20); + let err = buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(1), + data: &[0u8;21], + }); + assert_eq!( + Err(FtReassembleError::DataLenNotMatchingBufferSize { + header_buffer_len: 20, + data_pkt_len: 21, + data_pkt_nr: 1, + number_of_packages: 12 + }), + err + ); } - let result = buffer.try_finalize().unwrap(); - assert_eq!(result.to_header(), TestPacket::result_header(16*3)); - assert_eq!(result.payload(), &sequence(0,16*3)); - } - - // reverse order - { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); - - let actions = [ - (false, TestPacket::new(48, false, &sequence(48,16))), - (false, TestPacket::new(16, true, &sequence(16,32))), - (true, TestPacket::new(0, true, &sequence(0,16))), - ]; - for a in actions { - a.1.send_to_buffer(&mut buffer).unwrap(); - assert_eq!(a.0, buffer.is_complete()); + // end package not matching end size (too big) + { + let mut buf = new_buf(11*20 + 15, 12, 20); + let err = buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(1), + data: &[0u8;16], + }); + assert_eq!( + Err(FtReassembleError::DataLenNotMatchingBufferSize { + header_buffer_len: 20, + data_pkt_len: 16, + data_pkt_nr: 1, + number_of_packages: 12 + }), + err + ); + } + // end package not matching end size (too small) + { + let mut buf = new_buf(11*20 + 15, 12, 20); + let err = buf.consume_data_pkg(&DltFtDataPkg{ + file_serial_number: DltFtUInt::U32(0), + package_nr: DltFtUInt::U64(1), + data: &[0u8;14], + }); + assert_eq!( + Err(FtReassembleError::DataLenNotMatchingBufferSize { + header_buffer_len: 20, + data_pkt_len: 14, + data_pkt_nr: 1, + number_of_packages: 12 + }), + err + ); } - let result = buffer.try_finalize().unwrap(); - assert_eq!(result.to_header(), TestPacket::result_header(16*4)); - assert_eq!(result.payload(), &sequence(0,16*4)); - } - - // error tp packet bigger then max (offset only) - { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(32, 32).unwrap()); - assert_eq!( - SegmentTooBig { offset: 32 + 16, payload_len: 16, max: 32 }, - TestPacket::new(32 + 16, true, &sequence(0,16)).send_to_buffer(&mut buffer).unwrap_err() - ); - } - - // error tp packet bigger then max (offset + payload) - { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(32, 32).unwrap()); - assert_eq!( - SegmentTooBig { offset: 16, payload_len: 32, max: 32 }, - TestPacket::new(16, true, &sequence(0,32)).send_to_buffer(&mut buffer).unwrap_err() - ); - } - - // check packets that fill exactly to the max work - { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(32, 32).unwrap()); - let test_packet = TestPacket::new(16, false, &sequence(0,16)); - - let packet = test_packet.to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - - assert_eq!(Ok(()), buffer.consume_tp(slice)); - } - - // packets conflicting with previously seen end - for bad_offset in 1..16 { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(16*100, 16*100).unwrap()); - let test_packet = TestPacket::new(48, true, &sequence(0,32 + bad_offset)); - - let packet = test_packet.to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - - assert_eq!( - UnalignedTpPayloadLen { offset: 48, payload_len: 32 + bad_offset }, - buffer.consume_tp(slice).unwrap_err() - ); - } - - // test that conflicting ends trigger errors (received a different end) - { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); - - // setup an end (aka no more segements) - TestPacket::new(32, false, &sequence(32,16)).send_to_buffer(&mut buffer).unwrap(); - - // test that a "non end" going over the end package triggers an error - assert_eq!( - ConflictingEnd { previous_end: 32 + 16, conflicting_end: 48 + 16 }, - TestPacket::new(48, true, &sequence(48,16)).send_to_buffer(&mut buffer).unwrap_err() - ); - - // test that a new end at an earlier position triggers an error - assert_eq!( - ConflictingEnd { previous_end: 32 + 16, conflicting_end: 16 + 16 }, - TestPacket::new(16, false, &sequence(16,16)).send_to_buffer(&mut buffer).unwrap_err() - ); } } - - #[test] - fn try_finalize() { - let mut buffer = DltFtBuffer::new(DltFtBufferConfig::new(1024, 2048).unwrap()); - - // not ended - assert_eq!(buffer.try_finalize(), None); - TestPacket::new(0, true, &sequence(0, 16)) - .send_to_buffer(&mut buffer) - .unwrap(); - assert_eq!(buffer.try_finalize(), None); - - // ended - TestPacket::new(16, false, &sequence(16, 16)) - .send_to_buffer(&mut buffer) - .unwrap(); - let result = buffer.try_finalize().unwrap(); - assert_eq!(result.to_header(), TestPacket::result_header(16 * 2)); - assert_eq!(result.payload(), &sequence(0, 16 * 2)); - } - */ } From 5e746717d8f16055013ccc8d217a36b6dc5e0b51 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Sun, 30 Jun 2024 22:09:29 +0200 Subject: [PATCH 23/25] Add pool tests --- src/ft/dlt_ft_pool.rs | 244 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 241 insertions(+), 3 deletions(-) diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index 9cea73f..dfedb45 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -67,12 +67,22 @@ where let m = entry.get_mut(); m.0.reinit_from_header_pkg(header_pkg)?; m.1 = timestamp; - - // TODO add error + // Note: Not sure if an error should be returned here as + // an other potentially active data stream was discarded. + // + // Or should the stream even be discarded to begin with? Ok(None) } Vacant(vac) => { - vac.insert((DltFtBuffer::new(header_pkg)?, timestamp)); + vac.insert(( + if let Some(mut buf) = self.finished.pop() { + buf.reinit_from_header_pkg(header_pkg)?; + buf + } else { + DltFtBuffer::new(header_pkg)? + }, + timestamp, + )); Ok(None) } } @@ -196,6 +206,234 @@ mod tests { assert_eq!(pool, pool.clone()); } + #[test] + fn consume() { + let get_data = |from: u64, to: u64| -> Vec { + let mut result = Vec::with_capacity((to - from) as usize); + for i in from..to { + result.push(i as u8); + } + result + }; + + // normal package reconstruction + { + let mut pool = DltFtPool::::new(); + { + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(3), + buffer_size: DltFtUInt::U32(20), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 3, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U32(1), + data: &get_data(0, 20), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 4, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U32(2), + data: &get_data(20, 40), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 5, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U32(3), + data: &get_data(40, 60), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 5, + &DltFtPkg::End(DltFtEndPkg { + file_serial_number: DltFtUInt::U32(123) + }) + ), + Ok(Some(DltFtCompleteInMemFile { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + creation_date: "2024-06-28", + data: &get_data(0, 60) + })) + ); + } + + // reconstruction with end in data (twice to check that buffer re-using works correctly) + for _ in 0..2 { + assert!(pool.active.is_empty()); + assert_eq!(1, pool.finished.len()); + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(3), + buffer_size: DltFtUInt::U32(20), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 3, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U32(1), + data: &get_data(0, 20), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 4, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U32(2), + data: &get_data(20, 40), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 5, + &DltFtPkg::End(DltFtEndPkg { + file_serial_number: DltFtUInt::U32(123) + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 5, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U32(3), + data: &get_data(40, 60), + }) + ), + Ok(Some(DltFtCompleteInMemFile { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + creation_date: "2024-06-28", + data: &get_data(0, 60) + })) + ); + } + } + + // package reconstruction with a stream overwrite even though not finished + { + let base = { + let mut pool = DltFtPool::::new(); + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(3), + buffer_size: DltFtUInt::U32(20), + }) + ), + Ok(None) + ); + assert_eq!( + pool.active.get(&(1, DltFtUInt::U32(123))).unwrap().0.file_name(), + "a.txt" + ); + pool + }; + + // ok case + { + let mut pool = base.clone(); + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), // same fid + file_name: "b.txt", // different name + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(3), + buffer_size: DltFtUInt::U32(20), + }) + ), + Ok(None) + ); + assert_eq!( + pool.active.get(&(1, DltFtUInt::U32(123))).unwrap().0.file_name(), + "b.txt" + ); + } + + // error case + { + let mut pool = base.clone(); + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), // same fid + file_name: "b.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(2), // bad num of package + buffer_size: DltFtUInt::U32(20), + }) + ).unwrap_err(); + assert_eq!( + pool.active.get(&(1, DltFtUInt::U32(123))).unwrap().0.file_name(), + "a.txt" + ); + } + } + } + /* From 5efe4cc6d2dfa3c3c21de4dc2508bb30a7a83137 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Sun, 30 Jun 2024 22:48:28 +0200 Subject: [PATCH 24/25] Finalize dlt ft pool tests --- src/ft/dlt_ft_pool.rs | 432 +++++++++++++++++++++++------------------- 1 file changed, 241 insertions(+), 191 deletions(-) diff --git a/src/ft/dlt_ft_pool.rs b/src/ft/dlt_ft_pool.rs index dfedb45..7b8d8f9 100644 --- a/src/ft/dlt_ft_pool.rs +++ b/src/ft/dlt_ft_pool.rs @@ -432,231 +432,281 @@ mod tests { ); } } - } - - /* - - struct TestPacket { - request_id: u32, - offset: u32, - more_segments: bool, - payload: Vec, - } - - impl TestPacket { - fn new(request_id: u32, offset: u32, more_segments: bool, payload: &[u8]) -> TestPacket { - TestPacket { - request_id, - offset, - more_segments, - payload: payload.iter().copied().collect(), - } + // error in new stream (no buffer) + { + let mut pool = DltFtPool::::new(); + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(2), // bad number of packages + buffer_size: DltFtUInt::U32(20), + }) + ).unwrap_err(); } - fn to_vec(&self) -> Vec { - let header = SomeipHeader { - message_id: 1234, - length: 8 + 4 + self.payload.len() as u32, - request_id: self.request_id, - interface_version: 1, - message_type: MessageType::Notification, - return_code: 0, - tp_header: { - let mut tp = TpHeader::new(self.more_segments); - tp.set_offset(self.offset).unwrap(); - Some(tp) - }, - }; - let mut result = Vec::with_capacity(SOMEIP_HEADER_LENGTH + 4 + self.payload.len()); - result.extend_from_slice(&header.base_to_bytes()); - result.extend_from_slice(&header.tp_header.as_ref().unwrap().to_bytes()); - result.extend_from_slice(&self.payload); - result - } + // error in new stream (with buffer) + { + let mut pool = DltFtPool::::new(); - fn result_header(&self, payload_length: u32) -> SomeipHeader { - SomeipHeader { - message_id: 1234, - length: payload_length + 8, - request_id: self.request_id, - interface_version: 1, - message_type: MessageType::Notification, - return_code: 0, - tp_header: None, - } + // start and end a stream + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(0), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(0), + buffer_size: DltFtUInt::U32(20), + }) + ).unwrap(); + pool.consume( + 1, + 2, + &DltFtPkg::End(DltFtEndPkg { + file_serial_number: DltFtUInt::U32(123), + }) + ).unwrap(); + + assert!(pool.active_bufs().is_empty()); + assert_eq!(1, pool.finished_bufs().len()); + + // trigger an error + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(2), // bad number of packages + buffer_size: DltFtUInt::U32(20), + }) + ).unwrap_err(); } - } - /// Returns a u8 vec counting up from "start" until len is reached (truncating bits greater then u8). - fn sequence(start: usize, len: usize) -> Vec { - let mut result = Vec::with_capacity(len); - for i in start..start + len { - result.push((i & 0xff) as u8); - } - result - } + // error in data package + { + let mut pool = DltFtPool::::new(); - #[rustfmt::skip] - #[test] - fn consume() { - use err::TpReassembleError::*; + // start and end a stream + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(1), + buffer_size: DltFtUInt::U32(20), + }) + ).unwrap(); + pool.consume( + 1, + 2, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(123), + package_nr: DltFtUInt::U32(2), // bad package number + data: &[], + }) + ).unwrap_err(); + } - // simple packet forwarding (without TP effect) + // error unknown data package stream { - // build a non tp packet - let header = SomeipHeader { - message_id: 1234, - length: 8 + 8 as u32, - request_id: 234, - interface_version: 1, - message_type: MessageType::Notification, - return_code: 0, - // no tp header - tp_header: None, - }; - let mut result = Vec::with_capacity(SOMEIP_HEADER_LENGTH + 8); - result.extend_from_slice(&header.base_to_bytes()); - result.extend_from_slice(&[0;8]); - - let someip_slice = SomeipMsgSlice::from_slice(&result).unwrap(); + let mut pool = DltFtPool::::new(); - let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); - let result = pool.consume((), (), someip_slice.clone()).unwrap(); - assert_eq!(Some(someip_slice), result); + // start and end a stream + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(1), + buffer_size: DltFtUInt::U32(20), + }) + ).unwrap(); + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::Data(DltFtDataPkg { + file_serial_number: DltFtUInt::U32(234), // unknown data stream + package_nr: DltFtUInt::U32(2), + data: &[], + }) + ), + Err(FtPoolError::DataForUnknownStream { file_serial_number: DltFtUInt::U32(234) }) + ); } - // normal reconstruction (without additional id) + // end unknown data stream error { - let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); - - let actions = [ - // start two streams in parallel - (TestPacket::new(1, 0, true, &sequence(1,16)), None, 1, 0), - (TestPacket::new(2, 0, true, &sequence(2,32)), None, 2, 0), - // stream 1 ends - (TestPacket::new(1, 16, false, &sequence(1 + 16,16)), Some(sequence(1,32)), 1, 1), - // stream 3 which imidiatly ends - (TestPacket::new(3, 0, false, &sequence(3,16*4)), Some(sequence(3, 16*4)), 1, 1), - // end stream 2 - (TestPacket::new(2, 32, true, &sequence(32 + 2,16*4)), None, 1, 1), - (TestPacket::new(2, 16*6, false, &sequence(16*6 + 2,16*3)), Some(sequence(2, 16*9)), 0, 2), - ]; - for a in actions { - let packet = a.0.to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - let result = pool.consume((), (), slice).unwrap(); - if let Some(expected_payload) = a.1 { - let msg = result.unwrap(); - assert_eq!(msg.to_header(), a.0.result_header(expected_payload.len() as u32)); - assert_eq!(msg.payload(), expected_payload); - } else { - assert!(result.is_none()); - } - assert_eq!(a.2, pool.active_bufs().len()); - assert_eq!(a.3, pool.finished_bufs().len()); - } + let mut pool = DltFtPool::::new(); + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::End(DltFtEndPkg { + file_serial_number: DltFtUInt::U32(234), // unknown data stream + }) + ), + Err(FtPoolError::EndForUnknownStream { file_serial_number: DltFtUInt::U32(234) }) + ); } - // normal reconstruction (with additional id) + // error package for unknown stream { - let mut pool: DltFtPool = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); - - // all actions have the same request id have differing id's - let actions = [ - // start two streams in parallel - (123, TestPacket::new(1, 0, true, &sequence(1,16)), None), - (234, TestPacket::new(1, 0, true, &sequence(2,32)), None), - // stream 1 ends - (123, TestPacket::new(1, 16, false, &sequence(1 + 16,16)), Some(sequence(1,32))), - // stream 3 which imidiatly ends - (345, TestPacket::new(1, 0, false, &sequence(3,16*4)), Some(sequence(3, 16*4))), - // end stream 2 - (234, TestPacket::new(1, 32, true, &sequence(32 + 2,16*4)), None), - (234, TestPacket::new(1, 16*6, false, &sequence(16*6 + 2,16*3)), Some(sequence(2, 16*9))), - ]; - for a in actions { - let packet = a.1.to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - let result = pool.consume(a.0.clone(), (), slice).unwrap(); - if let Some(expected_payload) = a.2 { - let msg = result.unwrap(); - assert_eq!(msg.to_header(), a.1.result_header(expected_payload.len() as u32)); - assert_eq!(msg.payload(), expected_payload); - } else { - assert!(result.is_none()); - } - } + let mut pool = DltFtPool::::new(); + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::Error(DltFtErrorPkg { + file_serial_number:DltFtUInt::U32(234), + error_code: DltFtErrorCode(DltFtInt::I32(123)), + linux_error_code: DltFtInt::I32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(123), + creation_date: "0-0-0", + number_of_packages: DltFtUInt::U32(1) + }) + ), + Ok(None) + ); } - // error during reconstruction (at start) + // error package for known stream { - let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); - - // should trigger an error as the payload is not a multiple of 1 - let packet = TestPacket::new(1, 0, true, &sequence(1,15)).to_vec(); - let someip_slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + let mut pool = DltFtPool::::new(); + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(1), + buffer_size: DltFtUInt::U32(20), + }) + ).unwrap(); assert_eq!( - pool.consume((), (), someip_slice).unwrap_err(), - UnalignedTpPayloadLen { offset: 0, payload_len: 15 } + pool.consume( + 1, + 2, + &DltFtPkg::Error(DltFtErrorPkg { + file_serial_number:DltFtUInt::U32(123), + error_code: DltFtErrorCode(DltFtInt::I32(123)), + linux_error_code: DltFtInt::I32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(123), + creation_date: "0-0-0", + number_of_packages: DltFtUInt::U32(1) + }) + ), + Ok(None) ); + assert!(pool.active.is_empty()); + assert_eq!(pool.finished.len(), 1); } - // error during reconstruction (after start) + // file not exist error package { - let mut pool: DltFtPool<(), ()> = DltFtPool::new(TpBufConfig::new(1024, 2048).unwrap()); - - { - let packet = TestPacket::new(1, 0, true, &sequence(1,16)).to_vec(); - let someip_slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - pool.consume((), (), someip_slice).unwrap(); - } + let mut pool = DltFtPool::::new(); + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::FileNotExistsError(DltFtFileNotExistErrorPkg { + error_code: DltFtErrorCode(DltFtInt::I32(123)), + linux_error_code: DltFtInt::I32(123), + file_name: "a.txt", + }) + ), + Ok(None) + ); + assert!(pool.active.is_empty()); + assert!(pool.finished.is_empty()); + } - // should trigger an error as the payload is not a multiple of 1 - let packet = TestPacket::new(1, 16, true, &sequence(1,15)).to_vec(); - let someip_slice = SomeipMsgSlice::from_slice(&packet).unwrap(); + // file info package + { + let mut pool = DltFtPool::::new(); assert_eq!( - pool.consume((), (), someip_slice).unwrap_err(), - UnalignedTpPayloadLen { offset: 16, payload_len: 15 } + pool.consume( + 1, + 2, + &DltFtPkg::Info(DltFtInfoPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(1), + }) + ), + Ok(None) ); + assert!(pool.active.is_empty()); + assert!(pool.finished.is_empty()); } } #[test] fn retain() { - let mut pool: DltFtPool = DltFtPool::new(Default::default()); - // request id 1, channel id 2, timestamp 123 - { - let packet = TestPacket::new(1, 0, true, &sequence(1, 16)).to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - let result = pool.consume(2u16, 123u32, slice).unwrap(); - assert!(result.is_none()); - assert_eq!(123, pool.active_bufs().get(&(2u16, 1u32)).unwrap().1); - } - // request id 1, channel id 2, timestamp 124 - { - let packet = TestPacket::new(1, 16, true, &sequence(16, 16)).to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - let result = pool.consume(2u16, 124u32, slice).unwrap(); - assert!(result.is_none()); - // check the timestamp was overwritten by the newer packet - assert_eq!(124, pool.active_bufs().get(&(2u16, 1u32)).unwrap().1); - } - // request id 1, channel id 3, timestamp 125 - { - let packet = TestPacket::new(1, 16, true, &sequence(16, 16)).to_vec(); - let slice = SomeipMsgSlice::from_slice(&packet).unwrap(); - let result = pool.consume(3u16, 125u32, slice).unwrap(); - assert!(result.is_none()); - } + let mut pool = DltFtPool::::new(); + + // start two streams + assert_eq!( + pool.consume( + 1, + 2, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(123), + file_name: "a.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-28", + number_of_packages: DltFtUInt::U32(3), + buffer_size: DltFtUInt::U32(20), + }) + ), + Ok(None) + ); + assert_eq!( + pool.consume( + 1, + 3, + &DltFtPkg::Header(DltFtHeaderPkg { + file_serial_number: DltFtUInt::U32(234), + file_name: "b.txt", + file_size: DltFtUInt::U32(20 * 3), + creation_date: "2024-06-29", + number_of_packages: DltFtUInt::U32(3), + buffer_size: DltFtUInt::U32(20), + }) + ), + Ok(None) + ); - // discard streams with a timestamp smaller then 125 - pool.retain(|timestamp| *timestamp >= 125); + // retain the second stream only + pool.retain(|t| *t > 2); - assert_eq!(1, pool.active.len()); - assert_eq!(1, pool.finished.len()); - assert_eq!(125, pool.active_bufs().get(&(3u16, 1u32)).unwrap().1); + assert_eq!(pool.active.len(), 1); + assert_eq!(pool.finished.len(), 1); + assert!(pool.active.get(&(1, DltFtUInt::U32(234))).is_some()); } - */ + } From 6682f16085406916548efa5684a7efca40904a90 Mon Sep 17 00:00:00 2001 From: Julian Schmid Date: Mon, 1 Jul 2024 07:54:24 +0200 Subject: [PATCH 25/25] Further tests and constants --- src/ft/dlt_ft_data_pkg.rs | 4 + src/ft/dlt_ft_end_pkg.rs | 8 + src/ft/dlt_ft_error_pkg.rs | 4 + src/ft/dlt_ft_file_not_exist_error_pkg.rs | 4 + src/ft/dlt_ft_header_pkg.rs | 4 + src/ft/dlt_ft_info_pkg.rs | 4 + src/ft/dlt_ft_pkg.rs | 174 ++++++++++++++++++++++ 7 files changed, 202 insertions(+) diff --git a/src/ft/dlt_ft_data_pkg.rs b/src/ft/dlt_ft_data_pkg.rs index 6e8dd98..9848f9a 100644 --- a/src/ft/dlt_ft_data_pkg.rs +++ b/src/ft/dlt_ft_data_pkg.rs @@ -16,4 +16,8 @@ pub struct DltFtDataPkg<'a> { impl<'a> DltFtDataPkg<'a> { /// Verbose string at the start and end of the "DLT File Transfer Data" package. pub const PKG_FLAG: &'static str = "FLDA"; + + /// Number of verbose arguments in a file transfer "data package" written + /// in the DLT extended header. + pub const NUM_ARGS: u16 = 5; } diff --git a/src/ft/dlt_ft_end_pkg.rs b/src/ft/dlt_ft_end_pkg.rs index 5916c86..aa1e393 100644 --- a/src/ft/dlt_ft_end_pkg.rs +++ b/src/ft/dlt_ft_end_pkg.rs @@ -1,3 +1,5 @@ +use arrayvec::{ArrayVec, CapacityError}; + use super::*; /// Package sent after a file transfer is complete. @@ -10,4 +12,10 @@ pub struct DltFtEndPkg { impl DltFtEndPkg { /// Verbose string at the start and end of the "DLT File Transfer End" package. pub const PKG_FLAG: &'static str = "FLFI"; + + /// Number of verbose arguments in a file transfer "end package" written + /// in the DLT extended header. + pub const NUM_ARGS: u16 = 3; + + } diff --git a/src/ft/dlt_ft_error_pkg.rs b/src/ft/dlt_ft_error_pkg.rs index 5cf4d3d..d6e5d5c 100644 --- a/src/ft/dlt_ft_error_pkg.rs +++ b/src/ft/dlt_ft_error_pkg.rs @@ -32,4 +32,8 @@ pub struct DltFtErrorPkg<'a, 'b> { impl<'a, 'b> DltFtErrorPkg<'a, 'b> { /// Verbose string at the start and end of the "DLT File Transfer Error" package. pub const PKG_FLAG: &'static str = "FLER"; + + /// Number of verbose arguments in a file transfer "error package" written + /// in the DLT extended header. + pub const NUM_ARGS: u16 = 9; } diff --git a/src/ft/dlt_ft_file_not_exist_error_pkg.rs b/src/ft/dlt_ft_file_not_exist_error_pkg.rs index 76375c0..01d242e 100644 --- a/src/ft/dlt_ft_file_not_exist_error_pkg.rs +++ b/src/ft/dlt_ft_file_not_exist_error_pkg.rs @@ -17,4 +17,8 @@ pub struct DltFtFileNotExistErrorPkg<'a> { impl<'a> DltFtFileNotExistErrorPkg<'a> { /// Verbose string at the start and end of the "DLT File Transfer Error" package. pub const PKG_FLAG: &'static str = "FLER"; + + /// Number of verbose arguments in a file transfer "file does not exist error + /// package" written in the DLT extended header. + pub const NUM_ARGS: u16 = 5; } diff --git a/src/ft/dlt_ft_header_pkg.rs b/src/ft/dlt_ft_header_pkg.rs index 22488a8..155236c 100644 --- a/src/ft/dlt_ft_header_pkg.rs +++ b/src/ft/dlt_ft_header_pkg.rs @@ -25,4 +25,8 @@ pub struct DltFtHeaderPkg<'a, 'b> { impl<'a, 'b> DltFtHeaderPkg<'a, 'b> { /// Verbose string at the start and end of the "DLT File Transfer Header" package. pub const PKG_FLAG: &'static str = "FLST"; + + /// Number of verbose arguments in a file transfer "header package" written + /// in the DLT extended header. + pub const NUM_ARGS: u16 = 8; } diff --git a/src/ft/dlt_ft_info_pkg.rs b/src/ft/dlt_ft_info_pkg.rs index faf9b7d..8919438 100644 --- a/src/ft/dlt_ft_info_pkg.rs +++ b/src/ft/dlt_ft_info_pkg.rs @@ -25,4 +25,8 @@ pub struct DltFtInfoPkg<'a, 'b> { impl<'a, 'b> DltFtInfoPkg<'a, 'b> { /// Verbose string at the start and end of the "DLT File Transfer Info" package. pub const PKG_FLAG: &'static str = "FLIF"; + + /// Number of verbose arguments in a file transfer "info package" written + /// in the DLT extended header. + pub const NUM_ARGS: u16 = 7; } diff --git a/src/ft/dlt_ft_pkg.rs b/src/ft/dlt_ft_pkg.rs index ec12b3e..6645942 100644 --- a/src/ft/dlt_ft_pkg.rs +++ b/src/ft/dlt_ft_pkg.rs @@ -156,3 +156,177 @@ impl<'a, 'b> DltFtPkg<'a, 'b> { Some(()) } } + + +#[cfg(test)] +mod test { + use super::*; + use arrayvec::ArrayVec; + + #[test] + fn try_from() { + // TODO + } + + #[test] + fn try_take_str_from_iter() { + // ok case + { + let mut bytes = ArrayVec::::new(); + StringValue{ + name: None, + value: "a", + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::try_take_str_from_iter(&mut VerboseIter::new( + false, 1, &bytes[..] + )), + Some("a") + ); + } + + // name error case + { + let mut bytes = ArrayVec::::new(); + StringValue{ + name: Some("name"), + value: "a", + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::try_take_str_from_iter(&mut VerboseIter::new( + false, 1, &bytes[..] + )), + None + ); + } + + // non string value + { + let mut bytes = ArrayVec::::new(); + U64Value{ + variable_info: None, + scaling: None, + value: 1, + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::try_take_str_from_iter(&mut VerboseIter::new( + false, 1, &bytes[..] + )), + None + ); + } + } + + #[test] + fn try_take_raw_from_iter() { + // ok case + { + let mut bytes = ArrayVec::::new(); + RawValue{ + name: None, + data: &[1,2,3], + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::try_take_raw_from_iter(&mut VerboseIter::new( + false, 1, &bytes[..] + )), + Some(&[1u8,2,3][..]) + ); + } + + // name error case + { + let mut bytes = ArrayVec::::new(); + RawValue{ + name: Some("name"), + data: &[1,2,3], + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::try_take_raw_from_iter(&mut VerboseIter::new( + false, 1, &bytes[..] + )), + None + ); + } + + // non raw value + { + let mut bytes = ArrayVec::::new(); + U64Value{ + variable_info: None, + scaling: None, + value: 1, + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::try_take_raw_from_iter(&mut VerboseIter::new( + false, 1, &bytes[..] + )), + None + ); + } + } + + #[test] + fn check_for_str() { + // ok case + { + let mut bytes = ArrayVec::::new(); + StringValue{ + name: None, + value: "a", + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::check_for_str("a", &mut VerboseIter::new( + false, 1, &bytes[..] + )), + Some(()) + ); + } + + // non matching string + { + let mut bytes = ArrayVec::::new(); + StringValue{ + name: None, + value: "a", + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::check_for_str("b", &mut VerboseIter::new( + false, 1, &bytes[..] + )), + None + ); + } + + // name error case + { + let mut bytes = ArrayVec::::new(); + StringValue{ + name: Some("name"), + value: "a", + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::check_for_str("a", &mut VerboseIter::new( + false, 1, &bytes[..] + )), + None + ); + } + + // non string value + { + let mut bytes = ArrayVec::::new(); + U64Value{ + variable_info: None, + scaling: None, + value: 1, + }.add_to_msg(&mut bytes, false).unwrap(); + assert_eq!( + DltFtPkg::check_for_str("a", &mut VerboseIter::new( + false, 1, &bytes[..] + )), + None + ); + } + } +}