From 7e94b3d8b0acf35e60d6ae2fa13d7a6c5ef9391e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sosth=C3=A8ne=20Gu=C3=A9don?= Date: Mon, 10 Mar 2025 10:40:00 +0100 Subject: [PATCH] Remove need for generic array and typenum constants This will also allow for more flexible `Storage` implementations that use `Vec` as a cache buffer and are not fixed to a single block size --- Cargo.toml | 5 +- examples/list.rs | 31 +++++++++--- src/consts.rs | 3 -- src/driver.rs | 117 ++++++++++++++++++++++++++++++++++++------- src/fs.rs | 118 ++++++++++++++++++++------------------------ src/lib.rs | 2 +- src/macros.rs | 104 ++++++++++++++++++++++---------------- src/object_safe.rs | 16 +++--- src/tests.rs | 60 +++++++++++----------- tests/test_serde.rs | 2 +- 10 files changed, 281 insertions(+), 177 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 58632039..397f1930 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,10 +22,13 @@ repository.workspace = true [package.metadata.docs.rs] all-features = true +[[example]] +name = "list" +required-features = ["alloc"] + [dependencies] bitflags = "2.9.0" delog = "0.1.0" -generic-array = "0.14" heapless = "0.7" littlefs2-core = { version = "0.1", path = "core" } littlefs2-sys = { version = "0.3.1", features = ["multiversion"] } diff --git a/examples/list.rs b/examples/list.rs index 06300945..24721799 100644 --- a/examples/list.rs +++ b/examples/list.rs @@ -5,7 +5,6 @@ use std::{ }; use littlefs2::{ - consts::{U1, U512}, driver::Storage, fs::{Allocation, FileType, Filesystem}, io::{Error, Result}, @@ -30,7 +29,7 @@ fn main() { file, len: actual_len, }; - let mut alloc = Allocation::new(); + let mut alloc = Allocation::new(&s); let fs = Filesystem::mount(&mut alloc, &mut s).expect("failed to mount filesystem"); let available_blocks = fs.available_blocks().unwrap(); @@ -70,13 +69,29 @@ struct FileStorage { } impl Storage for FileStorage { - type CACHE_SIZE = U512; - type LOOKAHEAD_SIZE = U1; + type CACHE_BUFFER = Vec; + type LOOKAHEAD_BUFFER = Vec; - const READ_SIZE: usize = 16; - const WRITE_SIZE: usize = 512; - const BLOCK_SIZE: usize = BLOCK_SIZE; - const BLOCK_COUNT: usize = BLOCK_COUNT; + fn read_size(&self) -> usize { + 16 + } + fn write_size(&self) -> usize { + 512 + } + fn block_size(&self) -> usize { + BLOCK_SIZE + } + fn block_count(&self) -> usize { + BLOCK_COUNT + } + + fn cache_size(&self) -> usize { + 512 + } + + fn lookahead_size(&self) -> usize { + 1 + } fn read(&mut self, off: usize, buf: &mut [u8]) -> Result { assert!(off + buf.len() <= BLOCK_SIZE * BLOCK_COUNT); diff --git a/src/consts.rs b/src/consts.rs index 2abb971b..99ee0165 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,8 +1,5 @@ #![allow(non_camel_case_types)] -/// Re-export of `typenum::consts`. -pub use generic_array::typenum::consts::*; - pub const PATH_MAX: usize = littlefs2_core::PathBuf::MAX_SIZE; pub const PATH_MAX_PLUS_ONE: usize = littlefs2_core::PathBuf::MAX_SIZE_PLUS_ONE; pub const FILENAME_MAX_PLUS_ONE: u32 = 255 + 1; diff --git a/src/driver.rs b/src/driver.rs index 52c896c2..f6c07ebc 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -1,10 +1,92 @@ //! The `Storage`, `Read`, `Write` and `Seek` driver. #![allow(non_camel_case_types)] -use generic_array::ArrayLength; - use crate::io::Result; +mod private { + pub trait Sealed {} +} + +/// Safety: implemented only by `[u8; N]` and `Vec` if the alloc feature is enabled +pub unsafe trait Buffer: private::Sealed { + /// The maximum capacity of the buffer type. + /// Can be [`usize::Max`]() + const MAX_CAPACITY: usize; + + /// Returns a buffer of bytes initialized and valid. If [`set_capacity`]() was called previously, + /// its last call defines the minimum number of valid bytes + fn as_ptr(&self) -> *const u8; + /// Returns a buffer of bytes initialized and valid. If [`set_capacity`]() was called previously, + /// its last call defines the minimum number of valid bytes + fn as_mut_ptr(&mut self) -> *mut u8; + + /// Current capacity, set by the last call to [`set_capacity`](Buffer::set_capacity) + /// or at initialization through [`with_capacity`](Buffer::with_capacity) + fn current_capacity(&self) -> usize; + + /// Can panic if `capacity` > `Self::MAX_CAPACITY` + fn set_capacity(&mut self, capacity: usize); + + /// Can panic if `capacity` > `Self::MAX_CAPACITY` + fn with_capacity(capacity: usize) -> Self; +} + +impl private::Sealed for [u8; N] {} + +unsafe impl Buffer for [u8; N] { + const MAX_CAPACITY: usize = N; + + fn as_ptr(&self) -> *const u8 { + <[u8]>::as_ptr(self) + } + + fn as_mut_ptr(&mut self) -> *mut u8 { + <[u8]>::as_mut_ptr(self) + } + + fn current_capacity(&self) -> usize { + N + } + + fn set_capacity(&mut self, _capacity: usize) { + // noop, fixed capacity + } + fn with_capacity(capacity: usize) -> Self { + assert!(capacity <= N); + [0; N] + } +} + +#[cfg(feature = "alloc")] +impl private::Sealed for alloc::vec::Vec {} + +#[cfg(feature = "alloc")] +unsafe impl Buffer for alloc::vec::Vec { + const MAX_CAPACITY: usize = usize::MAX; + + fn as_ptr(&self) -> *const u8 { + <[u8]>::as_ptr(self) + } + + fn as_mut_ptr(&mut self) -> *mut u8 { + <[u8]>::as_mut_ptr(self) + } + + fn current_capacity(&self) -> usize { + self.capacity() + } + + fn set_capacity(&mut self, capacity: usize) { + self.resize(capacity, 0) + } + + fn with_capacity(capacity: usize) -> Self { + let mut this = alloc::vec::Vec::with_capacity(capacity); + this.set_capacity(capacity); + this + } +} + /// Users of this library provide a "storage driver" by implementing this trait. /// /// The `write` method is assumed to be synchronized to storage immediately. @@ -12,44 +94,43 @@ use crate::io::Result; /// Do note that due to caches, files still must be synched. And unfortunately, /// this can't be automatically done in `drop`, since it needs mut refs to both /// filesystem and storage. -/// -/// The `*_SIZE` types must be `generic_array::typenume::consts` such as `U256`. -/// -/// Why? Currently, associated constants can not be used (as constants...) to define -/// arrays. This "will be fixed" as part of const generics. -/// Once that's done, we can get rid of `generic-array`s, and replace the -/// `*_SIZE` types with `usize`s. pub trait Storage { // /// Error type for user-provided read/write/erase methods // type Error = usize; /// Minimum size of block read in bytes. Not in superblock - const READ_SIZE: usize; + fn read_size(&self) -> usize; /// Minimum size of block write in bytes. Not in superblock - const WRITE_SIZE: usize; + fn write_size(&self) -> usize; /// Size of an erasable block in bytes, as unsigned typenum. /// Must be a multiple of both `READ_SIZE` and `WRITE_SIZE`. /// [At least 128](https://github.com/littlefs-project/littlefs/issues/264#issuecomment-519963153). Stored in superblock. - const BLOCK_SIZE: usize; + fn block_size(&self) -> usize; /// Number of erasable blocks. /// Hence storage capacity is `BLOCK_COUNT * BLOCK_SIZE` - const BLOCK_COUNT: usize; + fn block_count(&self) -> usize; /// Suggested values are 100-1000, higher is more performant but /// less wear-leveled. Default of -1 disables wear-leveling. /// Value zero is invalid, must be positive or -1. - const BLOCK_CYCLES: isize = -1; + fn block_cycles(&self) -> isize { + -1 + } /// littlefs uses a read cache, a write cache, and one cache per per file. - /// Must be a multiple of `READ_SIZE` and `WRITE_SIZE`. - /// Must be a factor of `BLOCK_SIZE`. - type CACHE_SIZE: ArrayLength; + type CACHE_BUFFER: Buffer; + /// Must be a multiple of `read_size` and `write_size`. + /// Must be a factor of `block_size`. + fn cache_size(&self) -> usize; + + /// Lookahead buffer used by littlefs + type LOOKAHEAD_BUFFER: Buffer; /// Size of the lookahead buffer used by littlefs, measured in multiples of 8 bytes. - type LOOKAHEAD_SIZE: ArrayLength; + fn lookahead_size(&self) -> usize; ///// Maximum length of a filename plus one. Stored in superblock. ///// Should default to 255+1, but associated type defaults don't exist currently. diff --git a/src/fs.rs b/src/fs.rs index 5bbdaea1..c299eeac 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -7,16 +7,12 @@ use core::{ cell::{RefCell, UnsafeCell}, mem, slice, }; -use generic_array::typenum::marker_traits::Unsigned; use littlefs2_sys as ll; -// so far, don't need `heapless-bytes`. -pub type Bytes = generic_array::GenericArray; - pub use littlefs2_core::{Attribute, DirEntry, FileOpenFlags, FileType, Metadata}; use crate::{ - driver, + driver::{self, Buffer}, io::{self, Error, OpenSeekFrom, Result}, path::{Path, PathBuf}, DISK_VERSION, @@ -44,42 +40,31 @@ pub fn u32_result(return_value: i32) -> Result { } struct Cache { - read: UnsafeCell>, - write: UnsafeCell>, + read: UnsafeCell, + write: UnsafeCell, // lookahead: aligned::Aligned>, - lookahead: UnsafeCell>, + lookahead: UnsafeCell, + size: usize, } impl Cache { - pub fn new() -> Self { + pub fn new(storage: &S) -> Self { + let cache_size = storage.cache_size(); Self { - read: Default::default(), - write: Default::default(), - lookahead: Default::default(), + read: UnsafeCell::new(S::CACHE_BUFFER::with_capacity(cache_size)), + write: UnsafeCell::new(S::CACHE_BUFFER::with_capacity(cache_size)), + lookahead: UnsafeCell::new(S::CACHE_BUFFER::with_capacity(cache_size)), + size: cache_size, } } } -impl Default for Cache { - fn default() -> Self { - Self::new() - } -} - pub struct Allocation { cache: Cache, config: ll::lfs_config, state: ll::lfs_t, } -// pub fn check_storage_requirements( - -impl Default for Allocation { - fn default() -> Self { - Self::new() - } -} - #[derive(Default, Clone, Debug)] #[non_exhaustive] pub struct Config { @@ -94,17 +79,18 @@ bitflags::bitflags! { } impl Allocation { - pub fn new() -> Self { - Self::with_config(Config::default()) + pub fn new(storage: &Storage) -> Self { + Self::with_config(storage, Config::default()) } - pub fn with_config(config: Config) -> Allocation { - let read_size: u32 = Storage::READ_SIZE as _; - let write_size: u32 = Storage::WRITE_SIZE as _; - let block_size: u32 = Storage::BLOCK_SIZE as _; - let cache_size: u32 = ::CACHE_SIZE::U32; - let lookahead_size: u32 = 8 * ::LOOKAHEAD_SIZE::U32; - let block_cycles: i32 = Storage::BLOCK_CYCLES as _; - let block_count: u32 = Storage::BLOCK_COUNT as _; + + pub fn with_config(storage: &Storage, config: Config) -> Allocation { + let read_size: u32 = storage.read_size() as _; + let write_size: u32 = storage.write_size() as _; + let block_size: u32 = storage.block_size() as _; + let cache_size: u32 = storage.cache_size() as _; + let lookahead_size: u32 = 8 * storage.lookahead_size() as u32; + let block_cycles: i32 = storage.block_cycles() as _; + let block_count: u32 = storage.block_count() as _; debug_assert!(block_cycles >= -1); debug_assert!(block_cycles != 0); @@ -130,7 +116,7 @@ impl Allocation { debug_assert!(cache_size <= block_size); debug_assert!(block_size % cache_size == 0); - let cache = Cache::new(); + let cache = Cache::new(storage); let filename_max_plus_one: u32 = crate::consts::FILENAME_MAX_PLUS_ONE; debug_assert!(filename_max_plus_one > 1); @@ -200,6 +186,7 @@ impl Allocation { // also consider "erasing" the lifetime completely pub struct Filesystem<'a, Storage: driver::Storage> { alloc: RefCell<&'a mut Allocation>, + cache_size: usize, storage: &'a mut Storage, } @@ -221,8 +208,8 @@ struct RemoveDirAllProgress { } impl Filesystem<'_, Storage> { - pub fn allocate() -> Allocation { - Allocation::new() + pub fn allocate(storage: &Storage) -> Allocation { + Allocation::new(storage) } pub fn format(storage: &mut Storage) -> Result<()> { @@ -230,7 +217,7 @@ impl Filesystem<'_, Storage> { } pub fn format_with_config(storage: &mut Storage, config: Config) -> Result<()> { - let alloc = &mut Allocation::with_config(config); + let alloc = &mut Allocation::with_config(storage, config); let fs = Filesystem::new(alloc, storage); let mut alloc = fs.alloc.borrow_mut(); let return_code = unsafe { ll::lfs_format(&mut alloc.state, &alloc.config) }; @@ -244,7 +231,7 @@ impl Filesystem<'_, Storage> { // TODO: check if this is equivalent to `is_formatted`. pub fn is_mountable_with_config(storage: &mut Storage, config: Config) -> bool { - let alloc = &mut Allocation::with_config(config); + let alloc = &mut Allocation::with_config(storage, config); Filesystem::mount(alloc, storage).is_ok() } @@ -269,7 +256,7 @@ impl Filesystem<'_, Storage> { config: Config, f: impl FnOnce(&Filesystem<'_, Storage>) -> Result, ) -> Result { - let mut alloc = Allocation::with_config(config); + let mut alloc = Allocation::with_config(storage, config); let fs = Filesystem::mount(&mut alloc, storage)?; f(&fs) } @@ -290,12 +277,12 @@ impl Filesystem<'_, Storage> { /// Total number of blocks in the filesystem pub fn total_blocks(&self) -> usize { - Storage::BLOCK_COUNT + self.storage.block_count() } /// Total number of bytes in the filesystem pub fn total_space(&self) -> usize { - Storage::BLOCK_COUNT * Storage::BLOCK_SIZE + self.storage.block_count() * self.storage.block_size() } /// Available number of unused blocks in the filesystem @@ -320,7 +307,7 @@ impl Filesystem<'_, Storage> { /// Second, files may be inlined. pub fn available_space(&self) -> Result { self.available_blocks() - .map(|blocks| blocks * Storage::BLOCK_SIZE) + .map(|blocks| blocks * self.storage.block_size()) } /// Remove a file or directory. @@ -562,7 +549,7 @@ impl Filesystem<'_, Storage> { let storage = unsafe { &mut *((*c).context as *mut Storage) }; debug_assert!(!c.is_null()); // let block_size = unsafe { c.read().block_size }; - let block_size = Storage::BLOCK_SIZE as u32; + let block_size = storage.block_size() as u32; let off = (block * block_size + off) as usize; let buf: &[u8] = unsafe { slice::from_raw_parts(buffer as *const u8, size as usize) }; @@ -574,9 +561,9 @@ impl Filesystem<'_, Storage> { extern "C" fn lfs_config_erase(c: *const ll::lfs_config, block: ll::lfs_block_t) -> c_int { // println!("in lfs_config_erase"); let storage = unsafe { &mut *((*c).context as *mut Storage) }; - let off = block as usize * Storage::BLOCK_SIZE; + let off = block as usize * storage.block_size(); - error_code_from(storage.erase(off, Storage::BLOCK_SIZE)) + error_code_from(storage.erase(off, storage.block_size())) } /// C callback interface used by LittleFS to sync data with the lower level interface below the @@ -590,22 +577,21 @@ impl Filesystem<'_, Storage> { /// The state of a `File`. Pre-allocate with `File::allocate`. pub struct FileAllocation { - cache: UnsafeCell>, + cache: UnsafeCell, + cache_size: usize, state: ll::lfs_file_t, config: ll::lfs_file_config, } -impl Default for FileAllocation { - fn default() -> Self { - Self::new() - } -} - impl FileAllocation { - pub fn new() -> Self { - let cache_size: u32 = ::CACHE_SIZE::to_u32(); + pub fn new(cache_size: usize) -> Self { debug_assert!(cache_size > 0); - unsafe { mem::MaybeUninit::zeroed().assume_init() } + Self { + cache: UnsafeCell::new(S::CACHE_BUFFER::with_capacity(cache_size)), + cache_size, + state: unsafe { mem::MaybeUninit::zeroed().assume_init() }, + config: unsafe { mem::MaybeUninit::zeroed().assume_init() }, + } } } @@ -617,8 +603,8 @@ pub struct File<'a, 'b, S: driver::Storage> { } impl<'a, 'b, Storage: driver::Storage> File<'a, 'b, Storage> { - pub fn allocate() -> FileAllocation { - FileAllocation::new() + pub fn allocate(fs: &Filesystem<'_, Storage>) -> FileAllocation { + FileAllocation::new(fs.cache_size) } /// Returns a new OpenOptions object. @@ -806,7 +792,8 @@ impl OpenOptions { alloc: &mut FileAllocation, path: &Path, ) -> Result> { - alloc.config.buffer = alloc.cache.get() as *mut _; + assert_eq!(fs.cache_size, alloc.cache_size); + alloc.config.buffer = alloc.cache.get_mut().as_mut_ptr() as *mut _; // We need to use addr_of_mut! here instead of & mut since // the FFI stores a copy of a pointer to the field state, // so we cannot assert unique mutable access. @@ -833,7 +820,7 @@ impl OpenOptions { path: &Path, f: impl FnOnce(&File<'a, '_, S>) -> Result, ) -> Result { - let mut alloc = FileAllocation::new(); // lifetime 'c + let mut alloc = File::allocate(fs); // lifetime 'c let mut file = unsafe { self.open(fs, &mut alloc, path)? }; // Q: what is the actually correct behaviour? // E.g. if res is Ok but closing gives an error. @@ -1141,6 +1128,7 @@ impl<'a, Storage: driver::Storage> Filesystem<'a, Storage> { fn new(alloc: &'a mut Allocation, storage: &'a mut Storage) -> Self { Self::set_alloc_config(alloc, storage); Filesystem { + cache_size: alloc.cache.size, alloc: RefCell::new(alloc), storage, } @@ -1418,7 +1406,7 @@ mod tests { }) .unwrap(); - let mut alloc = Allocation::new(); + let mut alloc = Allocation::new(&test_storage); let fs = Filesystem::mount(&mut alloc, &mut test_storage).unwrap(); // fs.write(b"/z.txt\0".try_into().unwrap(), &jackson5).unwrap(); fs.write(path!("z.txt"), jackson5).unwrap(); @@ -1529,11 +1517,11 @@ mod tests { Ok(()) })?; - let mut a1 = File::allocate(); + let mut a1 = File::allocate(fs); let f1 = unsafe { File::create(fs, &mut a1, b"a.txt\0".try_into().unwrap())? }; f1.write(b"some text")?; - let mut a2 = File::allocate(); + let mut a2 = File::allocate(fs); let f2 = unsafe { File::create(fs, &mut a2, b"b.txt\0".try_into().unwrap())? }; f2.write(b"more text")?; diff --git a/src/lib.rs b/src/lib.rs index c7c5f92d..a4688c52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,7 +107,7 @@ let mut storage = RamStorage::new(&mut ram); // must format before first mount Filesystem::format(&mut storage).unwrap(); // must allocate state statically before use -let mut alloc = Filesystem::allocate(); +let mut alloc = Filesystem::allocate(&storage); let mut fs = Filesystem::mount(&mut alloc, &mut storage).unwrap(); // may use common `OpenOptions` diff --git a/src/macros.rs b/src/macros.rs index c6e448e7..64220287 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -11,12 +11,10 @@ macro_rules! ram_storage { erase_value=$erase_value:expr, read_size=$read_size:expr, write_size=$write_size:expr, - cache_size_ty=$cache_size:path, + cache_size=$cache_size:expr, block_size=$block_size:expr, block_count=$block_count:expr, - lookahead_size_ty=$lookahead_size:path, - filename_max_plus_one_ty=$filename_max_plus_one:path, - path_max_plus_one_ty=$path_max_plus_one:path, + lookahead_size=$lookahead_size:expr, ) => { pub struct $Backend { @@ -43,15 +41,30 @@ macro_rules! ram_storage { } impl<'backend> $crate::driver::Storage for $Name<'backend> { - const READ_SIZE: usize = $read_size; - const WRITE_SIZE: usize = $write_size; - type CACHE_SIZE = $cache_size; - const BLOCK_SIZE: usize = $block_size; - const BLOCK_COUNT: usize = $block_count; - type LOOKAHEAD_SIZE = $lookahead_size; + fn read_size(&self) -> usize { + $read_size + } + fn write_size(&self) -> usize { + $write_size + } + fn block_size(&self) -> usize { + $block_size + } + fn cache_size(&self) -> usize { + $cache_size + } + type CACHE_BUFFER = [u8; $cache_size]; + fn block_count(&self) -> usize { + $block_count + } + + fn lookahead_size(&self) -> usize { + $lookahead_size + } + type LOOKAHEAD_BUFFER = [u8; $lookahead_size * 8]; fn read(&mut self, offset: usize, buf: &mut [u8]) -> $crate::io::Result { - let read_size: usize = Self::READ_SIZE; + let read_size: usize = self.read_size(); debug_assert!(offset % read_size == 0); debug_assert!(buf.len() % read_size == 0); for (from, to) in self.backend.buf[offset..].iter().zip(buf.iter_mut()) { @@ -61,7 +74,7 @@ macro_rules! ram_storage { } fn write(&mut self, offset: usize, data: &[u8]) -> $crate::io::Result { - let write_size: usize = Self::WRITE_SIZE; + let write_size: usize = self.write_size(); debug_assert!(offset % write_size == 0); debug_assert!(data.len() % write_size == 0); for (from, to) in data.iter().zip(self.backend.buf[offset..].iter_mut()) { @@ -71,7 +84,7 @@ macro_rules! ram_storage { } fn erase(&mut self, offset: usize, len: usize) -> $crate::io::Result { - let block_size: usize = Self::BLOCK_SIZE; + let block_size: usize = self.block_size(); debug_assert!(offset % block_size == 0); debug_assert!(len % block_size == 0); for byte in self.backend.buf[offset..offset + len].iter_mut() { @@ -88,12 +101,10 @@ macro_rules! ram_storage { erase_value = 0xff, read_size = 1, write_size = 1, - cache_size_ty = $crate::consts::U32, + cache_size = 32, block_size = 128, block_count = $bytes / 128, - lookahead_size_ty = $crate::consts::U1, - filename_max_plus_one_ty = $crate::consts::U256, - path_max_plus_one_ty = $crate::consts::U256, + lookahead_size = 1, ); }; (tiny) => { @@ -103,12 +114,10 @@ macro_rules! ram_storage { erase_value = 0xff, read_size = 32, write_size = 32, - cache_size_ty = $crate::consts::U32, + cache_size = 32, block_size = 128, block_count = 8, - lookahead_size_ty = $crate::consts::U1, - filename_max_plus_one_ty = $crate::consts::U256, - path_max_plus_one_ty = $crate::consts::U256, + lookahead_size = 1, ); }; (large) => { @@ -118,12 +127,10 @@ macro_rules! ram_storage { erase_value = 0xff, read_size = 32, write_size = 32, - cache_size_ty = $crate::consts::U32, + cache_size = 32, block_size = 256, block_count = 512, - lookahead_size_ty = $crate::consts::U4, - filename_max_plus_one_ty = $crate::consts::U256, - path_max_plus_one_ty = $crate::consts::U256, + lookahead_size = 4, ); }; } @@ -136,12 +143,10 @@ macro_rules! const_ram_storage { erase_value=$erase_value:expr, read_size=$read_size:expr, write_size=$write_size:expr, - cache_size_ty=$cache_size:path, + cache_size=$cache_size:expr, block_size=$block_size:expr, block_count=$block_count:expr, - lookahead_size_ty=$lookahead_size:path, - filename_max_plus_one_ty=$filename_max_plus_one:path, - path_max_plus_one_ty=$path_max_plus_one:path, + lookahead_size=$lookahead_size:expr, ) => { pub struct $Name { @@ -167,15 +172,32 @@ macro_rules! const_ram_storage { } impl $crate::driver::Storage for $Name { - const READ_SIZE: usize = $read_size; - const WRITE_SIZE: usize = $write_size; - type CACHE_SIZE = $cache_size; - const BLOCK_SIZE: usize = $block_size; - const BLOCK_COUNT: usize = $block_count; - type LOOKAHEAD_SIZE = $lookahead_size; + fn read_size(&self) -> usize { + $read_size + } + + fn write_size(&self) -> usize { + $write_size + } + + fn cache_size(&self) -> usize { + $cache_size + } + type CACHE_BUFFER = [u8; $cache_size]; + fn block_size(&self) -> usize { + $block_size + } + fn block_count(&self) -> usize { + $block_count + } + + fn lookahead_size(&self) -> usize { + $lookahead_size + } + type LOOKAHEAD_BUFFER = [u8; $lookahead_size * 8]; fn read(&mut self, offset: usize, buf: &mut [u8]) -> $crate::io::Result { - let read_size: usize = Self::READ_SIZE; + let read_size = self.read_size(); debug_assert!(offset % read_size == 0); debug_assert!(buf.len() % read_size == 0); for (from, to) in self.buf[offset..].iter().zip(buf.iter_mut()) { @@ -185,7 +207,7 @@ macro_rules! const_ram_storage { } fn write(&mut self, offset: usize, data: &[u8]) -> $crate::io::Result { - let write_size: usize = Self::WRITE_SIZE; + let write_size = self.write_size(); debug_assert!(offset % write_size == 0); debug_assert!(data.len() % write_size == 0); for (from, to) in data.iter().zip(self.buf[offset..].iter_mut()) { @@ -195,7 +217,7 @@ macro_rules! const_ram_storage { } fn erase(&mut self, offset: usize, len: usize) -> $crate::io::Result { - let block_size: usize = Self::BLOCK_SIZE; + let block_size: usize = self.block_size(); debug_assert!(offset % block_size == 0); debug_assert!(len % block_size == 0); for byte in self.buf[offset..offset + len].iter_mut() { @@ -211,12 +233,10 @@ macro_rules! const_ram_storage { erase_value = 0xff, read_size = 16, write_size = 512, - cache_size_ty = $crate::consts::U512, + cache_size = 512, block_size = 512, block_count = $bytes / 512, - lookahead_size_ty = $crate::consts::U1, - filename_max_plus_one_ty = $crate::consts::U256, - path_max_plus_one_ty = $crate::consts::U256, + lookahead_size = 1, ); }; } diff --git a/src/object_safe.rs b/src/object_safe.rs index fb102859..23635e35 100644 --- a/src/object_safe.rs +++ b/src/object_safe.rs @@ -1,7 +1,5 @@ //! Object-safe traits for [`File`][], [`Filesystem`][] and [`Storage`][]. -use generic_array::typenum::Unsigned as _; - use crate::{ driver::Storage, fs::{Attribute, File, FileOpenFlags, Filesystem, Metadata}, @@ -179,31 +177,31 @@ pub trait DynStorage { impl DynStorage for S { fn read_size(&self) -> usize { - Self::READ_SIZE + ::read_size(self) } fn write_size(&self) -> usize { - Self::WRITE_SIZE + ::write_size(self) } fn block_size(&self) -> usize { - Self::BLOCK_SIZE + ::block_size(self) } fn block_count(&self) -> usize { - Self::BLOCK_COUNT + ::block_count(self) } fn block_cycles(&self) -> isize { - Self::BLOCK_CYCLES + ::block_cycles(self) } fn cache_size(&self) -> usize { - S::CACHE_SIZE::to_usize() + ::cache_size(self) } fn lookahead_size(&self) -> usize { - S::LOOKAHEAD_SIZE::to_usize() + ::lookahead_size(self) } fn read(&mut self, off: usize, buf: &mut [u8]) -> Result { diff --git a/src/tests.rs b/src/tests.rs index 2a0bf9c2..74097253 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,5 +1,4 @@ use core::convert::TryInto; -use generic_array::typenum::consts; use littlefs2_core::PathBuf; use crate::{ @@ -9,18 +8,19 @@ use crate::{ path, BACKEND_VERSION, DISK_VERSION, }; +const RAM_STORAGE_BLOCK_COUNT: usize = 32; +const LARGER_RAM_STORAGE_BLOCK_COUNT: usize = 64; + ram_storage!( name = OtherRamStorage, backend = OtherRam, erase_value = 0xff, read_size = 1, write_size = 32, - cache_size_ty = consts::U32, + cache_size = 32, block_size = 256, block_count = 512, - lookahead_size_ty = consts::U1, - filename_max_plus_one_ty = consts::U256, - path_max_plus_one_ty = consts::U256, + lookahead_size = 1, ); ram_storage!( @@ -29,12 +29,10 @@ ram_storage!( erase_value = 0xff, read_size = 20 * 5, write_size = 20 * 7, - cache_size_ty = consts::U700, + cache_size = 700, block_size = 20 * 35, - block_count = 32, - lookahead_size_ty = consts::U16, - filename_max_plus_one_ty = consts::U256, - path_max_plus_one_ty = consts::U256, + block_count = RAM_STORAGE_BLOCK_COUNT, + lookahead_size = 16, ); ram_storage!( @@ -43,12 +41,10 @@ ram_storage!( erase_value = 0xff, read_size = 20 * 5, write_size = 20 * 7, - cache_size_ty = consts::U700, + cache_size = 700, block_size = 20 * 35, - block_count = 64, - lookahead_size_ty = consts::U16, - filename_max_plus_one_ty = consts::U256, - path_max_plus_one_ty = consts::U256, + block_count = LARGER_RAM_STORAGE_BLOCK_COUNT, + lookahead_size = 16, ); #[test] @@ -61,7 +57,7 @@ fn version() { fn format() { let mut backend = OtherRam::default(); let mut storage = OtherRamStorage::new(&mut backend); - let mut alloc = Filesystem::allocate(); + let mut alloc = Filesystem::allocate(&storage); // should fail: FS is not formatted assert_eq!( @@ -93,7 +89,7 @@ fn borrow_fs_allocation() { let mut backend = OtherRam::default(); let mut storage = OtherRamStorage::new(&mut backend); - let mut alloc_fs = Filesystem::allocate(); + let mut alloc_fs = Filesystem::allocate(&storage); Filesystem::format(&mut storage).unwrap(); let _fs = Filesystem::mount(&mut alloc_fs, &mut storage).unwrap(); // previous `_fs` is fine as it's masked, due to NLL @@ -110,7 +106,7 @@ fn borrow_fs_allocation2() { let mut backend = OtherRam::default(); let mut storage = OtherRamStorage::new(&mut backend); - let mut alloc_fs = Filesystem::allocate(); + let mut alloc_fs = Filesystem::allocate(&storage); Filesystem::format(&mut storage).unwrap(); let _fs = Filesystem::mount(&mut alloc_fs, &mut storage).unwrap(); // previous `_fs` is fine as it's masked, due to NLL @@ -541,9 +537,9 @@ fn test_iter_dirs() { fn test_mount_or_else_clobber_alloc() { let mut backend = Ram::default(); let mut storage = RamStorage::new(&mut backend); - let alloc = &mut Allocation::new(); + let alloc = &mut Allocation::new(&storage); Filesystem::mount_or_else(alloc, &mut storage, |_, storage, alloc| { - *alloc = Allocation::new(); + *alloc = Allocation::new(storage); Filesystem::format(storage).unwrap(); Ok(()) }) @@ -566,7 +562,7 @@ fn test_mount_or_else_clobber_alloc() { fn shrinking() { let backend = &mut Ram::default(); let storage = &mut RamStorage::new(backend); - let alloc = &mut Allocation::new(); + let alloc = &mut Allocation::new(storage); Filesystem::format(storage).unwrap(); let fs = Filesystem::mount(alloc, storage).unwrap(); @@ -582,15 +578,18 @@ fn shrinking() { let larger_backend = &mut LargerRam::default(); larger_backend.buf[..backend.buf.len()].copy_from_slice(&backend.buf); let larger_storage = &mut LargerRamStorage::new(larger_backend); - let larger_alloc = &mut Allocation::new(); + let larger_alloc = &mut Allocation::new(larger_storage); assert!(matches!( Filesystem::mount(larger_alloc, larger_storage), Err(Error::INVALID) )); - let larger_alloc = &mut Allocation::with_config(crate::fs::Config { - mount_flags: MountFlags::DISABLE_BLOCK_COUNT_CHECK, - }); + let larger_alloc = &mut Allocation::with_config( + larger_storage, + crate::fs::Config { + mount_flags: MountFlags::DISABLE_BLOCK_COUNT_CHECK, + }, + ); let fs = Filesystem::mount(larger_alloc, larger_storage).unwrap(); assert_eq!(fs.read::<10>(path!("some-file")).unwrap(), &[42; 10]); @@ -599,14 +598,14 @@ fn shrinking() { &[42; 1024] ); - fs.grow(LargerRamStorage::BLOCK_COUNT).unwrap(); + fs.grow(LARGER_RAM_STORAGE_BLOCK_COUNT).unwrap(); assert_eq!(fs.read::<10>(path!("some-file")).unwrap(), &[42; 10]); assert_eq!( fs.read::<1024>(path!("some-large-file")).unwrap(), &[42; 1024] ); - fs.shrink(RamStorage::BLOCK_COUNT).unwrap(); + fs.shrink(RAM_STORAGE_BLOCK_COUNT).unwrap(); assert_eq!(fs.read::<10>(path!("some-file")).unwrap(), &[42; 10]); assert_eq!( fs.read::<1024>(path!("some-large-file")).unwrap(), @@ -618,7 +617,7 @@ fn shrinking() { fn shrinking_full() { let larger_backend = &mut LargerRam::default(); let larger_storage = &mut LargerRamStorage::new(larger_backend); - let larger_alloc = &mut Allocation::new(); + let larger_alloc = &mut Allocation::new(larger_storage); Filesystem::format(larger_storage).unwrap(); let fs = Filesystem::mount(larger_alloc, larger_storage).unwrap(); @@ -632,8 +631,11 @@ fn shrinking_full() { } } + let backend = &mut Ram::default(); + let storage = RamStorage::new(backend); + assert!(matches!( - fs.shrink(RamStorage::BLOCK_COUNT), + fs.shrink(storage.block_count()), Err(Error::DIR_NOT_EMPTY) )) } diff --git a/tests/test_serde.rs b/tests/test_serde.rs index de05c7f5..cbe7ddad 100644 --- a/tests/test_serde.rs +++ b/tests/test_serde.rs @@ -19,7 +19,7 @@ fn main() { let mut storage = RamStorage::new(&mut ram); Filesystem::format(&mut storage).unwrap(); - let mut alloc = Filesystem::allocate(); + let mut alloc = Filesystem::allocate(&storage); let fs = Filesystem::mount(&mut alloc, &mut storage).unwrap(); let entity = Entity::default();