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}