external_memory_tools/
lib.rs

1//! Traits for handling bytes data from external memory.
2//!
3//! Currently only read functionality is supported.
4#![no_std]
5#![deny(unused_crate_dependencies)]
6
7#[cfg(not(feature = "std"))]
8extern crate core;
9
10#[cfg(not(feature = "std"))]
11#[macro_use]
12extern crate alloc;
13
14#[cfg(feature = "std")]
15#[macro_use]
16extern crate std;
17
18#[cfg(not(feature = "std"))]
19use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
20
21#[cfg(not(feature = "std"))]
22use alloc::string::String;
23
24#[cfg(feature = "std")]
25use std::{
26    error::Error,
27    fmt::{Debug, Display, Formatter, Result as FmtResult},
28    string::String,
29};
30
31/// External addressable memory.
32pub trait ExternalMemory: Debug {
33    /// Errors specific to memory accessing.
34    type ExternalMemoryError: Debug + Display + Eq + PartialEq;
35}
36
37/// `ExternalMemory` could also be applied to regular RAM.
38impl ExternalMemory for () {
39    type ExternalMemoryError = NoEntries;
40}
41
42/// Empty error enum, for cases with fault-free memory access.
43#[derive(Debug, Eq, PartialEq)]
44pub enum NoEntries {}
45
46impl Display for NoEntries {
47    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
48        write!(f, "")
49    }
50}
51
52/// Bytes access through [`ExternalMemory`].
53///
54/// Could be implemented, for example, for a combination of an address in
55/// external memory and corresponding bytes slice length.
56pub trait AddressableBuffer<E: ExternalMemory>: Sized {
57    /// Bytes read from buffer.
58    type ReadBuffer: AsRef<[u8]>;
59
60    /// Total length of the addressable buffer.
61    fn total_len(&self) -> usize;
62
63    /// Read bytes slice of known length at known relative position.
64    ///
65    /// Important to keep `read_slice`, **not `read_byte`** as a basic reader
66    /// tool, because of commonly occuring pages in memory.
67    fn read_slice(
68        &self,
69        ext_memory: &mut E,
70        position: usize,
71        slice_len: usize,
72    ) -> Result<Self::ReadBuffer, BufferError<E>>;
73
74    /// Read single byte at known position.
75    fn read_byte(&self, ext_memory: &mut E, position: usize) -> Result<u8, BufferError<E>> {
76        let byte_slice = self.read_slice(ext_memory, position, 1)?;
77        Ok(byte_slice.as_ref()[0])
78    }
79
80    /// Restrict the length of the addressable buffer.
81    fn limit_length(&self, new_len: usize) -> Result<Self, BufferError<E>>;
82}
83
84/// `AddressableBuffer` could be also implemented for regular bytes slices.
85impl<'a, E: ExternalMemory> AddressableBuffer<E> for &'a [u8] {
86    type ReadBuffer = &'a [u8];
87    fn total_len(&self) -> usize {
88        self.len()
89    }
90    fn read_slice(
91        &self,
92        _ext_memory: &mut E,
93        position: usize,
94        slice_len: usize,
95    ) -> Result<Self::ReadBuffer, BufferError<E>> {
96        if self.len() < position {
97            return Err(BufferError::OutOfRange {
98                position,
99                total_length: self.len(),
100            });
101        }
102        match self.get(position..position + slice_len) {
103            Some(a) => Ok(a),
104            None => Err(BufferError::DataTooShort {
105                position,
106                minimal_length: slice_len,
107            }),
108        }
109    }
110    fn limit_length(&self, new_len: usize) -> Result<Self, BufferError<E>> {
111        self.get(..new_len).ok_or(BufferError::DataTooShort {
112            position: 0,
113            minimal_length: new_len,
114        })
115    }
116}
117
118/// Errors in buffer access.
119#[derive(Debug, Eq, PartialEq)]
120pub enum BufferError<E: ExternalMemory> {
121    DataTooShort {
122        position: usize,
123        minimal_length: usize,
124    },
125    External(E::ExternalMemoryError),
126    OutOfRange {
127        position: usize,
128        total_length: usize,
129    },
130}
131
132impl<E: ExternalMemory> BufferError<E> {
133    fn error_text(&self) -> String {
134        match &self {
135            BufferError::DataTooShort { position, minimal_length } => format!("Data is too short for expected content. Expected at least {minimal_length} element(s) after position {position}."),
136            BufferError::External(e) => format!("Error accessing external memory. {e}"),
137            BufferError::OutOfRange { position, total_length } => format!("Position {position} is out of range for data length {total_length}."),
138        }
139    }
140}
141
142impl<E: ExternalMemory> Display for BufferError<E> {
143    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
144        write!(f, "{}", self.error_text())
145    }
146}
147
148#[cfg(feature = "std")]
149impl<E: ExternalMemory> Error for BufferError<E> {
150    fn source(&self) -> Option<&(dyn Error + 'static)> {
151        None
152    }
153}