Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ 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, Vec};
pub use object_safe::{
DirEntriesCallback, DirIterationTell, DirIterator, 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
28 changes: 26 additions & 2 deletions core/src/object_safe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use crate::{
const _: Option<&dyn DynFile> = None;
const _: Option<&dyn DynFilesystem> = None;

pub type DirEntriesCallback<'a, R = ()> =
&'a mut dyn FnMut(&mut dyn Iterator<Item = Result<DirEntry>>) -> Result<R>;
pub type DirEntriesCallback<'a, R = ()> = &'a mut dyn FnMut(&mut dyn DirIterator) -> Result<R>;
pub type FileCallback<'a, R = ()> = &'a mut dyn FnMut(&dyn DynFile) -> Result<R>;
pub type Predicate<'a> = &'a dyn Fn(&DirEntry) -> bool;

Expand Down Expand Up @@ -87,6 +86,31 @@ impl dyn DynFile + '_ {
}
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct DirIterationTell {
#[doc(hidden)]
pub tell_result: u32,
}

pub trait DirIterator: Iterator<Item = Result<DirEntry>> {
/// Return the position of the directory
///
/// The returned offset is only meant to be consumed by seek and may not make
/// sense, but does indicate the current position in the directory iteration.
///
/// Returns the position of the directory, which can be returned to using [`seek`](Self::seek).
fn tell(&self) -> Result<DirIterationTell>;

/// Change the position of the directory
///
/// The new off must be a value previous returned from [`tell`](Self::tell) and specifies
/// an absolute offset in the directory seek.
fn seek(&self, state: DirIterationTell) -> Result<()>;

/// Change the position of the directory to the beginning of the directory
fn rewind(&self) -> Result<()>;
}

/// Object-safe trait for filesystems.
///
/// The following methods cannot support generic return types in the callbacks:
Expand Down
64 changes: 63 additions & 1 deletion src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@ use core::{
mem, slice,
};
use generic_array::typenum::marker_traits::Unsigned;
use littlefs2_core::DirIterator;
use littlefs2_sys as ll;

// so far, don't need `heapless-bytes`.
pub type Bytes<SIZE> = generic_array::GenericArray<u8, SIZE>;

pub use littlefs2_core::{Attribute, DirEntry, FileOpenFlags, FileType, Metadata};
pub use littlefs2_core::{
Attribute, DirEntry, DirIterationTell, FileOpenFlags, FileType, Metadata,
};

use crate::{
driver,
Expand Down Expand Up @@ -940,6 +943,65 @@ pub struct ReadDir<'a, 'b, S: driver::Storage> {
path: &'b Path,
}

impl<S: driver::Storage> DirIterator for ReadDir<'_, '_, S> {
/// Return the position of the directory
///
/// The returned offset is only meant to be consumed by seek and may not make
/// sense, but does indicate the current position in the directory iteration.
///
/// Returns the position of the directory, which can be returned to using [`seek`](Self::seek).
fn tell(&self) -> Result<DirIterationTell> {
let value = unsafe {
ll::lfs_dir_tell(
&mut self.fs.alloc.borrow_mut().state,
&mut (**self.alloc.borrow_mut()).state,
)
};
if let Ok(value_positive) = value.try_into() {
Ok(DirIterationTell {
tell_result: value_positive,
})
} else {
Err(result_from((), value as _).unwrap_err())
}
}

/// Change the position of the directory
///
/// The new off must be a value previous returned from [`tell`](Self::tell) and specifies
/// an absolute offset in the directory seek.
fn seek(&self, state: DirIterationTell) -> Result<()> {
let value = unsafe {
ll::lfs_dir_seek(
&mut self.fs.alloc.borrow_mut().state,
&mut (**self.alloc.borrow_mut()).state,
state.tell_result,
)
};
if value < 0 {
Err(result_from((), value as _).unwrap_err())
} else {
Ok(())
}
}

/// Change the position of the directory to the beginning of the directory
fn rewind(&self) -> Result<()> {
let res = unsafe {
ll::lfs_dir_rewind(
&mut self.fs.alloc.borrow_mut().state,
&mut (**self.alloc.borrow_mut()).state,
)
};

if res < 0 {
Err(result_from((), res).unwrap_err())
} else {
Ok(())
}
}
}

impl<S: driver::Storage> Iterator for ReadDir<'_, '_, S> {
type Item = Result<DirEntry>;

Expand Down
50 changes: 39 additions & 11 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ use generic_array::typenum::consts;
use crate::{
fs::{Attribute, File, Filesystem},
io::{Error, OpenSeekFrom, Read, Result, SeekFrom},
path, BACKEND_VERSION, DISK_VERSION,
path,
path::PathBuf,
BACKEND_VERSION, DISK_VERSION,
};
use littlefs2_core::DirIterator;

ram_storage!(
name = OtherRamStorage,
Expand Down Expand Up @@ -492,31 +495,56 @@ fn test_iter_dirs() {
file.set_len(37)?;
fs.create_file_and_then(path!("/tmp/file.b"), |file| file.set_len(42))
})?;
let mut tells = Vec::new();

fs.read_dir_and_then(path!("/tmp"), |dir| {
fs.read_dir_and_then(path!("/tmp"), |mut dir| {
let mut found_files: usize = 0;
let mut sizes = [0usize; 4];
let mut i = 0;

for (i, entry) in dir.enumerate() {
tells.push(dir.tell()?);
while let Some(entry) = dir.next() {
tells.push(dir.tell()?);
let entry = entry?;

// assert_eq!(entry.file_name(), match i {
// 0 => b".\0",
// 1 => b"..\0",
// 2 => b"file.a\0",
// 3 => b"file.b\0",
// _ => panic!("oh noes"),
// });
let expected_name = match i {
0 => PathBuf::from(path!(".")),
1 => PathBuf::from(path!("..")),
2 => PathBuf::from(path!("file.a")),
3 => PathBuf::from(path!("file.b")),
_ => panic!("oh noes"),
};

assert_eq!(entry.file_name(), &*expected_name);

sizes[i] = entry.metadata().len();
found_files += 1;
i += 1;
}

assert_eq!(sizes, [0, 0, 37, 42]);
assert_eq!(found_files, 4);

assert_eq!(tells.len(), 5);

for (i, tell) in tells.iter().enumerate() {
dir.rewind().unwrap();
let mut found_files: usize = 0;
let mut sizes = Vec::new();
dir.seek(*tell)?;

for entry in &mut dir {
let entry = entry?;
sizes.push(entry.metadata().len());
found_files += 1;
}

assert_eq!(sizes, [0, 0, 37, 42][i..]);
assert_eq!(found_files, 4 - i);
}
Ok(())
})
.unwrap();
Ok(())
})
.unwrap();
}
Expand Down
Binary file added tests-old-fs/empty.bin
Binary file not shown.
Binary file added tests-old-fs/recurse.bin
Binary file not shown.
Binary file added tests-old-fs/root.bin
Binary file not shown.
Loading