From 4feeb3eead3e6cd0ad58767b1875fe852d0fe786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Harabie=C5=84?= Date: Wed, 27 Sep 2017 14:05:58 +0200 Subject: [PATCH] Use references instead of Rc for shared state. --- examples/cat.rs | 4 +- examples/ls.rs | 4 +- src/dir.rs | 75 ++++++++++++++++++++++------------ src/file.rs | 12 +++--- src/fs.rs | 84 +++++++++++++++++++-------------------- src/lib.rs | 1 + tests/integration-test.rs | 61 ++++++++++++++-------------- 7 files changed, 134 insertions(+), 107 deletions(-) diff --git a/examples/cat.rs b/examples/cat.rs index 38ed191..ad751b7 100644 --- a/examples/cat.rs +++ b/examples/cat.rs @@ -10,8 +10,8 @@ use rfat::FatFileSystem; fn main() { let file = File::open("resources/fat32.img").unwrap(); - let buf_rdr = BufReader::new(file); - let mut fs = FatFileSystem::new(Box::new(buf_rdr)).unwrap(); + let mut buf_rdr = BufReader::new(file); + let fs = FatFileSystem::new(&mut buf_rdr).unwrap(); let mut root_dir = fs.root_dir(); let mut file = root_dir.open_file(&env::args().nth(1).unwrap()).unwrap(); let mut buf = vec![]; diff --git a/examples/ls.rs b/examples/ls.rs index 8e32fb7..0248f92 100644 --- a/examples/ls.rs +++ b/examples/ls.rs @@ -24,8 +24,8 @@ fn format_file_size(size: u64) -> String { fn main() { let file = File::open("resources/fat32.img").unwrap(); - let buf_rdr = BufReader::new(file); - let mut fs = FatFileSystem::new(Box::new(buf_rdr)).unwrap(); + let mut buf_rdr = BufReader::new(file); + let fs = FatFileSystem::new(&mut buf_rdr).unwrap(); let mut root_dir = fs.root_dir(); let mut dir = match env::args().nth(1) { None => root_dir, diff --git a/src/dir.rs b/src/dir.rs index 8f0bb3c..1caabd1 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -7,9 +7,34 @@ use std::str; use byteorder::{LittleEndian, ReadBytesExt}; use chrono::{DateTime, Date, TimeZone, Local}; -use fs::{FatSharedStateRef, ReadSeek}; +use fs::{FatSharedStateRef, FatSlice}; use file::FatFile; +pub(crate) enum FatDirReader<'a, 'b: 'a> { + File(FatFile<'a, 'b>), + Root(FatSlice<'a, 'b>), +} + +impl <'a, 'b> Read for FatDirReader<'a, 'b> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match self { + &mut FatDirReader::File(ref mut file) => file.read(buf), + &mut FatDirReader::Root(ref mut raw) => raw.read(buf), + } + } +} + +impl <'a, 'b> Seek for FatDirReader<'a, 'b> { + fn seek(&mut self, pos: SeekFrom) -> io::Result { + match self { + &mut FatDirReader::File(ref mut file) => file.seek(pos), + &mut FatDirReader::Root(ref mut raw) => raw.seek(pos), + } + } +} + + + bitflags! { #[derive(Default)] pub struct FatFileAttributes: u8 { @@ -61,13 +86,13 @@ enum FatDirEntryData { } #[derive(Clone)] -pub struct FatDirEntry { +pub struct FatDirEntry<'a, 'b: 'a> { data: FatDirFileEntryData, lfn: Vec, - state: FatSharedStateRef, + state: FatSharedStateRef<'a, 'b>, } -impl FatDirEntry { +impl <'a, 'b> FatDirEntry<'a, 'b> { pub fn short_file_name(&self) -> String { let name = str::from_utf8(&self.data.name[0..8]).unwrap().trim_right(); let ext = str::from_utf8(&self.data.name[8..11]).unwrap().trim_right(); @@ -98,19 +123,19 @@ impl FatDirEntry { ((self.data.first_cluster_hi as u32) << 16) | self.data.first_cluster_lo as u32 } - pub fn to_file(&self) -> FatFile { + pub fn to_file(&self) -> FatFile<'a, 'b> { if self.is_dir() { panic!("This is a directory"); } - FatFile::new(self.first_cluster(), Some(self.data.size), self.state.clone()) + FatFile::new(self.first_cluster(), Some(self.data.size), self.state) } - pub fn to_dir(&self) -> FatDir { + pub fn to_dir(&self) -> FatDir<'a, 'b> { if !self.is_dir() { panic!("This is a file"); } - let file = FatFile::new(self.first_cluster(), None, self.state.clone()); - FatDir::new(Box::new(file), self.state.clone()) + let file = FatFile::new(self.first_cluster(), None, self.state); + FatDir::new(FatDirReader::File(file), self.state) } pub fn len(&self) -> u64 { @@ -136,28 +161,28 @@ impl FatDirEntry { fn convert_date_time(dos_date: u16, dos_time: u16) -> DateTime { let (hour, min, sec) = (dos_time >> 11, (dos_time >> 5) & 0x3F, (dos_time & 0x1F) * 2); - Self::convert_date(dos_date).and_hms(hour as u32, min as u32, sec as u32) + FatDirEntry::convert_date(dos_date).and_hms(hour as u32, min as u32, sec as u32) } } -impl fmt::Debug for FatDirEntry { +impl <'a, 'b> fmt::Debug for FatDirEntry<'a, 'b> { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { self.data.fmt(f) } } -pub struct FatDir { - rdr: Box, - state: FatSharedStateRef, +pub struct FatDir<'a, 'b: 'a> { + rdr: FatDirReader<'a, 'b>, + state: FatSharedStateRef<'a, 'b>, } -impl FatDir { +impl <'a, 'b> FatDir<'a, 'b> { - pub(crate) fn new(rdr: Box, state: FatSharedStateRef) -> FatDir { + pub(crate) fn new(rdr: FatDirReader<'a, 'b>, state: FatSharedStateRef<'a, 'b>) -> FatDir<'a, 'b> { FatDir { rdr, state } } - pub fn list(&mut self) -> io::Result> { + pub fn list(&mut self) -> io::Result>> { self.rewind(); Ok(self.map(|x| x.unwrap()).collect()) } @@ -202,15 +227,15 @@ impl FatDir { } } - fn split_path<'a>(path: &'a str) -> (&'a str, Option<&'a str>) { + fn split_path<'c>(path: &'c str) -> (&'c str, Option<&'c str>) { let mut path_split = path.trim_matches('/').splitn(2, "/"); let comp = path_split.next().unwrap(); let rest_opt = path_split.next(); (comp, rest_opt) } - fn find_entry(&mut self, name: &str) -> io::Result { - let entries: Vec = self.list()?; + fn find_entry(&mut self, name: &str) -> io::Result> { + let entries: Vec> = self.list()?; for e in entries { if e.file_name().eq_ignore_ascii_case(name) { return Ok(e); @@ -219,7 +244,7 @@ impl FatDir { Err(io::Error::new(ErrorKind::NotFound, "file not found")) } - pub fn open_dir(&mut self, path: &str) -> io::Result { + pub fn open_dir(&mut self, path: &str) -> io::Result> { let (name, rest_opt) = Self::split_path(path); let e = self.find_entry(name)?; match rest_opt { @@ -228,7 +253,7 @@ impl FatDir { } } - pub fn open_file(&mut self, path: &str) -> io::Result { + pub fn open_file(&mut self, path: &str) -> io::Result> { let (name, rest_opt) = Self::split_path(path); let e = self.find_entry(name)?; match rest_opt { @@ -238,10 +263,10 @@ impl FatDir { } } -impl Iterator for FatDir { - type Item = io::Result; +impl <'a, 'b> Iterator for FatDir<'a, 'b> { + type Item = io::Result>; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option>> { let mut lfn_buf = Vec::::new(); loop { let res = self.read_dir_entry_data(); diff --git a/src/file.rs b/src/file.rs index 6ca3eb6..7a67589 100644 --- a/src/file.rs +++ b/src/file.rs @@ -6,16 +6,16 @@ use std::io; use fs::FatSharedStateRef; -pub struct FatFile { +pub struct FatFile<'a, 'b: 'a> { first_cluster: u32, size: Option, offset: u32, current_cluster: Option, - state: FatSharedStateRef, + state: FatSharedStateRef<'a, 'b>, } -impl FatFile { - pub(crate) fn new(first_cluster: u32, size: Option, state: FatSharedStateRef) -> FatFile { +impl <'a, 'b> FatFile<'a, 'b> { + pub(crate) fn new(first_cluster: u32, size: Option, state: FatSharedStateRef<'a, 'b>) -> Self { FatFile { first_cluster, size, state, current_cluster: Some(first_cluster), @@ -24,7 +24,7 @@ impl FatFile { } } -impl Read for FatFile { +impl <'a, 'b> Read for FatFile<'a, 'b> { fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut buf_offset: usize = 0; let cluster_size = self.state.borrow().get_cluster_size(); @@ -55,7 +55,7 @@ impl Read for FatFile { } } -impl Seek for FatFile { +impl <'a, 'b> Seek for FatFile<'a, 'b> { fn seek(&mut self, pos: SeekFrom) -> io::Result { let new_offset = match pos { SeekFrom::Current(x) => self.offset as i64 + x, diff --git a/src/fs.rs b/src/fs.rs index 0f786ad..df2232c 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1,15 +1,13 @@ -use std::cell::RefCell; +use core::cell::RefCell; use std::cmp; use std::io::prelude::*; -use std::io::{Error, ErrorKind}; -use std::io::SeekFrom; +use std::io::{Error, ErrorKind, SeekFrom}; use std::io; use std::str; -use std::rc::Rc; use byteorder::{LittleEndian, ReadBytesExt}; use file::FatFile; -use dir::FatDir; +use dir::{FatDirReader, FatDir}; use table::{FatTable, FatTable12, FatTable16, FatTable32}; // FAT implementation based on: @@ -24,17 +22,17 @@ pub enum FatType { pub trait ReadSeek: Read + Seek {} impl ReadSeek for T where T: Read + Seek {} -pub(crate) struct FatSharedState { - pub rdr: Box, - pub fat_type: FatType, - pub boot: FatBootRecord, - pub first_fat_sector: u32, - pub first_data_sector: u32, - pub root_dir_sectors: u32, - pub table: Box, +pub(crate) struct FatSharedState<'a> { + pub(crate) rdr: &'a mut ReadSeek, + pub(crate) fat_type: FatType, + pub(crate) boot: FatBootRecord, + pub(crate) first_fat_sector: u32, + pub(crate) first_data_sector: u32, + pub(crate) root_dir_sectors: u32, + pub(crate) table: Box, } -impl FatSharedState { +impl <'a> FatSharedState<'a> { pub(crate) fn offset_from_sector(&self, sector: u32) -> u64 { (sector as u64) * self.boot.bpb.bytes_per_sector as u64 } @@ -52,12 +50,6 @@ impl FatSharedState { } } -pub(crate) type FatSharedStateRef = Rc>; - -pub struct FatFileSystem { - pub(crate) state: FatSharedStateRef, -} - #[allow(dead_code)] #[derive(Default, Debug, Clone)] pub(crate) struct FatBiosParameterBlock { @@ -111,12 +103,19 @@ impl Default for FatBootRecord { } } -impl FatFileSystem { +pub(crate) type FatSharedStateCell<'a> = RefCell>; +pub(crate) type FatSharedStateRef<'a, 'b: 'a> = &'a RefCell>; + +pub struct FatFileSystem<'a> { + pub(crate) state: FatSharedStateCell<'a>, +} + +impl <'a> FatFileSystem<'a> { - pub fn new(mut rdr: Box) -> io::Result { + pub fn new(mut rdr: &'a mut T) -> io::Result> { let boot = Self::read_boot_record(&mut *rdr)?; if boot.boot_sig != [0x55, 0xAA] { - return Err(Error::new(ErrorKind::Other, "invalid signature")); + return Err(Error::new(ErrorKind::Other, "invBox::newalid signature")); } let total_sectors = if boot.bpb.total_sectors_16 == 0 { boot.bpb.total_sectors_32 } else { boot.bpb.total_sectors_16 as u32 }; @@ -132,9 +131,9 @@ impl FatFileSystem { rdr.seek(SeekFrom::Start(fat_offset as u64))?; let table_size_bytes = table_size * boot.bpb.bytes_per_sector as u32; let table: Box = match fat_type { - FatType::Fat12 => Box::new(FatTable12::read(&mut rdr, table_size_bytes as usize)?), - FatType::Fat16 => Box::new(FatTable16::read(&mut rdr, table_size_bytes as usize)?), - FatType::Fat32 => Box::new(FatTable32::read(&mut rdr, table_size_bytes as usize)?), + FatType::Fat12 => Box::new(FatTable12::read(rdr, table_size_bytes as usize)?), + FatType::Fat16 => Box::new(FatTable16::read(rdr, table_size_bytes as usize)?), + FatType::Fat32 => Box::new(FatTable32::read(rdr, table_size_bytes as usize)?), _ => panic!("TODO: exfat") }; @@ -148,9 +147,8 @@ impl FatFileSystem { table, }; - let state_rc = Rc::new(RefCell::new(state)); Ok(FatFileSystem { - state: state_rc, + state: RefCell::new(state), }) } @@ -166,14 +164,16 @@ impl FatFileSystem { str::from_utf8(&self.state.borrow().boot.bpb.volume_label).unwrap().trim_right().to_string() } - pub fn root_dir(&mut self) -> FatDir { - let state = self.state.borrow(); - let root_rdr: Box = match state.fat_type { - FatType::Fat12 | FatType::Fat16 => Box::new(FatSlice::from_sectors( - state.first_data_sector - state.root_dir_sectors, state.root_dir_sectors, self.state.clone())), - _ => Box::new(FatFile::new(state.boot.bpb.root_cluster, None, self.state.clone())) // FIXME + pub fn root_dir<'b>(&'b self) -> FatDir<'b, 'a> { + let root_rdr = { + let state = self.state.borrow(); + match state.fat_type { + FatType::Fat12 | FatType::Fat16 => FatDirReader::Root(FatSlice::from_sectors( + state.first_data_sector - state.root_dir_sectors, state.root_dir_sectors, &self.state)), + _ => FatDirReader::File(FatFile::new(state.boot.bpb.root_cluster, None, &self.state)), + } }; - FatDir::new(root_rdr, self.state.clone()) + FatDir::new(root_rdr, &self.state) } fn read_bpb(rdr: &mut Read) -> io::Result { @@ -248,25 +248,25 @@ impl FatFileSystem { } } -struct FatSlice { +pub(crate) struct FatSlice<'a, 'b: 'a> { begin: u64, size: u64, offset: u64, - state: FatSharedStateRef, + state: FatSharedStateRef<'a, 'b>, } -impl FatSlice { - pub(crate) fn new(begin: u64, size: u64, state: FatSharedStateRef) -> FatSlice { +impl <'a, 'b> FatSlice<'a, 'b> { + pub(crate) fn new(begin: u64, size: u64, state: FatSharedStateRef<'a, 'b>) -> Self { FatSlice { begin, size, state, offset: 0 } } - pub(crate) fn from_sectors(first_sector: u32, sectors_count: u32, state: FatSharedStateRef) -> FatSlice { + pub(crate) fn from_sectors(first_sector: u32, sectors_count: u32, state: FatSharedStateRef<'a, 'b>) -> Self { let bytes_per_sector = state.borrow().boot.bpb.bytes_per_sector as u64; Self::new(first_sector as u64 * bytes_per_sector, sectors_count as u64 * bytes_per_sector, state) } } -impl Read for FatSlice { +impl <'a, 'b> Read for FatSlice<'a, 'b> { fn read(&mut self, buf: &mut [u8]) -> io::Result { let offset = self.begin + self.offset; let read_size = cmp::min((self.size - self.offset) as usize, buf.len()); @@ -278,7 +278,7 @@ impl Read for FatSlice { } } -impl Seek for FatSlice { +impl <'a, 'b> Seek for FatSlice<'a, 'b> { fn seek(&mut self, pos: SeekFrom) -> io::Result { let new_offset = match pos { SeekFrom::Current(x) => self.offset as i64 + x, diff --git a/src/lib.rs b/src/lib.rs index 324c433..9a7b656 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ extern crate byteorder; extern crate chrono; +extern crate core; #[macro_use] extern crate bitflags; diff --git a/tests/integration-test.rs b/tests/integration-test.rs index 161b569..f1b058a 100644 --- a/tests/integration-test.rs +++ b/tests/integration-test.rs @@ -7,18 +7,19 @@ use std::str; use rfat::{FatFileSystem, FatType}; -const TEST_TEXT: &'static str = "Rust is cool!\n"; -const FAT12_IMG: &'static str = "resources/fat12.img"; -const FAT16_IMG: &'static str = "resources/fat16.img"; -const FAT32_IMG: &'static str = "resources/fat32.img"; +const TEST_TEXT: &str = "Rust is cool!\n"; +const FAT12_IMG: &str = "resources/fat12.img"; +const FAT16_IMG: &str = "resources/fat16.img"; +const FAT32_IMG: &str = "resources/fat32.img"; -fn open_fs(filename: &str) -> FatFileSystem { +fn call_with_fs(f: &Fn(FatFileSystem) -> (), filename: &str) { let file = File::open(filename).unwrap(); - let buf_rdr = BufReader::new(file); - FatFileSystem::new(Box::new(buf_rdr)).unwrap() + let mut buf_rdr = BufReader::new(file); + let fs = FatFileSystem::new(&mut buf_rdr).unwrap(); + f(fs); } -fn test_root_dir(mut fs: FatFileSystem) { +fn test_root_dir(fs: FatFileSystem) { let mut root_dir = fs.root_dir(); let entries = root_dir.list().unwrap(); let short_names = entries.iter().map(|e| e.short_file_name()).collect::>(); @@ -33,20 +34,20 @@ fn test_root_dir(mut fs: FatFileSystem) { #[test] fn test_root_dir_fat12() { - test_root_dir(open_fs(FAT12_IMG)); + call_with_fs(&test_root_dir, FAT12_IMG) } #[test] fn test_root_dir_fat16() { - test_root_dir(open_fs(FAT16_IMG)); + call_with_fs(&test_root_dir, FAT16_IMG) } #[test] fn test_root_dir_fat32() { - test_root_dir(open_fs(FAT32_IMG)); + call_with_fs(&test_root_dir, FAT32_IMG) } -fn test_read_seek_short_file(mut fs: FatFileSystem) { +fn test_read_seek_short_file(fs: FatFileSystem) { let mut root_dir = fs.root_dir(); let mut short_file = root_dir.open_file("short.txt").unwrap(); let mut buf = Vec::new(); @@ -61,20 +62,20 @@ fn test_read_seek_short_file(mut fs: FatFileSystem) { #[test] fn test_read_seek_short_file_fat12() { - test_read_seek_short_file(open_fs(FAT12_IMG)) + call_with_fs(&test_read_seek_short_file, FAT12_IMG) } #[test] fn test_read_seek_short_file_fat16() { - test_read_seek_short_file(open_fs(FAT16_IMG)) + call_with_fs(&test_read_seek_short_file, FAT16_IMG) } #[test] fn test_read_seek_short_file_fat32() { - test_read_seek_short_file(open_fs(FAT32_IMG)) + call_with_fs(&test_read_seek_short_file, FAT32_IMG) } -fn test_read_long_file(mut fs: FatFileSystem) { +fn test_read_long_file(fs: FatFileSystem) { let mut root_dir = fs.root_dir(); let mut long_file = root_dir.open_file("long.txt").unwrap(); let mut buf = Vec::new(); @@ -90,20 +91,20 @@ fn test_read_long_file(mut fs: FatFileSystem) { #[test] fn test_read_long_file_fat12() { - test_read_long_file(open_fs(FAT12_IMG)) + call_with_fs(&test_read_long_file, FAT12_IMG) } #[test] fn test_read_long_file_fat16() { - test_read_long_file(open_fs(FAT16_IMG)) + call_with_fs(&test_read_long_file, FAT16_IMG) } #[test] fn test_read_long_file_fat32() { - test_read_long_file(open_fs(FAT32_IMG)) + call_with_fs(&test_read_long_file, FAT32_IMG) } -fn test_get_dir_by_path(mut fs: FatFileSystem) { +fn test_get_dir_by_path(fs: FatFileSystem) { let mut root_dir = fs.root_dir(); let mut dir = root_dir.open_dir("very/long/path/").unwrap(); let entries = dir.list().unwrap(); @@ -113,20 +114,20 @@ fn test_get_dir_by_path(mut fs: FatFileSystem) { #[test] fn test_get_dir_by_path_fat12() { - test_get_dir_by_path(open_fs(FAT12_IMG)) + call_with_fs(&test_get_dir_by_path, FAT12_IMG) } #[test] fn test_get_dir_by_path_fat16() { - test_get_dir_by_path(open_fs(FAT16_IMG)) + call_with_fs(&test_get_dir_by_path, FAT16_IMG) } #[test] fn test_get_dir_by_path_fat32() { - test_get_dir_by_path(open_fs(FAT32_IMG)) + call_with_fs(&test_get_dir_by_path, FAT32_IMG) } -fn test_get_file_by_path(mut fs: FatFileSystem) { +fn test_get_file_by_path(fs: FatFileSystem) { let mut root_dir = fs.root_dir(); let mut file = root_dir.open_file("very/long/path/test.txt").unwrap(); let mut buf = Vec::new(); @@ -141,17 +142,17 @@ fn test_get_file_by_path(mut fs: FatFileSystem) { #[test] fn test_get_file_by_path_fat12() { - test_get_file_by_path(open_fs(FAT12_IMG)) + call_with_fs(&test_get_file_by_path, FAT12_IMG) } #[test] fn test_get_file_by_path_fat16() { - test_get_file_by_path(open_fs(FAT16_IMG)) + call_with_fs(&test_get_file_by_path, FAT16_IMG) } #[test] fn test_get_file_by_path_fat32() { - test_get_file_by_path(open_fs(FAT32_IMG)) + call_with_fs(&test_get_file_by_path, FAT32_IMG) } fn test_volume_metadata(fs: FatFileSystem, fat_type: FatType) { @@ -162,15 +163,15 @@ fn test_volume_metadata(fs: FatFileSystem, fat_type: FatType) { #[test] fn test_volume_metadata_fat12() { - test_volume_metadata(open_fs(FAT12_IMG), FatType::Fat12) + call_with_fs(&|fs| test_volume_metadata(fs, FatType::Fat12), FAT12_IMG) } #[test] fn test_volume_metadata_fat16() { - test_volume_metadata(open_fs(FAT16_IMG), FatType::Fat16) + call_with_fs(&|fs| test_volume_metadata(fs, FatType::Fat16), FAT16_IMG) } #[test] fn test_volume_metadata_fat32() { - test_volume_metadata(open_fs(FAT32_IMG), FatType::Fat32) + call_with_fs(&|fs| test_volume_metadata(fs, FatType::Fat32), FAT32_IMG) }