Skip to content

Commit

Permalink
core: Make heapless support optional and support multiple versions
Browse files Browse the repository at this point in the history
The DynFile::read_to_end, DynFilesystem::read and
DynFilesystem::read_chunk methods use a heapless::Vec to store a
flexible amount of data.  This patch introduces a Vec trait that
abstracts over different versions of heapless so that we can optionally
support multiple heapless versions at the same time.

Fixes: #77
  • Loading branch information
robin-nitrokey committed Sep 13, 2024
1 parent e225f06 commit 932a40f
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ jobs:

- name: Check
run: |
cargo check --package littlefs2-core
cargo check --package littlefs2-core --features heapless07
cargo check --package littlefs2-core --features heapless08
cargo check --package littlefs2-core --features serde
cargo check --package littlefs2-core --all-features
cargo check --workspace --all-targets
cargo check --workspace --all-targets --all-features
cargo check --workspace --all-targets --no-default-features
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Change the `set_attribute` function in `DynFilesystem` and `Filesystem` to accept an ID and a slice instead of an `Attribute`.
- Add a buffer argument to the `attribute` function in `DynFilesystem` and `Filesystem` and return a slice of that buffer containing the read data.
- Change the `Attribute` struct to store a slice with the read data and the total size of the attribute on the filesystem.
- Introduce `object_safe::Vec` trait and change `DynFile::read_to_end`, `DynFilesystem::read` and `DynFilesstem::read_chunk` to be generic over a `Vec` implementation to support multiple `heapless` versions (disabled by default).

### Removed

Expand Down
5 changes: 4 additions & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ repository.workspace = true

[dependencies]
bitflags = "2.6.0"
heapless = "0.7"
heapless07 = { package = "heapless", version = "0.7", optional = true }
heapless08 = { package = "heapless", version = "0.8", optional = true }
serde = { version = "1", default-features = false, features = ["derive"], optional = true }

[features]
heapless07 = ["dep:heapless07"]
heapless08 = ["dep:heapless08"]
serde = ["dep:serde"]
2 changes: 1 addition & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod path;

pub use fs::{Attribute, DirEntry, FileOpenFlags, FileType, Metadata};
pub use io::{Error, OpenSeekFrom, Read, Result, Seek, SeekFrom, Write};
pub use object_safe::{DirEntriesCallback, DynFile, DynFilesystem, FileCallback, Predicate};
pub use object_safe::{DirEntriesCallback, DynFile, DynFilesystem, FileCallback, Predicate, Vec};
pub use path::{Ancestors, Iter, Path, PathBuf, PathError};

/// Creates a path from a string without a trailing null.
Expand Down
52 changes: 37 additions & 15 deletions core/src/object_safe.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use heapless::Vec;

use crate::{
fs::{Attribute, DirEntry, FileOpenFlags, Metadata},
io::{Error, OpenSeekFrom, Read, Result, Seek, Write},
Expand All @@ -15,6 +13,33 @@ pub type DirEntriesCallback<'a, R = ()> =
pub type FileCallback<'a, R = ()> = &'a mut dyn FnMut(&dyn DynFile) -> Result<R>;
pub type Predicate<'a> = &'a dyn Fn(&DirEntry) -> bool;

pub trait Vec: Default + AsRef<[u8]> + AsMut<[u8]> {
fn resize_to_capacity(&mut self);
fn truncate(&mut self, n: usize);
}

#[cfg(feature = "heapless07")]
impl<const N: usize> Vec for heapless07::Vec<u8, N> {
fn resize_to_capacity(&mut self) {
self.resize_default(self.capacity()).unwrap();
}

fn truncate(&mut self, n: usize) {
heapless07::Vec::truncate(self, n)
}
}

#[cfg(feature = "heapless08")]
impl<const N: usize> Vec for heapless08::Vec<u8, N> {
fn resize_to_capacity(&mut self) {
self.resize_default(self.capacity()).unwrap();
}

fn truncate(&mut self, n: usize) {
heapless08::Vec::truncate(self, n)
}
}

/// Object-safe trait for files.
///
/// The methods for opening files cannot be implemented in this trait. Use these methods instead:
Expand All @@ -29,10 +54,10 @@ pub trait DynFile: Read + Seek + Write {
}

impl dyn DynFile + '_ {
pub fn read_to_end<const N: usize>(&self, buf: &mut Vec<u8, N>) -> Result<usize> {
let had = buf.len();
buf.resize_default(buf.capacity()).unwrap();
let read = self.read(&mut buf[had..])?;
pub fn read_to_end<V: Vec>(&self, buf: &mut V) -> Result<usize> {
let had = buf.as_ref().len();
buf.resize_to_capacity();
let read = self.read(&mut buf.as_mut()[had..])?;
buf.truncate(had + read);
Ok(read)
}
Expand Down Expand Up @@ -87,24 +112,21 @@ pub trait DynFilesystem {
}

impl dyn DynFilesystem + '_ {
pub fn read<const N: usize>(&self, path: &Path) -> Result<Vec<u8, N>> {
let mut contents = Vec::new();
pub fn read<V: Vec>(&self, path: &Path) -> Result<V> {
let mut contents = V::default();
self.open_file_and_then(path, &mut |file| {
file.read_to_end(&mut contents)?;
Ok(())
})?;
Ok(contents)
}

pub fn read_chunk<const N: usize>(
&self,
path: &Path,
pos: OpenSeekFrom,
) -> Result<(Vec<u8, N>, usize)> {
let mut contents = Vec::new();
pub fn read_chunk<V: Vec>(&self, path: &Path, pos: OpenSeekFrom) -> Result<(V, usize)> {
let mut contents = V::default();
contents.resize_to_capacity();
let file_len = self.open_file_and_then(path, &mut |file| {
file.seek(pos.into())?;
let read_n = file.read(&mut contents)?;
let read_n = file.read(contents.as_mut())?;
contents.truncate(read_n);
file.len()
})?;
Expand Down

0 comments on commit 932a40f

Please sign in to comment.