Skip to content

Commit

Permalink
Merge pull request #148 from rust-embedded-community/block-cache
Browse files Browse the repository at this point in the history
Add a universal block cache
  • Loading branch information
thejpster authored Oct 19, 2024
2 parents ae72878 + 242e05f commit c67d6ca
Show file tree
Hide file tree
Showing 5 changed files with 452 additions and 455 deletions.
258 changes: 166 additions & 92 deletions src/blockdevice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,6 @@ pub struct Block {
pub contents: [u8; Block::LEN],
}

/// The linear numeric address of a block (or sector).
///
/// The first block on a disk gets `BlockIdx(0)` (which usually contains the
/// Master Boot Record).
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockIdx(pub u32);

/// The a number of blocks (or sectors).
///
/// Add this to a `BlockIdx` to get an actual address on disk.
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockCount(pub u32);

/// An iterator returned from `Block::range`.
pub struct BlockIter {
inclusive_end: BlockIdx,
current: BlockIdx,
}

/// A block device - a device which can read and write blocks (or
/// sectors). Only supports devices which are <= 2 TiB in size.
pub trait BlockDevice {
/// The errors that the `BlockDevice` can return. Must be debug formattable.
type Error: core::fmt::Debug;
/// Read one or more blocks, starting at the given block index.
fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Write one or more blocks, starting at the given block index.
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Determine how many blocks this device can hold.
fn num_blocks(&self) -> Result<BlockCount, Self::Error>;
}

impl Block {
/// All our blocks are a fixed length of 512 bytes. We do not support
/// 'Advanced Format' Hard Drives with 4 KiB blocks, nor weird old
Expand All @@ -67,64 +33,6 @@ impl Block {
}
}

impl Default for Block {
fn default() -> Self {
Self::new()
}
}

impl core::ops::Add<BlockCount> for BlockIdx {
type Output = BlockIdx;
fn add(self, rhs: BlockCount) -> BlockIdx {
BlockIdx(self.0 + rhs.0)
}
}

impl core::ops::AddAssign<BlockCount> for BlockIdx {
fn add_assign(&mut self, rhs: BlockCount) {
self.0 += rhs.0
}
}

impl core::ops::Add<BlockCount> for BlockCount {
type Output = BlockCount;
fn add(self, rhs: BlockCount) -> BlockCount {
BlockCount(self.0 + rhs.0)
}
}

impl core::ops::AddAssign<BlockCount> for BlockCount {
fn add_assign(&mut self, rhs: BlockCount) {
self.0 += rhs.0
}
}

impl core::ops::Sub<BlockCount> for BlockIdx {
type Output = BlockIdx;
fn sub(self, rhs: BlockCount) -> BlockIdx {
BlockIdx(self.0 - rhs.0)
}
}

impl core::ops::SubAssign<BlockCount> for BlockIdx {
fn sub_assign(&mut self, rhs: BlockCount) {
self.0 -= rhs.0
}
}

impl core::ops::Sub<BlockCount> for BlockCount {
type Output = BlockCount;
fn sub(self, rhs: BlockCount) -> BlockCount {
BlockCount(self.0 - rhs.0)
}
}

impl core::ops::SubAssign<BlockCount> for BlockCount {
fn sub_assign(&mut self, rhs: BlockCount) {
self.0 -= rhs.0
}
}

