Simplify code dealing with LFN entries generation

This commit is contained in:
Rafał Harabień 2018-06-24 18:54:42 +02:00
parent a503cb4562
commit dce8b32577
2 changed files with 82 additions and 40 deletions

View File

@ -13,6 +13,11 @@ use dir_entry::{LFN_ENTRY_LAST_FLAG, LFN_PART_LEN};
use file::File;
use fs::{DiskSlice, FileSystem, ReadWriteSeek};
#[cfg(feature = "alloc")]
type LfnUtf16 = Vec<u16>;
#[cfg(not(feature = "alloc"))]
type LfnUtf16 = ();
pub(crate) enum DirRawStream<'a, T: ReadWriteSeek + 'a> {
File(File<'a, T>),
Root(DiskSlice<'a, T>),
@ -385,30 +390,6 @@ impl<'a, T: ReadWriteSeek + 'a> Dir<'a, T> {
}
}
#[cfg(feature = "alloc")]
fn create_lfn_entries(&self, name: &str, short_name: &[u8]) -> io::Result<(DirRawStream<'a, T>, u64)> {
// get short name checksum
let lfn_chsum = lfn_checksum(&short_name);
// convert long name to UTF-16
let lfn_utf16 = name.encode_utf16().collect::<Vec<u16>>();
let lfn_iter = LfnEntriesGenerator::new(&lfn_utf16, lfn_chsum);
// find space for new entries
let num_entries = lfn_iter.len() + 1; // multiple lfn entries + one file entry
let mut stream = self.find_free_entries(num_entries)?;
let start_pos = stream.seek(io::SeekFrom::Current(0))?;
// write LFN entries first
for lfn_entry in lfn_iter {
lfn_entry.serialize(&mut stream)?;
}
Ok((stream, start_pos))
}
#[cfg(not(feature = "alloc"))]
fn create_lfn_entries(&self, _name: &str, _short_name: &[u8]) -> io::Result<(DirRawStream<'a, T>, u64)> {
let mut stream = self.find_free_entries(1)?;
let start_pos = stream.seek(io::SeekFrom::Current(0))?;
Ok((stream, start_pos))
}
fn create_sfn_entry(&self, short_name: [u8; 11], attrs: FileAttributes, first_cluster: Option<u32>) -> DirFileEntryData {
let mut raw_entry = DirFileEntryData::new(short_name, attrs);
raw_entry.set_first_cluster(first_cluster, self.fs.fat_type());
@ -419,12 +400,39 @@ impl<'a, T: ReadWriteSeek + 'a> Dir<'a, T> {
raw_entry
}
fn write_entry(&self, name: &str, raw_entry: DirFileEntryData) -> io::Result<DirEntry<'a, T>> {
trace!("write_entry {}", name);
#[cfg(feature = "alloc")]
fn encode_lfn_utf16(name: &str) -> Vec<u16> {
name.encode_utf16().collect::<Vec<u16>>()
}
#[cfg(not(feature = "alloc"))]
fn encode_lfn_utf16(_name: &str) -> () {
()
}
fn create_lfn_entries_generator(name: &str, short_name: &[u8]) -> io::Result<(LfnEntriesGenerator, LfnUtf16)> {
// check if name doesn't contain unsupported characters
validate_long_name(name)?;
// generate long entries
let (mut stream, start_pos) = self.create_lfn_entries(&name, raw_entry.name())?;
// convert long name to UTF-16
let lfn_utf16 = Self::encode_lfn_utf16(name);
// get short name checksum
let lfn_chsum = lfn_checksum(&short_name);
// create LFN entries generator
let lfn_iter = LfnEntriesGenerator::new(&lfn_utf16, lfn_chsum);
Ok((lfn_iter, lfn_utf16))
}
fn write_entry(&self, name: &str, raw_entry: DirFileEntryData) -> io::Result<DirEntry<'a, T>> {
trace!("write_entry {}", name);
// create LFN entries generator
let (lfn_iter, lfn_utf16) = Self::create_lfn_entries_generator(name, raw_entry.name())?;
// find space for new entries (multiple LFN entries and 1 SFN entry)
let num_entries = lfn_iter.len() + 1;
let mut stream = self.find_free_entries(num_entries)?;
let start_pos = stream.seek(io::SeekFrom::Current(0))?;
// write LFN entries before SFN entry
for lfn_entry in lfn_iter {
lfn_entry.serialize(&mut stream)?;
}
// write short name entry
raw_entry.serialize(&mut stream)?;
let end_pos = stream.seek(io::SeekFrom::Current(0))?;
@ -434,8 +442,7 @@ impl<'a, T: ReadWriteSeek + 'a> Dir<'a, T> {
return Ok(DirEntry {
data: raw_entry,
short_name,
#[cfg(feature = "alloc")]
lfn: Vec::new(),
lfn_utf16,
fs: self.fs,
entry_pos: abs_pos.unwrap(), // SAFE: abs_pos is absent only for empty file
offset_range: (start_pos, end_pos),
@ -482,7 +489,6 @@ impl<'a, T: ReadWriteSeek> DirIter<'a, T> {
}
fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'a, T>>> {
#[cfg(feature = "alloc")]
let mut lfn_buf = LongNameBuilder::new();
let mut offset = self.stream.seek(SeekFrom::Current(0))?;
let mut begin_offset = offset;
@ -495,7 +501,6 @@ impl<'a, T: ReadWriteSeek> DirIter<'a, T> {
}
// Check if this is deleted or volume ID entry
if self.should_ship_entry(&raw_entry) {
#[cfg(feature = "alloc")]
lfn_buf.clear();
begin_offset = offset;
continue;
@ -505,15 +510,13 @@ impl<'a, T: ReadWriteSeek> DirIter<'a, T> {
// Get entry position on volume
let abs_pos = self.stream.abs_pos().map(|p| p - DIR_ENTRY_SIZE);
// Check if LFN checksum is valid
#[cfg(feature = "alloc")]
lfn_buf.validate_chksum(data.name());
// Return directory entry
let short_name = ShortName::new(data.name());
return Ok(Some(DirEntry {
data,
short_name,
#[cfg(feature = "alloc")]
lfn: lfn_buf.to_vec(),
lfn_utf16: lfn_buf.to_vec(),
fs: self.fs,
entry_pos: abs_pos.unwrap(), // SAFE: abs_pos is empty only for empty file
offset_range: (begin_offset, offset),
@ -521,7 +524,6 @@ impl<'a, T: ReadWriteSeek> DirIter<'a, T> {
},
DirEntryData::Lfn(data) => {
// Append to LFN buffer
#[cfg(feature = "alloc")]
lfn_buf.process(&data);
},
}
@ -581,7 +583,6 @@ fn validate_long_name(name: &str) -> io::Result<()> {
Ok(())
}
#[cfg(feature = "alloc")]
fn lfn_checksum(short_name: &[u8]) -> u8 {
let mut chksum = num::Wrapping(0u8);
for i in 0..11 {
@ -677,6 +678,21 @@ impl LongNameBuilder {
}
}
// Dummy implementation for non-alloc build
#[cfg(not(feature = "alloc"))]
struct LongNameBuilder {}
#[cfg(not(feature = "alloc"))]
impl LongNameBuilder {
fn new() -> Self {
LongNameBuilder {}
}
fn clear(&mut self) {}
fn to_vec(self) {}
fn truncate(&mut self) {}
fn process(&mut self, _data: &DirLfnEntryData) {}
fn validate_chksum(&mut self, _short_name: &[u8]) {}
}
#[cfg(feature = "alloc")]
struct LfnEntriesGenerator<'a> {
name_parts_iter: iter::Rev<slice::Chunks<'a, u16>>,
@ -750,6 +766,30 @@ impl<'a> Iterator for LfnEntriesGenerator<'a> {
#[cfg(feature = "alloc")]
impl<'a> ExactSizeIterator for LfnEntriesGenerator<'a> {}
// Dummy implementation for non-alloc build
#[cfg(not(feature = "alloc"))]
struct LfnEntriesGenerator {}
#[cfg(not(feature = "alloc"))]
impl LfnEntriesGenerator {
fn new(_name_utf16: &(), _checksum: u8) -> Self {
LfnEntriesGenerator {}
}
}
#[cfg(not(feature = "alloc"))]
impl Iterator for LfnEntriesGenerator {
type Item = DirLfnEntryData;
fn next(&mut self) -> Option<Self::Item> {
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(0))
}
}
#[cfg(not(feature = "alloc"))]
impl ExactSizeIterator for LfnEntriesGenerator {}
#[derive(Default, Debug, Clone)]
struct ShortNameGenerator {
chksum: u16,

View File

@ -503,7 +503,9 @@ pub struct DirEntry<'a, T: ReadWriteSeek + 'a> {
pub(crate) data: DirFileEntryData,
pub(crate) short_name: ShortName,
#[cfg(feature = "alloc")]
pub(crate) lfn: Vec<u16>,
pub(crate) lfn_utf16: Vec<u16>,
#[cfg(not(feature = "alloc"))]
pub(crate) lfn_utf16: (),
pub(crate) entry_pos: u64,
pub(crate) offset_range: (u64, u64),
pub(crate) fs: &'a FileSystem<T>,
@ -528,8 +530,8 @@ impl<'a, T: ReadWriteSeek> DirEntry<'a, T> {
/// Returns long file name or if it doesn't exist fallbacks to short file name.
#[cfg(feature = "alloc")]
pub fn file_name(&self) -> String {
if self.lfn.len() > 0 {
String::from_utf16_lossy(&self.lfn)
if self.lfn_utf16.len() > 0 {
String::from_utf16_lossy(&self.lfn_utf16)
} else {
self.data.lowercase_name().to_string(self.fs.options.oem_cp_converter)
}