Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove need for generic array and typenum constants #101

Open
wants to merge 10 commits into
base: main
Choose a base branch
from

Conversation

sosthene-nitrokey
Copy link
Contributor

This also allows for more flexible Storage implementations that use Vec<u8> as a cache buffer and are not fixed to a single block size, this is used in the list example to allow listing filesystem with varying block sizes without requiring recompilation.

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
@sosthene-nitrokey
Copy link
Contributor Author

Since the default values were mostly targetted for our lpc55 use case and made it annoying to debug the nrf version that has a smaller block size, I wanted to make it easier to switch between the two.

This can be useful for a lot of host tooling that could not be made previously.

src/driver.rs Outdated
}

/// Safety: implemented only by `[u8; N]` and `Vec<u8>` if the alloc feature is enabled
pub unsafe trait Buffer: private::Sealed {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIS the Filesystem implementation only uses with_capacity and the as_ptr/as_mut_ptr methods. Why do we need resizing as part of the trait?

src/fs.rs Outdated
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer taking a &Storage to ensure that the correct value is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could lead to UB with safe code.

The config of the littlefs struct is only defined in the initial allocation and includes the size of the buffer. This is defined by a safe function, it's output could potentially change during execution, while the filesystem is opened, so we need to cache the value and each time we do something with an allocation we must check that the length of the buffers is correct (I can remove the need for assert by instead requesting the correct length for the buffer at that point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, that was a typo – I meant &Filesystem<_, Storage> (just like File::allocate).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the need for the cache_size here and made the length of the buffer be set on usage of the allocation, where we're already in a "faillible context".

src/driver.rs Outdated
fn set_capacity(&mut self, capacity: usize);

/// Can panic if `capacity` > `Self::MAX_CAPACITY`
fn with_capacity(capacity: usize) -> Self;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn’t this be fallible so that invalid configuration leads to an error instead of a panic?

Copy link
Contributor Author

@sosthene-nitrokey sosthene-nitrokey Mar 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this to make the function private as part of Sealed, and Buffer is "publicly empty", that way there is no risk of public use of this.

I'm also documenting the fact that this can panic from the definitions in the Storage trait.

Copy link
Contributor Author

@sosthene-nitrokey sosthene-nitrokey Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made it fallible.

@robin-nitrokey
Copy link
Member

I think this is the right approach to get rid of the configuration constants. What do you think about using a Config or StorageConfig struct and a single Storage::config function instead of having separate functions for every configuration? That would make it easier to implement the trait, especially if the values are configurable like in the list example.

@sosthene-nitrokey
Copy link
Contributor Author

That does seem better overall but on the other hand it does not allow for default values, which we make use of.

One could argue it's better anyway because users should be aware of default values.

It also means that we can't add new fields with default values without it becoming a breaking change

src/driver.rs Outdated
pub trait Sealed {
/// The maximum capacity of the buffer type.
/// Can be [`usize::Max`]()
const MAX_CAPACITY: usize;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIS this is not used currently. Cache::new could use it to validate the sizes, but I still think it would be the easiest solution to remove this constant and make with_len fallible.

src/driver.rs Outdated
Comment on lines 12 to 10
/// Returns a buffer of bytes initialized and valid. If [`set_capacity`]() was called previously,
/// its last call defines the minimum number of valid bytes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should refer to with_len instead of set_capacity.

/// 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See as_ptr.

src/driver.rs Outdated
Comment on lines 19 to 17
/// Current capacity, set by the last call to [`set_capacity`](Buffer::set_capacity)
/// or at initialization through [`with_capacity`](Buffer::with_capacity)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be updated for with_len.

@sosthene-nitrokey
Copy link
Contributor Author

Some changes:

  • Made set_len faillible
  • Added an empty() constructor
  • Made set_len be called on Filesystem initialization and FileAllocation usage. cache_size is still cached to avoid it changing and causing UB. This allows the creation of the allocation to still be infaillible, not changing the usage API. In case of failure to allocate the required capacity, we return NO_MEMORY instead of panicking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants