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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
use super::encoder::Encoder;
use crate::{RLNCError, common::gf256::Gf256};
use rand::Rng;
/// `Recoder` takes already coded pieces and recodes these coded pieces using
/// a new random sampled coding vector. This is useful for distributing coded
/// pieces more widely without needing to decode back to original data.
///
/// A `Recoder` essentially acts as a new encoder, but it operates on the *encoded* source pieces.
///
/// The `Recoder` stores the coding vectors and coded pieces from the input.
/// Internally, it uses an `Encoder` initialized with the coded pieces.
#[derive(Clone, Debug)]
pub struct Recoder {
coding_vectors: Vec<Gf256>,
encoder: Encoder,
num_pieces_received: usize,
full_coded_piece_byte_len: usize,
num_pieces_coded_together: usize,
}
impl Recoder {
/// Number of pieces original data got splitted into to be coded together.
pub fn get_original_num_pieces_coded_together(&self) -> usize {
self.num_pieces_coded_together
}
/// Number of pieces received by Recoder, which is getting recoded together, producing new pieces.
pub fn get_num_pieces_recoded_together(&self) -> usize {
self.num_pieces_received
}
/// After padding the original data, it gets splitted into `self.get_original_num_pieces_coded_together()` many pieces, which results into these many bytes per piece.
pub fn get_piece_byte_len(&self) -> usize {
self.full_coded_piece_byte_len - self.num_pieces_coded_together
}
/// Each full coded piece consists of `self.get_original_num_pieces_coded_together()` random coefficients, appended by corresponding encoded piece of `self.get_piece_byte_len()` bytes.
pub fn get_full_coded_piece_byte_len(&self) -> usize {
self.full_coded_piece_byte_len
}
/// Creates a new `Recoder` instance from a vector of received coded pieces.
///
/// Each full coded piece in `data` is of `full_coded_piece_byte_len` bytes.
/// A full coded piece = coding vector ++ coded piece
///
/// The `Recoder` extracts the coding vectors and coded pieces from the input
/// data. It then initializes an internal `Encoder` that implicitly
/// represents the source pieces extracted from the input.
///
/// # Arguments
///
/// * `data`: A vector of bytes containing the concatenated full coded pieces, each of
/// `full_coded_piece_byte_len` bytes length.
/// * `full_coded_piece_byte_len`: The byte length of a full coded piece.
/// * `num_pieces_coded_together`: The number of original pieces that were
/// linearly combined to create each coded piece. This is also the length
/// of the coding vector prepended to each full coded piece.
///
/// # Returns
/// Returns `Ok(Recoder)` on successful creation.
/// Returns `Err(RLNCError::NotEnoughPiecesToRecode)` if the input `data` is empty
/// or does not contain at least one full coded piece.
/// Returns `Err(RLNCError::PieceLengthZero)` if `full_coded_piece_byte_len` is zero.
/// Returns `Err(RLNCError::PieceCountZero)` if `num_pieces_coded_together` is zero.
/// Returns `Err(RLNCError::PieceLengthTooShort)` if `full_coded_piece_byte_len` is not greater than `num_pieces_coded_together`.
pub fn new(data: Vec<u8>, full_coded_piece_byte_len: usize, num_pieces_coded_together: usize) -> Result<Recoder, RLNCError> {
if data.is_empty() {
return Err(RLNCError::NotEnoughPiecesToRecode);
}
if full_coded_piece_byte_len == 0 {
return Err(RLNCError::PieceLengthZero);
}
if num_pieces_coded_together == 0 {
return Err(RLNCError::PieceCountZero);
}
if full_coded_piece_byte_len <= num_pieces_coded_together {
return Err(RLNCError::PieceLengthTooShort);
}
let piece_byte_len = full_coded_piece_byte_len - num_pieces_coded_together;
let num_pieces_received = data.len() / full_coded_piece_byte_len;
let mut coding_vectors = Vec::with_capacity(num_pieces_received * num_pieces_coded_together);
let mut coded_pieces = Vec::with_capacity(num_pieces_received * piece_byte_len);
data.chunks_exact(full_coded_piece_byte_len).for_each(|full_coded_piece| {
let coding_vector = &full_coded_piece[..num_pieces_coded_together];
let coded_piece = &full_coded_piece[num_pieces_coded_together..];
coding_vectors.extend(coding_vector.iter().map(|&symbol| Gf256::new(symbol)));
coded_pieces.extend_from_slice(coded_piece);
});
let encoder = unsafe { Encoder::without_padding(coded_pieces, num_pieces_received).unwrap_unchecked() };
Ok(Recoder {
coding_vectors,
encoder,
num_pieces_received,
full_coded_piece_byte_len,
num_pieces_coded_together,
})
}
/// Generates a new coded piece by recoding the source pieces using a randomly sampled coding vector.
///
/// This method generates a random recoding vector (length `self.get_num_pieces_recoded_together()`),
/// computes the resulting coding vector for the original source pieces by
/// multiplying the random vector by the matrix of received coding vectors,
/// and then uses the internal `Encoder` to produce a new coded piece based
/// on this computed coding vector.
///
/// The output is a vector containing the computed source coding vector
/// prepended to the newly generated coded piece.
///
/// # Arguments
///
/// * `rng`: Used to sample the random recoding vector.
///
/// # Returns
///
/// Returns a `Vec<u8>` representing the new coded piece prepended with its
/// source coding vector. The length of the returned vector is
/// `self.get_full_coded_piece_byte_len()`.
pub fn recode<R: Rng + ?Sized>(&self, rng: &mut R) -> Vec<u8> {
let random_recoding_vector = (0..self.num_pieces_received).map(|_| rng.random()).collect::<Vec<Gf256>>();
// Compute the resulting coding vector for the original source pieces
// by multiplying the random sampled recoding vector by the matrix of received coding vectors.
let computed_coding_vector = (0..self.num_pieces_coded_together)
.map(|coeff_idx| {
random_recoding_vector
.iter()
.enumerate()
.fold(Gf256::default(), |acc, (recoding_vec_idx, &cur)| {
let row_begins_at = recoding_vec_idx * self.num_pieces_coded_together;
acc + cur * self.coding_vectors[row_begins_at + coeff_idx]
})
.get()
})
.collect::<Vec<u8>>();
let full_coded_piece = unsafe { self.encoder.code_with_coding_vector(&random_recoding_vector).unwrap_unchecked() };
let coded_piece = &full_coded_piece[self.num_pieces_received..];
let mut full_recoded_piece = vec![0u8; self.full_coded_piece_byte_len];
full_recoded_piece[..self.num_pieces_coded_together].copy_from_slice(&computed_coding_vector);
full_recoded_piece[self.num_pieces_coded_together..].copy_from_slice(coded_piece);
full_recoded_piece
}
}
#[cfg(test)]
mod tests {
use super::{RLNCError, Recoder};
use crate::full::encoder::Encoder;
use rand::Rng;
#[test]
fn test_recoder_new_invalid_inputs() {
let mut rng = rand::rng();
let data_byte_len = 1024usize;
let piece_count = 32usize;
let encoder = Encoder::new((0..data_byte_len).map(|_| rng.random()).collect::<Vec<u8>>(), piece_count)
.expect("Failed to create Encoder for recoder new invalid inputs test");
let full_coded_piece_byte_len = encoder.get_full_coded_piece_byte_len();
let num_pieces_coded_together = encoder.get_piece_count();
// Test case 1: Empty `data` vector
let empty_data: Vec<u8> = Vec::new();
let result_empty_data = Recoder::new(empty_data, full_coded_piece_byte_len, num_pieces_coded_together);
assert!(result_empty_data.is_err());
assert_eq!(
result_empty_data.expect_err("Expected NotEnoughPiecesToRecode error for empty data"),
RLNCError::NotEnoughPiecesToRecode
);
// Test case 2: `full_coded_piece_byte_len` is zero
let data_non_empty = vec![1, 2, 3]; // Needs at least one piece worth of data for non-empty input
let result_zero_full_len = Recoder::new(data_non_empty.clone(), 0, num_pieces_coded_together);
assert!(result_zero_full_len.is_err());
assert_eq!(
result_zero_full_len.expect_err("Expected PieceLengthZero error for zero full coded piece length"),
RLNCError::PieceLengthZero
);
// Test case 3: `num_pieces_coded_together` is zero
let result_zero_piece_count = Recoder::new(data_non_empty.clone(), full_coded_piece_byte_len, 0);
assert!(result_zero_piece_count.is_err());
assert_eq!(
result_zero_piece_count.expect_err("Expected PieceCountZero error for zero pieces coded together"),
RLNCError::PieceCountZero
);
// Test case 4: `full_coded_piece_byte_len` is not greater than `num_pieces_coded_together`
// Case 4.1: Equal
let result_equal_len = Recoder::new(
data_non_empty.clone(),
num_pieces_coded_together, // full_coded_piece_byte_len = num_pieces_coded_together
num_pieces_coded_together,
);
assert!(result_equal_len.is_err());
assert_eq!(
result_equal_len.expect_err("Expected PieceLengthTooShort error when full length equals piece count"),
RLNCError::PieceLengthTooShort
);
// Case 4.2: Less than
let result_less_len = Recoder::new(
data_non_empty.clone(),
num_pieces_coded_together - 1, // full_coded_piece_byte_len < num_pieces_coded_together
num_pieces_coded_together,
);
assert!(result_less_len.is_err());
assert_eq!(
result_less_len.expect_err("Expected PieceLengthTooShort error when full length is less than piece count"),
RLNCError::PieceLengthTooShort
);
// Test case 5: Valid input (using existing encoder setup to generate valid data)
let num_pieces_to_recode_with = 5;
let coded_pieces_for_recoder: Vec<u8> = (0..num_pieces_to_recode_with).flat_map(|_| encoder.code(&mut rng)).collect();
let result_valid = Recoder::new(coded_pieces_for_recoder, full_coded_piece_byte_len, num_pieces_coded_together);
assert!(result_valid.is_ok());
let recoder = result_valid.expect("Expected Recoder to be created successfully with valid inputs");
assert_eq!(recoder.get_original_num_pieces_coded_together(), num_pieces_coded_together);
assert_eq!(recoder.get_num_pieces_recoded_together(), num_pieces_to_recode_with);
}
#[test]
fn test_recoder_getters() {
let mut rng = rand::rng();
let data_byte_len = 1024usize;
let piece_count = 32usize; // Original number of pieces
let data = (0..data_byte_len).map(|_| rng.random()).collect::<Vec<u8>>();
let encoder = Encoder::new(data, piece_count).expect("Failed to create Encoder for recoder getters test");
let num_pieces_to_recode_with = 10; // Number of coded pieces given to recoder
let full_coded_piece_byte_len = encoder.get_full_coded_piece_byte_len();
let original_piece_byte_len = encoder.get_piece_byte_len();
let coded_pieces_for_recoder: Vec<u8> = (0..num_pieces_to_recode_with).flat_map(|_| encoder.code(&mut rng)).collect();
let recoder = Recoder::new(
coded_pieces_for_recoder,
full_coded_piece_byte_len,
piece_count, // `num_pieces_coded_together` from original encoder
)
.expect("Recoder creation failed");
assert_eq!(recoder.get_original_num_pieces_coded_together(), piece_count);
assert_eq!(recoder.get_num_pieces_recoded_together(), num_pieces_to_recode_with);
assert_eq!(recoder.get_piece_byte_len(), original_piece_byte_len);
assert_eq!(recoder.get_full_coded_piece_byte_len(), full_coded_piece_byte_len);
}
}