1use bitvec::prelude::*;
8use std::borrow::Cow;
9
10use crate::error::{Error, Result};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum PayloadBlockState {
15 NotPresent = 0,
17 Undefined = 1,
19 Zero = 2,
21 Unmapped = 3,
23 FullyPresent = 6,
25 PartiallyPresent = 7,
27}
28
29impl PayloadBlockState {
30 pub(crate) fn from_raw(raw: u8) -> Option<Self> {
34 match raw {
35 0 => Some(Self::NotPresent),
36 1 => Some(Self::Undefined),
37 2 => Some(Self::Zero),
38 3 => Some(Self::Unmapped),
39 6 => Some(Self::FullyPresent),
40 7 => Some(Self::PartiallyPresent),
41 _ => None,
42 }
43 }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum SectorBitmapState {
49 NotPresent = 0,
51 Present = 6,
53}
54
55impl SectorBitmapState {
56 pub(crate) fn from_raw(raw: u8) -> Option<Self> {
60 match raw {
61 0 => Some(Self::NotPresent),
62 6 => Some(Self::Present),
63 _ => None,
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum BatState {
71 Payload(PayloadBlockState),
73 SectorBitmap(SectorBitmapState),
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub struct BatEntry<'a> {
84 bytes: &'a [u8; 8],
86 is_sector_bitmap: bool,
88}
89
90impl BatEntry<'_> {
91 #[inline]
93 pub(crate) fn raw_state(&self) -> u8 {
94 self.bytes.view_bits::<Lsb0>()[0..3].load::<u8>()
95 }
96
97 #[inline]
99 #[must_use]
100 pub fn file_offset_mb(&self) -> u64 {
101 self.bytes.view_bits::<Lsb0>()[20..64].load::<u64>()
102 }
103
104 pub(crate) fn payload_state(&self) -> Option<PayloadBlockState> {
108 PayloadBlockState::from_raw(self.raw_state())
109 }
110
111 pub(crate) fn sector_bitmap_state(&self) -> Option<SectorBitmapState> {
115 SectorBitmapState::from_raw(self.raw_state())
116 }
117
118 pub fn state(&self) -> Result<BatState> {
128 let raw = self.raw_state();
129 if self.is_sector_bitmap {
130 match SectorBitmapState::from_raw(raw) {
131 Some(s) => Ok(BatState::SectorBitmap(s)),
132 None => Err(Error::InvalidSectorBitmapState(raw)),
133 }
134 } else {
135 match PayloadBlockState::from_raw(raw) {
136 Some(s) => Ok(BatState::Payload(s)),
137 None => Err(Error::InvalidBlockState(raw)),
138 }
139 }
140 }
141
142 pub(crate) fn is_sector_bitmap(&self) -> bool {
144 self.is_sector_bitmap
145 }
146}
147
148#[derive(Clone)]
153pub struct Bat<'a> {
154 data: Cow<'a, [u8]>,
156 chunk_ratio: u64,
158}
159
160impl<'a> Bat<'a> {
161 pub(crate) fn new(data: &'a [u8], chunk_ratio: u64) -> Self {
166 Self {
167 data: Cow::Borrowed(data),
168 chunk_ratio,
169 }
170 }
171
172 #[must_use]
174 pub(crate) fn len(&self) -> usize {
175 self.data.len() / 8
176 }
177
178 #[cfg(test)]
180 #[must_use]
181 pub(crate) fn is_empty(&self) -> bool {
182 self.data.is_empty()
183 }
184
185 fn is_sector_bitmap_entry(&self, index: usize) -> bool {
189 if self.chunk_ratio == 0 {
190 return false;
191 }
192 let Ok(chunk_ratio) = usize::try_from(self.chunk_ratio) else {
193 return false;
194 };
195 let stride = chunk_ratio.saturating_add(1);
196 index % stride == chunk_ratio
197 }
198
199 pub fn entry(&self, index: u64) -> Result<BatEntry<'_>> {
210 let idx = usize::try_from(index).map_err(|_| Error::BatEntryNotFound { index })?;
211 let offset = idx * 8;
212 if offset.saturating_add(8) > self.data.len() {
213 return Err(Error::BatEntryNotFound { index });
214 }
215 let bytes: &[u8; 8] = self.data[offset..offset + 8]
216 .try_into()
217 .expect("length already validated");
218 Ok(BatEntry {
219 bytes,
220 is_sector_bitmap: self.is_sector_bitmap_entry(idx),
221 })
222 }
223
224 fn entry_unchecked(&self, index: usize) -> BatEntry<'_> {
226 let offset = index * 8;
227 let bytes: &[u8; 8] = self.data[offset..offset + 8]
228 .try_into()
229 .expect("iterator index is within bounds");
230 BatEntry {
231 bytes,
232 is_sector_bitmap: self.is_sector_bitmap_entry(index),
233 }
234 }
235
236 pub fn entries(&self) -> impl Iterator<Item = BatEntry<'_>> + '_ {
241 (0..self.len()).map(move |i| self.entry_unchecked(i))
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248
249 fn make_buf(entries: &[u64]) -> Vec<u8> {
251 let mut buf = Vec::with_capacity(entries.len() * 8);
252 for &e in entries {
253 buf.extend_from_slice(&e.to_le_bytes());
254 }
255 buf
256 }
257
258 #[test]
259 fn entry_count_matches_buffer() {
260 let buf = make_buf(&[0, 0, 0, 0, 0, 0]);
261 let bat = Bat::new(&buf, 4);
262 assert_eq!(bat.len(), 6);
263 assert!(!bat.is_empty());
264 }
265
266 #[test]
267 fn empty_bat() {
268 let bat = Bat::new(&[], 4);
269 assert!(bat.is_empty());
270 }
271
272 #[test]
273 fn state_extraction_all_payload_values() {
274 let buf = make_buf(&[0, 1, 2, 3, 0, 6, 7, 4, 5]);
276 let bat = Bat::new(&buf, 4);
277
278 assert_eq!(
280 bat.entry(0).unwrap().payload_state(),
281 Some(PayloadBlockState::NotPresent)
282 );
283 assert_eq!(
284 bat.entry(1).unwrap().payload_state(),
285 Some(PayloadBlockState::Undefined)
286 );
287 assert_eq!(
288 bat.entry(2).unwrap().payload_state(),
289 Some(PayloadBlockState::Zero)
290 );
291 assert_eq!(
292 bat.entry(3).unwrap().payload_state(),
293 Some(PayloadBlockState::Unmapped)
294 );
295
296 assert!(bat.entry(4).unwrap().is_sector_bitmap());
298
299 assert_eq!(
301 bat.entry(5).unwrap().payload_state(),
302 Some(PayloadBlockState::FullyPresent)
303 );
304 assert_eq!(
305 bat.entry(6).unwrap().payload_state(),
306 Some(PayloadBlockState::PartiallyPresent)
307 );
308
309 assert_eq!(bat.entry(7).unwrap().payload_state(), None);
311 assert_eq!(bat.entry(8).unwrap().payload_state(), None);
312 }
313
314 #[test]
315 fn state_extraction_sector_bitmap() {
316 let buf = make_buf(&[0, 0, 0, 0, 0, 0, 0, 0, 6]);
317 let bat = Bat::new(&buf, 4);
318
319 let sb = bat.entry(4).unwrap();
321 assert!(sb.is_sector_bitmap());
322 assert_eq!(
323 sb.sector_bitmap_state(),
324 Some(SectorBitmapState::NotPresent)
325 );
326 assert_eq!(
327 sb.state().unwrap(),
328 BatState::SectorBitmap(SectorBitmapState::NotPresent)
329 );
330
331 let buf2 = make_buf(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 6]);
335 let bat2 = Bat::new(&buf2, 4);
336 let sb2 = bat2.entry(9).unwrap();
337 assert!(sb2.is_sector_bitmap());
338 assert_eq!(sb2.sector_bitmap_state(), Some(SectorBitmapState::Present));
339 }
340
341 #[test]
342 fn file_offset_mb_extraction() {
343 let offset_mb: u64 = 0x1234; let mut raw_bytes = [0u8; 8];
347 {
348 let bits = raw_bytes.view_bits_mut::<Lsb0>();
349 bits[0..3].store::<u8>(6u8); bits[20..64].store::<u64>(offset_mb);
351 }
352 let buf = make_buf(&[u64::from_le_bytes(raw_bytes)]);
353 let bat = Bat::new(&buf, 4);
354
355 let entry = bat.entry(0).unwrap();
356 assert_eq!(entry.raw_state(), 6);
357 assert_eq!(entry.file_offset_mb(), offset_mb);
358 }
359
360 #[test]
361 fn file_offset_mb_44bit_max() {
362 let max_offset: u64 = 0x0FFF_FFFF_FFFF;
364 let mut raw_bytes = [0u8; 8];
365 {
366 let bits = raw_bytes.view_bits_mut::<Lsb0>();
367 bits[0..3].store::<u8>(0u8); bits[20..64].store::<u64>(max_offset);
369 }
370 let buf = make_buf(&[u64::from_le_bytes(raw_bytes)]);
371 let bat = Bat::new(&buf, 4);
372
373 assert_eq!(bat.entry(0).unwrap().file_offset_mb(), max_offset);
374 }
375
376 #[test]
377 fn entry_out_of_bounds() {
378 let buf = make_buf(&[0, 0]);
379 let bat = Bat::new(&buf, 4);
380
381 assert!(bat.entry(2).is_err());
382 assert!(bat.entry(100).is_err());
383 }
384
385 #[test]
386 fn entries_iterator_yields_correct_count() {
387 let buf = make_buf(&[0, 1, 2, 3, 4, 5, 6, 7]);
388 let bat = Bat::new(&buf, 4);
389
390 let entries: Vec<_> = bat.entries().collect();
391 assert_eq!(entries.len(), 8);
392
393 for (i, entry) in entries.iter().enumerate() {
395 assert_eq!(
396 entry.raw_state(),
397 u8::try_from(i).expect("test index fits u8")
398 );
399 }
400 }
401
402 #[test]
403 fn interleaving_pattern() {
404 let buf = make_buf(&[0; 8]);
407 let bat = Bat::new(&buf, 3);
408
409 assert!(!bat.entry(0).unwrap().is_sector_bitmap());
410 assert!(!bat.entry(1).unwrap().is_sector_bitmap());
411 assert!(!bat.entry(2).unwrap().is_sector_bitmap());
412 assert!(bat.entry(3).unwrap().is_sector_bitmap());
413 assert!(!bat.entry(4).unwrap().is_sector_bitmap());
414 assert!(!bat.entry(5).unwrap().is_sector_bitmap());
415 assert!(!bat.entry(6).unwrap().is_sector_bitmap());
416 assert!(bat.entry(7).unwrap().is_sector_bitmap());
417 }
418
419 #[test]
420 fn bat_entry_state_returns_correct_variant() {
421 let buf = make_buf(&[6, 0, 0, 0, 6]);
422 let bat = Bat::new(&buf, 4);
423
424 assert_eq!(
426 bat.entry(0).unwrap().state().unwrap(),
427 BatState::Payload(PayloadBlockState::FullyPresent)
428 );
429
430 assert_eq!(
432 bat.entry(4).unwrap().state().unwrap(),
433 BatState::SectorBitmap(SectorBitmapState::Present)
434 );
435 }
436
437 #[test]
438 fn invalid_state_4_returns_error_for_payload() {
439 let buf = make_buf(&[4]);
441 let bat = Bat::new(&buf, 4);
442 let err = bat.entry(0).unwrap().state().unwrap_err();
443 assert!(matches!(err, Error::InvalidBlockState(4)));
444 }
445
446 #[test]
447 fn invalid_state_5_returns_error_for_payload() {
448 let buf = make_buf(&[5]);
450 let bat = Bat::new(&buf, 4);
451 let err = bat.entry(0).unwrap().state().unwrap_err();
452 assert!(matches!(err, Error::InvalidBlockState(5)));
453 }
454}