impl core::ops::Deref for Block {
type Target = [u8; 512];
fn deref(&self) -> &[u8; 512] {
Expand Down Expand Up @@ -159,6 +67,107 @@ impl core::fmt::Debug for Block {
}
}

impl Default for Block {
fn default() -> Self {
Self::new()
}
}

/// A block device - a device which can read and write blocks (or
/// sectors). Only supports devices which are <= 2 TiB in size.
pub trait BlockDevice {
/// The errors that the `BlockDevice` can return. Must be debug formattable.
type Error: core::fmt::Debug;
/// Read one or more blocks, starting at the given block index.
fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Write one or more blocks, starting at the given block index.
fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>;
/// Determine how many blocks this device can hold.
fn num_blocks(&self) -> Result<BlockCount, Self::Error>;
}

/// A caching layer for block devices
///
/// Caches a single block.
#[derive(Debug)]
pub struct BlockCache<D> {
block_device: D,
block: [Block; 1],
block_idx: Option<BlockIdx>,
}

impl<D> BlockCache<D>
where
D: BlockDevice,
{
/// Create a new block cache
pub fn new(block_device: D) -> BlockCache<D> {
BlockCache {
block_device,
block: [Block::new()],
block_idx: None,
}
}

/// Read a block, and return a reference to it.
pub fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> {
if self.block_idx != Some(block_idx) {
self.block_idx = None;
self.block_device.read(&mut self.block, block_idx)?;
self.block_idx = Some(block_idx);
}
Ok(&self.block[0])
}

/// Read a block, and return a reference to it.
pub fn read_mut(&mut self, block_idx: BlockIdx) -> Result<&mut Block, D::Error> {
if self.block_idx != Some(block_idx) {
self.block_idx = None;
self.block_device.read(&mut self.block, block_idx)?;
self.block_idx = Some(block_idx);
}
Ok(&mut self.block[0])
}

/// Write back a block you read with [`Self::read_mut`] and then modified.
pub fn write_back(&mut self) -> Result<(), D::Error> {
self.block_device.write(
&self.block,
self.block_idx.expect("write_back with no read"),
)
}

/// Access a blank sector
pub fn blank_mut(&mut self, block_idx: BlockIdx) -> &mut Block {
self.block_idx = Some(block_idx);
for b in self.block[0].iter_mut() {
*b = 0;
}
&mut self.block[0]
}

/// Access the block device
pub fn block_device(&mut self) -> &mut D {
// invalidate the cache
self.block_idx = None;
// give them the block device
&mut self.block_device
}

/// Get the block device back
pub fn free(self) -> D {
self.block_device
}
}

/// The linear numeric address of a block (or sector).
///
/// The first block on a disk gets `BlockIdx(0)` (which usually contains the
/// Master Boot Record).
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockIdx(pub u32);

impl BlockIdx {
/// Convert a block index into a 64-bit byte offset from the start of the
/// volume. Useful if your underlying block device actually works in
Expand All @@ -174,6 +183,65 @@ impl BlockIdx {
}
}

impl core::ops::Add<BlockCount> for BlockIdx {
type Output = BlockIdx;
fn add(self, rhs: BlockCount) -> BlockIdx {
BlockIdx(self.0 + rhs.0)
}
}

impl core::ops::AddAssign<BlockCount> for BlockIdx {
fn add_assign(&mut self, rhs: BlockCount) {
self.0 += rhs.0
}
}

impl core::ops::Sub<BlockCount> for BlockIdx {
type Output = BlockIdx;
fn sub(self, rhs: BlockCount) -> BlockIdx {
BlockIdx(self.0 - rhs.0)
}
}

impl core::ops::SubAssign<BlockCount> for BlockIdx {
fn sub_assign(&mut self, rhs: BlockCount) {
self.0 -= rhs.0
}
}

/// The a number of blocks (or sectors).
///
/// Add this to a `BlockIdx` to get an actual address on disk.
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BlockCount(pub u32);

impl core::ops::Add<BlockCount> for BlockCount {
type Output = BlockCount;
fn add(self, rhs: BlockCount) -> BlockCount {
BlockCount(self.0 + rhs.0)
}
}

impl core::ops::AddAssign<BlockCount> for BlockCount {
fn add_assign(&mut self, rhs: BlockCount) {
self.0 += rhs.0
}
}

impl core::ops::Sub<BlockCount> for BlockCount {
type Output = BlockCount;
fn sub(self, rhs: BlockCount) -> BlockCount {
BlockCount(self.0 - rhs.0)
}
}

impl core::ops::SubAssign<BlockCount> for BlockCount {
fn sub_assign(&mut self, rhs: BlockCount) {
self.0 -= rhs.0
}
}

impl BlockCount {
/// How many blocks are required to hold this many bytes.
///
Expand All @@ -200,6 +268,12 @@ impl BlockCount {
}
}

/// An iterator returned from `Block::range`.
pub struct BlockIter {
inclusive_end: BlockIdx,
current: BlockIdx,
}

impl BlockIter {
/// Create a new `BlockIter`, from the given start block, through (and
/// including) the given end block.
Expand Down
31 changes: 0 additions & 31 deletions src/fat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,6 @@ pub enum FatType {
Fat32,
}

pub(crate) struct BlockCache {
block: Block,
idx: Option<BlockIdx>,
}
impl BlockCache {
pub fn empty() -> Self {
BlockCache {
block: Block::new(),
idx: None,
}
}
pub(crate) fn read<D>(
&mut self,
block_device: &D,
block_idx: BlockIdx,
) -> Result<&Block, Error<D::Error>>
where
D: BlockDevice,
{
if Some(block_idx) != self.idx {
self.idx = Some(block_idx);
block_device
.read(core::slice::from_mut(&mut self.block), block_idx)
.map_err(Error::DeviceError)?;
}
Ok(&self.block)
}
}

mod bpb;
mod info;
mod ondiskdirentry;
Expand All @@ -53,8 +24,6 @@ pub use info::{Fat16Info, Fat32Info, FatSpecificInfo, InfoSector};
pub use ondiskdirentry::OnDiskDirEntry;
pub use volume::{parse_volume, FatVolume, VolumeName};

use crate::{Block, BlockDevice, BlockIdx, Error};

// ****************************************************************************
//
// Unit Tests
Expand Down
Loading

0 comments on commit c67d6ca

Please sign in to comment.