|
| 1 | +use reed_solomon_simd::{ReedSolomonDecoder, ReedSolomonEncoder}; |
| 2 | + |
| 3 | +// TODO(AndrewL): Consider combining this with `generate_coding_shards`. |
| 4 | +// TODO(AndrewL): Consider adding custom error type and using it here. |
| 5 | +pub fn split_data_into_shards(message: Vec<u8>, num_data_shards: usize) -> Option<Vec<Vec<u8>>> { |
| 6 | + if !message.len().is_multiple_of(num_data_shards) { |
| 7 | + return None; |
| 8 | + } |
| 9 | + let shard_size = message.len() / num_data_shards; |
| 10 | + Some(message.chunks_exact(shard_size).map(|chunk| chunk.to_vec()).collect()) |
| 11 | +} |
| 12 | + |
| 13 | +/// Generate coding shards using Reed-Solomon encoding. |
| 14 | +// TODO(AndrewL): Consider adding custom error type and using it here. |
| 15 | +pub fn generate_coding_shards( |
| 16 | + data_shards: &[Vec<u8>], |
| 17 | + num_coding_shards: usize, |
| 18 | +) -> Result<Vec<Vec<u8>>, String> { |
| 19 | + if num_coding_shards == 0 { |
| 20 | + // ReedSolomonEncoder does not support 0 coding shards |
| 21 | + return Ok(Vec::new()); |
| 22 | + } |
| 23 | + |
| 24 | + let num_data_shards = data_shards.len(); |
| 25 | + // TODO(AndrewL): Consider accepting a shard size as an argument. |
| 26 | + let shard_size = data_shards.first().ok_or("No data shards".to_string())?.len(); |
| 27 | + |
| 28 | + let mut encoder = ReedSolomonEncoder::new(num_data_shards, num_coding_shards, shard_size) |
| 29 | + .map_err(|e| format!("Failed to create Reed-Solomon encoder: {}", e))?; |
| 30 | + |
| 31 | + for shard in data_shards.iter().take(num_data_shards) { |
| 32 | + encoder |
| 33 | + .add_original_shard(shard) |
| 34 | + .map_err(|e| format!("Failed to add data shard: {}", e))?; |
| 35 | + } |
| 36 | + |
| 37 | + let result = encoder.encode().map_err(|e| format!("Failed to encode: {}", e))?; |
| 38 | + |
| 39 | + let coding_shards = result.recovery_iter().map(|shard| shard.to_vec()).collect(); |
| 40 | + |
| 41 | + Ok(coding_shards) |
| 42 | +} |
| 43 | + |
| 44 | +/// Reconstruct the original message from available shards using Reed-Solomon error correction. |
| 45 | +// TODO(AndrewL): Consider adding custom error type and using it here. |
| 46 | +// TODO(AndrewL): Rename this to `reconstruct_data_shards`. |
| 47 | +pub fn reconstruct_message_from_shards( |
| 48 | + // TODO(AndrewL): Change this to a HashMap<usize, Vec<u8>>. |
| 49 | + shards: &[(usize, Vec<u8>)], |
| 50 | + num_data_shards: usize, |
| 51 | + num_coding_shards: usize, |
| 52 | +) -> Result<Vec<Vec<u8>>, String> { |
| 53 | + if num_coding_shards == 0 { |
| 54 | + return Ok(shards.iter().map(|(_, s)| s.to_vec()).collect()); |
| 55 | + } |
| 56 | + // TODO(AndrewL): Consider accepting a shard size as an argument. |
| 57 | + let shard_size = shards.first().ok_or("No shards".to_string())?.1.len(); |
| 58 | + |
| 59 | + let mut decoder = ReedSolomonDecoder::new(num_data_shards, num_coding_shards, shard_size) |
| 60 | + .map_err(|e| format!("Failed to create Reed-Solomon decoder: {}", e))?; |
| 61 | + |
| 62 | + for (index, shard_data) in shards { |
| 63 | + if *index < num_data_shards { |
| 64 | + decoder |
| 65 | + .add_original_shard(*index, shard_data) |
| 66 | + .map_err(|e| format!("Failed to add original shard: {}", e))?; |
| 67 | + } else { |
| 68 | + decoder |
| 69 | + .add_recovery_shard(index - num_data_shards, shard_data) |
| 70 | + .map_err(|e| format!("Failed to add coding shard: {}", e))?; |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + let result = decoder.decode().map_err(|e| format!("Failed to decode: {}", e))?; |
| 75 | + |
| 76 | + let mut shard_map = std::collections::HashMap::new(); |
| 77 | + for (index, shard) in shards { |
| 78 | + shard_map.insert(index, shard); |
| 79 | + } |
| 80 | + |
| 81 | + let mut data_shards = Vec::with_capacity(num_data_shards); |
| 82 | + for index in 0..num_data_shards { |
| 83 | + if let Some(shard_shard) = shard_map.get(&index) { |
| 84 | + data_shards.push(shard_shard.to_vec()); |
| 85 | + } else if let Some(restored_data) = result.restored_original(index) { |
| 86 | + data_shards.push(restored_data.to_vec()); |
| 87 | + } else { |
| 88 | + return Err(format!( |
| 89 | + "Missing data shard at index {} and no restored data available", |
| 90 | + index |
| 91 | + )); |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + Ok(data_shards) |
| 96 | +} |
| 97 | + |
| 98 | +pub fn combine_data_shards(data_shards: Vec<Vec<u8>>) -> Vec<u8> { |
| 99 | + data_shards.iter().flatten().copied().collect() |
| 100 | +} |
0 commit comments