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}