1//! An abstraction of block devices.
2
3#![cfg_attr(not(test), no_std)]
4#![warn(missing_docs)]
5#![allow(async_fn_in_trait)]
6
7use aligned::Aligned;
8
9/// A trait for a block devices
10///
11/// [`BlockDevice<const SIZE: usize>`](BlockDevice) can be initialized with the following parameters.
12///
13/// - `const SIZE`: The size of the block in the block device.
14/// - `type Align`: The [`aligned::Alignment`] of the block buffers for this implementation.
15/// - `type Error`: The error type for the implementation.
16///
17/// The generic parameter `SIZE` on [BlockDevice] is the number of _bytes_ in a block
18/// for this block device.
19///
20/// All addresses are zero indexed, and the unit is blocks. For example to read bytes
21/// from 1024 to 1536 on a 512 byte block device, the supplied block address would be 2.
22///
23/// <div class="warning"><b>NOTE to implementors</b>: Alignment of the buffer <b>must</b> be multiple of SIZE to avoid
24/// padding bytes when casting between blocks and slices.</div>
25///
26/// This trait can be implemented multiple times to support various different block sizes.
27pub trait BlockDevice<const SIZE: usize> {
28 /// The error type for the BlockDevice implementation.
29 type Error: core::fmt::Debug;
30
31 /// The alignment requirements of the block buffers.
32 type Align: aligned::Alignment;
33
34 /// Read one or more blocks at the given block address.
35 async fn read(
36 &mut self,
37 block_address: u32,
38 data: &mut [Aligned<Self::Align, [u8; SIZE]>],
39 ) -> Result<(), Self::Error>;
40
41 /// Write one or more blocks at the given block address.
42 async fn write(
43 &mut self,
44 block_address: u32,
45 data: &[Aligned<Self::Align, [u8; SIZE]>],
46 ) -> Result<(), Self::Error>;
47
48 /// Report the size of the block device in bytes.
49 async fn size(&mut self) -> Result<u64, Self::Error>;
50}
51
52impl<T: BlockDevice<SIZE>, const SIZE: usize> BlockDevice<SIZE> for &mut T {
53 type Error = T::Error;
54 type Align = T::Align;
55
56 async fn read(
57 &mut self,
58 block_address: u32,
59 data: &mut [Aligned<Self::Align, [u8; SIZE]>],
60 ) -> Result<(), Self::Error> {
61 (*self).read(block_address, data).await
62 }
63
64 async fn write(
65 &mut self,
66 block_address: u32,
67 data: &[Aligned<Self::Align, [u8; SIZE]>],
68 ) -> Result<(), Self::Error> {
69 (*self).write(block_address, data).await
70 }
71
72 async fn size(&mut self) -> Result<u64, Self::Error> {
73 (*self).size().await
74 }
75}
76
77/// Cast a byte slice to an aligned slice of blocks.
78///
79/// This function panics if
80///
81/// * ALIGNment is not a multiple of SIZE
82/// * The input slice is not a multiple of SIZE
83/// * The input slice does not have the correct alignment.
84pub fn slice_to_blocks<ALIGN, const SIZE: usize>(slice: &[u8]) -> &[Aligned<ALIGN, [u8; SIZE]>]
85where
86 ALIGN: aligned::Alignment,
87{
88 let align: usize = core::mem::align_of::<Aligned<ALIGN, ()>>();
89 assert!(slice.len() % SIZE == 0);
90 assert!(slice.len() % align == 0);
91 assert!(slice.as_ptr().cast::<u8>() as usize % align == 0);
92 // Note unsafe: we check the buf has the correct SIZE and ALIGNment before casting
93 unsafe {
94 core::slice::from_raw_parts(
95 data:slice.as_ptr() as *const Aligned<ALIGN, [u8; SIZE]>,
96 len:slice.len() / SIZE,
97 )
98 }
99}
100
101/// Cast a mutable byte slice to an aligned mutable slice of blocks.
102///
103/// This function panics if
104///
105/// * ALIGNment is not a multiple of SIZE
106/// * The input slice is not a multiple of SIZE
107/// * The input slice does not have the correct alignment.
108pub fn slice_to_blocks_mut<ALIGN, const SIZE: usize>(
109 slice: &mut [u8],
110) -> &mut [Aligned<ALIGN, [u8; SIZE]>]
111where
112 ALIGN: aligned::Alignment,
113{
114 let align: usize = core::mem::align_of::<Aligned<ALIGN, [u8; SIZE]>>();
115 assert!(slice.len() % SIZE == 0);
116 assert!(slice.len() % align == 0);
117 assert!(slice.as_ptr().cast::<u8>() as usize % align == 0);
118 // Note unsafe: we check the buf has the correct SIZE and ALIGNment before casting
119 unsafe {
120 core::slice::from_raw_parts_mut(
121 data:slice.as_mut_ptr() as *mut Aligned<ALIGN, [u8; SIZE]>,
122 len:slice.len() / SIZE,
123 )
124 }
125}
126
127/// Cast a slice of aligned blocks to a byte slice
128///
129/// This function panics if
130///
131/// * ALIGNment is not a multiple of SIZE
132pub fn blocks_to_slice<ALIGN, const SIZE: usize>(buf: &[Aligned<ALIGN, [u8; SIZE]>]) -> &[u8]
133where
134 ALIGN: aligned::Alignment,
135{
136 // We only need to assert that ALIGN is a multiple of SIZE, the other invariants are checked via the type system.
137 // This relationship must be true to avoid padding bytes which will introduce UB when casting.
138 let align: usize = core::mem::align_of::<Aligned<ALIGN, ()>>();
139 assert!(SIZE % align == 0);
140 // Note unsafe: we check the buf has the correct SIZE and ALIGNment before casting
141 unsafe { core::slice::from_raw_parts(data:buf.as_ptr() as *const u8, len:buf.len() * SIZE) }
142}
143
144/// Cast a mutable slice of aligned blocks to a mutable byte slice
145///
146/// This function panics if
147///
148/// * ALIGNment is not a multiple of SIZE
149pub fn blocks_to_slice_mut<ALIGN, const SIZE: usize>(
150 buf: &mut [Aligned<ALIGN, [u8; SIZE]>],
151) -> &mut [u8]
152where
153 ALIGN: aligned::Alignment,
154{
155 // We only need to assert that ALIGN is a multiple of SIZE, the other invariants are checked via the type system.
156 // This relationship must be true to avoid padding bytes which will introduce UB when casting.
157 let align: usize = core::mem::align_of::<Aligned<ALIGN, ()>>();
158 assert!(SIZE % align == 0);
159 // Note unsafe: we check the buf has the correct SIZE and ALIGNment before casting
160 unsafe { core::slice::from_raw_parts_mut(data:buf.as_mut_ptr() as *mut u8, len:buf.len() * SIZE) }
161}
162
163#[cfg(test)]
164mod test {
165 use super::*;
166
167 #[test]
168 fn test_conversion_round_trip() {
169 let blocks = &mut [
170 Aligned::<aligned::A4, _>([0; 512]),
171 Aligned::<aligned::A4, _>([0; 512]),
172 ];
173 let slice = blocks_to_slice_mut(blocks);
174 assert!(slice.len() == 1024);
175 let blocks: &mut [Aligned<aligned::A4, [u8; 512]>] = slice_to_blocks_mut(slice);
176 assert!(blocks.len() == 2);
177 }
178}
179