fuel_block_committer_encoding/blob/
encoder.rs

1use std::marker::PhantomData;
2
3use bitvec::{order::Msb0, slice::BitSlice};
4use static_assertions::const_assert;
5mod storage;
6use storage::Storage;
7
8use super::{header::Header, Blob};
9use crate::constants::{FIELD_ELEMENTS_PER_BLOB, USABLE_BITS_PER_FIELD_ELEMENT};
10
11#[derive(Default, Debug, Clone)]
12pub struct Encoder {
13    _private: PhantomData<()>,
14}
15
16impl Encoder {
17    pub fn new() -> Self {
18        Self::default()
19    }
20}
21
22impl Encoder {
23    pub const fn blobs_needed_to_encode(&self, num_bytes: usize) -> usize {
24        #[allow(clippy::cast_possible_truncation)]
25        const USABLE_BITS_PER_BLOB: usize = USABLE_BITS_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB;
26
27        const NUM_BITS_FOR_METADATA: usize = Header::V1_SIZE_BITS;
28
29        const NUM_BYTES_FOR_DATA: usize =
30            (USABLE_BITS_PER_BLOB - NUM_BITS_FOR_METADATA).saturating_div(8);
31
32        const_assert!(NUM_BYTES_FOR_DATA > 0);
33
34        num_bytes.div_ceil(NUM_BYTES_FOR_DATA)
35    }
36
37    pub fn encode(&self, orig_data: &[u8], id: u32) -> anyhow::Result<Vec<Blob>> {
38        let mut storage = Storage::new();
39
40        let mut data = BitSlice::<u8, Msb0>::from_slice(orig_data);
41        while !data.is_empty() {
42            let amount_ingested = storage.ingest(data);
43            data = &data[amount_ingested..];
44        }
45
46        Ok(storage.finalize(id))
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    #[test]
53    fn can_handle_zero_input() {
54        // given
55        let no_data = [];
56
57        // when
58        let blobs = super::Encoder::new().encode(&no_data, 0).unwrap();
59
60        // then
61        assert!(blobs.is_empty());
62    }
63}