Skip to main content

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