try to make no-std build
This commit is contained in:
parent
6a0f6bd6c9
commit
1d2af1eda1
@ -33,8 +33,6 @@ pub mod hk;
|
||||
pub mod mode;
|
||||
pub mod objects;
|
||||
pub mod params;
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||
pub mod pool;
|
||||
pub mod power;
|
||||
pub mod pus;
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::tmtc::TargetId;
|
||||
use core::mem::size_of;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spacepackets::{ByteConversionError, SizeMissmatch};
|
||||
|
||||
|
@ -73,63 +73,17 @@
|
||||
//! assert_eq!(buf_read_back[0], 7);
|
||||
//! }
|
||||
//! ```
|
||||
use alloc::format;
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
|
||||
pub use alloc_mod::*;
|
||||
use core::fmt::{Display, Formatter};
|
||||
use delegate::delegate;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "std")]
|
||||
use std::boxed::Box;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
type NumBlocks = u16;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub type ShareablePoolProvider = Box<dyn PoolProvider + Send + Sync>;
|
||||
#[cfg(feature = "std")]
|
||||
pub type SharedPool = Arc<RwLock<ShareablePoolProvider>>;
|
||||
|
||||
/// Configuration structure of the [local pool][LocalPool]
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `cfg`: Vector of tuples which represent a subpool. The first entry in the tuple specifies the
|
||||
/// number of memory blocks in the subpool, the second entry the size of the blocks
|
||||
#[derive(Clone)]
|
||||
pub struct PoolCfg {
|
||||
cfg: Vec<(NumBlocks, usize)>,
|
||||
}
|
||||
|
||||
impl PoolCfg {
|
||||
pub fn new(cfg: Vec<(NumBlocks, usize)>) -> Self {
|
||||
PoolCfg { cfg }
|
||||
}
|
||||
|
||||
pub fn sanitize(&mut self) -> usize {
|
||||
self.cfg
|
||||
.retain(|&(bucket_num, size)| bucket_num > 0 && size < LocalPool::MAX_SIZE);
|
||||
self.cfg
|
||||
.sort_unstable_by(|(_, sz0), (_, sz1)| sz0.partial_cmp(sz1).unwrap());
|
||||
self.cfg.len()
|
||||
}
|
||||
}
|
||||
|
||||
type PoolSize = usize;
|
||||
|
||||
/// Pool implementation providing sub-pools with fixed size memory blocks. More details in
|
||||
/// the [module documentation][super::pool]
|
||||
pub struct LocalPool {
|
||||
pool_cfg: PoolCfg,
|
||||
pool: Vec<Vec<u8>>,
|
||||
sizes_lists: Vec<Vec<PoolSize>>,
|
||||
}
|
||||
|
||||
/// Simple address type used for transactions with the local pool.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
@ -138,6 +92,14 @@ pub struct StoreAddr {
|
||||
pub(crate) packet_idx: NumBlocks,
|
||||
}
|
||||
|
||||
impl StoreAddr {
|
||||
pub const INVALID_ADDR: u32 = 0xFFFFFFFF;
|
||||
|
||||
pub fn raw(&self) -> u32 {
|
||||
((self.pool_idx as u32) << 16) | self.packet_idx as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StoreAddr {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
write!(
|
||||
@ -148,14 +110,6 @@ impl Display for StoreAddr {
|
||||
}
|
||||
}
|
||||
|
||||
impl StoreAddr {
|
||||
pub const INVALID_ADDR: u32 = 0xFFFFFFFF;
|
||||
|
||||
pub fn raw(&self) -> u32 {
|
||||
((self.pool_idx as u32) << 16) | self.packet_idx as u32
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum StoreIdError {
|
||||
@ -191,7 +145,7 @@ pub enum StoreError {
|
||||
/// Valid subpool and packet index, but no data is stored at the given address
|
||||
DataDoesNotExist(StoreAddr),
|
||||
/// Internal or configuration errors
|
||||
InternalError(String),
|
||||
InternalError(u32),
|
||||
}
|
||||
|
||||
impl Display for StoreError {
|
||||
@ -226,298 +180,351 @@ impl Error for StoreError {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PoolProvider {
|
||||
/// Add new data to the pool. The provider should attempt to reserve a memory block with the
|
||||
/// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can
|
||||
/// be used to access the data stored in the pool
|
||||
fn add(&mut self, data: &[u8]) -> Result<StoreAddr, StoreError>;
|
||||
type PoolSize = usize;
|
||||
const STORE_FREE: PoolSize = PoolSize::MAX;
|
||||
const POOL_MAX_SIZE: PoolSize = STORE_FREE - 1;
|
||||
|
||||
/// The provider should attempt to reserve a free memory block with the appropriate size and
|
||||
/// then return a mutable reference to it. Yields a [StoreAddr] which can be used to access
|
||||
/// the data stored in the pool
|
||||
fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError>;
|
||||
pub mod alloc_mod {
|
||||
use crate::pool::{
|
||||
NumBlocks, PoolSize, StoreAddr, StoreError, StoreIdError, POOL_MAX_SIZE, STORE_FREE,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use delegate::delegate;
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
/// Modify data added previously using a given [StoreAddr] by yielding a mutable reference
|
||||
/// to it
|
||||
fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError>;
|
||||
#[cfg(feature = "std")]
|
||||
pub type ShareablePoolProvider = Box<dyn PoolProvider + Send + Sync>;
|
||||
#[cfg(feature = "std")]
|
||||
pub type SharedPool = Arc<RwLock<ShareablePoolProvider>>;
|
||||
|
||||
/// This function behaves like [Self::modify], but consumes the provided address and returns a
|
||||
/// RAII conformant guard object.
|
||||
/// Configuration structure of the [local pool][LocalPool]
|
||||
///
|
||||
/// Unless the guard [PoolRwGuard::release] method is called, the data for the
|
||||
/// given address will be deleted automatically when the guard is dropped.
|
||||
/// This can prevent memory leaks. Users can read (and modify) the data and release the guard
|
||||
/// if the data in the store is valid for further processing. If the data is faulty, no
|
||||
/// manual deletion is necessary when returning from a processing function prematurely.
|
||||
fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard;
|
||||
|
||||
/// Read data by yielding a read-only reference given a [StoreAddr]
|
||||
fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError>;
|
||||
|
||||
/// This function behaves like [Self::read], but consumes the provided address and returns a
|
||||
/// RAII conformant guard object.
|
||||
/// # Parameters
|
||||
///
|
||||
/// Unless the guard [PoolRwGuard::release] method is called, the data for the
|
||||
/// given address will be deleted automatically when the guard is dropped.
|
||||
/// This can prevent memory leaks. Users can read the data and release the guard
|
||||
/// if the data in the store is valid for further processing. If the data is faulty, no
|
||||
/// manual deletion is necessary when returning from a processing function prematurely.
|
||||
fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard;
|
||||
|
||||
/// Delete data inside the pool given a [StoreAddr]
|
||||
fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError>;
|
||||
fn has_element_at(&self, addr: &StoreAddr) -> Result<bool, StoreError>;
|
||||
|
||||
/// Retrieve the length of the data at the given store address.
|
||||
fn len_of_data(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||
if !self.has_element_at(addr)? {
|
||||
return Err(StoreError::DataDoesNotExist(*addr));
|
||||
}
|
||||
Ok(self.read(addr)?.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalPool {
|
||||
const STORE_FREE: PoolSize = PoolSize::MAX;
|
||||
const MAX_SIZE: PoolSize = Self::STORE_FREE - 1;
|
||||
/// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize
|
||||
/// the given configuration as well.
|
||||
pub fn new(mut cfg: PoolCfg) -> LocalPool {
|
||||
let subpools_num = cfg.sanitize();
|
||||
let mut local_pool = LocalPool {
|
||||
pool_cfg: cfg,
|
||||
pool: Vec::with_capacity(subpools_num),
|
||||
sizes_lists: Vec::with_capacity(subpools_num),
|
||||
};
|
||||
for &(num_elems, elem_size) in local_pool.pool_cfg.cfg.iter() {
|
||||
let next_pool_len = elem_size * num_elems as usize;
|
||||
local_pool.pool.push(vec![0; next_pool_len]);
|
||||
let next_sizes_list_len = num_elems as usize;
|
||||
local_pool
|
||||
.sizes_lists
|
||||
.push(vec![Self::STORE_FREE; next_sizes_list_len]);
|
||||
}
|
||||
local_pool
|
||||
/// * `cfg`: Vector of tuples which represent a subpool. The first entry in the tuple specifies the
|
||||
/// number of memory blocks in the subpool, the second entry the size of the blocks
|
||||
#[derive(Clone)]
|
||||
pub struct PoolCfg {
|
||||
cfg: Vec<(NumBlocks, usize)>,
|
||||
}
|
||||
|
||||
fn addr_check(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||
self.validate_addr(addr)?;
|
||||
let pool_idx = addr.pool_idx as usize;
|
||||
let size_list = self.sizes_lists.get(pool_idx).unwrap();
|
||||
let curr_size = size_list[addr.packet_idx as usize];
|
||||
if curr_size == Self::STORE_FREE {
|
||||
return Err(StoreError::DataDoesNotExist(*addr));
|
||||
impl PoolCfg {
|
||||
pub fn new(cfg: Vec<(NumBlocks, usize)>) -> Self {
|
||||
PoolCfg { cfg }
|
||||
}
|
||||
|
||||
pub fn cfg(&self) -> &Vec<(NumBlocks, usize)> {
|
||||
&self.cfg
|
||||
}
|
||||
|
||||
pub fn sanitize(&mut self) -> usize {
|
||||
self.cfg
|
||||
.retain(|&(bucket_num, size)| bucket_num > 0 && size < POOL_MAX_SIZE);
|
||||
self.cfg
|
||||
.sort_unstable_by(|(_, sz0), (_, sz1)| sz0.partial_cmp(sz1).unwrap());
|
||||
self.cfg.len()
|
||||
}
|
||||
Ok(curr_size)
|
||||
}
|
||||
|
||||
fn validate_addr(&self, addr: &StoreAddr) -> Result<(), StoreError> {
|
||||
let pool_idx = addr.pool_idx as usize;
|
||||
if pool_idx >= self.pool_cfg.cfg.len() {
|
||||
return Err(StoreError::InvalidStoreId(
|
||||
StoreIdError::InvalidSubpool(addr.pool_idx),
|
||||
Some(*addr),
|
||||
));
|
||||
}
|
||||
if addr.packet_idx >= self.pool_cfg.cfg[addr.pool_idx as usize].0 {
|
||||
return Err(StoreError::InvalidStoreId(
|
||||
StoreIdError::InvalidPacketIdx(addr.packet_idx),
|
||||
Some(*addr),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
pub struct PoolGuard<'a> {
|
||||
pool: &'a mut LocalPool,
|
||||
pub addr: StoreAddr,
|
||||
no_deletion: bool,
|
||||
deletion_failed_error: Option<StoreError>,
|
||||
}
|
||||
|
||||
fn reserve(&mut self, data_len: usize) -> Result<StoreAddr, StoreError> {
|
||||
let subpool_idx = self.find_subpool(data_len, 0)?;
|
||||
let (slot, size_slot_ref) = self.find_empty(subpool_idx)?;
|
||||
*size_slot_ref = data_len;
|
||||
Ok(StoreAddr {
|
||||
pool_idx: subpool_idx,
|
||||
packet_idx: slot,
|
||||
})
|
||||
}
|
||||
|
||||
fn find_subpool(&self, req_size: usize, start_at_subpool: u16) -> Result<u16, StoreError> {
|
||||
for (i, &(_, elem_size)) in self.pool_cfg.cfg.iter().enumerate() {
|
||||
if i < start_at_subpool as usize {
|
||||
continue;
|
||||
}
|
||||
if elem_size >= req_size {
|
||||
return Ok(i as u16);
|
||||
/// This helper object
|
||||
impl<'a> PoolGuard<'a> {
|
||||
pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
addr,
|
||||
no_deletion: false,
|
||||
deletion_failed_error: None,
|
||||
}
|
||||
}
|
||||
Err(StoreError::DataTooLarge(req_size))
|
||||
|
||||
pub fn read(&self) -> Result<&[u8], StoreError> {
|
||||
self.pool.read(&self.addr)
|
||||
}
|
||||
|
||||
/// Releasing the pool guard will disable the automatic deletion of the data when the guard
|
||||
/// is dropped.
|
||||
pub fn release(&mut self) {
|
||||
self.no_deletion = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: &StoreAddr, data: &[u8]) -> Result<(), StoreError> {
|
||||
let packet_pos = self.raw_pos(addr).ok_or_else(|| {
|
||||
StoreError::InternalError(format!(
|
||||
"write: Error in raw_pos func with address {addr:?}"
|
||||
))
|
||||
})?;
|
||||
let subpool = self.pool.get_mut(addr.pool_idx as usize).ok_or_else(|| {
|
||||
StoreError::InternalError(format!(
|
||||
"write: Error retrieving pool slice with address {addr:?}"
|
||||
))
|
||||
})?;
|
||||
let pool_slice = &mut subpool[packet_pos..packet_pos + data.len()];
|
||||
pool_slice.copy_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_empty(&mut self, subpool: u16) -> Result<(u16, &mut usize), StoreError> {
|
||||
if let Some(size_list) = self.sizes_lists.get_mut(subpool as usize) {
|
||||
for (i, elem_size) in size_list.iter_mut().enumerate() {
|
||||
if *elem_size == Self::STORE_FREE {
|
||||
return Ok((i as u16, elem_size));
|
||||
impl Drop for PoolGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
if !self.no_deletion {
|
||||
if let Err(e) = self.pool.delete(self.addr) {
|
||||
self.deletion_failed_error = Some(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(StoreError::InvalidStoreId(
|
||||
StoreIdError::InvalidSubpool(subpool),
|
||||
None,
|
||||
));
|
||||
}
|
||||
Err(StoreError::StoreFull(subpool))
|
||||
}
|
||||
|
||||
fn raw_pos(&self, addr: &StoreAddr) -> Option<usize> {
|
||||
let (_, size) = self.pool_cfg.cfg.get(addr.pool_idx as usize)?;
|
||||
Some(addr.packet_idx as usize * size)
|
||||
}
|
||||
}
|
||||
|
||||
impl PoolProvider for LocalPool {
|
||||
fn add(&mut self, data: &[u8]) -> Result<StoreAddr, StoreError> {
|
||||
let data_len = data.len();
|
||||
if data_len > Self::MAX_SIZE {
|
||||
return Err(StoreError::DataTooLarge(data_len));
|
||||
}
|
||||
let addr = self.reserve(data_len)?;
|
||||
self.write(&addr, data)?;
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> {
|
||||
if len > Self::MAX_SIZE {
|
||||
return Err(StoreError::DataTooLarge(len));
|
||||
}
|
||||
let addr = self.reserve(len)?;
|
||||
let raw_pos = self.raw_pos(&addr).unwrap();
|
||||
let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len];
|
||||
Ok((addr, block))
|
||||
}
|
||||
|
||||
fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> {
|
||||
let curr_size = self.addr_check(addr)?;
|
||||
let raw_pos = self.raw_pos(addr).unwrap();
|
||||
let block =
|
||||
&mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size];
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard {
|
||||
PoolRwGuard::new(self, addr)
|
||||
}
|
||||
|
||||
fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError> {
|
||||
let curr_size = self.addr_check(addr)?;
|
||||
let raw_pos = self.raw_pos(addr).unwrap();
|
||||
let block = &self.pool.get(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size];
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard {
|
||||
PoolGuard::new(self, addr)
|
||||
}
|
||||
|
||||
fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError> {
|
||||
self.addr_check(&addr)?;
|
||||
let block_size = self.pool_cfg.cfg.get(addr.pool_idx as usize).unwrap().1;
|
||||
let raw_pos = self.raw_pos(&addr).unwrap();
|
||||
let block =
|
||||
&mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + block_size];
|
||||
let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap();
|
||||
size_list[addr.packet_idx as usize] = Self::STORE_FREE;
|
||||
block.fill(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_element_at(&self, addr: &StoreAddr) -> Result<bool, StoreError> {
|
||||
self.validate_addr(addr)?;
|
||||
let pool_idx = addr.pool_idx as usize;
|
||||
let size_list = self.sizes_lists.get(pool_idx).unwrap();
|
||||
let curr_size = size_list[addr.packet_idx as usize];
|
||||
if curr_size == Self::STORE_FREE {
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PoolGuard<'a> {
|
||||
pool: &'a mut LocalPool,
|
||||
pub addr: StoreAddr,
|
||||
no_deletion: bool,
|
||||
deletion_failed_error: Option<StoreError>,
|
||||
}
|
||||
|
||||
/// This helper object
|
||||
impl<'a> PoolGuard<'a> {
|
||||
pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self {
|
||||
Self {
|
||||
pool,
|
||||
addr,
|
||||
no_deletion: false,
|
||||
deletion_failed_error: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self) -> Result<&[u8], StoreError> {
|
||||
self.pool.read(&self.addr)
|
||||
pub struct PoolRwGuard<'a> {
|
||||
guard: PoolGuard<'a>,
|
||||
}
|
||||
|
||||
/// Releasing the pool guard will disable the automatic deletion of the data when the guard
|
||||
/// is dropped.
|
||||
pub fn release(&mut self) {
|
||||
self.no_deletion = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PoolGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
if !self.no_deletion {
|
||||
if let Err(e) = self.pool.delete(self.addr) {
|
||||
self.deletion_failed_error = Some(e);
|
||||
impl<'a> PoolRwGuard<'a> {
|
||||
pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self {
|
||||
Self {
|
||||
guard: PoolGuard::new(pool, addr),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modify(&mut self) -> Result<&mut [u8], StoreError> {
|
||||
self.guard.pool.modify(&self.guard.addr)
|
||||
}
|
||||
|
||||
delegate!(
|
||||
to self.guard {
|
||||
pub fn read(&self) -> Result<&[u8], StoreError>;
|
||||
/// Releasing the pool guard will disable the automatic deletion of the data when the guard
|
||||
/// is dropped.
|
||||
pub fn release(&mut self);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PoolRwGuard<'a> {
|
||||
guard: PoolGuard<'a>,
|
||||
}
|
||||
pub trait PoolProvider {
|
||||
/// Add new data to the pool. The provider should attempt to reserve a memory block with the
|
||||
/// appropriate size and then copy the given data to the block. Yields a [StoreAddr] which can
|
||||
/// be used to access the data stored in the pool
|
||||
fn add(&mut self, data: &[u8]) -> Result<StoreAddr, StoreError>;
|
||||
|
||||
impl<'a> PoolRwGuard<'a> {
|
||||
pub fn new(pool: &'a mut LocalPool, addr: StoreAddr) -> Self {
|
||||
Self {
|
||||
guard: PoolGuard::new(pool, addr),
|
||||
/// The provider should attempt to reserve a free memory block with the appropriate size and
|
||||
/// then return a mutable reference to it. Yields a [StoreAddr] which can be used to access
|
||||
/// the data stored in the pool
|
||||
fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError>;
|
||||
|
||||
/// Modify data added previously using a given [StoreAddr] by yielding a mutable reference
|
||||
/// to it
|
||||
fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError>;
|
||||
|
||||
/// This function behaves like [Self::modify], but consumes the provided address and returns a
|
||||
/// RAII conformant guard object.
|
||||
///
|
||||
/// Unless the guard [PoolRwGuard::release] method is called, the data for the
|
||||
/// given address will be deleted automatically when the guard is dropped.
|
||||
/// This can prevent memory leaks. Users can read (and modify) the data and release the guard
|
||||
/// if the data in the store is valid for further processing. If the data is faulty, no
|
||||
/// manual deletion is necessary when returning from a processing function prematurely.
|
||||
fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard;
|
||||
|
||||
/// Read data by yielding a read-only reference given a [StoreAddr]
|
||||
fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError>;
|
||||
|
||||
/// This function behaves like [Self::read], but consumes the provided address and returns a
|
||||
/// RAII conformant guard object.
|
||||
///
|
||||
/// Unless the guard [PoolRwGuard::release] method is called, the data for the
|
||||
/// given address will be deleted automatically when the guard is dropped.
|
||||
/// This can prevent memory leaks. Users can read the data and release the guard
|
||||
/// if the data in the store is valid for further processing. If the data is faulty, no
|
||||
/// manual deletion is necessary when returning from a processing function prematurely.
|
||||
fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard;
|
||||
|
||||
/// Delete data inside the pool given a [StoreAddr]
|
||||
fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError>;
|
||||
fn has_element_at(&self, addr: &StoreAddr) -> Result<bool, StoreError>;
|
||||
|
||||
/// Retrieve the length of the data at the given store address.
|
||||
fn len_of_data(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||
if !self.has_element_at(addr)? {
|
||||
return Err(StoreError::DataDoesNotExist(*addr));
|
||||
}
|
||||
Ok(self.read(addr)?.len())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modify(&mut self) -> Result<&mut [u8], StoreError> {
|
||||
self.guard.pool.modify(&self.guard.addr)
|
||||
/// Pool implementation providing sub-pools with fixed size memory blocks. More details in
|
||||
/// the [module documentation][super::pool]
|
||||
pub struct LocalPool {
|
||||
pool_cfg: PoolCfg,
|
||||
pool: Vec<Vec<u8>>,
|
||||
sizes_lists: Vec<Vec<PoolSize>>,
|
||||
}
|
||||
|
||||
delegate!(
|
||||
to self.guard {
|
||||
pub fn read(&self) -> Result<&[u8], StoreError>;
|
||||
/// Releasing the pool guard will disable the automatic deletion of the data when the guard
|
||||
/// is dropped.
|
||||
pub fn release(&mut self);
|
||||
impl LocalPool {
|
||||
/// Create a new local pool from the [given configuration][PoolCfg]. This function will sanitize
|
||||
/// the given configuration as well.
|
||||
pub fn new(mut cfg: PoolCfg) -> LocalPool {
|
||||
let subpools_num = cfg.sanitize();
|
||||
let mut local_pool = LocalPool {
|
||||
pool_cfg: cfg,
|
||||
pool: Vec::with_capacity(subpools_num),
|
||||
sizes_lists: Vec::with_capacity(subpools_num),
|
||||
};
|
||||
for &(num_elems, elem_size) in local_pool.pool_cfg.cfg.iter() {
|
||||
let next_pool_len = elem_size * num_elems as usize;
|
||||
local_pool.pool.push(vec![0; next_pool_len]);
|
||||
let next_sizes_list_len = num_elems as usize;
|
||||
local_pool
|
||||
.sizes_lists
|
||||
.push(vec![STORE_FREE; next_sizes_list_len]);
|
||||
}
|
||||
local_pool
|
||||
}
|
||||
);
|
||||
|
||||
fn addr_check(&self, addr: &StoreAddr) -> Result<usize, StoreError> {
|
||||
self.validate_addr(addr)?;
|
||||
let pool_idx = addr.pool_idx as usize;
|
||||
let size_list = self.sizes_lists.get(pool_idx).unwrap();
|
||||
let curr_size = size_list[addr.packet_idx as usize];
|
||||
if curr_size == STORE_FREE {
|
||||
return Err(StoreError::DataDoesNotExist(*addr));
|
||||
}
|
||||
Ok(curr_size)
|
||||
}
|
||||
|
||||
fn validate_addr(&self, addr: &StoreAddr) -> Result<(), StoreError> {
|
||||
let pool_idx = addr.pool_idx as usize;
|
||||
if pool_idx >= self.pool_cfg.cfg.len() {
|
||||
return Err(StoreError::InvalidStoreId(
|
||||
StoreIdError::InvalidSubpool(addr.pool_idx),
|
||||
Some(*addr),
|
||||
));
|
||||
}
|
||||
if addr.packet_idx >= self.pool_cfg.cfg[addr.pool_idx as usize].0 {
|
||||
return Err(StoreError::InvalidStoreId(
|
||||
StoreIdError::InvalidPacketIdx(addr.packet_idx),
|
||||
Some(*addr),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reserve(&mut self, data_len: usize) -> Result<StoreAddr, StoreError> {
|
||||
let subpool_idx = self.find_subpool(data_len, 0)?;
|
||||
let (slot, size_slot_ref) = self.find_empty(subpool_idx)?;
|
||||
*size_slot_ref = data_len;
|
||||
Ok(StoreAddr {
|
||||
pool_idx: subpool_idx,
|
||||
packet_idx: slot,
|
||||
})
|
||||
}
|
||||
|
||||
fn find_subpool(&self, req_size: usize, start_at_subpool: u16) -> Result<u16, StoreError> {
|
||||
for (i, &(_, elem_size)) in self.pool_cfg.cfg.iter().enumerate() {
|
||||
if i < start_at_subpool as usize {
|
||||
continue;
|
||||
}
|
||||
if elem_size >= req_size {
|
||||
return Ok(i as u16);
|
||||
}
|
||||
}
|
||||
Err(StoreError::DataTooLarge(req_size))
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: &StoreAddr, data: &[u8]) -> Result<(), StoreError> {
|
||||
let packet_pos = self.raw_pos(addr).ok_or(StoreError::InternalError(0))?;
|
||||
let subpool = self
|
||||
.pool
|
||||
.get_mut(addr.pool_idx as usize)
|
||||
.ok_or(StoreError::InternalError(1))?;
|
||||
let pool_slice = &mut subpool[packet_pos..packet_pos + data.len()];
|
||||
pool_slice.copy_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_empty(&mut self, subpool: u16) -> Result<(u16, &mut usize), StoreError> {
|
||||
if let Some(size_list) = self.sizes_lists.get_mut(subpool as usize) {
|
||||
for (i, elem_size) in size_list.iter_mut().enumerate() {
|
||||
if *elem_size == STORE_FREE {
|
||||
return Ok((i as u16, elem_size));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(StoreError::InvalidStoreId(
|
||||
StoreIdError::InvalidSubpool(subpool),
|
||||
None,
|
||||
));
|
||||
}
|
||||
Err(StoreError::StoreFull(subpool))
|
||||
}
|
||||
|
||||
fn raw_pos(&self, addr: &StoreAddr) -> Option<usize> {
|
||||
let (_, size) = self.pool_cfg.cfg.get(addr.pool_idx as usize)?;
|
||||
Some(addr.packet_idx as usize * size)
|
||||
}
|
||||
}
|
||||
|
||||
impl PoolProvider for LocalPool {
|
||||
fn add(&mut self, data: &[u8]) -> Result<StoreAddr, StoreError> {
|
||||
let data_len = data.len();
|
||||
if data_len > POOL_MAX_SIZE {
|
||||
return Err(StoreError::DataTooLarge(data_len));
|
||||
}
|
||||
let addr = self.reserve(data_len)?;
|
||||
self.write(&addr, data)?;
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
fn free_element(&mut self, len: usize) -> Result<(StoreAddr, &mut [u8]), StoreError> {
|
||||
if len > POOL_MAX_SIZE {
|
||||
return Err(StoreError::DataTooLarge(len));
|
||||
}
|
||||
let addr = self.reserve(len)?;
|
||||
let raw_pos = self.raw_pos(&addr).unwrap();
|
||||
let block =
|
||||
&mut self.pool.get_mut(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + len];
|
||||
Ok((addr, block))
|
||||
}
|
||||
|
||||
fn modify(&mut self, addr: &StoreAddr) -> Result<&mut [u8], StoreError> {
|
||||
let curr_size = self.addr_check(addr)?;
|
||||
let raw_pos = self.raw_pos(addr).unwrap();
|
||||
let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()
|
||||
[raw_pos..raw_pos + curr_size];
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
fn modify_with_guard(&mut self, addr: StoreAddr) -> PoolRwGuard {
|
||||
PoolRwGuard::new(self, addr)
|
||||
}
|
||||
|
||||
fn read(&self, addr: &StoreAddr) -> Result<&[u8], StoreError> {
|
||||
let curr_size = self.addr_check(addr)?;
|
||||
let raw_pos = self.raw_pos(addr).unwrap();
|
||||
let block =
|
||||
&self.pool.get(addr.pool_idx as usize).unwrap()[raw_pos..raw_pos + curr_size];
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
fn read_with_guard(&mut self, addr: StoreAddr) -> PoolGuard {
|
||||
PoolGuard::new(self, addr)
|
||||
}
|
||||
|
||||
fn delete(&mut self, addr: StoreAddr) -> Result<(), StoreError> {
|
||||
self.addr_check(&addr)?;
|
||||
let block_size = self.pool_cfg.cfg.get(addr.pool_idx as usize).unwrap().1;
|
||||
let raw_pos = self.raw_pos(&addr).unwrap();
|
||||
let block = &mut self.pool.get_mut(addr.pool_idx as usize).unwrap()
|
||||
[raw_pos..raw_pos + block_size];
|
||||
let size_list = self.sizes_lists.get_mut(addr.pool_idx as usize).unwrap();
|
||||
size_list[addr.packet_idx as usize] = STORE_FREE;
|
||||
block.fill(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_element_at(&self, addr: &StoreAddr) -> Result<bool, StoreError> {
|
||||
self.validate_addr(addr)?;
|
||||
let pool_idx = addr.pool_idx as usize;
|
||||
let size_list = self.sizes_lists.get(pool_idx).unwrap();
|
||||
let curr_size = size_list[addr.packet_idx as usize];
|
||||
if curr_size == STORE_FREE {
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -539,17 +546,17 @@ mod tests {
|
||||
// Values where number of buckets is 0 or size is too large should be removed
|
||||
let mut pool_cfg = PoolCfg::new(vec![(0, 0), (1, 0), (2, LocalPool::MAX_SIZE)]);
|
||||
pool_cfg.sanitize();
|
||||
assert_eq!(pool_cfg.cfg, vec![(1, 0)]);
|
||||
assert_eq!(pool_cfg.cfg(), vec![(1, 0)]);
|
||||
// Entries should be ordered according to bucket size
|
||||
pool_cfg = PoolCfg::new(vec![(16, 6), (32, 3), (8, 12)]);
|
||||
pool_cfg.sanitize();
|
||||
assert_eq!(pool_cfg.cfg, vec![(32, 3), (16, 6), (8, 12)]);
|
||||
assert_eq!(pool_cfg.cfg(), vec![(32, 3), (16, 6), (8, 12)]);
|
||||
// Unstable sort is used, so order of entries with same block length should not matter
|
||||
pool_cfg = PoolCfg::new(vec![(12, 12), (14, 16), (10, 12)]);
|
||||
pool_cfg.sanitize();
|
||||
assert!(
|
||||
pool_cfg.cfg == vec![(12, 12), (10, 12), (14, 16)]
|
||||
|| pool_cfg.cfg == vec![(10, 12), (12, 12), (14, 16)]
|
||||
pool_cfg.cfg() == vec![(12, 12), (10, 12), (14, 16)]
|
||||
|| pool_cfg.cfg() == vec![(10, 12), (12, 12), (14, 16)]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,10 @@ use crate::pus::{
|
||||
EcssTcReceiver, EcssTmSender, PartialPusHandlingError, PusPacketHandlerResult,
|
||||
PusPacketHandlingError, PusServiceBase, PusServiceHandler,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use spacepackets::ecss::event::Subservice;
|
||||
use spacepackets::ecss::tc::PusTcReader;
|
||||
use spacepackets::ecss::PusPacket;
|
||||
use std::boxed::Box;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
pub struct PusService5EventHandler {
|
||||
|
@ -8,18 +8,22 @@ use core::fmt::{Display, Formatter};
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
#[cfg(feature = "alloc")]
|
||||
use dyn_clone::DynClone;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
use spacepackets::ecss::tc::{PusTcCreator, PusTcReader};
|
||||
use spacepackets::ecss::tm::PusTmCreator;
|
||||
use spacepackets::ecss::PusError;
|
||||
use spacepackets::{ByteConversionError, SizeMissmatch, SpHeader};
|
||||
use std::error::Error;
|
||||
|
||||
pub mod event;
|
||||
pub mod event_man;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod event_srv;
|
||||
pub mod hk;
|
||||
pub mod mode;
|
||||
pub mod scheduler;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod scheduler_srv;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod test;
|
||||
|
@ -2,22 +2,18 @@
|
||||
//!
|
||||
//! The core data structure of this module is the [PusScheduler]. This structure can be used
|
||||
//! to perform the scheduling of telecommands like specified in the ECSS standard.
|
||||
use crate::pool::{StoreAddr, StoreError};
|
||||
use core::fmt::{Debug, Display, Formatter};
|
||||
use core::time::Duration;
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
use spacepackets::ecss::scheduling::TimeWindowType;
|
||||
use spacepackets::ecss::tc::{GenericPusTcSecondaryHeader, IsPusTelecommand};
|
||||
use spacepackets::ecss::PusError;
|
||||
use spacepackets::time::{CcsdsTimeProvider, TimestampError, UnixTimestamp};
|
||||
use spacepackets::time::{CcsdsTimeProvider, TimestampError};
|
||||
use spacepackets::CcsdsPacket;
|
||||
#[cfg(feature = "std")]
|
||||
use std::error::Error;
|
||||
|
||||
//#[cfg(feature = "std")]
|
||||
//pub use std_mod::*;
|
||||
|
||||
use crate::pool::StoreAddr;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use alloc_mod::*;
|
||||
|
||||
@ -61,78 +57,6 @@ impl RequestId {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ScheduleError {
|
||||
PusError(PusError),
|
||||
/// The release time is within the time-margin added on top of the current time.
|
||||
/// The first parameter is the current time, the second one the time margin, and the third one
|
||||
/// the release time.
|
||||
ReleaseTimeInTimeMargin(UnixTimestamp, Duration, UnixTimestamp),
|
||||
/// Nested time-tagged commands are not allowed.
|
||||
NestedScheduledTc,
|
||||
StoreError(StoreError),
|
||||
TcDataEmpty,
|
||||
TimestampError(TimestampError),
|
||||
WrongSubservice,
|
||||
WrongService,
|
||||
}
|
||||
|
||||
impl Display for ScheduleError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
ScheduleError::PusError(e) => {
|
||||
write!(f, "Pus Error: {e}")
|
||||
}
|
||||
ScheduleError::ReleaseTimeInTimeMargin(current_time, margin, timestamp) => {
|
||||
write!(
|
||||
f,
|
||||
"Error: time margin too short, current time: {current_time:?}, time margin: {margin:?}, release time: {timestamp:?}"
|
||||
)
|
||||
}
|
||||
ScheduleError::NestedScheduledTc => {
|
||||
write!(f, "Error: nested scheduling is not allowed")
|
||||
}
|
||||
ScheduleError::StoreError(e) => {
|
||||
write!(f, "Store Error: {e}")
|
||||
}
|
||||
ScheduleError::TcDataEmpty => {
|
||||
write!(f, "Error: empty Tc Data field")
|
||||
}
|
||||
ScheduleError::TimestampError(e) => {
|
||||
write!(f, "Timestamp Error: {e}")
|
||||
}
|
||||
ScheduleError::WrongService => {
|
||||
write!(f, "Error: Service not 11.")
|
||||
}
|
||||
ScheduleError::WrongSubservice => {
|
||||
write!(f, "Error: Subservice not 4.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PusError> for ScheduleError {
|
||||
fn from(e: PusError) -> Self {
|
||||
ScheduleError::PusError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoreError> for ScheduleError {
|
||||
fn from(e: StoreError) -> Self {
|
||||
ScheduleError::StoreError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TimestampError> for ScheduleError {
|
||||
fn from(e: TimestampError) -> Self {
|
||||
ScheduleError::TimestampError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for ScheduleError {}
|
||||
|
||||
/// This is the format stored internally by the TC scheduler for each scheduled telecommand.
|
||||
/// It consists of the address of that telecommand in the TC pool and a request ID.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@ -156,11 +80,6 @@ impl TcInfo {
|
||||
}
|
||||
}
|
||||
|
||||
enum DeletionResult {
|
||||
WithoutStoreDeletion(Option<StoreAddr>),
|
||||
WithStoreDeletion(Result<bool, StoreError>),
|
||||
}
|
||||
|
||||
pub struct TimeWindow<TimeProvder> {
|
||||
time_window_type: TimeWindowType,
|
||||
start_time: Option<TimeProvder>,
|
||||
@ -217,8 +136,8 @@ impl<TimeProvider: CcsdsTimeProvider + Clone> TimeWindow<TimeProvider> {
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod alloc_mod {
|
||||
use super::*;
|
||||
use crate::pool::{PoolProvider, StoreAddr, StoreError};
|
||||
use crate::pus::scheduler::{DeletionResult, RequestId, ScheduleError, TcInfo, TimeWindow};
|
||||
use alloc::collections::btree_map::{Entry, Range};
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec;
|
||||
@ -235,6 +154,83 @@ pub mod alloc_mod {
|
||||
#[cfg(feature = "std")]
|
||||
use std::time::SystemTimeError;
|
||||
|
||||
enum DeletionResult {
|
||||
WithoutStoreDeletion(Option<StoreAddr>),
|
||||
WithStoreDeletion(Result<bool, StoreError>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum ScheduleError {
|
||||
PusError(PusError),
|
||||
/// The release time is within the time-margin added on top of the current time.
|
||||
/// The first parameter is the current time, the second one the time margin, and the third one
|
||||
/// the release time.
|
||||
ReleaseTimeInTimeMargin(UnixTimestamp, Duration, UnixTimestamp),
|
||||
/// Nested time-tagged commands are not allowed.
|
||||
NestedScheduledTc,
|
||||
StoreError(StoreError),
|
||||
TcDataEmpty,
|
||||
TimestampError(TimestampError),
|
||||
WrongSubservice,
|
||||
WrongService,
|
||||
}
|
||||
|
||||
impl Display for ScheduleError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
ScheduleError::PusError(e) => {
|
||||
write!(f, "Pus Error: {e}")
|
||||
}
|
||||
ScheduleError::ReleaseTimeInTimeMargin(current_time, margin, timestamp) => {
|
||||
write!(
|
||||
f,
|
||||
"Error: time margin too short, current time: {current_time:?}, time margin: {margin:?}, release time: {timestamp:?}"
|
||||
)
|
||||
}
|
||||
ScheduleError::NestedScheduledTc => {
|
||||
write!(f, "Error: nested scheduling is not allowed")
|
||||
}
|
||||
ScheduleError::StoreError(e) => {
|
||||
write!(f, "Store Error: {e}")
|
||||
}
|
||||
ScheduleError::TcDataEmpty => {
|
||||
write!(f, "Error: empty Tc Data field")
|
||||
}
|
||||
ScheduleError::TimestampError(e) => {
|
||||
write!(f, "Timestamp Error: {e}")
|
||||
}
|
||||
ScheduleError::WrongService => {
|
||||
write!(f, "Error: Service not 11.")
|
||||
}
|
||||
ScheduleError::WrongSubservice => {
|
||||
write!(f, "Error: Subservice not 4.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PusError> for ScheduleError {
|
||||
fn from(e: PusError) -> Self {
|
||||
ScheduleError::PusError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StoreError> for ScheduleError {
|
||||
fn from(e: StoreError) -> Self {
|
||||
ScheduleError::StoreError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TimestampError> for ScheduleError {
|
||||
fn from(e: TimestampError) -> Self {
|
||||
ScheduleError::TimestampError(e)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Error for ScheduleError {}
|
||||
|
||||
/// This is the core data structure for scheduling PUS telecommands with [alloc] support.
|
||||
///
|
||||
/// It is assumed that the actual telecommand data is stored in a separate TC pool offering
|
||||
|
Loading…
Reference in New Issue
Block a user