fuel_block_committer_encoding/blob/
encoder.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
use std::marker::PhantomData;

use bitvec::{order::Msb0, slice::BitSlice};
use static_assertions::const_assert;
mod storage;
use storage::Storage;

use super::{header::Header, Blob};
use crate::constants::{FIELD_ELEMENTS_PER_BLOB, USABLE_BITS_PER_FIELD_ELEMENT};

#[derive(Default, Debug, Clone)]
pub struct Encoder {
    _private: PhantomData<()>,
}

impl Encoder {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }
}

impl Encoder {
    #[must_use]
    pub const fn blobs_needed_to_encode(&self, num_bytes: usize) -> usize {
        #[allow(clippy::cast_possible_truncation)]
        const USABLE_BITS_PER_BLOB: usize = USABLE_BITS_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB;

        const NUM_BITS_FOR_METADATA: usize = Header::V1_SIZE_BITS;

        const NUM_BYTES_FOR_DATA: usize =
            (USABLE_BITS_PER_BLOB - NUM_BITS_FOR_METADATA).saturating_div(8);

        const_assert!(NUM_BYTES_FOR_DATA > 0);

        num_bytes.div_ceil(NUM_BYTES_FOR_DATA)
    }

    pub fn encode(&self, orig_data: &[u8], id: u32) -> anyhow::Result<Vec<Blob>> {
        let mut storage = Storage::new();

        let mut data = BitSlice::<u8, Msb0>::from_slice(orig_data);
        while !data.is_empty() {
            let amount_ingested = storage.ingest(data);
            data = &data[amount_ingested..];
        }

        Ok(storage.finalize(id))
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn can_handle_zero_input() {
        // given
        let no_data = [];

        // when
        let blobs = super::Encoder::new().encode(&no_data, 0).unwrap();

        // then
        assert!(blobs.is_empty());
    }
}