Skip to main content

bonsai_disk/
lib.rs

1#![deny(unconditional_recursion)]
2#![no_std]
3
4
5
6use generic_array::ArrayLength;
7
8
9
10/// Represents a disk that can be read from and written to.
11///
12/// The disk is divided into blocks of a fixed size.
13///
14/// Reads and writes are in bytes but must be aligned to the disk's write granularity.
15///
16/// Erases are in full block units.
17///
18/// The first byte of the disk is at offset `0`, which is also the start of the
19/// first block with index `0`.
20///
21pub trait Disk: embedded_io::ErrorType {
22	/// The size of an erasable block in bytes.
23	const ERASE_BLOCK_SIZE: usize;
24
25	/// The minimum size of a write operation in bytes.
26	///
27	/// Also defines the write alignment.
28	///
29	/// Currently, also used for read granularity.
30	#[allow(non_camel_case_types)]
31	type WRITE_GRANULARITY: ArrayLength;
32
33	/// Reads data from the disk into the buffer.
34	///
35	/// The given offset is in bytes and must be aligned to `WRITE_GRANULARITY`.
36	/// The buffer must be at least `WRITE_GRANULARITY` bytes long.
37	fn read(&mut self, offset: usize, buf: &mut [u8]) -> Result<usize, Self::Error>;
38
39	/// Writes data from the buffer to the disk.
40	///
41	/// The given offset is in bytes and must be aligned to `WRITE_GRANULARITY`.
42	/// The buffer must be at least `WRITE_GRANULARITY` bytes long.
43	///
44	/// A write should only happen on a section of a block that has been
45	/// previously erased and not yet written to.
46	/// It is permissible to write independent sections of a block in any order.
47	fn write(&mut self, offset: usize, buf: &[u8]) -> Result<usize, Self::Error>;
48
49	/// Erases the block at the given block index.
50	///
51	/// The block index is in units of `ERASE_BLOCK_SIZE`.
52	///
53	/// The first block has index `0` and the last block has index
54	/// `self.block_count() - 1`.
55	fn erase(&mut self, block: usize) -> Result<(), Self::Error>;
56
57	/// Returns the number of blocks on the disk.
58	///
59	/// The number of blocks is fixed and does not change.
60	fn block_count(&self) -> usize;
61
62	/// Reads the exact number of bytes required to fill the buffer.
63	///
64	/// The given offset is in bytes and must be aligned to `WRITE_GRANULARITY`.
65	/// The buffer size must be a multiple of `WRITE_GRANULARITY`.
66	///
67	/// If the buffer cannot be completely filled, an error is returned.
68	#[track_caller]
69	fn read_exact(
70		&mut self,
71		mut offset: usize,
72		mut buf: &mut [u8],
73	) -> Result<(), embedded_io::ReadExactError<Self::Error>> {
74		while !buf.is_empty() {
75			let bytes_read = self.read(offset, buf)?;
76			if bytes_read == 0 {
77				return Err(embedded_io::ReadExactError::UnexpectedEof);
78			}
79			offset += bytes_read;
80			buf = &mut buf[bytes_read..];
81		}
82		Ok(())
83	}
84
85	/// Writes the exact number of bytes from the buffer to the disk.
86	///
87	/// The given offset is in bytes and must be aligned to `WRITE_GRANULARITY`.
88	/// The buffer size must be a multiple of `WRITE_GRANULARITY`.
89	///
90	/// If the buffer cannot be completely written, an error is returned.
91	#[track_caller]
92	fn write_all(&mut self, mut offset: usize, mut buf: &[u8]) -> Result<(), Self::Error> {
93		while !buf.is_empty() {
94			let bytes_written = self.write(offset, buf)?;
95			if bytes_written == 0 {
96				panic!("write() returned Ok(0)");
97			}
98			offset += bytes_written;
99			buf = &buf[bytes_written..];
100		}
101		Ok(())
102	}
103}
104
105impl<T: Disk> Disk for &mut T {
106	type WRITE_GRANULARITY = T::WRITE_GRANULARITY;
107
108	const ERASE_BLOCK_SIZE: usize = T::ERASE_BLOCK_SIZE;
109
110	#[track_caller]
111	fn read(&mut self, offset: usize, buf: &mut [u8]) -> Result<usize, Self::Error> {
112		(**self).read(offset, buf)
113	}
114
115	#[track_caller]
116	fn write(&mut self, offset: usize, buf: &[u8]) -> Result<usize, Self::Error> {
117		(**self).write(offset, buf)
118	}
119
120	#[track_caller]
121	fn erase(&mut self, block: usize) -> Result<(), Self::Error> {
122		(**self).erase(block)
123	}
124
125	#[track_caller]
126	fn block_count(&self) -> usize {
127		(**self).block_count()
128	}
129}