Add chunked file API

This commit is contained in:
Sosthène Guédon
2023-06-12 11:07:14 +02:00
committed by sosthene-nitrokey
parent 154e054329
commit ec304b8663
3 changed files with 88 additions and 6 deletions
+34 -1
View File
@@ -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},
};
@@ -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,
@@ -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)]
+18
View File
@@ -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.
+36 -5
View File
@@ -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!(
@@ -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]
@@ -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();