1use std::convert::TryInto;
29
30const HEADER_SIZE_IN_BYTES: usize = 16;
31const BLOCK_LENGTH_SIZE_IN_BYTES: usize = 8;
32const BLOCK_ALIGNMENT_IN_BYTES: usize = 16;
33
34pub struct B3FReader<'a> {
35 data: &'a [u8],
36}
37
38impl<'a> B3FReader<'a> {
39 pub fn new(data: &'a [u8]) -> Option<B3FReader<'a>> {
40 if data.len() < 16 {
41 return None;
42 }
43
44 let magic_number = u32::from_ne_bytes(data[0..4].try_into().ok()?);
45 if magic_number != 0xBB33FF00 {
46 return None;
47 }
48
49 let reader = B3FReader { data };
50
51 Some(reader)
52 }
53
54 pub fn file_tag_as_u32(&self) -> u32 {
55 u32::from_ne_bytes(self.data[4..8].try_into().unwrap())
56 }
57
58 pub fn file_tag_as_u8(&self) -> &[u8] {
59 &self.data[4..8]
60 }
61
62 pub fn version(&self) -> u32 {
63 u32::from_ne_bytes(self.data[8..12].try_into().unwrap())
64 }
65
66 pub fn block_count(&self) -> usize {
67 u32::from_ne_bytes(self.data[12..16].try_into().unwrap()) as usize
68 }
69
70 pub fn get_block(
71 &self,
72 index: usize,
73 ) -> &'a [u8] {
74 debug_assert_eq!(BLOCK_LENGTH_SIZE_IN_BYTES, 8);
76 let begin_size_offset = HEADER_SIZE_IN_BYTES + (index * BLOCK_LENGTH_SIZE_IN_BYTES);
77 let size_data = &self.data[begin_size_offset..];
78 let mut begin = u64::from_ne_bytes(size_data[0..8].try_into().unwrap()) as usize;
79 let end = u64::from_ne_bytes(size_data[8..16].try_into().unwrap()) as usize;
80
81 begin = ((begin + BLOCK_ALIGNMENT_IN_BYTES - 1) / BLOCK_ALIGNMENT_IN_BYTES)
83 * BLOCK_ALIGNMENT_IN_BYTES;
84
85 let mut data_offset =
86 HEADER_SIZE_IN_BYTES + ((self.block_count() + 1) * BLOCK_LENGTH_SIZE_IN_BYTES);
87 data_offset = ((data_offset + BLOCK_ALIGNMENT_IN_BYTES - 1) / BLOCK_ALIGNMENT_IN_BYTES)
88 * BLOCK_ALIGNMENT_IN_BYTES;
89 &self.data[data_offset..][begin..end]
90 }
91}