fuel_block_committer_encoding/blob/
header.rs

1use anyhow::{bail, Result};
2use bitvec::{field::BitField, order::Msb0, slice::BitSlice, vec::BitVec, view::BitView};
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5pub struct HeaderV1 {
6    pub bundle_id: u32,
7    /// Number of bits containing data
8    pub num_bits: u32,
9    pub is_last: bool,
10    pub idx: u32,
11}
12
13impl HeaderV1 {
14    const BUNDLE_ID_BITS: usize = 32;
15    const NUM_BITS_BITS: usize = 21;
16    const IS_LAST_BITS: usize = 1;
17    const IDX_SIZE_BITS: usize = 17;
18    pub const TOTAL_SIZE_BITS: usize =
19        Self::BUNDLE_ID_BITS + Self::NUM_BITS_BITS + Self::IS_LAST_BITS + Self::IDX_SIZE_BITS;
20
21    fn encode(&self, buffer: &mut BitVec<u8, Msb0>) {
22        buffer.extend_from_bitslice(self.bundle_id.view_bits::<Msb0>());
23
24        buffer.extend_from_bitslice(&self.num_bits.view_bits::<Msb0>()[32 - Self::NUM_BITS_BITS..]);
25
26        buffer.push(self.is_last);
27
28        buffer.extend_from_bitslice(&self.idx.view_bits::<Msb0>()[32 - Self::IDX_SIZE_BITS..]);
29    }
30
31    fn decode(data: &BitSlice<u8, Msb0>) -> Result<(Self, usize)> {
32        if data.len() < Self::TOTAL_SIZE_BITS {
33            bail!(
34                "not enough data to decode header, expected {} bits, got {}",
35                Self::TOTAL_SIZE_BITS,
36                data.len()
37            )
38        }
39
40        let bundle_id = data[..Self::BUNDLE_ID_BITS].load_be();
41        let remaining_data = &data[Self::BUNDLE_ID_BITS..];
42
43        let num_bits = remaining_data[..Self::NUM_BITS_BITS].load_be::<u32>();
44        let remaining_data = &remaining_data[Self::NUM_BITS_BITS..];
45
46        let is_last = remaining_data[0];
47        let remaining_data = &remaining_data[1..];
48
49        let idx = remaining_data[..Self::IDX_SIZE_BITS].load_be::<u32>();
50        let remaining_data = &remaining_data[Self::IDX_SIZE_BITS..];
51
52        let header = Self {
53            bundle_id,
54            num_bits,
55            is_last,
56            idx,
57        };
58
59        let amount_read = data
60            .len()
61            .checked_sub(remaining_data.len())
62            .expect("remaining data to always be smaller than original data");
63
64        Ok((header, amount_read))
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69pub enum Header {
70    V1(HeaderV1),
71}
72
73impl Header {
74    const VERSION_BITS: usize = 16;
75    pub const V1_SIZE_BITS: usize = HeaderV1::TOTAL_SIZE_BITS + Self::VERSION_BITS;
76
77    pub(crate) fn encode(&self) -> BitVec<u8, Msb0> {
78        match self {
79            Self::V1(blob_header_v1) => {
80                let mut buffer = BitVec::<u8, Msb0>::new();
81
82                let version = 1u16;
83                buffer.extend_from_bitslice(version.view_bits::<Msb0>());
84
85                blob_header_v1.encode(&mut buffer);
86
87                buffer
88            }
89        }
90    }
91    pub(crate) fn decode(data: &BitSlice<u8, Msb0>) -> Result<(Self, usize)> {
92        if data.len() < Self::VERSION_BITS {
93            bail!(
94                "not enough data to decode header version, expected {} bits, got {}",
95                Self::VERSION_BITS,
96                data.len()
97            );
98        }
99
100        let version = data[..Self::VERSION_BITS].load_be::<u16>();
101
102        let remaining_data = &data[Self::VERSION_BITS..];
103        let read_version_bits = data
104            .len()
105            .checked_sub(remaining_data.len())
106            .expect("remaining_data should always be smaller than data");
107        match version {
108            1 => {
109                let (header, read_bits) = HeaderV1::decode(remaining_data)?;
110                Ok((
111                    Self::V1(header),
112                    read_bits
113                        .checked_add(read_version_bits)
114                        .expect("never to encode more than usize bits"),
115                ))
116            }
117            version => bail!("Unsupported version {version}"),
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use bitvec::{field::BitField, order::Msb0};
125
126    use super::Header;
127
128    #[test]
129    fn detects_unsupported_version() {
130        // given
131        let mut encoded_header = Header::V1(super::HeaderV1 {
132            bundle_id: 0,
133            num_bits: 0,
134            is_last: false,
135            idx: 0,
136        })
137        .encode();
138
139        encoded_header[0..16].store_be(2u16);
140
141        // when
142        let header = Header::decode(&encoded_header).unwrap_err();
143
144        // then
145        assert_eq!(header.to_string(), "Unsupported version 2");
146    }
147
148    #[test]
149    fn complains_if_not_enough_data_is_given_for_version() {
150        // given
151        let encoded_header = Header::V1(super::HeaderV1 {
152            bundle_id: 0,
153            num_bits: 0,
154            is_last: false,
155            idx: 0,
156        })
157        .encode();
158
159        // when
160        let err = Header::decode(&encoded_header[..10]).unwrap_err();
161
162        // then
163        assert_eq!(
164            err.to_string(),
165            "not enough data to decode header version, expected 16 bits, got 10"
166        );
167    }
168
169    #[test]
170    fn complains_if_not_enough_data_is_given_for_header() {
171        // given
172        let encoded_header = Header::V1(super::HeaderV1 {
173            bundle_id: 0,
174            num_bits: 0,
175            is_last: false,
176            idx: 0,
177        })
178        .encode();
179
180        // when
181        let err = Header::decode(&encoded_header[..18]).unwrap_err();
182
183        // then
184        assert_eq!(
185            err.to_string(),
186            "not enough data to decode header, expected 71 bits, got 2"
187        );
188    }
189
190    #[test]
191    fn reports_correct_amount_read() {
192        // given
193        let mut encoded_header = Header::V1(super::HeaderV1 {
194            bundle_id: 0,
195            num_bits: 0,
196            is_last: false,
197            idx: 0,
198        })
199        .encode();
200
201        // some extra data that should be ignored
202        encoded_header.extend_from_bitslice(&bitvec::bitvec![u8, Msb0; 0; 10]);
203
204        // when
205        let (_, amount_read) = Header::decode(&encoded_header).unwrap();
206
207        // then
208        assert_eq!(amount_read, 87);
209    }
210}