diff --git a/src/fs.rs b/src/fs.rs index bb11249d1..4e1ef87b1 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -12,7 +12,7 @@ pub type Bytes = generic_array::GenericArray; 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( + &self, + path: &Path, + pos: OpenSeekFrom, + ) -> Result<(heapless::Vec, usize)> { + let mut contents: heapless::Vec = 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)] diff --git a/src/io.rs b/src/io.rs index 9d18ce80f..9f0f6ec70 100644 --- a/src/io.rs +++ b/src/io.rs @@ -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 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. diff --git a/src/tests.rs b/src/tests.rs index a28f2b260..14a450a6e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -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();