mirror of
https://github.com/trussed-dev/littlefs2.git
synced 2026-06-20 04:16:32 -07:00
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:
+4
-1
@@ -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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user