mirror of
https://github.com/trussed-dev/littlefs2.git
synced 2026-06-20 04:16:32 -07:00
WIP: Remove UB by using closures
This commit is contained in:
committed by
Nicolas Stalder
parent
274a8dfef7
commit
45d8faacd4
+9
-4
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "littlefs2"
|
||||
description = "Idiomatic Rust API for littlefs"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.1"
|
||||
authors = ["Nicolas Stalder <n@stalder.io>", "Brandon Edens <brandonedens@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
@@ -10,13 +10,14 @@ categories = ["embedded", "filesystem", "no-std"]
|
||||
repository = "https://github.com/nickray/littlefs2"
|
||||
|
||||
[dependencies]
|
||||
# aligned = "0.3.2"
|
||||
aligned = { version = "0.3.2", optional = true }
|
||||
bitflags = "1.0.4"
|
||||
# cortex-m-semihosting = "0.3.5"
|
||||
# cstr_core = "0.1.2"
|
||||
cty = "0.2.1"
|
||||
generic-array = "0.13.2"
|
||||
# heapless = "0.5.1"
|
||||
heapless-bytes = { path = "../heapless-bytes", optional = true }
|
||||
|
||||
# Listed as regular dependency behind feature flag,
|
||||
# since dev-dependencies cannot be optional, and we
|
||||
@@ -24,12 +25,12 @@ generic-array = "0.13.2"
|
||||
# and test the `no_std` situation mainly.
|
||||
# Run UI tests with `cargo test --features ui-tests`.
|
||||
trybuild = { version = "1.0", optional = true }
|
||||
ufmt = "0.1.0"
|
||||
|
||||
[dependencies.littlefs2-sys]
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
# git = "https://github.com/nickray/littlefs2-sys"
|
||||
# branch = "main"
|
||||
# path = "../littlefs2-sys"
|
||||
|
||||
[dev-dependencies]
|
||||
desse = "0.2.1"
|
||||
@@ -42,6 +43,10 @@ serde = { version = "1.0", default-features = false, features = ["derive"] }
|
||||
# serde-json-core = { version = "0.1.0" }
|
||||
|
||||
[features]
|
||||
# default = ["closures"]
|
||||
# use experimental closure-based API
|
||||
closures = ["aligned", "heapless-bytes"]
|
||||
dir-entry-path = []
|
||||
# enable assertions in backend C code
|
||||
ll-assertions = ["littlefs2-sys/assertions"]
|
||||
# enable trace in backend C code
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//! The `Storage`, `Read`, `Write` and `Seek` driver.
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use generic_array::ArrayLength;
|
||||
use littlefs2_sys as ll;
|
||||
@@ -56,6 +57,7 @@ pub trait Storage {
|
||||
/// Hence, we further restrict `LOOKAHEAD_SIZE` to be a multiple of 32.
|
||||
/// Our LOOKAHEADWORDS_SIZE is this multiple.
|
||||
type LOOKAHEADWORDS_SIZE: ArrayLength<u32>;
|
||||
// type LOOKAHEAD_SIZE: ArrayLength<u8>;
|
||||
|
||||
/// Maximum length of a filename plus one. Stored in superblock.
|
||||
/// Should default to 255+1, but associated type defaults don't exist currently.
|
||||
|
||||
@@ -16,6 +16,7 @@ use crate::{
|
||||
self,
|
||||
Error,
|
||||
Result,
|
||||
SeekFrom,
|
||||
},
|
||||
path::{
|
||||
Filename,
|
||||
@@ -27,7 +28,7 @@ use crate::{
|
||||
// use aligned::{A4, Aligned};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use littlefs2_sys as ll;
|
||||
pub use littlefs2_sys as ll;
|
||||
|
||||
use generic_array::{
|
||||
GenericArray,
|
||||
@@ -86,6 +87,15 @@ where
|
||||
Storage: 'alloc,
|
||||
|
||||
{
|
||||
pub fn within<R>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut Filesystem<'_, Storage>, &mut Storage) -> io::Result<R>,
|
||||
)
|
||||
-> Result<R>
|
||||
{
|
||||
f(&mut Filesystem { alloc: self.alloc }, self.storage)
|
||||
}
|
||||
|
||||
pub fn mount(
|
||||
alloc: &'alloc mut FilesystemAllocation<Storage>,
|
||||
storage: &'storage mut Storage,
|
||||
@@ -173,6 +183,95 @@ where
|
||||
Error::result_from(return_code).map(|_| info.into())
|
||||
}
|
||||
|
||||
/// Returns a pseudo-iterator over the entries within a directory.
|
||||
pub fn read_dir(
|
||||
&mut self,
|
||||
path: impl Into<Path<Storage>>,
|
||||
) ->
|
||||
Result<ReadDir<Storage>>
|
||||
{
|
||||
let mut read_dir = ReadDir {
|
||||
state: unsafe { mem::MaybeUninit::zeroed().assume_init() },
|
||||
_storage: PhantomData,
|
||||
};
|
||||
|
||||
let return_code = unsafe {
|
||||
ll::lfs_dir_open(
|
||||
&mut self.alloc.state,
|
||||
&mut read_dir.state,
|
||||
&path.into() as *const _ as *const cty::c_char,
|
||||
)
|
||||
};
|
||||
|
||||
Error::result_from(return_code).map(|_| read_dir)
|
||||
}
|
||||
|
||||
|
||||
/// Read attribute.
|
||||
pub fn attribute(
|
||||
&mut self,
|
||||
path: impl Into<Path<Storage>>,
|
||||
id: u8,
|
||||
) ->
|
||||
Result<Option<Attribute<Storage>>>
|
||||
{
|
||||
let mut attribute = Attribute::new(id);
|
||||
let attr_max = <Storage as driver::Storage>::ATTRBYTES_MAX::to_u32();
|
||||
|
||||
let return_code = unsafe { ll::lfs_getattr(
|
||||
&mut self.alloc.state,
|
||||
&path.into() as *const _ as *const cty::c_char,
|
||||
id,
|
||||
&mut attribute.data as *mut _ as *mut cty::c_void,
|
||||
attr_max,
|
||||
) };
|
||||
|
||||
if return_code >= 0 {
|
||||
attribute.size = cmp::min(attr_max, return_code as u32) as usize;
|
||||
return Ok(Some(attribute));
|
||||
}
|
||||
if return_code == ll::lfs_error_LFS_ERR_NOATTR {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
Error::result_from(return_code)?;
|
||||
// TODO: get rid of this
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
/// Remove attribute.
|
||||
pub fn remove_attribute(
|
||||
&mut self,
|
||||
path: impl Into<Path<Storage>>,
|
||||
id: u8,
|
||||
) -> Result<()> {
|
||||
let return_code = unsafe { ll::lfs_removeattr(
|
||||
&mut self.alloc.state,
|
||||
&path.into() as *const _ as *const cty::c_char,
|
||||
id,
|
||||
) };
|
||||
Error::result_from(return_code)
|
||||
}
|
||||
|
||||
/// Set attribute.
|
||||
pub fn set_attribute(
|
||||
&mut self,
|
||||
path: impl Into<Path<Storage>>,
|
||||
attribute: &Attribute<Storage>
|
||||
) ->
|
||||
Result<()>
|
||||
{
|
||||
let return_code = unsafe { ll::lfs_setattr(
|
||||
&mut self.alloc.state,
|
||||
&path.into() as *const _ as *const cty::c_char,
|
||||
attribute.id,
|
||||
&attribute.data as *const _ as *const cty::c_void,
|
||||
attribute.size as u32,
|
||||
) };
|
||||
|
||||
Error::result_from(return_code)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -186,9 +285,9 @@ where
|
||||
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::to_u32();
|
||||
let cache_size: u32 = <Storage as driver::Storage>::CACHE_SIZE::U32;
|
||||
let lookahead_size: u32 =
|
||||
32 * <Storage as driver::Storage>::LOOKAHEADWORDS_SIZE::to_u32();
|
||||
32 * <Storage as driver::Storage>::LOOKAHEADWORDS_SIZE::U32;
|
||||
let block_cycles: i32 = Storage::BLOCK_CYCLES as _;
|
||||
let block_count: u32 = Storage::BLOCK_COUNT as _;
|
||||
|
||||
@@ -1012,6 +1111,7 @@ impl OpenOptions {
|
||||
S: driver::Storage,
|
||||
{
|
||||
// fs.alloc.config.context = storage as *mut _ as *mut cty::c_void;
|
||||
// fs_with.alloc.config.context = fs_with.storage as *mut _ as *mut cty::c_void;
|
||||
alloc.config.buffer = alloc.cache.as_mut_slice() as *mut _ as *mut cty::c_void;
|
||||
|
||||
let return_code = unsafe { ll::lfs_file_opencfg(
|
||||
@@ -1028,36 +1128,6 @@ impl OpenOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/** Enumeration of possible methods to seek within an I/O object.
|
||||
|
||||
Use the [`Seek`](../io/trait.Seek.html) trait.
|
||||
*/
|
||||
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
|
||||
pub enum SeekFrom {
|
||||
Start(u32),
|
||||
End(i32),
|
||||
Current(i32),
|
||||
}
|
||||
|
||||
impl SeekFrom {
|
||||
pub(crate) fn off(self) -> i32 {
|
||||
match self {
|
||||
SeekFrom::Start(u) => u as i32,
|
||||
SeekFrom::End(i) => i,
|
||||
SeekFrom::Current(i) => i,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn whence(self) -> i32 {
|
||||
match self {
|
||||
SeekFrom::Start(_) => 0,
|
||||
SeekFrom::End(_) => 2,
|
||||
SeekFrom::Current(_) => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /// The state of a `Dir`. Pre-allocate with `File::allocate()`.
|
||||
// pub struct DirAllocation
|
||||
// {
|
||||
@@ -1117,6 +1187,13 @@ where
|
||||
fs_with: &'fs mut FilesystemWith<'fsalloc, 'storage, S>,
|
||||
}
|
||||
|
||||
impl<S: driver::Storage> Drop for FileWith<'_, '_, '_, '_, S> {
|
||||
fn drop(&mut self) {
|
||||
self.nonconsuming_close_for_drop().expect("could not close FileWith");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'falloc, 'fs, 'fsalloc, 'storage, S> FileWith<'falloc, 'fs, 'fsalloc, 'storage, S>
|
||||
where
|
||||
S: driver::Storage,
|
||||
@@ -1165,9 +1242,20 @@ where
|
||||
Error::result_from(return_code)
|
||||
}
|
||||
|
||||
pub fn nonconsuming_close_for_drop(&mut self) ->
|
||||
Result<()>
|
||||
{
|
||||
// fs.alloc.config.context = storage as *mut _ as *mut cty::c_void;
|
||||
let return_code = unsafe { ll::lfs_file_close(
|
||||
&mut self.fs_with.alloc.state,
|
||||
&mut self.alloc.state,
|
||||
) };
|
||||
Error::result_from(return_code)
|
||||
}
|
||||
|
||||
/// Synchronize file contents to storage.
|
||||
pub fn sync(&mut self) -> Result<()> {
|
||||
// assert!(self.fs_with.alloc.config.context == self.fs_with.storage as *mut _ as *mut cty::c_void);
|
||||
assert!(self.fs_with.alloc.config.context == self.fs_with.storage as *mut _ as *mut cty::c_void);
|
||||
// fs.alloc.config.context = storage as *mut _ as *mut cty::c_void;
|
||||
let return_code = unsafe { ll::lfs_file_sync(
|
||||
&mut self.fs_with.alloc.state,
|
||||
@@ -1235,17 +1323,20 @@ where
|
||||
let cache_size: u32 = <S as driver::Storage>::CACHE_SIZE::to_u32();
|
||||
debug_assert!(cache_size > 0);
|
||||
|
||||
let config = ll::lfs_file_config {
|
||||
buffer: core::ptr::null_mut(),
|
||||
attrs: core::ptr::null_mut(),
|
||||
attr_count: 0,
|
||||
};
|
||||
// let config = ll::lfs_file_config {
|
||||
// buffer: core::ptr::null_mut(),
|
||||
// attrs: core::ptr::null_mut(),
|
||||
// attr_count: 0,
|
||||
// };
|
||||
|
||||
FileAllocation {
|
||||
cache: Default::default(),
|
||||
state: unsafe { mem::MaybeUninit::zeroed().assume_init() },
|
||||
config,
|
||||
}
|
||||
// FileAllocation {
|
||||
// cache: Default::default(),
|
||||
// state: unsafe { mem::MaybeUninit::zeroed().assume_init() },
|
||||
// config,
|
||||
// }
|
||||
|
||||
// does not help with our variation on https://github.com/ARMmbed/littlefs/issues/145
|
||||
unsafe { mem::MaybeUninit::zeroed().assume_init() }
|
||||
}
|
||||
|
||||
pub fn open<'fsalloc: 'falloc>(
|
||||
|
||||
+1159
File diff suppressed because it is too large
Load Diff
@@ -3,11 +3,11 @@
|
||||
pub mod prelude;
|
||||
|
||||
use littlefs2_sys as ll;
|
||||
use ufmt::derive::uDebug;
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
Filesystem,
|
||||
SeekFrom,
|
||||
},
|
||||
driver::Storage,
|
||||
};
|
||||
@@ -108,6 +108,48 @@ pub trait WriteWith {
|
||||
/// Write out all pending writes to storage.
|
||||
fn flush(&mut self) -> Result<()>;
|
||||
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> Result<()> {
|
||||
while !buf.is_empty() {
|
||||
match self.write(buf) {
|
||||
Ok(0) => {
|
||||
// failed to write whole buffer
|
||||
return Err(Error::Io)
|
||||
}
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/** Enumeration of possible methods to seek within an I/O object.
|
||||
|
||||
Use the [`Seek`](../io/trait.Seek.html) trait.
|
||||
*/
|
||||
#[derive(Clone,Copy,Debug,Eq,PartialEq)]
|
||||
pub enum SeekFrom {
|
||||
Start(u32),
|
||||
End(i32),
|
||||
Current(i32),
|
||||
}
|
||||
|
||||
impl SeekFrom {
|
||||
pub(crate) fn off(self) -> i32 {
|
||||
match self {
|
||||
SeekFrom::Start(u) => u as i32,
|
||||
SeekFrom::End(i) => i,
|
||||
SeekFrom::Current(i) => i,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn whence(self) -> i32 {
|
||||
match self {
|
||||
SeekFrom::Start(_) => 0,
|
||||
SeekFrom::End(_) => 2,
|
||||
SeekFrom::Current(_) => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The `Seek` trait provides a cursor which can be moved within a file.
|
||||
@@ -135,7 +177,7 @@ pub trait SeekWith {
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
/// Definition of errors that might be returned by filesystem functionality.
|
||||
#[derive(Clone,Copy,Debug,PartialEq)]
|
||||
#[derive(Clone,Copy,Debug,PartialEq,uDebug)]
|
||||
pub enum Error {
|
||||
/// Input / output error occurred.
|
||||
Io,
|
||||
|
||||
@@ -114,6 +114,8 @@ pub mod macros;
|
||||
pub mod driver;
|
||||
|
||||
pub mod fs;
|
||||
#[cfg(feature = "closures")]
|
||||
pub mod fsc;
|
||||
pub mod io;
|
||||
pub mod path;
|
||||
|
||||
|
||||
+99
-2
@@ -20,7 +20,7 @@ macro_rules! ram_storage { (
|
||||
result=$Result:ident,
|
||||
|
||||
) => {
|
||||
struct $Backend {
|
||||
pub struct $Backend {
|
||||
buf: [u8; $block_size * $block_count],
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ macro_rules! ram_storage { (
|
||||
}
|
||||
}
|
||||
|
||||
struct $Name<'backend> {
|
||||
pub struct $Name<'backend> {
|
||||
backend: &'backend mut $Backend,
|
||||
}
|
||||
|
||||
@@ -137,3 +137,100 @@ macro_rules! ram_storage { (
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! const_ram_storage { (
|
||||
|
||||
name=$Name:ident,
|
||||
trait=$StorageTrait:path,
|
||||
erase_value=$erase_value:expr,
|
||||
read_size=$read_size:expr,
|
||||
write_size=$write_size:expr,
|
||||
cache_size_ty=$cache_size:path,
|
||||
block_size=$block_size:expr,
|
||||
block_count=$block_count:expr,
|
||||
lookaheadwords_size_ty=$lookaheadwords_size:path,
|
||||
filename_max_plus_one_ty=$filename_max_plus_one:path,
|
||||
path_max_plus_one_ty=$path_max_plus_one:path,
|
||||
result=$Result:ident,
|
||||
|
||||
) => {
|
||||
pub struct $Name {
|
||||
buf: [u8; $block_size * $block_count],
|
||||
}
|
||||
|
||||
impl $Name {
|
||||
const ERASE_VALUE: u8 = $erase_value;
|
||||
pub const fn new() -> Self {
|
||||
// Self::default()
|
||||
Self { buf: [$erase_value; $block_size * $block_count] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for $Name {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
buf: [$erase_value; $block_size * $block_count],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $StorageTrait 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 LOOKAHEADWORDS_SIZE = $lookaheadwords_size;
|
||||
type FILENAME_MAX_PLUS_ONE = $filename_max_plus_one;
|
||||
type PATH_MAX_PLUS_ONE = $path_max_plus_one;
|
||||
type ATTRBYTES_MAX = consts::U1022;
|
||||
|
||||
fn read(&self, offset: usize, buf: &mut [u8]) -> $Result<usize> {
|
||||
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.buf[offset..].iter().zip(buf.iter_mut()) {
|
||||
*to = *from;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write(&mut self, offset: usize, data: &[u8]) -> $Result<usize> {
|
||||
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.buf[offset..].iter_mut()) {
|
||||
*to = *from;
|
||||
}
|
||||
Ok(data.len())
|
||||
}
|
||||
|
||||
fn erase(&mut self, offset: usize, len: usize) -> $Result<usize> {
|
||||
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() {
|
||||
*byte = Self::ERASE_VALUE;
|
||||
}
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
};
|
||||
($Name:ident, $bytes:expr) => {
|
||||
const_ram_storage!(
|
||||
name=$Name,
|
||||
trait=LfsStorage,
|
||||
erase_value=0xff,
|
||||
read_size=16,
|
||||
write_size=512,
|
||||
cache_size_ty=consts::U512,
|
||||
block_size=512,
|
||||
block_count=$bytes/512,
|
||||
lookaheadwords_size_ty=consts::U1,
|
||||
filename_max_plus_one_ty=consts::U256,
|
||||
path_max_plus_one_ty=consts::U256,
|
||||
result=LfsResult,
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
+37
-3
@@ -66,19 +66,23 @@ where
|
||||
// pub fn new<F: AsRef<[u8]> + ?Sized>(f: &F) -> Self {
|
||||
pub fn new(f: &[u8]) -> Self {
|
||||
let mut padded_filename: GenericArray<u8, S::FILENAME_MAX_PLUS_ONE> = Default::default();
|
||||
let name_max = <S as driver::Storage>::FILENAME_MAX_PLUS_ONE::to_usize();
|
||||
let name_max = <S as driver::Storage>::FILENAME_MAX_PLUS_ONE::USIZE;
|
||||
// let len = cmp::min(name_max - 1, f.as_ref().len());
|
||||
// padded_filename[..len].copy_from_slice(&f.as_ref()[..len]);
|
||||
let len = cmp::min(name_max - 1, f.len());
|
||||
padded_filename[..len].copy_from_slice(&f[..len]);
|
||||
Filename(padded_filename)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A slice of a specification of the location of a [`File`](../fs/struct.File.html).
|
||||
///
|
||||
/// This module is rather incomplete, compared to `std::path`.
|
||||
pub struct Path<S> (GenericArray<u8, S::PATH_MAX_PLUS_ONE>)
|
||||
pub struct Path<S> (pub(crate) GenericArray<u8, S::PATH_MAX_PLUS_ONE>)
|
||||
where
|
||||
S: driver::Storage,
|
||||
<S as driver::Storage>::PATH_MAX_PLUS_ONE: ArrayLength<u8>,
|
||||
@@ -97,6 +101,16 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> PartialEq for Path<S>
|
||||
where
|
||||
S: driver::Storage,
|
||||
<S as driver::Storage>::PATH_MAX_PLUS_ONE: ArrayLength<u8>,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
// to make `Metadata` Debug
|
||||
impl<S> fmt::Debug for Path<S>
|
||||
where
|
||||
@@ -116,7 +130,7 @@ where
|
||||
/// Silently truncates to maximum configured path length
|
||||
pub fn new<P: AsRef<[u8]> + ?Sized>(p: &P) -> Self {
|
||||
let mut padded_path: GenericArray<u8, S::PATH_MAX_PLUS_ONE> = Default::default();
|
||||
let name_max = <S as driver::Storage>::PATH_MAX_PLUS_ONE::to_usize();
|
||||
let name_max = <S as driver::Storage>::PATH_MAX_PLUS_ONE::USIZE;
|
||||
let len = cmp::min(name_max - 1, p.as_ref().len());
|
||||
padded_path[..len].copy_from_slice(&p.as_ref()[..len]);
|
||||
Path(padded_path)
|
||||
@@ -133,6 +147,26 @@ where
|
||||
pub fn has_root(&self) -> bool {
|
||||
self.0.len() > 0 && self.0[0] == b'/'
|
||||
}
|
||||
|
||||
// what to do about possible "array-too-small" errors?
|
||||
// what does littlefs actually do?
|
||||
// one option would be:
|
||||
//
|
||||
// enum Path {
|
||||
// NotTruncated(RawPath),
|
||||
// Truncated(RawPath),
|
||||
// }
|
||||
//
|
||||
// impl Deref<RawPath> for Path { ... }
|
||||
//
|
||||
// that is, never fail, but tag if truncation was necessary
|
||||
// this way, no need to do error handling for the rare cases,
|
||||
// but can still detect them
|
||||
|
||||
// pub fn join<P: AsRef<Path>>(&self, path: P) -> Path {
|
||||
// }
|
||||
// pub fn try_join<P: AsRef<Path>>(&self, path: P) -> Result<Path> {
|
||||
// }
|
||||
}
|
||||
|
||||
impl<S> From<&str> for Path<S>
|
||||
|
||||
Reference in New Issue
Block a user