gpt_disk_types/
partition_array.rs1use crate::{BlockSize, Crc32, GptPartitionEntrySize, Lba, U32Le};
10use core::fmt::{self, Display, Formatter};
11
12#[cfg(feature = "bytemuck")]
13use {
14 crate::GptPartitionEntry,
15 bytemuck::{from_bytes, from_bytes_mut},
16 core::mem,
17 core::ops::Range,
18};
19
20#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
22pub struct GptPartitionEntryArrayLayout {
23 pub start_lba: Lba,
25
26 pub entry_size: GptPartitionEntrySize,
28
29 pub num_entries: u32,
31}
32
33impl GptPartitionEntryArrayLayout {
34 #[must_use]
37 pub fn num_blocks(&self, block_size: BlockSize) -> Option<u64> {
38 let block_size = block_size.to_u64();
39 let num_bytes_exact = self.num_bytes_exact()?;
40
41 let mut num_blocks = num_bytes_exact / block_size;
42 if num_bytes_exact % block_size != 0 {
43 num_blocks = num_blocks.checked_add(1)?;
44 }
45
46 Some(num_blocks)
47 }
48
49 #[must_use]
52 pub fn num_blocks_as_usize(&self, block_size: BlockSize) -> Option<usize> {
53 self.num_blocks(block_size)?.try_into().ok()
54 }
55
56 #[must_use]
65 pub fn num_bytes_exact(&self) -> Option<u64> {
66 let entry_size = self.entry_size.to_u64();
67 let num_entries = u64::from(self.num_entries);
68 entry_size.checked_mul(num_entries)
69 }
70
71 #[must_use]
80 pub fn num_bytes_exact_as_usize(&self) -> Option<usize> {
81 self.num_bytes_exact()?.try_into().ok()
82 }
83
84 #[must_use]
92 pub fn num_bytes_rounded_to_block(
93 &self,
94 block_size: BlockSize,
95 ) -> Option<u64> {
96 let num_blocks = self.num_blocks(block_size)?;
97 num_blocks.checked_mul(block_size.to_u64())
98 }
99
100 #[must_use]
108 pub fn num_bytes_rounded_to_block_as_usize(
109 &self,
110 block_size: BlockSize,
111 ) -> Option<usize> {
112 self.num_bytes_rounded_to_block(block_size)?.try_into().ok()
113 }
114}
115
116impl Display for GptPartitionEntryArrayLayout {
117 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
118 write!(
119 f,
120 "start_lba={}/entry_size={}/num_entries={}",
121 self.start_lba, self.entry_size, self.num_entries
122 )
123 }
124}
125
126#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
128pub enum GptPartitionEntryArrayError {
129 BufferTooSmall,
134
135 Overflow,
137}
138
139impl Display for GptPartitionEntryArrayError {
140 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
141 match self {
142 Self::BufferTooSmall => f.write_str("storage buffer is too small"),
143 Self::Overflow => f.write_str("numeric overflow occurred"),
144 }
145 }
146}
147
148impl core::error::Error for GptPartitionEntryArrayError {}
149
150#[allow(missing_debug_implementations)]
152pub struct GptPartitionEntryArray<'a> {
153 layout: GptPartitionEntryArrayLayout,
154 num_bytes_exact: usize,
155 storage: &'a mut [u8],
156}
157
158impl<'a> GptPartitionEntryArray<'a> {
159 pub fn new(
165 layout: GptPartitionEntryArrayLayout,
166 block_size: BlockSize,
167 storage: &'a mut [u8],
168 ) -> Result<Self, GptPartitionEntryArrayError> {
169 let num_bytes_required = layout
170 .num_bytes_rounded_to_block_as_usize(block_size)
171 .ok_or(GptPartitionEntryArrayError::Overflow)?;
172
173 let num_bytes_exact = layout
174 .num_bytes_exact_as_usize()
175 .ok_or(GptPartitionEntryArrayError::Overflow)?;
176
177 let storage = storage
178 .get_mut(..num_bytes_required)
179 .ok_or(GptPartitionEntryArrayError::BufferTooSmall)?;
180
181 Ok(Self {
182 layout,
183 num_bytes_exact,
184 storage,
185 })
186 }
187
188 #[must_use]
190 pub fn storage(&self) -> &[u8] {
191 self.storage
192 }
193
194 #[must_use]
196 pub fn storage_mut(&mut self) -> &mut [u8] {
197 self.storage
198 }
199
200 #[must_use]
202 pub fn layout(&self) -> &GptPartitionEntryArrayLayout {
203 &self.layout
204 }
205
206 pub fn set_start_lba(&mut self, start_lba: Lba) {
208 self.layout.start_lba = start_lba;
209 }
210
211 #[cfg(feature = "bytemuck")]
212 fn get_entry_byte_range(&self, index: u32) -> Option<Range<usize>> {
213 if index >= self.layout.num_entries {
214 return None;
215 }
216
217 let start = usize::try_from(
218 u64::from(index) * u64::from(self.layout.entry_size.to_u32()),
219 )
220 .ok()?;
221 Some(start..start + mem::size_of::<GptPartitionEntry>())
222 }
223
224 #[cfg(feature = "bytemuck")]
226 #[must_use]
227 pub fn get_partition_entry(
228 &self,
229 index: u32,
230 ) -> Option<&GptPartitionEntry> {
231 Some(from_bytes(&self.storage[self.get_entry_byte_range(index)?]))
232 }
233
234 #[cfg(feature = "bytemuck")]
236 #[must_use]
237 pub fn get_partition_entry_mut(
238 &mut self,
239 index: u32,
240 ) -> Option<&mut GptPartitionEntry> {
241 let range = self.get_entry_byte_range(index)?;
242 Some(from_bytes_mut(&mut self.storage[range]))
243 }
244
245 #[must_use]
251 pub fn calculate_crc32(&self) -> Crc32 {
252 let crc = crc::Crc::<u32>::new(&Crc32::ALGORITHM);
253 let mut digest = crc.digest();
254 digest.update(&self.storage[..self.num_bytes_exact]);
255 Crc32(U32Le(digest.finalize().to_le_bytes()))
256 }
257}