start removing alloc dependency

This commit is contained in:
Robin Mueller
2025-09-24 20:12:05 +02:00
committed by Robin Mueller
parent f48267692c
commit b79d5d7de8
11 changed files with 277 additions and 252 deletions

View File

@@ -21,7 +21,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
- name: Install nextest
uses: taiki-e/install-action@nextest
- run: cargo nextest run --all-features
- run: cargo nextest run --features "serde, defmt"
- run: cargo test --doc
msrv:
@@ -45,7 +45,7 @@ jobs:
- uses: dtolnay/rust-toolchain@stable
with:
targets: "armv7-unknown-linux-gnueabihf, thumbv7em-none-eabihf"
- run: cargo check --release --target=${{matrix.target}} --no-default-features --features "alloc"
- run: cargo check --release --target=${{matrix.target}} --no-default-features --features "packet-buf-1k, defmt"
fmt:
name: Check formatting
@@ -53,6 +53,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check
docs:
@@ -61,7 +63,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --all-features
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --features "serde, defmt"
clippy:
name: Clippy
@@ -69,4 +71,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- run: cargo clippy -- -D warnings

View File

@@ -27,7 +27,7 @@ serde = { version = "1", optional = true }
defmt = { version = "1", optional = true }
[features]
default = ["std"]
default = ["std", "packet-buf-2k"]
std = [
"alloc",
"thiserror/std",
@@ -40,6 +40,18 @@ alloc = [
serde = ["dep:serde", "spacepackets/serde", "hashbrown/serde", "heapless/serde"]
defmt = ["dep:defmt", "spacepackets/defmt"]
# Available packet buffer sizes. Only one should be enabled.
# 256 bytes
packet-buf-256 = []
# 512 bytes
packet-buf-512 = []
# 1024 bytes
packet-buf-1k = []
# 2048 bytes
packet-buf-2k = []
# 4096 bytes
packet-buf-4k = []
[dev-dependencies]
tempfile = "3"
rand = "0.9"
@@ -49,5 +61,5 @@ chrono = "0.4"
clap = { version = "4", features = ["derive"] }
[package.metadata.docs.rs]
all-features = true
features = ["serde", "defmt"]
rustdoc-args = ["--generate-link-to-definition"]

View File

@@ -17,7 +17,7 @@ The underlying base packet library used to generate the packets to be sent is th
`cfdp-rs` currently supports following high-level features:
- Unacknowledged (class 1) file transfers for both source and destination side.
- Acknowledged (class 2) file transfers for both source side and destination side.
- Acknowledged (class 2) file transfers for both source and destination side.
The following features have not been implemented yet. PRs or notifications for demand are welcome!
@@ -26,28 +26,8 @@ The following features have not been implemented yet. PRs or notifications for d
- Start and end of transmission and reception opportunity handling
- Keep Alive and Prompt PDU handling
## Rust features
The goal of this library is to be flexible enough to support the use-cases of both on-board
software and of ground software. It has support to make integration on `std` systems as simple
as possible, but also has sufficient abstraction to allow for integration on`no_std` environments
and can be used on these systems as well as long as the `alloc` feature is activated.
Please note even though the `alloc` feature is required for the core handlers, these components
will only allocate memory at initialization time and thus are still viable for systems where
run-time allocation is prohibited.
### Default features
- [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
- [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which require allocation support.
Enabled by the `std` feature.
### Optional Features
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
[`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
Check out the [documentation](https://docs.rs/cfdp-rs) for more information on available
Rust features.
# Examples
@@ -56,13 +36,11 @@ examples.
# Coverage
Coverage was generated using [`grcov`](https://github.com/mozilla/grcov). If you have not done so
already, install the `llvm-tools-preview`:
Coverage can be generated using [`llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov). If you have not done so
already, install the tool:
```sh
rustup component add llvm-tools-preview
cargo install grcov --locked
cargo +stable install cargo-llvm-cov --locked
```
After that, you can simply run `coverage.py` to test the project with coverage. You can optionally
supply the `--open` flag to open the coverage report in your webbrowser.
After this, you can run `cargo llvm-cov nextest` to run all the tests and display coverage.

View File

@@ -17,7 +17,7 @@ use cfdp::{
dest::DestinationHandler,
filestore::NativeFilestore,
lost_segments::LostSegmentsList,
request::{PutRequestOwned, StaticPutRequestCacher},
request::PutRequestOwned,
source::SourceHandler,
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams},
};
@@ -313,7 +313,6 @@ fn main() {
);
let (source_tm_tx, source_tm_rx) = mpsc::channel::<PduOwnedWithInfo>();
let (dest_tm_tx, dest_tm_rx) = mpsc::channel::<PduOwnedWithInfo>();
let put_request_cacher = StaticPutRequestCacher::new(2048);
let remote_cfg_python = RemoteEntityConfig::new_with_default_values(
PYTHON_ID.into(),
1024,
@@ -327,8 +326,6 @@ fn main() {
local_cfg_source,
source_tm_tx,
NativeFilestore::default(),
put_request_cacher,
2048,
remote_cfg_python,
StdTimerCreator::default(),
seq_count_provider,
@@ -342,7 +339,6 @@ fn main() {
);
let mut dest_handler = DestinationHandler::new(
local_cfg_dest,
1024,
dest_tm_tx,
NativeFilestore::default(),
remote_cfg_python,

View File

@@ -1,4 +1,4 @@
all: check build clippy fmt docs test coverage
all: check build embedded clippy fmt docs test coverage
clippy:
cargo clippy -- -D warnings
@@ -7,25 +7,25 @@ fmt:
cargo fmt --all -- --check
check:
cargo check --all-features
cargo check --features "serde, defmt"
test:
cargo nextest r --all-features
cargo nextest r --features "serde, defmt"
cargo test --doc
build:
cargo build --all-features
cargo build --features "serde, defmt"
embedded:
cargo build --target thumbv7em-none-eabihf --no-default-features --features "alloc"
cargo build --target thumbv7em-none-eabihf --no-default-features --features "defmt, packet-buf-1k"
docs:
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features
cargo +nightly doc --features "serde, defmt"
docs-html:
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
cargo +nightly doc --all-features --open
cargo +nightly doc --features "serde, defmt" --open
coverage:
cargo llvm-cov nextest

21
src/buf_len.rs Normal file
View File

@@ -0,0 +1,21 @@
#[cfg(not(any(
feature = "packet-buf-256",
feature = "packet-buf-512",
feature = "packet-buf-1k",
feature = "packet-buf-2k",
feature = "packet-buf-4k"
)))]
compile_error!(
"One of the features `packet-buf-256`, `packet-buf-512`, `packet-buf-1k`, `packet-buf-2k`, or `packet-buf-4k` must be enabled."
);
#[cfg(feature = "packet-buf-256")]
pub const PACKET_BUF_LEN: usize = 256;
#[cfg(feature = "packet-buf-512")]
pub const PACKET_BUF_LEN: usize = 512;
#[cfg(feature = "packet-buf-1k")]
pub const PACKET_BUF_LEN: usize = 1024;
#[cfg(feature = "packet-buf-2k")]
pub const PACKET_BUF_LEN: usize = 2048;
#[cfg(feature = "packet-buf-4k")]
pub const PACKET_BUF_LEN: usize = 4096;

View File

@@ -327,9 +327,11 @@ pub enum DestError {
/// Prompt PDUs in addition to ACK PDUs where the acknowledged PDU is the Finished PDU.
/// All generated packets are sent using the user provided [PduSender].
///
/// The handler requires the [alloc] feature but will allocated all required memory on construction
/// time. This means that the handler is still suitable for embedded systems where run-time
/// allocation is prohibited. Furthermore, it uses the [VirtualFilestore] abstraction to allow
///
/// The handler has an internal buffer for PDU generation and checksum generation. The size of this
/// buffer is select via Cargo features and defaults to 2048 bytes. It does not allocate
/// memory during run-time and thus is suitable for embedded systems where allocation is
/// not possible. Furthermore, it uses the [VirtualFilestore] abstraction to allow
/// usage on systems without a [std] filesystem.
///
/// This handler is able to deal with file copy operations to directories, similarly to how the
@@ -353,7 +355,7 @@ pub struct DestinationHandler<
step: core::cell::Cell<TransactionStep>,
state: State,
transaction_params: TransactionParams<CountdownInstance>,
pdu_and_cksum_buffer: RefCell<alloc::vec::Vec<u8>>,
pdu_and_cksum_buffer: RefCell<[u8; crate::buf_len::PACKET_BUF_LEN]>,
pub pdu_sender: PduSenderInstance,
pub vfs: VirtualFileStoreInstance,
pub remote_cfg_table: RemoteConfigStoreInstance,
@@ -379,12 +381,10 @@ impl<PduSenderInstance: PduSender, UserFaultHookInstance: UserFaultHook>
#[cfg(feature = "std")]
pub fn new_std(
local_cfg: LocalEntityConfig<UserFaultHookInstance>,
pdu_and_cksum_buf_size: usize,
pdu_sender: PduSenderInstance,
) -> Self {
Self::new(
local_cfg,
pdu_and_cksum_buf_size,
pdu_sender,
crate::filestore::NativeFilestore::default(),
crate::RemoteConfigStoreStd::default(),
@@ -435,7 +435,6 @@ impl<
/// where the standard time APIs might not be available.
pub fn new(
local_cfg: LocalEntityConfig<UserFaultHookInstance>,
pdu_and_cksum_buf_size: usize,
pdu_sender: PduSenderInstance,
vfs: VirtualFilestoreInstance,
remote_cfg_table: RemoteConfigStoreInstance,
@@ -447,7 +446,7 @@ impl<
step: Cell::new(TransactionStep::Idle),
state: State::Idle,
transaction_params: Default::default(),
pdu_and_cksum_buffer: core::cell::RefCell::new(alloc::vec![0; pdu_and_cksum_buf_size]),
pdu_and_cksum_buffer: core::cell::RefCell::new([0; crate::buf_len::PACKET_BUF_LEN]),
pdu_sender,
vfs,
remote_cfg_table,
@@ -2328,7 +2327,6 @@ mod tests {
};
DestinationHandler::new(
local_entity_cfg,
2048,
test_packet_sender,
NativeFilestore::default(),
basic_remote_cfg_table(LOCAL_ID, 1024, true),

View File

@@ -8,7 +8,7 @@
//! `cfdp-rs` currently supports following high-level features:
//!
//! - Unacknowledged (class 1) file transfers for both source and destination side.
//! - Acknowledged (class 2) file transfers for both source side and destination side.
//! - Acknowledged (class 2) file transfers for both source and destination side.
//!
//! The following features have not been implemented yet. PRs or notifications for demand are welcome!
//!
@@ -32,18 +32,43 @@
//! The goal of this library is to be flexible enough to support the use-cases of both on-board
//! software and of ground software. It has support to make integration on [std] systems as simple
//! as possible, but also has sufficient abstraction to allow for integration on `no_std`
//! environments and can be used on these systems as well as long as the [alloc] feature is used
//! as well.
//! environments and can be used on these systems as well.
//!
//! Please note even though the [alloc] feature is required for the core handlers, these components
//! will only allocate memory at initialization time and thus are still viable for systems where
//! run-time allocation is prohibited.
//! The core handlers inside this library do not allocate memory dynamically. The internal buffer
//! size used for PDU generation and checksum calculation is statically determined via a Rust
//! feature and defaults to 2048 bytes.
//!
//! The core of this library are the [crate::dest::DestinationHandler] and the
//! [crate::source::SourceHandler] components which model the CFDP destination and source entity
//! respectively. You can find high-level and API documentation for both handlers in the respective
//! [crate::dest] and [crate::source] module.
//!
//! # Rust Features
//!
//! ## Default features
//!
//! - [`std`](https://doc.rust-lang.org/std/): Enables functionality relying on the standard library.
//! - [`alloc`](https://doc.rust-lang.org/alloc/): Enables features which require allocation support.
//! Enabled by the `std` feature.
//!
//! ## Optional Features
//!
//! - [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
//! - [`defmt`](https://defmt.ferrous-systems.com/): Add support for the `defmt` by adding the
//! [`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
//!
//! ## Buffer size selection
//!
//! The following features can be used to select the internal buffer size used for PDU generation
//! and checksum calculation. Selection of this value possibly limits the size of the generated PDU
//! packets. Only one feature may be enabled.
//!
//! - `packet-buf-256` for 256 bytes
//! - `packet-buf-512` for 512 bytes
//! - `packet-buf-1k` for 1024 bytes
//! - `packet-buf-2k` for 2048 bytes. This is the default.
//! - `packet-buf-4k` for 4096 bytes
//!
//! # Examples
//!
//! This library currently features two example application which showcase how the provided
@@ -89,12 +114,11 @@ extern crate alloc;
#[cfg(any(feature = "std", test))]
extern crate std;
#[cfg(feature = "alloc")]
pub mod buf_len;
pub mod dest;
pub mod filestore;
pub mod lost_segments;
pub mod request;
#[cfg(feature = "alloc")]
pub mod source;
pub mod time;
pub mod user;
@@ -374,6 +398,7 @@ impl RemoteConfigStore for RemoteConfigList {
}
}
#[cfg(feature = "alloc")]
impl RemoteConfigList {
pub fn remove_config(&mut self, remote_id: u64) -> bool {
for (idx, cfg) in self.0.iter().enumerate() {
@@ -386,7 +411,7 @@ impl RemoteConfigList {
}
}
/// This is a thin wrapper around a [alloc::vec::Vec] to store remote entity configurations.
/// This is a thin wrapper around a [heapless::vec::Vec] to store remote entity configurations.
/// It implements the full [RemoteEntityConfig] trait.
#[derive(Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]

View File

@@ -1,7 +1,10 @@
use core::str::Utf8Error;
use spacepackets::{
ByteConversionError,
cfdp::{
SegmentationControl, TransmissionMode,
tlv::{GenericTlv, Tlv, TlvType},
tlv::{GenericTlv, ReadableTlv as _, Tlv, TlvType, WritableTlv as _},
},
util::UnsignedByteField,
};
@@ -226,16 +229,171 @@ pub fn generic_tlv_list_type_check<TlvProvider: GenericTlv>(
true
}
pub struct StaticPutRequestFields {
pub destination_id: UnsignedByteField,
/// Static buffer to store source file path.
pub source_file_buf: [u8; u8::MAX as usize],
/// Current source path length.
pub source_file_len: usize,
/// Static buffer to store dest file path.
pub dest_file_buf: [u8; u8::MAX as usize],
/// Current destination path length.
pub dest_file_len: usize,
pub trans_mode: Option<TransmissionMode>,
pub closure_requested: Option<bool>,
pub seg_ctrl: Option<SegmentationControl>,
}
impl Default for StaticPutRequestFields {
fn default() -> Self {
Self {
destination_id: UnsignedByteField::new(0, 0),
source_file_buf: [0; u8::MAX as usize],
source_file_len: Default::default(),
dest_file_buf: [0; u8::MAX as usize],
dest_file_len: Default::default(),
trans_mode: Default::default(),
closure_requested: Default::default(),
seg_ctrl: Default::default(),
}
}
}
impl StaticPutRequestFields {
pub fn clear(&mut self) {
self.destination_id = UnsignedByteField::new(0, 0);
self.source_file_len = 0;
self.dest_file_len = 0;
self.trans_mode = None;
self.closure_requested = None;
self.seg_ctrl = None;
}
}
/// This is a put request cache structure which can be used to cache [ReadablePutRequest]s
/// without requiring run-time allocation. The user must specify the static buffer sizes used
/// to store TLVs or list of TLVs.
pub struct StaticPutRequestCacher<const BUF_SIZE: usize> {
pub static_fields: StaticPutRequestFields,
opts_buf: [u8; BUF_SIZE],
opts_len: usize,
}
impl<const BUF_SIZE: usize> Default for StaticPutRequestCacher<BUF_SIZE> {
fn default() -> Self {
Self::new()
}
}
impl<const BUF_SIZE: usize> StaticPutRequestCacher<BUF_SIZE> {
pub fn new() -> Self {
Self {
static_fields: StaticPutRequestFields::default(),
opts_buf: [0; BUF_SIZE],
opts_len: 0,
}
}
pub fn set(
&mut self,
put_request: &impl ReadablePutRequest,
) -> Result<(), ByteConversionError> {
self.static_fields.destination_id = put_request.destination_id();
if let Some(source_file) = put_request.source_file() {
if source_file.len() > u8::MAX as usize {
return Err(ByteConversionError::ToSliceTooSmall {
found: self.static_fields.source_file_buf.len(),
expected: source_file.len(),
});
}
self.static_fields.source_file_buf[..source_file.len()]
.copy_from_slice(source_file.as_bytes());
self.static_fields.source_file_len = source_file.len();
}
if let Some(dest_file) = put_request.dest_file() {
if dest_file.len() > u8::MAX as usize {
return Err(ByteConversionError::ToSliceTooSmall {
found: self.static_fields.source_file_buf.len(),
expected: dest_file.len(),
});
}
self.static_fields.dest_file_buf[..dest_file.len()]
.copy_from_slice(dest_file.as_bytes());
self.static_fields.dest_file_len = dest_file.len();
}
self.static_fields.trans_mode = put_request.trans_mode();
self.static_fields.closure_requested = put_request.closure_requested();
self.static_fields.seg_ctrl = put_request.seg_ctrl();
let mut current_idx = 0;
let mut store_tlv = |tlv: &Tlv| {
if current_idx + tlv.len_full() > self.opts_buf.len() {
return Err(ByteConversionError::ToSliceTooSmall {
found: self.opts_buf.len(),
expected: current_idx + tlv.len_full(),
});
}
// We checked the buffer lengths, so this should never fail.
tlv.write_to_bytes(&mut self.opts_buf[current_idx..current_idx + tlv.len_full()])
.unwrap();
current_idx += tlv.len_full();
Ok(())
};
if let Some(fs_req) = put_request.fs_requests() {
for fs_req in fs_req {
store_tlv(&fs_req)?;
}
}
if let Some(msgs_to_user) = put_request.msgs_to_user() {
for msg_to_user in msgs_to_user {
store_tlv(&msg_to_user)?;
}
}
self.opts_len = current_idx;
Ok(())
}
pub fn has_source_file(&self) -> bool {
self.static_fields.source_file_len > 0
}
pub fn has_dest_file(&self) -> bool {
self.static_fields.dest_file_len > 0
}
pub fn source_file(&self) -> Result<&str, Utf8Error> {
core::str::from_utf8(
&self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
)
}
pub fn dest_file(&self) -> Result<&str, Utf8Error> {
core::str::from_utf8(&self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len])
}
pub fn opts_len(&self) -> usize {
self.opts_len
}
pub fn opts_slice(&self) -> &[u8] {
&self.opts_buf[0..self.opts_len]
}
/// This clears the cacher structure. This is a cheap operation because it only
/// sets [Option]al values to [None] and the length of stores TLVs to 0.
///
/// Please note that this method will not set the values in the buffer to 0.
pub fn clear(&mut self) {
self.static_fields.clear();
self.opts_len = 0;
}
}
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use core::str::Utf8Error;
use super::*;
use alloc::string::ToString;
use spacepackets::{
ByteConversionError,
cfdp::tlv::{ReadableTlv, TlvOwned, WritableTlv, msg_to_user::MsgToUserTlv},
};
use spacepackets::cfdp::tlv::{TlvOwned, msg_to_user::MsgToUserTlv};
/// Owned variant of [PutRequest] with no lifetimes which is also [Clone]able.
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -395,161 +553,6 @@ pub mod alloc_mod {
None
}
}
pub struct StaticPutRequestFields {
pub destination_id: UnsignedByteField,
/// Static buffer to store source file path.
pub source_file_buf: [u8; u8::MAX as usize],
/// Current source path length.
pub source_file_len: usize,
/// Static buffer to store dest file path.
pub dest_file_buf: [u8; u8::MAX as usize],
/// Current destination path length.
pub dest_file_len: usize,
pub trans_mode: Option<TransmissionMode>,
pub closure_requested: Option<bool>,
pub seg_ctrl: Option<SegmentationControl>,
}
impl Default for StaticPutRequestFields {
fn default() -> Self {
Self {
destination_id: UnsignedByteField::new(0, 0),
source_file_buf: [0; u8::MAX as usize],
source_file_len: Default::default(),
dest_file_buf: [0; u8::MAX as usize],
dest_file_len: Default::default(),
trans_mode: Default::default(),
closure_requested: Default::default(),
seg_ctrl: Default::default(),
}
}
}
impl StaticPutRequestFields {
pub fn clear(&mut self) {
self.destination_id = UnsignedByteField::new(0, 0);
self.source_file_len = 0;
self.dest_file_len = 0;
self.trans_mode = None;
self.closure_requested = None;
self.seg_ctrl = None;
}
}
/// This is a put request cache structure which can be used to cache [ReadablePutRequest]s
/// without requiring run-time allocation. The user must specify the static buffer sizes used
/// to store TLVs or list of TLVs.
pub struct StaticPutRequestCacher {
pub static_fields: StaticPutRequestFields,
opts_buf: alloc::vec::Vec<u8>,
opts_len: usize, // fs_request_start_end_pos: Option<(usize, usize)>
}
impl StaticPutRequestCacher {
pub fn new(max_len_opts_buf: usize) -> Self {
Self {
static_fields: StaticPutRequestFields::default(),
opts_buf: alloc::vec![0; max_len_opts_buf],
opts_len: 0,
}
}
pub fn set(
&mut self,
put_request: &impl ReadablePutRequest,
) -> Result<(), ByteConversionError> {
self.static_fields.destination_id = put_request.destination_id();
if let Some(source_file) = put_request.source_file() {
if source_file.len() > u8::MAX as usize {
return Err(ByteConversionError::ToSliceTooSmall {
found: self.static_fields.source_file_buf.len(),
expected: source_file.len(),
});
}
self.static_fields.source_file_buf[..source_file.len()]
.copy_from_slice(source_file.as_bytes());
self.static_fields.source_file_len = source_file.len();
}
if let Some(dest_file) = put_request.dest_file() {
if dest_file.len() > u8::MAX as usize {
return Err(ByteConversionError::ToSliceTooSmall {
found: self.static_fields.source_file_buf.len(),
expected: dest_file.len(),
});
}
self.static_fields.dest_file_buf[..dest_file.len()]
.copy_from_slice(dest_file.as_bytes());
self.static_fields.dest_file_len = dest_file.len();
}
self.static_fields.trans_mode = put_request.trans_mode();
self.static_fields.closure_requested = put_request.closure_requested();
self.static_fields.seg_ctrl = put_request.seg_ctrl();
let mut current_idx = 0;
let mut store_tlv = |tlv: &Tlv| {
if current_idx + tlv.len_full() > self.opts_buf.len() {
return Err(ByteConversionError::ToSliceTooSmall {
found: self.opts_buf.len(),
expected: current_idx + tlv.len_full(),
});
}
// We checked the buffer lengths, so this should never fail.
tlv.write_to_bytes(&mut self.opts_buf[current_idx..current_idx + tlv.len_full()])
.unwrap();
current_idx += tlv.len_full();
Ok(())
};
if let Some(fs_req) = put_request.fs_requests() {
for fs_req in fs_req {
store_tlv(&fs_req)?;
}
}
if let Some(msgs_to_user) = put_request.msgs_to_user() {
for msg_to_user in msgs_to_user {
store_tlv(&msg_to_user)?;
}
}
self.opts_len = current_idx;
Ok(())
}
pub fn has_source_file(&self) -> bool {
self.static_fields.source_file_len > 0
}
pub fn has_dest_file(&self) -> bool {
self.static_fields.dest_file_len > 0
}
pub fn source_file(&self) -> Result<&str, Utf8Error> {
core::str::from_utf8(
&self.static_fields.source_file_buf[0..self.static_fields.source_file_len],
)
}
pub fn dest_file(&self) -> Result<&str, Utf8Error> {
core::str::from_utf8(
&self.static_fields.dest_file_buf[0..self.static_fields.dest_file_len],
)
}
pub fn opts_len(&self) -> usize {
self.opts_len
}
pub fn opts_slice(&self) -> &[u8] {
&self.opts_buf[0..self.opts_len]
}
/// This clears the cacher structure. This is a cheap operation because it only
/// sets [Option]al values to [None] and the length of stores TLVs to 0.
///
/// Please note that this method will not set the values in the buffer to 0.
pub fn clear(&mut self) {
self.static_fields.clear();
self.opts_len = 0;
}
}
}
#[cfg(test)]
@@ -689,7 +692,7 @@ mod tests {
#[test]
fn test_put_request_cacher_basic() {
let put_request_cached = StaticPutRequestCacher::new(128);
let put_request_cached = StaticPutRequestCacher::<128>::new();
assert_eq!(put_request_cached.static_fields.source_file_len, 0);
assert_eq!(put_request_cached.static_fields.dest_file_len, 0);
assert_eq!(put_request_cached.opts_len(), 0);
@@ -698,7 +701,7 @@ mod tests {
#[test]
fn test_put_request_cacher_set() {
let mut put_request_cached = StaticPutRequestCacher::new(128);
let mut put_request_cached = StaticPutRequestCacher::<128>::new();
let src_file = "/tmp/hello.txt";
let dest_file = "/tmp/hello2.txt";
let put_request =
@@ -720,7 +723,7 @@ mod tests {
#[test]
fn test_put_request_cacher_set_and_clear() {
let mut put_request_cached = StaticPutRequestCacher::new(128);
let mut put_request_cached = StaticPutRequestCacher::<128>::new();
let src_file = "/tmp/hello.txt";
let dest_file = "/tmp/hello2.txt";
let put_request =

View File

@@ -267,9 +267,10 @@ impl<CountdownInstance: Countdown> TransactionParams<CountdownInstance> {
///
/// A put request will only be accepted if the handler is in the idle state.
///
/// The handler requires the [alloc] feature but will allocated all required memory on construction
/// time. This means that the handler is still suitable for embedded systems where run-time
/// allocation is prohibited. Furthermore, it uses the [VirtualFilestore] abstraction to allow
/// The handler has an internal buffer for PDU generation and checksum generation. The size of this
/// buffer is select via Cargo features and defaults to 2048 bytes. It does not allocate
/// memory during run-time and thus is suitable for embedded systems where allocation is
/// not possible. Furthermore, it uses the [VirtualFilestore] abstraction to allow
/// usage on systems without a [std] filesystem.
/// This handler does not support concurrency out of the box. Instead, if concurrent handling
/// is required, it is recommended to create a new handler and run all active handlers inside a
@@ -285,8 +286,8 @@ pub struct SourceHandler<
> {
local_cfg: LocalEntityConfig<UserFaultHookInstance>,
pdu_sender: PduSenderInstance,
pdu_and_cksum_buffer: RefCell<alloc::vec::Vec<u8>>,
put_request_cacher: StaticPutRequestCacher,
pdu_and_cksum_buffer: RefCell<[u8; crate::buf_len::PACKET_BUF_LEN]>,
put_request_cacher: StaticPutRequestCacher<{ crate::buf_len::PACKET_BUF_LEN }>,
remote_cfg_table: RemoteConfigStoreInstance,
vfs: Vfs,
state_helper: StateHelper,
@@ -326,10 +327,6 @@ impl<
/// for embedded systems where a standard runtime might not be available.
/// * `put_request_cacher` - The put request cacher is used cache put requests without
/// requiring run-time allocation.
/// * `pdu_and_cksum_buf_size` - The handler requires a buffer to generate PDUs and perform
/// checksum calculations. The user can specify the size of this buffer, so this should be
/// set to the maximum expected PDU size or a conservative upper bound for this size, for
/// example 2048 or 4096 bytes.
/// * `remote_cfg_table` - The [RemoteEntityConfig] used to look up remote
/// entities and target specific configuration for file copy operations.
/// * `timer_creator` - [TimerCreator] used by the CFDP handler to generate
@@ -342,8 +339,6 @@ impl<
cfg: LocalEntityConfig<UserFaultHookInstance>,
pdu_sender: PduSenderInstance,
vfs: Vfs,
put_request_cacher: StaticPutRequestCacher,
pdu_and_cksum_buf_size: usize,
remote_cfg_table: RemoteConfigStoreInstance,
timer_creator: TimerCreatorInstance,
seq_count_provider: SequenceCounterInstance,
@@ -352,9 +347,9 @@ impl<
local_cfg: cfg,
remote_cfg_table,
pdu_sender,
pdu_and_cksum_buffer: RefCell::new(alloc::vec![0; pdu_and_cksum_buf_size]),
pdu_and_cksum_buffer: RefCell::new([0; crate::buf_len::PACKET_BUF_LEN]),
vfs,
put_request_cacher,
put_request_cacher: StaticPutRequestCacher::<{ crate::buf_len::PACKET_BUF_LEN }>::new(),
state_helper: Default::default(),
transaction_params: Default::default(),
anomalies: Default::default(),
@@ -802,7 +797,7 @@ impl<
.unwrap()
.default_crc_type,
self.transaction_params.file_params.file_size,
&mut self.pdu_and_cksum_buffer.borrow_mut(),
self.pdu_and_cksum_buffer.borrow_mut().as_mut_slice(),
)?;
self.transaction_params.file_params.checksum_completed_file = Some(checksum);
self.prepare_and_send_eof_pdu(user, checksum)?;
@@ -1081,7 +1076,7 @@ impl<
fn pdu_send_helper(&self, pdu: &(impl WritablePduPacket + CfdpPdu)) -> Result<(), SourceError> {
let mut pdu_buffer_mut = self.pdu_and_cksum_buffer.borrow_mut();
let written_len = pdu.write_to_bytes(&mut pdu_buffer_mut)?;
let written_len = pdu.write_to_bytes(pdu_buffer_mut.as_mut_slice())?;
self.pdu_sender.send_pdu(
pdu.pdu_type(),
pdu.file_directive_type(),
@@ -1171,7 +1166,7 @@ impl<
.unwrap()
.default_crc_type,
self.transaction_params.file_params.progress,
&mut self.pdu_and_cksum_buffer.borrow_mut(),
self.pdu_and_cksum_buffer.borrow_mut().as_mut_slice(),
)?;
self.prepare_and_send_eof_pdu(user, checksum)?;
*sent_packets += 1;
@@ -1338,7 +1333,6 @@ mod tests {
indication_cfg: IndicationConfig::default(),
fault_handler: FaultHandler::new(TestFaultHandler::default()),
};
let static_put_request_cacher = StaticPutRequestCacher::new(2048);
let (srcfile_handle, destfile) = init_full_filepaths_textfile();
let srcfile = String::from(srcfile_handle.to_path_buf().to_str().unwrap());
let expiry_control = TimerExpiryControl::default();
@@ -1348,8 +1342,6 @@ mod tests {
local_entity_cfg,
sender,
NativeFilestore::default(),
static_put_request_cacher,
1024,
basic_remote_cfg_table(
REMOTE_ID,
max_packet_len,

View File

@@ -17,7 +17,7 @@ use cfdp::{
dest::DestinationHandler,
filestore::NativeFilestore,
lost_segments::LostSegmentsList,
request::{PutRequestOwned, StaticPutRequestCacher},
request::PutRequestOwned,
source::SourceHandler,
user::{CfdpUser, FileSegmentRecvdParams, MetadataReceivedParams, TransactionFinishedParams},
};
@@ -167,7 +167,6 @@ fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
);
let (source_tx, source_rx) = mpsc::channel::<PduOwnedWithInfo>();
let (dest_tx, dest_rx) = mpsc::channel::<PduOwnedWithInfo>();
let put_request_cacher = StaticPutRequestCacher::new(2048);
let remote_cfg_of_dest = RemoteEntityConfig::new_with_default_values(
REMOTE_ID.into(),
1024,
@@ -181,8 +180,6 @@ fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
local_cfg_source,
source_tx,
NativeFilestore::default(),
put_request_cacher,
2048,
remote_cfg_of_dest,
StdTimerCreator::default(),
seq_count_provider,
@@ -204,7 +201,6 @@ fn end_to_end_test(transmission_mode: TransmissionMode, with_closure: bool) {
);
let mut dest_handler = DestinationHandler::new(
local_cfg_dest,
1024,
dest_tx,
NativeFilestore::default(),
remote_cfg_of_source,