#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(all(feature = "alloc", not(feature = "std")))]
extern crate alloc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::vec::Vec;
use crate::error::{CoreError, CoreResult};
use core::marker::PhantomData;
use num_traits::{Float, NumAssignOps, Zero};
pub use error_handling::{EmbeddedError, EmbeddedResult};
pub use memory_estimation::{estimate_stack_usage, MemoryRequirement};
pub use stack_array::{BufferFullError, FixedSizeBuffer, StackArray};
#[inline]
pub fn stack_add<T, const N: usize>(a: &StackArray<T, N>, b: &StackArray<T, N>) -> StackArray<T, N>
where
T: Float + NumAssignOps + Copy + Default,
{
let mut result = StackArray::new();
for i in 0..N {
result.data[i] = a.data[i] + b.data[i];
}
result
}
#[inline]
pub fn stack_mul<T, const N: usize>(a: &StackArray<T, N>, b: &StackArray<T, N>) -> StackArray<T, N>
where
T: Float + NumAssignOps + Copy + Default,
{
let mut result = StackArray::new();
for i in 0..N {
result.data[i] = a.data[i] * b.data[i];
}
result
}
#[inline]
pub fn stack_sum<T, const N: usize>(arr: &StackArray<T, N>) -> T
where
T: Float + NumAssignOps + Copy + Default + Zero,
{
let mut sum = T::zero();
for i in 0..N {
sum += arr.data[i];
}
sum
}
#[inline]
pub fn stack_mean<T, const N: usize>(arr: &StackArray<T, N>) -> T
where
T: Float + NumAssignOps + Copy + Default + Zero,
{
if N == 0 {
return T::zero();
}
stack_sum(arr) / T::from(N).expect("Failed to convert N to T")
}
pub mod memory_estimation {
use core::mem;
#[derive(Debug, Copy, Clone)]
pub struct MemoryRequirement {
pub stack_bytes: usize,
pub heap_bytes: usize,
pub flash_bytes: usize,
}
impl MemoryRequirement {
pub const fn new(stack_bytes: usize, heap_bytes: usize, flash_bytes: usize) -> Self {
Self {
stack_bytes,
heap_bytes,
flash_bytes,
}
}
pub const fn basic() -> Self {
Self::new(1024, 0, 4096)
}
pub const fn signal_processing() -> Self {
Self::new(4096, 0, 16384)
}
pub const fn linalg() -> Self {
Self::new(8192, 0, 32768)
}
}
pub const fn estimate_stack_usage<T>(count: usize) -> usize {
mem::size_of::<T>() * count
}
}
pub mod error_handling {
use core::fmt;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum EmbeddedError {
BufferOverflow,
BufferUnderflow,
InvalidSize,
OutOfMemory,
NumericalError,
NotSupported,
}
impl fmt::Display for EmbeddedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EmbeddedError::BufferOverflow => write!(f, "Buffer overflow"),
EmbeddedError::BufferUnderflow => write!(f, "Buffer underflow"),
EmbeddedError::InvalidSize => write!(f, "Invalid size"),
EmbeddedError::OutOfMemory => write!(f, "Out of memory"),
EmbeddedError::NumericalError => write!(f, "Numerical error"),
EmbeddedError::NotSupported => write!(f, "Not supported in no_std mode"),
}
}
}
pub type EmbeddedResult<T> = Result<T, EmbeddedError>;
}
pub mod stack_array {
use core::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BufferFullError;
impl core::fmt::Display for BufferFullError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "buffer is full")
}
}
#[derive(Debug, Clone)]
pub struct StackArray<T, const N: usize> {
pub(super) data: [T; N],
_marker: PhantomData<T>,
}
impl<T: Copy + Default, const N: usize> StackArray<T, N> {
#[inline]
pub fn new() -> Self {
Self {
data: [T::default(); N],
_marker: PhantomData,
}
}
#[inline]
pub fn from_slice(slice: &[T]) -> Option<Self> {
if slice.len() != N {
return None;
}
let mut result = Self::new();
result.data.copy_from_slice(slice);
Some(result)
}
#[inline]
pub const fn len(&self) -> usize {
N
}
#[inline]
pub const fn is_empty(&self) -> bool {
N == 0
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.data
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.data
}
}
impl<T: Copy + Default, const N: usize> Default for StackArray<T, N> {
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> core::ops::Index<usize> for StackArray<T, N> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.data[index]
}
}
impl<T, const N: usize> core::ops::IndexMut<usize> for StackArray<T, N> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.data[index]
}
}
#[derive(Debug, Clone)]
pub struct FixedSizeBuffer<T, const N: usize> {
pub(super) data: [T; N],
len: usize,
_marker: PhantomData<T>,
}
impl<T: Copy + Default, const N: usize> FixedSizeBuffer<T, N> {
#[inline]
pub fn new() -> Self {
Self {
data: [T::default(); N],
len: 0,
_marker: PhantomData,
}
}
#[inline]
pub fn push(&mut self, value: T) -> Result<(), BufferFullError> {
if self.len >= N {
return Err(BufferFullError);
}
self.data[self.len] = value;
self.len += 1;
Ok(())
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
self.len -= 1;
Some(self.data[self.len])
}
#[inline]
pub const fn len(&self) -> usize {
self.len
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub const fn is_full(&self) -> bool {
self.len == N
}
#[inline]
pub const fn capacity(&self) -> usize {
N
}
#[inline]
pub fn clear(&mut self) {
self.len = 0;
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.data[..self.len]
}
}
impl<T: Copy + Default, const N: usize> Default for FixedSizeBuffer<T, N> {
fn default() -> Self {
Self::new()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stack_array_basic() {
let mut arr = StackArray::<f32, 8>::new();
arr[0] = 1.0;
arr[1] = 2.0;
assert_eq!(arr.len(), 8);
assert_eq!(arr[0], 1.0);
assert_eq!(arr[1], 2.0);
}
#[test]
fn test_fixed_size_buffer() {
let mut buffer = FixedSizeBuffer::<f32, 4>::new();
assert!(buffer.is_empty());
buffer.push(1.0).expect("Failed to push");
buffer.push(2.0).expect("Failed to push");
assert_eq!(buffer.len(), 2);
assert_eq!(buffer.pop(), Some(2.0));
assert_eq!(buffer.len(), 1);
}
#[test]
fn test_stack_operations() {
let mut a = StackArray::<f32, 4>::new();
let mut b = StackArray::<f32, 4>::new();
for i in 0..4 {
a[i] = i as f32;
b[i] = (i + 1) as f32;
}
let sum = stack_add(&a, &b);
assert_eq!(sum[0], 1.0);
assert_eq!(sum[1], 3.0);
let product = stack_mul(&a, &b);
assert_eq!(product[0], 0.0);
assert_eq!(product[1], 2.0);
}
#[test]
fn test_stack_statistics() {
let mut arr = StackArray::<f32, 4>::new();
arr[0] = 1.0;
arr[1] = 2.0;
arr[2] = 3.0;
arr[3] = 4.0;
let total = stack_sum(&arr);
assert_eq!(total, 10.0);
let avg = stack_mean(&arr);
assert_eq!(avg, 2.5);
}
}