diff --git a/src/blocked_device.rs b/src/blocked_device.rs new file mode 100644 index 0000000..be05aaf --- /dev/null +++ b/src/blocked_device.rs @@ -0,0 +1,141 @@ +use util::*; +use vfs::Device; + +/// Device which can only R/W in blocks +pub trait BlockedDevice { + fn block_size_log2(&self) -> u8; + fn read_at(&mut self, block_id: usize, buf: &mut [u8]) -> bool; + fn write_at(&mut self, block_id: usize, buf: &[u8]) -> bool; +} + +macro_rules! try0 { + ($len:expr, $res:expr) => { + if(!$res) {return Some($len);} + }; +} + +/// Helper functions to R/W BlockedDevice in bytes +impl Device for T { + fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Option { + let iter = BlockIter { + begin: offset, + end: offset + buf.len(), + block_size_log2: self.block_size_log2(), + }; + + // For each block + for mut range in iter { + let len = range.origin_begin() - offset; + let buf = &mut buf[range.origin_begin() - offset..range.origin_end() - offset]; + if range.is_full() { + // Read to target buf directly + try0!(len, BlockedDevice::read_at(self, range.block, buf)); + } else { + use core::mem::uninitialized; + let mut block_buf: [u8; 4096] = unsafe { uninitialized() }; + assert!(self.block_size_log2() <= 12); + // Read to local buf first + try0!(len, BlockedDevice::read_at(self, range.block, &mut block_buf)); + // Copy to target buf then + buf.copy_from_slice(&mut block_buf[range.begin..range.end]); + } + } + Some(buf.len()) + } + + fn write_at(&mut self, offset: usize, buf: &[u8]) -> Option { + let iter = BlockIter { + begin: offset, + end: offset + buf.len(), + block_size_log2: self.block_size_log2(), + }; + + // For each block + for mut range in iter { + let len = range.origin_begin() - offset; + let buf = &buf[range.origin_begin() - offset..range.origin_end() - offset]; + if range.is_full() { + // Write to target buf directly + try0!(len, BlockedDevice::write_at(self, range.block, buf)); + } else { + use core::mem::uninitialized; + let mut block_buf: [u8; 4096] = unsafe { uninitialized() }; + assert!(self.block_size_log2() <= 12); + // Read to local buf first + try0!(len, BlockedDevice::read_at(self, range.block, &mut block_buf)); + // Write to local buf + block_buf[range.begin..range.end].copy_from_slice(buf); + // Write back to target buf + try0!(len, BlockedDevice::write_at(self, range.block, &block_buf)); + } + } + Some(buf.len()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + impl BlockedDevice for [u8; 16] { + fn block_size_log2(&self) -> u8 { 2 } + fn read_at(&mut self, block_id: usize, buf: &mut [u8]) -> bool { + if block_id >= 4 { + return false; + } + let begin = block_id << 2; + buf[..4].copy_from_slice(&mut self[begin..begin + 4]); + true + } + fn write_at(&mut self, block_id: usize, buf: &[u8]) -> bool { + if block_id >= 4 { + return false; + } + let begin = block_id << 2; + self[begin..begin + 4].copy_from_slice(&buf[..4]); + true + } + } + + #[test] + fn read() { + let mut buf: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + let mut res: [u8; 6] = [0; 6]; + + // all inside + let ret = Device::read_at(&mut buf, 3, &mut res); + assert_eq!(ret, Some(6)); + assert_eq!(res, [3, 4, 5, 6, 7, 8]); + + // partly inside + let ret = Device::read_at(&mut buf, 11, &mut res); + assert_eq!(ret, Some(5)); + assert_eq!(res, [11, 12, 13, 14, 15, 8]); + + // all outside + let ret = Device::read_at(&mut buf, 16, &mut res); + assert_eq!(ret, Some(0)); + assert_eq!(res, [11, 12, 13, 14, 15, 8]); + } + + #[test] + fn write() { + let mut buf: [u8; 16] = [0; 16]; + let mut res: [u8; 6] = [3, 4, 5, 6, 7, 8]; + + // all inside + let ret = Device::write_at(&mut buf, 3, &res); + assert_eq!(ret, Some(6)); + assert_eq!(buf, [0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0]); + + // partly inside + let ret = Device::write_at(&mut buf, 11, &res); + assert_eq!(ret, Some(5)); + assert_eq!(buf, [0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 3, 4, 5, 6, 7]); + + // all outside + let ret = Device::write_at(&mut buf, 16, &res); + assert_eq!(ret, Some(0)); + assert_eq!(buf, [0, 0, 0, 3, 4, 5, 6, 7, 8, 0, 0, 3, 4, 5, 6, 7]); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ffef679..e9d0141 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,8 @@ macro_rules! eprintln { } mod dirty; +mod util; +mod blocked_device; mod vfs; mod sfs; mod structs; @@ -34,6 +36,7 @@ mod tests; pub use sfs::*; pub use vfs::*; +pub use blocked_device::BlockedDevice; #[cfg(feature = "ucore")] #[global_allocator] diff --git a/src/sfs.rs b/src/sfs.rs index cbf7c89..aa1bced 100644 --- a/src/sfs.rs +++ b/src/sfs.rs @@ -2,12 +2,13 @@ use spin::Mutex; use bit_set::BitSet; use alloc::{boxed::Box, Vec, BTreeMap, rc::{Rc, Weak}, String}; use core::cell::{RefCell, RefMut}; -use dirty::Dirty; -use super::structs::*; -use super::vfs::{self, Device}; use core::mem::{uninitialized, size_of}; use core::slice; use core::fmt::{Debug, Formatter, Error}; +use dirty::Dirty; +use structs::*; +use vfs::{self, Device}; +use util::*; trait DeviceExt: Device { fn read_block(&mut self, id: BlockId, offset: usize, buf: &mut [u8]) -> vfs::Result<()> { @@ -180,6 +181,7 @@ impl INode { let iter = BlockIter { begin: size.min(begin), end: size.min(end), + block_size_log2: BLKSIZE_LOG2, }; // For each block @@ -319,40 +321,6 @@ impl Drop for INode { } } -/// Given a range and iterate sub-range for each block -struct BlockIter { - begin: usize, - end: usize, -} - -#[derive(Debug, Eq, PartialEq)] -struct BlockRange { - block: BlockId, - begin: usize, - end: usize, -} - -impl BlockRange { - fn len(&self) -> usize { - self.end - self.begin - } -} - -impl Iterator for BlockIter { - type Item = BlockRange; - - fn next(&mut self) -> Option<::Item> { - if self.begin >= self.end { - return None; - } - let block = self.begin / BLKSIZE; - let begin = self.begin % BLKSIZE; - let end = if block == self.end / BLKSIZE { self.end % BLKSIZE } else { BLKSIZE }; - self.begin += end - begin; - Some(BlockRange { block, begin, end }) - } -} - /// filesystem for sfs /// @@ -576,17 +544,3 @@ impl From for vfs::FileType { } } } - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn block_iter() { - let mut iter = BlockIter { begin: 0x123, end: 0x2018 }; - assert_eq!(iter.next(), Some(BlockRange { block: 0, begin: 0x123, end: 0x1000 })); - assert_eq!(iter.next(), Some(BlockRange { block: 1, begin: 0, end: 0x1000 })); - assert_eq!(iter.next(), Some(BlockRange { block: 2, begin: 0, end: 0x18 })); - assert_eq!(iter.next(), None); - } -} \ No newline at end of file diff --git a/src/structs.rs b/src/structs.rs index 3bd7caf..2b07667 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -153,7 +153,9 @@ pub type INodeId = BlockId; /// magic number for sfs pub const MAGIC: u32 = 0x2f8dbe2a; /// size of block -pub const BLKSIZE: usize = 4096; +pub const BLKSIZE: usize = 1usize << BLKSIZE_LOG2; +/// log2( size of block ) +pub const BLKSIZE_LOG2: u8 = 12; /// number of direct blocks in inode pub const NDIRECT: usize = 12; /// max length of infomation diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..507630d --- /dev/null +++ b/src/util.rs @@ -0,0 +1,60 @@ +/// Given a range and iterate sub-range for each block +pub struct BlockIter { + pub begin: usize, + pub end: usize, + pub block_size_log2: u8, +} + +#[derive(Debug, Eq, PartialEq)] +pub struct BlockRange { + pub block: usize, + pub begin: usize, + pub end: usize, + pub block_size_log2: u8, +} + +impl BlockRange { + pub fn len(&self) -> usize { + self.end - self.begin + } + pub fn is_full(&self) -> bool { + self.len() == (1usize << self.block_size_log2) + } + pub fn origin_begin(&self) -> usize { + (self.block << self.block_size_log2) + self.begin + } + pub fn origin_end(&self) -> usize { + (self.block << self.block_size_log2) + self.end + } +} + +impl Iterator for BlockIter { + type Item = BlockRange; + + fn next(&mut self) -> Option<::Item> { + if self.begin >= self.end { + return None; + } + let block_size_log2 = self.block_size_log2; + let block_size = 1usize << self.block_size_log2; + let block = self.begin / block_size; + let begin = self.begin % block_size; + let end = if block == self.end / block_size { self.end % block_size } else { block_size }; + self.begin += end - begin; + Some(BlockRange { block, begin, end, block_size_log2 }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn block_iter() { + let mut iter = BlockIter { begin: 0x123, end: 0x2018, block_size_log2: 12 }; + assert_eq!(iter.next(), Some(BlockRange { block: 0, begin: 0x123, end: 0x1000, block_size_log2: 12 })); + assert_eq!(iter.next(), Some(BlockRange { block: 1, begin: 0, end: 0x1000, block_size_log2: 12 })); + assert_eq!(iter.next(), Some(BlockRange { block: 2, begin: 0, end: 0x18, block_size_log2: 12 })); + assert_eq!(iter.next(), None); + } +} \ No newline at end of file