From 1d508c64c583f5d2eee298ffff2d7d6c44afb51c Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 6 Aug 2024 18:26:09 +0100 Subject: [PATCH 1/9] Add BlockDevice trait. --- src/lib.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 3070723..a9303c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,3 +53,32 @@ pub trait Storage: ReadStorage { /// and might as such do RMW operations at an undesirable performance impact. fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>; } + +/// The size in bytes of a single block read or written by a [`BlockDevice`]. +pub const BLOCK_SIZE: usize = 512; + +/// A single block which may be read or written by a [`BlockDevice`]. +/// +/// Also referred to as a sector in some contexts. +pub type Block = [u8; BLOCK_SIZE]; + +/// A device which can read and write whole numbers of blocks. +pub trait BlockDevice { + /// The error type returned by methods on this trait. + type Error; + + /// Returns the size of the device in blocks. + fn block_count(&self) -> Result; + + /// Reads some number of blocks from the device, starting at `first_block_index`. + /// + /// `first_block_index + blocks.len()` must not be greater than the size returned by + /// `block_count`. + fn read(&mut self, first_block_index: u64, blocks: &mut [Block]) -> Result<(), Self::Error>; + + /// Writes some number of blocks to the device, starting at `first_block_index`. + /// + /// `first_block_index + blocks.len()` must not be greater than the size returned by + /// `block_count`. + fn write(&mut self, first_block_index: u64, blocks: &[Block]) -> Result<(), Self::Error>; +} From bf3e225615f9049e2ac330983b108666f7b0a509 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Wed, 7 Aug 2024 14:40:30 +0100 Subject: [PATCH 2/9] Make block size a parameter of BlockDevice trait. --- src/lib.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a9303c6..56560f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,16 +54,10 @@ pub trait Storage: ReadStorage { fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>; } -/// The size in bytes of a single block read or written by a [`BlockDevice`]. -pub const BLOCK_SIZE: usize = 512; - -/// A single block which may be read or written by a [`BlockDevice`]. -/// -/// Also referred to as a sector in some contexts. -pub type Block = [u8; BLOCK_SIZE]; - /// A device which can read and write whole numbers of blocks. -pub trait BlockDevice { +/// +/// Blocks are also referred to as sectors in some contexts. +pub trait BlockDevice { /// The error type returned by methods on this trait. type Error; @@ -74,11 +68,19 @@ pub trait BlockDevice { /// /// `first_block_index + blocks.len()` must not be greater than the size returned by /// `block_count`. - fn read(&mut self, first_block_index: u64, blocks: &mut [Block]) -> Result<(), Self::Error>; + fn read( + &mut self, + first_block_index: u64, + blocks: &mut [[u8; BLOCK_SIZE]], + ) -> Result<(), Self::Error>; /// Writes some number of blocks to the device, starting at `first_block_index`. /// /// `first_block_index + blocks.len()` must not be greater than the size returned by /// `block_count`. - fn write(&mut self, first_block_index: u64, blocks: &[Block]) -> Result<(), Self::Error>; + fn write( + &mut self, + first_block_index: u64, + blocks: &[[u8; BLOCK_SIZE]], + ) -> Result<(), Self::Error>; } From 317d3001b6cd5e2cff74f75bcefc39ef75bdc732 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 29 Oct 2024 17:40:58 +0000 Subject: [PATCH 3/9] Use u64 for block count. This should be consistent with the type used for block indices. --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 56560f4..e2daf50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ pub trait BlockDevice { type Error; /// Returns the size of the device in blocks. - fn block_count(&self) -> Result; + fn block_count(&self) -> Result; /// Reads some number of blocks from the device, starting at `first_block_index`. /// From 025315a5c388f946268a722c446b5cd426965b68 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 29 Oct 2024 18:37:36 +0000 Subject: [PATCH 4/9] Bump MSRV to 1.59. This is needed for const generics. --- .github/workflows/ci.yml | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00a2e81..1499595 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: include: # Test MSRV - - rust: 1.50.0 + - rust: 1.59.0 TARGET: x86_64-unknown-linux-gnu # Test nightly but don't fail diff --git a/README.md b/README.md index 993f50e..baaae58 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ These issues / PRs will be labeled as `proposal`s in the issue tracker. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.50.0 and up. It *might* +This crate is guaranteed to compile on stable Rust 1.59.0 and up. It _might_ compile with older versions but that may change in any new patch release. ## License From 8a340f455f0c1aba959fd64db3554bc570fcf419 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 29 Oct 2024 20:07:11 +0000 Subject: [PATCH 5/9] Add and use BlockCount and BlockIdx newtypes. --- src/block.rs | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 33 +----------- 2 files changed, 143 insertions(+), 31 deletions(-) create mode 100644 src/block.rs diff --git a/src/block.rs b/src/block.rs new file mode 100644 index 0000000..ec81c5b --- /dev/null +++ b/src/block.rs @@ -0,0 +1,141 @@ +use core::ops::{Add, AddAssign, Sub, SubAssign}; + +/// A device which can read and write whole numbers of blocks. +/// +/// Blocks are also referred to as sectors in some contexts. +pub trait BlockDevice { + /// The error type returned by methods on this trait. + type Error; + + /// Returns the size of the device in blocks. + fn block_count(&self) -> Result; + + /// Reads some number of blocks from the device, starting at `first_block_index`. + /// + /// `first_block_index + blocks.len()` must not be greater than the size returned by + /// `block_count`. + fn read( + &mut self, + first_block_index: BlockIdx, + blocks: &mut [[u8; BLOCK_SIZE]], + ) -> Result<(), Self::Error>; + + /// Writes some number of blocks to the device, starting at `first_block_index`. + /// + /// `first_block_index + blocks.len()` must not be greater than the size returned by + /// `block_count`. + fn write( + &mut self, + first_block_index: BlockIdx, + blocks: &[[u8; BLOCK_SIZE]], + ) -> Result<(), Self::Error>; +} + +/// The linear numeric address of a block (or sector). +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct BlockIdx(pub u64); + +impl BlockIdx { + /// Creates an iterator from the current `BlockIdx` through the given number of blocks. + pub fn range(self, num: BlockCount) -> BlockIter { + BlockIter::new(self, self + BlockCount(num.0)) + } +} + +impl From for u64 { + fn from(value: BlockIdx) -> Self { + value.0.into() + } +} + +impl Add for BlockIdx { + type Output = BlockIdx; + + fn add(self, rhs: BlockCount) -> BlockIdx { + BlockIdx(self.0 + rhs.0) + } +} + +impl AddAssign for BlockIdx { + fn add_assign(&mut self, rhs: BlockCount) { + self.0 += rhs.0 + } +} + +impl Sub for BlockIdx { + type Output = BlockIdx; + + fn sub(self, rhs: BlockCount) -> BlockIdx { + BlockIdx(self.0 - rhs.0) + } +} + +impl SubAssign for BlockIdx { + fn sub_assign(&mut self, rhs: BlockCount) { + self.0 -= rhs.0 + } +} + +/// A number of blocks (or sectors). +/// +/// This may be added to a [`BlockIdx`] to get another `BlockIdx`. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct BlockCount(pub u64); + +impl Add for BlockCount { + type Output = BlockCount; + + fn add(self, rhs: BlockCount) -> BlockCount { + BlockCount(self.0 + rhs.0) + } +} + +impl AddAssign for BlockCount { + fn add_assign(&mut self, rhs: BlockCount) { + self.0 += rhs.0 + } +} + +impl Sub for BlockCount { + type Output = BlockCount; + + fn sub(self, rhs: BlockCount) -> BlockCount { + BlockCount(self.0 - rhs.0) + } +} + +impl SubAssign for BlockCount { + fn sub_assign(&mut self, rhs: BlockCount) { + self.0 -= rhs.0 + } +} + +/// An iterator returned from `Block::range`. +pub struct BlockIter { + inclusive_end: BlockIdx, + current: BlockIdx, +} + +impl BlockIter { + /// Creates a new `BlockIter`, from the given start block, through (and including) the given end + /// block. + pub const fn new(start: BlockIdx, inclusive_end: BlockIdx) -> BlockIter { + BlockIter { + inclusive_end, + current: start, + } + } +} + +impl Iterator for BlockIter { + type Item = BlockIdx; + fn next(&mut self) -> Option { + if self.current.0 >= self.inclusive_end.0 { + None + } else { + let this = self.current; + self.current += BlockCount(1); + Some(this) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index e2daf50..851eff6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,8 @@ #![deny(missing_docs)] #![deny(unsafe_code)] +/// Types and traits for block devices. +pub mod block; /// Currently contains [`OverlapIterator`] pub mod iter; /// Technology specific traits for NOR Flashes @@ -53,34 +55,3 @@ pub trait Storage: ReadStorage { /// and might as such do RMW operations at an undesirable performance impact. fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error>; } - -/// A device which can read and write whole numbers of blocks. -/// -/// Blocks are also referred to as sectors in some contexts. -pub trait BlockDevice { - /// The error type returned by methods on this trait. - type Error; - - /// Returns the size of the device in blocks. - fn block_count(&self) -> Result; - - /// Reads some number of blocks from the device, starting at `first_block_index`. - /// - /// `first_block_index + blocks.len()` must not be greater than the size returned by - /// `block_count`. - fn read( - &mut self, - first_block_index: u64, - blocks: &mut [[u8; BLOCK_SIZE]], - ) -> Result<(), Self::Error>; - - /// Writes some number of blocks to the device, starting at `first_block_index`. - /// - /// `first_block_index + blocks.len()` must not be greater than the size returned by - /// `block_count`. - fn write( - &mut self, - first_block_index: u64, - blocks: &[[u8; BLOCK_SIZE]], - ) -> Result<(), Self::Error>; -} From fbad743ab086b056e52d47a7670d61b7faec15ab Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Tue, 29 Oct 2024 20:49:49 +0000 Subject: [PATCH 6/9] Add optional defmt support. --- .github/workflows/ci.yml | 2 +- Cargo.toml | 7 +++++++ README.md | 2 +- src/block.rs | 2 ++ 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1499595..32338ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: include: # Test MSRV - - rust: 1.59.0 + - rust: 1.60.0 TARGET: x86_64-unknown-linux-gnu # Test nightly but don't fail diff --git a/Cargo.toml b/Cargo.toml index c851083..31b4bd2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,10 @@ documentation = "https://docs.rs/embedded-storage" readme = "README.md" keywords = ["storage"] categories = ["embedded", "hardware-support", "no-std"] + +[dependencies] +defmt = { version = "0.3.8", optional = true } + +[features] +default = [] +defmt = ["dep:defmt"] diff --git a/README.md b/README.md index baaae58..7212f44 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ These issues / PRs will be labeled as `proposal`s in the issue tracker. ## Minimum Supported Rust Version (MSRV) -This crate is guaranteed to compile on stable Rust 1.59.0 and up. It _might_ +This crate is guaranteed to compile on stable Rust 1.60.0 and up. It _might_ compile with older versions but that may change in any new patch release. ## License diff --git a/src/block.rs b/src/block.rs index ec81c5b..0b599fa 100644 --- a/src/block.rs +++ b/src/block.rs @@ -33,6 +33,7 @@ pub trait BlockDevice { /// The linear numeric address of a block (or sector). #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BlockIdx(pub u64); impl BlockIdx { @@ -80,6 +81,7 @@ impl SubAssign for BlockIdx { /// /// This may be added to a [`BlockIdx`] to get another `BlockIdx`. #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct BlockCount(pub u64); impl Add for BlockCount { From 5618b24f571f225e266a6809eb6fe37e0a29636e Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Wed, 30 Oct 2024 11:37:57 +0000 Subject: [PATCH 7/9] Swap order of parameters. --- src/block.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/block.rs b/src/block.rs index 0b599fa..5c13156 100644 --- a/src/block.rs +++ b/src/block.rs @@ -16,8 +16,8 @@ pub trait BlockDevice { /// `block_count`. fn read( &mut self, - first_block_index: BlockIdx, blocks: &mut [[u8; BLOCK_SIZE]], + first_block_index: BlockIdx, ) -> Result<(), Self::Error>; /// Writes some number of blocks to the device, starting at `first_block_index`. @@ -26,8 +26,8 @@ pub trait BlockDevice { /// `block_count`. fn write( &mut self, - first_block_index: BlockIdx, blocks: &[[u8; BLOCK_SIZE]], + first_block_index: BlockIdx, ) -> Result<(), Self::Error>; } From a3d19438ce8bb10712705bc4a03246a32f229234 Mon Sep 17 00:00:00 2001 From: Andrew Walbran Date: Wed, 30 Oct 2024 11:48:00 +0000 Subject: [PATCH 8/9] Add to changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9225845..b68f9fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `start()` and `end()` method to the `Region` trait. - Much faster `OverlapIterator`. +- Add `BlockDevice` trait, along with `BlockIdx` and `BlockCount` types. ## [0.3.1] - 2023-12-04 From 459c429c1954074a3ece11a91732b48201ef2bff Mon Sep 17 00:00:00 2001 From: Jonathan 'theJPster' Pallant Date: Fri, 6 Dec 2024 22:04:34 +0000 Subject: [PATCH 9/9] Add alignment to BlockDevice Inspired by https://docs.rs/block-device-driver/0.2.0/block_device_driver/trait.BlockDevice.html --- Cargo.toml | 1 + src/block.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 31b4bd2..e6ab6fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ keywords = ["storage"] categories = ["embedded", "hardware-support", "no-std"] [dependencies] +aligned = "0.4.2" defmt = { version = "0.3.8", optional = true } [features] diff --git a/src/block.rs b/src/block.rs index 5c13156..2d94334 100644 --- a/src/block.rs +++ b/src/block.rs @@ -7,26 +7,72 @@ pub trait BlockDevice { /// The error type returned by methods on this trait. type Error; + /// How aligned do we need the blocks to be? + /// + /// See the [`aligned`] crate for more details. + type Alignment: aligned::Alignment; + /// Returns the size of the device in blocks. fn block_count(&self) -> Result; - /// Reads some number of blocks from the device, starting at `first_block_index`. + /// Creates some stack allocated empty blocks, with suitable alignment. + fn empty_blocks( + &self, + ) -> [aligned::Aligned; N] { + [aligned::Aligned([0u8; BLOCK_SIZE]); N] + } + + /// Reads some blocks from the device, starting at `first_block_index`. + /// + /// The buffer we read into must be suitably aligned. You can create a + /// buffer like: + /// + /// ```rust + /// # use embedded_storage::block::{BlockDevice, BlockIdx}; + /// # fn example(block_device: &mut T) -> Result<(), T::Error> where T: BlockDevice { /// - /// `first_block_index + blocks.len()` must not be greater than the size returned by - /// `block_count`. + /// let mut buffer = block_device.empty_blocks::<4>(); + /// block_device.read(&mut buffer[..], BlockIdx(0))?; + /// + /// # Ok(()) + /// # } + /// ``` + /// + /// You will get an error if you request more blocks than the block device + /// has (i.e. if `first_block_index + blocks.len()` is greater than the size + /// returned by `block_count`). fn read( &mut self, - blocks: &mut [[u8; BLOCK_SIZE]], + blocks: &mut [aligned::Aligned], first_block_index: BlockIdx, ) -> Result<(), Self::Error>; - /// Writes some number of blocks to the device, starting at `first_block_index`. + /// Writes some number of blocks to the device, starting at + /// `first_block_index`. + /// + /// The buffer we write out must be suitably aligned. You can create a + /// buffer like: + /// + /// ```rust + /// # use embedded_storage::block::{BlockDevice, BlockIdx}; + /// # fn example(block_device: &mut T) -> Result<(), T::Error> where T: BlockDevice { + /// + /// let mut buffer = block_device.empty_blocks::<4>(); + /// for block in buffer.iter_mut() { + /// block.fill(0xCC); + /// } + /// block_device.write(&buffer[..], BlockIdx(0))?; + /// + /// # Ok(()) + /// # } + /// ``` /// - /// `first_block_index + blocks.len()` must not be greater than the size returned by - /// `block_count`. + /// You will get an error if you request more blocks than the block device + /// has (i.e. if first_block_index + blocks.len() is greater than the size + /// returned by block_count). fn write( &mut self, - blocks: &[[u8; BLOCK_SIZE]], + blocks: &[aligned::Aligned], first_block_index: BlockIdx, ) -> Result<(), Self::Error>; } @@ -132,12 +178,51 @@ impl BlockIter { impl Iterator for BlockIter { type Item = BlockIdx; fn next(&mut self) -> Option { - if self.current.0 >= self.inclusive_end.0 { - None - } else { + if self.current.0 <= self.inclusive_end.0 { let this = self.current; self.current += BlockCount(1); Some(this) + } else { + None } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn block_idx_addition() { + let a = BlockIdx(100); + let len = BlockCount(50); + let b = a + len; + assert_eq!(b, BlockIdx(150)); + } + + #[test] + fn block_idx_subtraction() { + let a = BlockIdx(100); + let len = BlockCount(50); + let b = a - len; + assert_eq!(b, BlockIdx(50)); + } + + #[test] + fn block_iter() { + let mut block_iter = BlockIter::new(BlockIdx(10), BlockIdx(12)); + let expected = [ + Some(BlockIdx(10)), + Some(BlockIdx(11)), + Some(BlockIdx(12)), + None, + ]; + let actual = [ + block_iter.next(), + block_iter.next(), + block_iter.next(), + block_iter.next(), + ]; + assert_eq!(actual, expected); + } +}