Skip to content

Commit fe2bdff

Browse files
Add an LfnBuffer type.
Used to buffer Long File Names. Designed to be fed 16-bit UCS-2 chunks in reverse, like you find in a directory.
1 parent 20e5702 commit fe2bdff

File tree

2 files changed

+104
-2
lines changed

2 files changed

+104
-2
lines changed

src/filesystem/filename.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Filename related types
22
33
use crate::fat::VolumeName;
4+
use crate::trace;
45

56
/// Various filename related errors that can occur.
67
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
@@ -219,6 +220,83 @@ impl core::fmt::Debug for ShortFileName {
219220
}
220221
}
221222

223+
/// Used to store a Long File Name
224+
///
225+
/// The const generic specifies the maximum capacity in bytes.
226+
pub struct LfnBuffer<const N: usize> {
227+
/// We fill this buffer in from the back
228+
inner: [u8; N],
229+
/// How many bytes are free.
230+
///
231+
/// This is also the byte index the string starts from.
232+
free: usize,
233+
/// Did we overflow?
234+
overflow: bool,
235+
}
236+
237+
impl<const N: usize> LfnBuffer<N> {
238+
/// Create a new, empty, LFN Buffer
239+
pub fn new() -> LfnBuffer<N> {
240+
LfnBuffer {
241+
inner: [0u8; N],
242+
free: N,
243+
overflow: false,
244+
}
245+
}
246+
247+
/// Empty out this buffer
248+
pub fn clear(&mut self) {
249+
self.free = N;
250+
self.overflow = false;
251+
}
252+
253+
/// Push the 13 UCS-2 characters into this string
254+
///
255+
/// We assume they are pushed last-chunk-first, as you would find
256+
/// them on disk.
257+
pub fn push(&mut self, buffer: &[u16; 13]) {
258+
// find the first null, if any
259+
let null_idx = buffer
260+
.iter()
261+
.position(|&b| b == 0x0000)
262+
.unwrap_or(buffer.len());
263+
// take all the wide chars, up to the null (or go to the end)
264+
let buffer = &buffer[0..null_idx];
265+
for ch in buffer.iter().rev() {
266+
let ch = char::from_u32(*ch as u32).unwrap_or('?');
267+
trace!("LFN push {:?}", ch);
268+
let mut ch_bytes = [0u8; 4];
269+
// a buffer of length 4 is always enough
270+
let ch_str = ch.encode_utf8(&mut ch_bytes);
271+
if self.free < ch_str.len() {
272+
self.overflow = true;
273+
return;
274+
}
275+
// store the encoded character in the buffer, working backwards
276+
for b in ch_str.bytes().rev() {
277+
self.free -= 1;
278+
self.inner[self.free] = b;
279+
}
280+
}
281+
}
282+
283+
/// View this LFN buffer as a string-slice
284+
pub fn as_str(&self) -> &str {
285+
if self.overflow {
286+
""
287+
} else {
288+
// we always only put UTF-8 encoded data in here
289+
unsafe { core::str::from_utf8_unchecked(&self.inner[self.free..]) }
290+
}
291+
}
292+
}
293+
294+
impl<const N: usize> core::default::Default for LfnBuffer<N> {
295+
fn default() -> Self {
296+
LfnBuffer::new()
297+
}
298+
}
299+
222300
// ****************************************************************************
223301
//
224302
// Unit Tests
@@ -321,6 +399,30 @@ mod test {
321399
.csum()
322400
);
323401
}
402+
403+
#[test]
404+
fn one_piece() {
405+
let mut buf: LfnBuffer<64> = LfnBuffer::new();
406+
buf.push(&[
407+
0x0030, 0x0031, 0x0032, 0x0033, 0x2202, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
408+
0xFFFF, 0xFFFF,
409+
]);
410+
assert_eq!(buf.as_str(), "0123∂");
411+
}
412+
413+
#[test]
414+
fn two_piece() {
415+
let mut buf: LfnBuffer<64> = LfnBuffer::new();
416+
buf.push(&[
417+
0x0030, 0x0031, 0x0032, 0x0033, 0x2202, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
418+
0xFFFF, 0xFFFF,
419+
]);
420+
buf.push(&[
421+
0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b,
422+
0x004c, 0x004d,
423+
]);
424+
assert_eq!(buf.as_str(), "ABCDEFGHIJKLM0123∂");
425+
}
324426
}
325427

326428
// ****************************************************************************

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ pub use crate::fat::{FatVolume, VolumeName};
8585

8686
#[doc(inline)]
8787
pub use crate::filesystem::{
88-
Attributes, ClusterId, DirEntry, Directory, File, FilenameError, Mode, RawDirectory, RawFile,
89-
ShortFileName, TimeSource, Timestamp, MAX_FILE_SIZE,
88+
Attributes, ClusterId, DirEntry, Directory, File, FilenameError, LfnBuffer, Mode, RawDirectory,
89+
RawFile, ShortFileName, TimeSource, Timestamp, MAX_FILE_SIZE,
9090
};
9191

9292
use filesystem::DirectoryInfo;

0 commit comments

Comments
 (0)