Skip to content
Merged
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
Add chunked file API
  • Loading branch information
sosthene-nitrokey committed Jun 12, 2023
commit 1e89dc13d08d79054f357852c5a7e75523ce291b
35 changes: 34 additions & 1 deletion src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub type Bytes<SIZE> = generic_array::GenericArray<u8, SIZE>;

use crate::{
driver,
io::{self, Result},
io::{self, OpenSeekFrom, Result},
path::{Path, PathBuf},
};

Expand Down Expand Up @@ -1248,6 +1248,24 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> {
Ok(contents)
}

/// Read a chunk of a file into a bytes vector
/// Returns the data and the size of the file
pub fn read_chunk<const N: usize>(
&self,
path: &Path,
pos: OpenSeekFrom,
) -> Result<(heapless::Vec<u8, N>, usize)> {
let mut contents: heapless::Vec<u8, N> = Default::default();
contents.resize_default(contents.capacity()).unwrap();
let file_len = File::open_and_then(self, path, |file| {
file.seek(pos.into())?;
let read_n = file.read(&mut contents)?;
contents.truncate(read_n);
file.len()
})?;
Ok((contents, file_len))
}

/// Write a slice as the entire contents of a file.
///
/// This function will create a file if it does not exist,
Expand All @@ -1261,6 +1279,21 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> {
})?;
Ok(())
}

/// Write a slice as a chunk of a file.
///
/// This function will not create a file if it does not exist,
/// it will fail if the file is not already large enough with regard to the `pos` parameter
pub fn write_chunk(&self, path: &Path, contents: &[u8], pos: OpenSeekFrom) -> Result<()> {
#[cfg(test)]
println!("writing {:?}", path);
File::open_and_then(self, path, |file| {
use io::Write;
file.seek(pos.into())?;
file.write_all(contents)
})?;
Ok(())
}
}

#[cfg(test)]
Expand Down
18 changes: 18 additions & 0 deletions src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ impl SeekFrom {
}
}

/// Enumeration of possible methods to seek within an file that was just opened
/// Used in the [`read_chunk`](crate::fs::Filesystem::read_chunk) and [`write_chunk`](crate::fs::Filesystem::write_chunk) methods,
/// Where [`SeekFrom::Current`](SeekFrom::Current) would not make sense.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum OpenSeekFrom {
Start(u32),
End(i32),
}

impl From<OpenSeekFrom> for SeekFrom {
fn from(value: OpenSeekFrom) -> Self {
match value {
OpenSeekFrom::Start(o) => Self::Start(o),
OpenSeekFrom::End(o) => Self::End(o),
}
}
}

/** The `Seek` trait provides a cursor which can be moved within a file.

It is possible to seek relative to either end or the current offset.
Expand Down
41 changes: 36 additions & 5 deletions src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use generic_array::typenum::consts;
use crate::{
driver,
fs::{Attribute, File, Filesystem},
io::{Error, Read, Result, SeekFrom},
path::Path,
io::{Error, OpenSeekFrom, Read, Result, SeekFrom},
};

ram_storage!(
Expand Down Expand Up @@ -62,10 +61,8 @@ fn format() {
// should succeed
assert!(Filesystem::format(&mut storage).is_ok());
// should succeed now that storage is formatted
let fs = Filesystem::mount(&mut alloc, &mut storage).unwrap();
let _fs = Filesystem::mount(&mut alloc, &mut storage).unwrap();
// check there are no segfaults
drop(fs);
drop(storage);
}

// #[macro_use]
Expand Down Expand Up @@ -312,6 +309,40 @@ fn test_seek() {
.unwrap();
}

#[test]
fn test_chunked() {
let mut backend = OtherRam::default();
let mut storage = OtherRamStorage::new(&mut backend);
let path = b"test_chunked.txt\0".try_into().unwrap();
let hello = b"hello world";
let more = b"but wait, there's more";

Filesystem::format(&mut storage).unwrap();
Filesystem::mount_and_then(&mut storage, |fs| {
fs.write(path, hello)?;
let (data, len) = fs.read_chunk::<1024>(path, OpenSeekFrom::Start(0)).unwrap();
assert_eq!(&data, hello);
assert_eq!(len, hello.len());
let (data, len) = fs.read_chunk::<1024>(path, OpenSeekFrom::Start(3)).unwrap();
assert_eq!(&data, &hello[3..]);
assert_eq!(len, hello.len());
fs.write_chunk(path, more, OpenSeekFrom::End(0)).unwrap();
let (data, len) = fs
.read_chunk::<1024>(path, OpenSeekFrom::Start(hello.len() as u32))
.unwrap();
assert_eq!(&data, more);
assert_eq!(len, hello.len() + more.len());
let (data, len) = fs
.read_chunk::<1024>(path, OpenSeekFrom::End(-(more.len() as i32)))
.unwrap();
assert_eq!(&data, more);
assert_eq!(len, hello.len() + more.len());

Ok(())
})
.unwrap();
}

#[test]
fn test_file_set_len() {
let mut backend = OtherRam::default();
Expand Down