start removing alloc dependency
This commit is contained in:
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
16
Cargo.toml
16
Cargo.toml
@@ -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"]
|
||||
|
36
README.md
36
README.md
@@ -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.
|
||||
|
@@ -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,
|
||||
|
14
justfile
14
justfile
@@ -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
21
src/buf_len.rs
Normal 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;
|
16
src/dest.rs
16
src/dest.rs
@@ -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),
|
||||
|
43
src/lib.rs
43
src/lib.rs
@@ -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))]
|
||||
|
331
src/request.rs
331
src/request.rs
@@ -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 =
|
||||
|
@@ -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,
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user