use libec;
use std::num::NonZeroUsize;
use trackable::error::ErrorKindExt;
use {BuildCoder, ErasureCode, Error, ErrorKind, Fragment, FragmentBuf, Result};
pub use libec::{Backend, Checksum};
#[derive(Debug, Clone)]
pub struct LibErasureCoderBuilder {
data_fragments: NonZeroUsize,
parity_fragments: NonZeroUsize,
backend: Backend,
checksum: Checksum,
}
impl LibErasureCoderBuilder {
pub fn new(data_fragments: NonZeroUsize, parity_fragments: NonZeroUsize) -> Self {
LibErasureCoderBuilder {
data_fragments,
parity_fragments,
backend: Backend::default(),
checksum: Checksum::default(),
}
}
pub fn backend(mut self, backend: Backend) -> Self {
self.backend = backend;
self
}
pub fn checksum(mut self, checksum: Checksum) -> Self {
self.checksum = checksum;
self
}
}
impl BuildCoder for LibErasureCoderBuilder {
type Coder = LibErasureCoder;
fn build_coder(&self) -> Result<Self::Coder> {
track!(
libec::Builder::new(self.data_fragments, self.parity_fragments)
.backend(self.backend)
.checksum(self.checksum)
.finish()
.map(LibErasureCoder::from)
.map_err(Error::from)
)
}
fn coder_id(&self) -> String {
format!(
"liberasurecode:{:?}:{:?}:{}:{}",
self.backend, self.checksum, self.data_fragments, self.parity_fragments
)
}
}
pub struct LibErasureCoder {
inner: libec::ErasureCoder,
}
impl LibErasureCoder {
pub fn new(data_fragments: NonZeroUsize, parity_fragments: NonZeroUsize) -> Result<Self> {
track!(LibErasureCoderBuilder::new(data_fragments, parity_fragments).build_coder())
}
pub fn inner_ref(&self) -> &libec::ErasureCoder {
&self.inner
}
pub fn inner_mut(&mut self) -> &mut libec::ErasureCoder {
&mut self.inner
}
pub fn into_inner(self) -> libec::ErasureCoder {
self.inner
}
}
impl ErasureCode for LibErasureCoder {
fn data_fragments(&self) -> NonZeroUsize {
self.inner.data_fragments()
}
fn parity_fragments(&self) -> NonZeroUsize {
self.inner.parity_fragments()
}
fn encode(&mut self, data: &[u8]) -> Result<Vec<FragmentBuf>> {
let fragments = self.inner.encode(data)?;
Ok(fragments)
}
fn decode(&mut self, fragments: &[&Fragment]) -> Result<Vec<u8>> {
let data = self.inner.decode(fragments)?;
Ok(data)
}
fn reconstruct(&mut self, index: usize, fragments: &[&Fragment]) -> Result<Vec<u8>> {
let fragment = self.inner.reconstruct(index, fragments.iter())?;
Ok(fragment)
}
}
impl From<libec::ErasureCoder> for LibErasureCoder {
fn from(f: libec::ErasureCoder) -> Self {
LibErasureCoder { inner: f }
}
}
impl From<libec::Error> for Error {
fn from(f: libec::Error) -> Self {
use libec::Error::*;
match f {
InsufficientFragments => ErrorKind::InvalidInput.cause(f).into(),
BadChecksum | BadHeader => ErrorKind::CorruptedFragments.cause(f).into(),
_ => ErrorKind::Other.cause(f).into(),
}
}
}
#[cfg(test)]
mod tests {
use std::num::NonZeroUsize;
use super::*;
use {ErasureCode, ErrorKind};
#[test]
fn it_works() {
let data_fragments = NonZeroUsize::new(4).unwrap();
let parity_fragments = NonZeroUsize::new(2).unwrap();
let mut coder = LibErasureCoder::new(data_fragments, parity_fragments).unwrap();
let data = vec![0, 1, 2, 3];
let encoded = coder.encode(&data).unwrap();
let encoded = encoded.iter().map(|f| f.as_ref()).collect::<Vec<_>>();
assert_eq!(Some(&data), coder.decode(&encoded[0..]).as_ref().ok());
assert_eq!(Some(&data), coder.decode(&encoded[1..]).as_ref().ok());
assert_eq!(Some(&data), coder.decode(&encoded[2..]).as_ref().ok());
assert_eq!(
Err(ErrorKind::InvalidInput),
coder.decode(&encoded[3..]).map_err(|e| *e.kind())
);
}
}