use crate::reed_solomon::{
engine::DefaultEngine,
rate::{DefaultRate, DefaultRateDecoder, DefaultRateEncoder, Rate, RateDecoder, RateEncoder},
DecoderResult, EncoderResult, Error, RecoveryDecoderResult,
};
pub struct Encoder(DefaultRateEncoder<DefaultEngine>);
impl Encoder {
pub fn add_original_shard<T: AsRef<[u8]>>(&mut self, original_shard: T) -> Result<(), Error> {
self.0.add_original_shard(original_shard)
}
pub fn encode(&mut self) -> Result<EncoderResult<'_>, Error> {
self.0.encode()
}
pub fn new(
original_count: usize,
recovery_count: usize,
shard_bytes: usize,
) -> Result<Self, Error> {
Ok(Self(DefaultRateEncoder::new(
original_count,
recovery_count,
shard_bytes,
DefaultEngine::new(),
None,
)?))
}
pub fn reset(
&mut self,
original_count: usize,
recovery_count: usize,
shard_bytes: usize,
) -> Result<(), Error> {
self.0.reset(original_count, recovery_count, shard_bytes)
}
pub fn supports(original_count: usize, recovery_count: usize) -> bool {
DefaultRate::<DefaultEngine>::supports(original_count, recovery_count)
}
}
pub struct Decoder(DefaultRateDecoder<DefaultEngine>);
impl Decoder {
pub fn add_original_shard<T: AsRef<[u8]>>(
&mut self,
index: usize,
original_shard: T,
) -> Result<(), Error> {
self.0.add_original_shard(index, original_shard)
}
pub fn add_recovery_shard<T: AsRef<[u8]>>(
&mut self,
index: usize,
recovery_shard: T,
) -> Result<(), Error> {
self.0.add_recovery_shard(index, recovery_shard)
}
pub fn decode(&mut self) -> Result<Option<DecoderResult<'_>>, Error> {
self.0.decode(false)
}
pub fn decode_with_recovery(&mut self) -> Result<Option<RecoveryDecoderResult<'_>>, Error> {
Ok(self.0.decode(true)?.map(RecoveryDecoderResult::new))
}
pub fn new(
original_count: usize,
recovery_count: usize,
shard_bytes: usize,
) -> Result<Self, Error> {
Ok(Self(DefaultRateDecoder::new(
original_count,
recovery_count,
shard_bytes,
DefaultEngine::new(),
None,
)?))
}
pub fn reset(
&mut self,
original_count: usize,
recovery_count: usize,
shard_bytes: usize,
) -> Result<(), Error> {
self.0.reset(original_count, recovery_count, shard_bytes)
}
pub fn supports(original_count: usize, recovery_count: usize) -> bool {
DefaultRate::<DefaultEngine>::supports(original_count, recovery_count)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reed_solomon::test_util;
use fixedbitset::FixedBitSet;
use std::collections::BTreeMap;
fn roundtrip(
encoder: &mut Encoder,
decoder: &mut Decoder,
original_count: usize,
recovery_hash: &str,
decoder_original: &[usize],
decoder_recovery: &[usize],
seed: u8,
) {
let original = test_util::generate_original(original_count, 1024, seed);
for original in &original {
encoder.add_original_shard(original).unwrap();
}
let result = encoder.encode().unwrap();
let recovery: Vec<_> = result.recovery_iter().collect();
test_util::assert_hash(&recovery, recovery_hash);
let mut original_received = FixedBitSet::with_capacity(original_count);
for i in decoder_original {
decoder.add_original_shard(*i, &original[*i]).unwrap();
original_received.set(*i, true);
}
for i in decoder_recovery {
decoder.add_recovery_shard(*i, recovery[*i]).unwrap();
}
let decoded = decoder.decode();
let restored: BTreeMap<_, _> = match &decoded {
Ok(Some(result)) => result.original_iter().collect(),
Ok(None) => BTreeMap::new(),
Err(e) => panic!("decode failed: {e:?}"),
};
for i in 0..original_count {
if !original_received[i] {
assert_eq!(restored[&i], original[i]);
}
}
}
#[test]
fn roundtrip_two_rounds_reset_low_to_high() {
let mut encoder = Encoder::new(2, 3, 1024).unwrap();
let mut decoder = Decoder::new(2, 3, 1024).unwrap();
roundtrip(
&mut encoder,
&mut decoder,
2,
test_util::LOW_2_3,
&[],
&[0, 1],
123,
);
encoder.reset(3, 2, 1024).unwrap();
decoder.reset(3, 2, 1024).unwrap();
roundtrip(
&mut encoder,
&mut decoder,
3,
test_util::HIGH_3_2,
&[1],
&[0, 1],
132,
);
}
#[test]
fn failed_encoder_reset_preserves_state() {
let original = test_util::generate_original(2, 1024, 123);
let mut encoder = Encoder::new(2, 3, 1024).unwrap();
assert_eq!(
encoder.reset(3, 2, 3),
Err(Error::InvalidShardSize { shard_bytes: 3 })
);
for shard in &original {
encoder.add_original_shard(shard).unwrap();
}
let result = encoder.encode().unwrap();
let recovery: Vec<_> = result.recovery_iter().collect();
test_util::assert_hash(&recovery, test_util::LOW_2_3);
}
#[test]
fn failed_decoder_reset_preserves_state() {
let original = test_util::generate_original(2, 1024, 123);
let mut encoder = Encoder::new(2, 3, 1024).unwrap();
for shard in &original {
encoder.add_original_shard(shard).unwrap();
}
let result = encoder.encode().unwrap();
let recovery: Vec<_> = result.recovery_iter().map(<[u8]>::to_vec).collect();
let mut decoder = Decoder::new(2, 3, 1024).unwrap();
assert_eq!(
decoder.reset(3, 2, 3),
Err(Error::InvalidShardSize { shard_bytes: 3 })
);
decoder.add_recovery_shard(0, &recovery[0]).unwrap();
decoder.add_recovery_shard(1, &recovery[1]).unwrap();
let decoded = decoder.decode();
let restored: BTreeMap<_, _> = match &decoded {
Ok(Some(result)) => result.original_iter().collect(),
Ok(None) => BTreeMap::new(),
Err(e) => panic!("decode failed: {e:?}"),
};
assert_eq!(restored[&0], original[0]);
assert_eq!(restored[&1], original[1]);
}
#[test]
fn supports() {
assert!(Encoder::supports(4096, 61440));
assert!(Encoder::supports(61440, 4096));
assert!(Decoder::supports(4096, 61440));
assert!(Decoder::supports(61440, 4096));
}
}