raptor_code/
encoder.rs

1use alloc::vec::Vec;
2
3use crate::partition::Partition;
4use crate::{common, raptor};
5
6/// A struct that represents a source block encoder that uses Raptor codes.
7pub struct SourceBlockEncoder {
8    intermediate: Vec<Vec<u8>>,
9    k: u32,
10    l: u32,
11    l_prime: u32,
12}
13
14impl SourceBlockEncoder {
15    /// Create a source block encoder, passing the list of source symbols
16    ///
17    /// # Parameters
18    ///
19    /// * `source_block`: A slice of vectors containing the source symbols.
20    /// * `max_source_symbols`: Max number of source symbols inside the source
21    ///   block
22    ///
23    /// Returns a `Result` containing:
24    /// - `Ok(SourceBlockEncoder)` if the encoder was successfully created.
25    /// - `Err(&'static str)` if the encoder could not be created (for example, 
26    ///   if the partitionning of the source_block results in
27    ///   too few encoding symbols (k < 4), leading to a not fully specified matrix).
28    ///
29    pub fn new(source_block: &[u8], max_source_symbols: usize) -> Result<Self, &'static str> {
30        let partition = Partition::new(source_block.len(), max_source_symbols);
31        let source_block = partition.create_source_block(source_block);
32        let k = source_block.len() as u32;
33        let mut raptor = raptor::Raptor::new(k);
34        let fully_specified = raptor.add_encoding_symbols(&source_block);
35        if !fully_specified {
36            if k < 4 {
37                return Err("Source Block is partitionned in too few encoding symbols (k < 4), Raptor matrix is not fully specified");
38            }
39            return Err("Raptor matrix is not fully specified");
40        }
41        raptor.reduce();
42
43        Ok(SourceBlockEncoder {
44            intermediate: raptor.intermediate_symbols().to_vec(),
45            k,
46            l: raptor.get_l(),
47            l_prime: raptor.get_l_prime(),
48        })
49    }
50
51    /// Return the number of source symbols (k) inside the block
52    pub fn nb_source_symbols(&self) -> u32 {
53        self.k
54    }
55
56    /// Generates an encoding symbol with the specified Encoding Symbol
57    /// Identifier (ESI).
58    ///
59    /// This method generates a encoding symbol using the Raptor code and the
60    /// intermediate symbols generated during the initialization of the encoder.
61    ///
62    /// # Parameters
63    ///
64    /// * `esi`: The Encoding Symbol Identifier (ESI) of the desired encoding
65    ///   symbol.
66    ///
67    /// # Returns
68    ///
69    /// A tuple containing:
70    /// * `Vec<u8>` : The generated encoding symbol
71    pub fn fountain(&mut self, esi: u32) -> Vec<u8> {
72        let mut block = Vec::new();
73        let indices = common::find_lt_indices(self.k, esi, self.l, self.l_prime);
74        for indice in indices {
75            if indice < self.intermediate.len() as u32 {
76                common::xor(&mut block, &self.intermediate[indice as usize]);
77            }
78        }
79
80        block
81    }
82}
83
84/// Encodes a source block into encoding symbols using Raptor codes.
85///
86/// # Parameters
87///
88/// * `source_block`: A slice of vectors containing the source symbols.
89/// * `max_source_symbols`: Max number of source symbols inside the source block
90/// * `nb_repair`: The number of repair symbols to be generated.
91///
92/// # Returns
93///
94/// a Tuple
95/// * `Vec<Vec<u8>>` : A vector of vectors of bytes representing the encoding
96///   symbols (source symbols + repair symbol).
97/// * `u32` : Number of source symbols (k)
98/// * `Err(&'static str)` if the encoder could not be created.  
99///   This can happen, for example, if partitioning the `source_block` results in  
100///   too few encoding symbols (k < 4), which would lead to an under-specified matrix.
101///
102/// The function uses Raptor codes to generate the specified number of repair
103/// symbols from the source block.
104pub fn encode_source_block(
105    source_block: &[u8],
106    max_source_symbols: usize,
107    nb_repair: usize,
108) -> Result<(Vec<Vec<u8>>, u32), &'static str> {
109    let mut encoder = SourceBlockEncoder::new(source_block, max_source_symbols)?;
110    let mut output: Vec<Vec<u8>> = Vec::new();
111    let n = encoder.nb_source_symbols() as usize + nb_repair;
112    for esi in 0..n as u32 {
113        output.push(encoder.fountain(esi));
114    }
115    Ok((output, encoder.nb_source_symbols()))
116}
117
118#[cfg(test)]
119mod tests {
120    use alloc::vec;
121    use alloc::vec::Vec;
122
123    #[test]
124    fn test_source_block_encoder() {
125        crate::tests::init();
126
127        let input: Vec<u8> = vec![1, 2, 7, 4, 0, 2, 54, 4, 1, 1, 10, 200, 1, 21, 3, 80];
128        let max_source_symbols = 4;
129        let nb_repair = 3;
130
131        let (encoded_block, k) =
132            super::encode_source_block(&input, max_source_symbols, nb_repair).unwrap();
133        log::debug!("Encoded with {} blocks", k);
134
135        // Try to decode the source block
136
137        let mut encoded_block: Vec<Option<Vec<u8>>> = encoded_block
138            .into_iter()
139            .map(|symbols| Some(symbols))
140            .collect();
141
142        // Simulate loss
143        encoded_block[0] = None;
144        encoded_block[1] = None;
145
146        let output =
147            crate::decoder::decode_source_block(&encoded_block, k as usize, input.len()).unwrap();
148        log::debug!("{:?} / {:?}", output, input);
149        assert!(output.len() == input.len());
150        assert!(output == input);
151    }
152}