Remove need for generic array and typenum constants

This will also allow for more flexible `Storage` implementations
that use `Vec<u8>` as a cache buffer and are not fixed to a single block size
This commit is contained in:
Sosthène Guédon
2025-06-18 15:26:01 +02:00
parent b3a7371d84
commit 7e94b3d8b0
10 changed files with 281 additions and 177 deletions
+4 -1
View File
@@ -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"] }
+23 -8
View File
@@ -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<u8>;
type LOOKAHEAD_BUFFER = Vec<u8>;
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<usize> {
assert!(off + buf.len() <= BLOCK_SIZE * BLOCK_COUNT);
-3
View File
@@ -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;
+99 -18
View File
@@ -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<u8>` 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<const N: usize> private::Sealed for [u8; N] {}
unsafe impl<const N: usize> 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<u8> {}
#[cfg(feature = "alloc")]
unsafe impl Buffer for alloc::vec::Vec<u8> {
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<u8>;
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<u64>;
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.
+53 -65
View File
@@ -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<SIZE> = generic_array::GenericArray<u8, SIZE>;
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<u32> {
}
struct Cache<Storage: driver::Storage> {
read: UnsafeCell<Bytes<Storage::CACHE_SIZE>>,
write: UnsafeCell<Bytes<Storage::CACHE_SIZE>>,
read: UnsafeCell<Storage::CACHE_BUFFER>,
write: UnsafeCell<Storage::CACHE_BUFFER>,
// lookahead: aligned::Aligned<aligned::A4, Bytes<Storage::LOOKAHEAD_SIZE>>,
lookahead: UnsafeCell<generic_array::GenericArray<u64, Storage::LOOKAHEAD_SIZE>>,
lookahead: UnsafeCell<Storage::CACHE_BUFFER>,
size: usize,
}
impl<S: driver::Storage> Cache<S> {
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<S: driver::Storage> Default for Cache<S> {
fn default() -> Self {
Self::new()
}
}
pub struct Allocation<Storage: driver::Storage> {
cache: Cache<Storage>,
config: ll::lfs_config,
state: ll::lfs_t,
}
// pub fn check_storage_requirements(
impl<Storage: driver::Storage> Default for Allocation<Storage> {
fn default() -> Self {
Self::new()
}
}
#[derive(Default, Clone, Debug)]
#[non_exhaustive]
pub struct Config {
@@ -94,17 +79,18 @@ bitflags::bitflags! {
}
impl<Storage: driver::Storage> Allocation<Storage> {
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<Storage> {
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 as driver::Storage>::CACHE_SIZE::U32;
let lookahead_size: u32 = 8 * <Storage as driver::Storage>::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<Storage> {
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<Storage: driver::Storage> Allocation<Storage> {
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<Storage: driver::Storage> Allocation<Storage> {
// also consider "erasing" the lifetime completely
pub struct Filesystem<'a, Storage: driver::Storage> {
alloc: RefCell<&'a mut Allocation<Storage>>,
cache_size: usize,
storage: &'a mut Storage,
}
@@ -221,8 +208,8 @@ struct RemoveDirAllProgress {
}
impl<Storage: driver::Storage> Filesystem<'_, Storage> {
pub fn allocate() -> Allocation<Storage> {
Allocation::new()
pub fn allocate(storage: &Storage) -> Allocation<Storage> {
Allocation::new(storage)
}
pub fn format(storage: &mut Storage) -> Result<()> {
@@ -230,7 +217,7 @@ impl<Storage: driver::Storage> 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<Storage: driver::Storage> 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<Storage: driver::Storage> Filesystem<'_, Storage> {
config: Config,
f: impl FnOnce(&Filesystem<'_, Storage>) -> Result<R>,
) -> Result<R> {
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<Storage: driver::Storage> 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<Storage: driver::Storage> Filesystem<'_, Storage> {
/// Second, files may be inlined.
pub fn available_space(&self) -> Result<usize> {
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<Storage: driver::Storage> 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<Storage: driver::Storage> 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<Storage: driver::Storage> Filesystem<'_, Storage> {
/// The state of a `File`. Pre-allocate with `File::allocate`.
pub struct FileAllocation<S: driver::Storage> {
cache: UnsafeCell<Bytes<S::CACHE_SIZE>>,
cache: UnsafeCell<S::CACHE_BUFFER>,
cache_size: usize,
state: ll::lfs_file_t,
config: ll::lfs_file_config,
}
impl<S: driver::Storage> Default for FileAllocation<S> {
fn default() -> Self {
Self::new()
}
}
impl<S: driver::Storage> FileAllocation<S> {
pub fn new() -> Self {
let cache_size: u32 = <S as driver::Storage>::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<Storage> {
FileAllocation::new()
pub fn allocate(fs: &Filesystem<'_, Storage>) -> FileAllocation<Storage> {
FileAllocation::new(fs.cache_size)
}
/// Returns a new OpenOptions object.
@@ -806,7 +792,8 @@ impl OpenOptions {
alloc: &mut FileAllocation<S>,
path: &Path,
) -> Result<File<'a, 'b, S>> {
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<R>,
) -> Result<R> {
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>, 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")?;
+1 -1
View File
@@ -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`
+62 -42
View File
@@ -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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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,
);
};
}
+7 -9
View File
@@ -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<S: Storage> DynStorage for S {
fn read_size(&self) -> usize {
Self::READ_SIZE
<S as Storage>::read_size(self)
}
fn write_size(&self) -> usize {
Self::WRITE_SIZE
<S as Storage>::write_size(self)
}
fn block_size(&self) -> usize {
Self::BLOCK_SIZE
<S as Storage>::block_size(self)
}
fn block_count(&self) -> usize {
Self::BLOCK_COUNT
<S as Storage>::block_count(self)
}
fn block_cycles(&self) -> isize {
Self::BLOCK_CYCLES
<S as Storage>::block_cycles(self)
}
fn cache_size(&self) -> usize {
S::CACHE_SIZE::to_usize()
<S as Storage>::cache_size(self)
}
fn lookahead_size(&self) -> usize {
S::LOOKAHEAD_SIZE::to_usize()
<S as Storage>::lookahead_size(self)
}
fn read(&mut self, off: usize, buf: &mut [u8]) -> Result<usize> {
+31 -29
View File
@@ -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)
))
}
+1 -1
View File
@@ -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();