From 15a996b1a65f64ece811ba4bf3d98aca79f456f0 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Thu, 22 Jan 2026 23:41:19 +0100 Subject: [PATCH 1/2] Reduce LfnBuffer size from 24 to 16 bytes on 32bits sys. And from 32 bytes to 24 bytes on 64bits systems. The reduction comes from the observation that the required buffer doesn't need to exceed the maximum bytes required to represent a lfn. A lfn has at most 255 UTF-16 characters. The buffer holds UTF-8 characters. A UTF-8 character takes at most 3 bytes. So, the buffer doesn't need to exceed 255*3 = 765 bytes. Thus, `free` can be stored in a u16. To avoid a breaking change, we accept buffer larger than u16::MAX and just slice it to retain at most u16::MAX bytes. Note that the optimization could go beyond by replacing `inner` with a pointer to the data and storing the length in a u16. `unpaired_surrogate` could also be a `Option` because `0` is not a valid surrogate character. Finally we could encode the `overflow` bool in the msb of `free`. All these optimizations make the code too complex IMO. --- src/filesystem/filename.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/filesystem/filename.rs b/src/filesystem/filename.rs index 7d58b65d..dbc48a89 100644 --- a/src/filesystem/filename.rs +++ b/src/filesystem/filename.rs @@ -228,7 +228,7 @@ pub struct LfnBuffer<'a> { /// How many bytes are free. /// /// This is also the byte index the string starts from. - free: usize, + free: u16, /// Did we overflow? overflow: bool, /// If a surrogate-pair is split over two directory entries, remember half of it here. @@ -238,10 +238,14 @@ pub struct LfnBuffer<'a> { impl<'a> LfnBuffer<'a> { /// Create a new, empty, LFN Buffer using the given mutable slice as its storage. pub fn new(storage: &'a mut [u8]) -> LfnBuffer<'a> { - let len = storage.len(); + // Because `free` is a `u16`, we keep at most `u16::MAX` bytes of the buffer. + // It is enough to hold all LFN because a LFN has at most 255 characters. + // A UTF-8 character takes at most 3 bytes. + // Thus, a buffer of 765 (255*3) bytes is able to represent any LFN. + let len = storage.len().min(usize::from(u16::MAX)); LfnBuffer { - inner: storage, - free: len, + inner: &mut storage[..len], + free: len as u16, overflow: false, unpaired_surrogate: None, } @@ -249,7 +253,7 @@ impl<'a> LfnBuffer<'a> { /// Empty out this buffer pub fn clear(&mut self) { - self.free = self.inner.len(); + self.free = self.inner.len() as u16; self.overflow = false; self.unpaired_surrogate = None; } @@ -324,7 +328,7 @@ impl<'a> LfnBuffer<'a> { // a buffer of length 4 is enough to encode any char let mut encoded_ch = [0u8; 4]; let encoded_ch = ch.encode_utf8(&mut encoded_ch); - if self.free < encoded_ch.len() { + if self.free < encoded_ch.len() as u16 { // the LFN buffer they gave us was not long enough. Note for // later, so we don't show them garbage. self.overflow = true; @@ -334,7 +338,7 @@ impl<'a> LfnBuffer<'a> { // already checked there was enough space. for b in encoded_ch.bytes().rev() { self.free -= 1; - self.inner[self.free] = b; + self.inner[usize::from(self.free)] = b; } } } @@ -348,7 +352,7 @@ impl<'a> LfnBuffer<'a> { "" } else { // we always only put UTF-8 encoded data in here - unsafe { core::str::from_utf8_unchecked(&self.inner[self.free..]) } + unsafe { core::str::from_utf8_unchecked(&self.inner[usize::from(self.free)..]) } } } } From bf25418ab393fb9c7904972f4c4dcd51a5cc4a00 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Fri, 23 Jan 2026 22:24:47 +0100 Subject: [PATCH 2/2] Factorize casting --- src/filesystem/filename.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/filesystem/filename.rs b/src/filesystem/filename.rs index dbc48a89..ecdd5fac 100644 --- a/src/filesystem/filename.rs +++ b/src/filesystem/filename.rs @@ -251,6 +251,11 @@ impl<'a> LfnBuffer<'a> { } } + /// Returns [`Self::free`] casted to `usize`. + fn free(&self) -> usize { + usize::from(self.free) + } + /// Empty out this buffer pub fn clear(&mut self) { self.free = self.inner.len() as u16; @@ -328,7 +333,7 @@ impl<'a> LfnBuffer<'a> { // a buffer of length 4 is enough to encode any char let mut encoded_ch = [0u8; 4]; let encoded_ch = ch.encode_utf8(&mut encoded_ch); - if self.free < encoded_ch.len() as u16 { + if self.free() < encoded_ch.len() { // the LFN buffer they gave us was not long enough. Note for // later, so we don't show them garbage. self.overflow = true; @@ -338,7 +343,7 @@ impl<'a> LfnBuffer<'a> { // already checked there was enough space. for b in encoded_ch.bytes().rev() { self.free -= 1; - self.inner[usize::from(self.free)] = b; + self.inner[self.free()] = b; } } } @@ -352,7 +357,7 @@ impl<'a> LfnBuffer<'a> { "" } else { // we always only put UTF-8 encoded data in here - unsafe { core::str::from_utf8_unchecked(&self.inner[usize::from(self.free)..]) } + unsafe { core::str::from_utf8_unchecked(&self.inner[self.free()..]) } } } }