#![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)]
use std::convert::{TryFrom, TryInto};
use std::iter;
pub fn ec_init_tables(
k: usize,
rows: usize,
encode_matrix: impl AsRef<[u8]>,
mut buf: impl AsMut<[u8]>,
) {
assert_eq!(encode_matrix.as_ref().len(), k * rows);
let gftbls_len = k * rows * 32;
assert_eq!(buf.as_mut().len(), gftbls_len);
unsafe {
libisal_sys::ec_init_tables(
k.try_into().unwrap(),
rows.try_into().unwrap(),
encode_matrix.as_ref().as_ptr(),
buf.as_mut().as_mut_ptr(),
);
}
}
#[must_use]
pub fn ec_init_tables_owned(k: usize, rows: usize, encode_matrix: impl AsRef<[u8]>) -> Vec<u8> {
assert_eq!(encode_matrix.as_ref().len(), k * rows);
let gftbls_len = k * rows * 32;
let mut gftbls = Vec::with_capacity(gftbls_len);
unsafe {
libisal_sys::ec_init_tables(
k.try_into().unwrap(),
rows.try_into().unwrap(),
encode_matrix.as_ref().as_ptr(),
gftbls.as_mut_ptr(),
);
gftbls.set_len(gftbls_len);
}
gftbls
}
pub fn ec_encode_data<T, M>(
len: usize,
k: usize,
rows: usize,
gftbls: impl AsRef<[u8]>,
data: impl AsRef<[T]>,
mut bufs: impl AsMut<[M]>,
) where
T: AsRef<[u8]>,
M: AsMut<[u8]>,
{
assert_eq!(gftbls.as_ref().len(), 32 * k * rows);
assert_eq!(bufs.as_mut().len(), rows);
assert!(bufs
.as_mut()
.iter_mut()
.all(|buf| buf.as_mut().len() == len));
let data_ptrs = data
.as_ref()
.iter()
.map(AsRef::as_ref)
.map(|s| s.as_ptr())
.collect::<Vec<_>>();
let coding_ptrs = bufs
.as_mut()
.iter_mut()
.map(AsMut::as_mut)
.map(|s| s.as_mut_ptr())
.collect::<Vec<_>>();
unsafe {
libisal_sys::ec_encode_data(
len.try_into().unwrap(),
k.try_into().unwrap(),
rows.try_into().unwrap(),
gftbls.as_ref().as_ptr(),
data_ptrs.as_ptr(),
coding_ptrs.as_ptr() as *mut _,
);
}
}
#[must_use]
pub fn ec_encode_data_owned<T>(
len: usize,
k: usize,
rows: usize,
gftbls: impl AsRef<[u8]>,
data: impl AsRef<[T]>,
) -> Vec<Vec<u8>>
where
T: AsRef<[u8]>,
{
assert_eq!(gftbls.as_ref().len(), 32 * k * rows);
let data_ptrs = data
.as_ref()
.iter()
.map(AsRef::as_ref)
.map(|s| s.as_ptr())
.collect::<Vec<_>>();
let mut coding = iter::repeat(len)
.map(Vec::with_capacity)
.take(rows)
.collect::<Vec<_>>();
let mut coding_ptrs: Vec<*mut u8> = coding.iter_mut().map(Vec::as_mut_ptr).collect();
unsafe {
libisal_sys::ec_encode_data(
len.try_into().unwrap(),
k.try_into().unwrap(),
rows.try_into().unwrap(),
gftbls.as_ref().as_ptr(),
data_ptrs.as_ptr(),
coding_ptrs.as_mut_ptr(),
);
for c in &mut coding {
c.set_len(len);
}
coding
}
}
#[must_use]
pub fn gf_mul(a: u8, b: u8) -> u8 {
unsafe { libisal_sys::gf_mul(a, b) }
}
#[must_use]
pub fn gf_inv(a: u8) -> u8 {
unsafe { libisal_sys::gf_inv(a) }
}
#[must_use]
pub fn gf_gen_rs_matrix(k: usize, m: usize) -> Vec<u8> {
let encode_matrix_len = m * k;
let mut encode_matrix = Vec::with_capacity(encode_matrix_len);
unsafe {
libisal_sys::gf_gen_rs_matrix(
encode_matrix.as_mut_ptr(),
m.try_into().unwrap(),
k.try_into().unwrap(),
);
encode_matrix.set_len(encode_matrix_len);
encode_matrix
}
}
#[must_use]
pub fn gf_gen_cauchy1_matrix(k: usize, m: usize) -> Vec<u8> {
let encode_matrix_len = m * k;
let mut encode_matrix = Vec::with_capacity(encode_matrix_len);
unsafe {
libisal_sys::gf_gen_cauchy1_matrix(
encode_matrix.as_mut_ptr(),
m.try_into().unwrap(),
k.try_into().unwrap(),
);
encode_matrix.set_len(encode_matrix_len);
encode_matrix
}
}
#[must_use]
pub fn gf_invert_matrix(mut matrix: Vec<u8>) -> Option<Vec<u8>> {
let len: f64 = u32::try_from(matrix.len()).unwrap().into();
let n = len.sqrt();
assert!(n.fract() == 0_f64, "Matrix must be square");
#[allow(clippy::cast_possible_truncation)]
let n = n as i32;
let inverted_matrix_len = matrix.len();
let mut inverted_matrix = Vec::with_capacity(inverted_matrix_len);
unsafe {
if libisal_sys::gf_invert_matrix(matrix.as_mut_ptr(), inverted_matrix.as_mut_ptr(), n) < 0 {
None
} else {
inverted_matrix.set_len(inverted_matrix_len);
Some(inverted_matrix)
}
}
}
#[must_use]
pub fn gf_gen_decode_matrix_simple(
encode_matrix: impl AsRef<[u8]>,
erased_idxs: impl AsRef<[usize]>,
k: usize,
m: usize,
) -> Option<Vec<u8>> {
let encode_matrix = encode_matrix.as_ref();
let erased_idxs = erased_idxs.as_ref();
let nerrs = erased_idxs.len();
if nerrs > m - k {
return None;
}
let b = encode_matrix
.chunks_exact(k)
.enumerate()
.filter_map(|(i, chunk)| {
if erased_idxs.contains(&i) {
None
} else {
Some(chunk)
}
})
.take(k)
.flatten()
.copied()
.collect::<Vec<u8>>();
let invert_matrix = gf_invert_matrix(b)?;
let mut decode_matrix: Vec<u8> = vec![0_u8; m * k];
for i in 0..nerrs {
if erased_idxs[i] < k {
for j in 0..k {
decode_matrix[k * i + j] = invert_matrix[k * erased_idxs[i] + j]
}
}
}
for p in 0..nerrs {
if erased_idxs[p] >= k {
for i in 0..k {
let mut s = 0;
for j in 0..k {
s ^= gf_mul(
invert_matrix[j * k + i],
encode_matrix[k * erased_idxs[p] + j],
)
}
decode_matrix[k * p + i] = s;
}
}
}
Some(decode_matrix)
}
#[cfg(test)]
mod tests {
use crate::{
ec_encode_data, ec_encode_data_owned, ec_init_tables, ec_init_tables_owned,
gf_gen_cauchy1_matrix, gf_gen_decode_matrix_simple, gf_gen_rs_matrix, gf_inv,
gf_invert_matrix, gf_mul,
};
use std::convert::TryInto;
#[test]
fn test_ec_init_tables() {
let k = 4;
let p = 2;
#[rustfmt::skip]
let encode_matrix: Vec<u8> = vec![
0x01, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x47, 0xA7, 0x7A, 0xBA,
0xA7, 0x47, 0xBA, 0x7A,
];
#[rustfmt::skip]
let expected_gftbls: Vec<u8> = vec![
0x00, 0x47, 0x8E, 0xC9, 0x01, 0x46, 0x8F, 0xC8, 0x02, 0x45, 0x8C, 0xCB, 0x03, 0x44, 0x8D, 0xCA, 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C,
0x00, 0xA7, 0x53, 0xF4, 0xA6, 0x01, 0xF5, 0x52, 0x51, 0xF6, 0x02, 0xA5, 0xF7, 0x50, 0xA4, 0x03, 0x00, 0xA2, 0x59, 0xFB, 0xB2, 0x10, 0xEB, 0x49, 0x79, 0xDB, 0x20, 0x82, 0xCB, 0x69, 0x92, 0x30,
0x00, 0x7A, 0xF4, 0x8E, 0xF5, 0x8F, 0x01, 0x7B, 0xF7, 0x8D, 0x03, 0x79, 0x02, 0x78, 0xF6, 0x8C, 0x00, 0xF3, 0xFB, 0x08, 0xEB, 0x18, 0x10, 0xE3, 0xCB, 0x38, 0x30, 0xC3, 0x20, 0xD3, 0xDB, 0x28,
0x00, 0xBA, 0x69, 0xD3, 0xD2, 0x68, 0xBB, 0x01, 0xB9, 0x03, 0xD0, 0x6A, 0x6B, 0xD1, 0x02, 0xB8, 0x00, 0x6F, 0xDE, 0xB1, 0xA1, 0xCE, 0x7F, 0x10, 0x5F, 0x30, 0x81, 0xEE, 0xFE, 0x91, 0x20, 0x4F,
0x00, 0xA7, 0x53, 0xF4, 0xA6, 0x01, 0xF5, 0x52, 0x51, 0xF6, 0x02, 0xA5, 0xF7, 0x50, 0xA4, 0x03, 0x00, 0xA2, 0x59, 0xFB, 0xB2, 0x10, 0xEB, 0x49, 0x79, 0xDB, 0x20, 0x82, 0xCB, 0x69, 0x92, 0x30,
0x00, 0x47, 0x8E, 0xC9, 0x01, 0x46, 0x8F, 0xC8, 0x02, 0x45, 0x8C, 0xCB, 0x03, 0x44, 0x8D, 0xCA, 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C,
0x00, 0xBA, 0x69, 0xD3, 0xD2, 0x68, 0xBB, 0x01, 0xB9, 0x03, 0xD0, 0x6A, 0x6B, 0xD1, 0x02, 0xB8, 0x00, 0x6F, 0xDE, 0xB1, 0xA1, 0xCE, 0x7F, 0x10, 0x5F, 0x30, 0x81, 0xEE, 0xFE, 0x91, 0x20, 0x4F,
0x00, 0x7A, 0xF4, 0x8E, 0xF5, 0x8F, 0x01, 0x7B, 0xF7, 0x8D, 0x03, 0x79, 0x02, 0x78, 0xF6, 0x8C, 0x00, 0xF3, 0xFB, 0x08, 0xEB, 0x18, 0x10, 0xE3, 0xCB, 0x38, 0x30, 0xC3, 0x20, 0xD3, 0xDB, 0x28,
];
let mut actual_gftbls = vec![0; 32 * k * p];
ec_init_tables(
k.try_into().unwrap(),
p.try_into().unwrap(),
&encode_matrix[k * k..],
&mut actual_gftbls[..],
);
assert_eq!(actual_gftbls, expected_gftbls);
let actual_gftbls = ec_init_tables_owned(
k.try_into().unwrap(),
p.try_into().unwrap(),
&encode_matrix[k * k..],
);
assert_eq!(actual_gftbls, expected_gftbls);
}
#[test]
fn test_ec_encode_data() {
let len = 1;
let k = 4;
let nerrs = 2;
#[rustfmt::skip]
let gftbls: Vec<u8> = vec![
0x00, 0x47, 0x8E, 0xC9, 0x01, 0x46, 0x8F, 0xC8, 0x02, 0x45, 0x8C, 0xCB, 0x03, 0x44, 0x8D, 0xCA, 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C,
0x00, 0xA7, 0x53, 0xF4, 0xA6, 0x01, 0xF5, 0x52, 0x51, 0xF6, 0x02, 0xA5, 0xF7, 0x50, 0xA4, 0x03, 0x00, 0xA2, 0x59, 0xFB, 0xB2, 0x10, 0xEB, 0x49, 0x79, 0xDB, 0x20, 0x82, 0xCB, 0x69, 0x92, 0x30,
0x00, 0x7A, 0xF4, 0x8E, 0xF5, 0x8F, 0x01, 0x7B, 0xF7, 0x8D, 0x03, 0x79, 0x02, 0x78, 0xF6, 0x8C, 0x00, 0xF3, 0xFB, 0x08, 0xEB, 0x18, 0x10, 0xE3, 0xCB, 0x38, 0x30, 0xC3, 0x20, 0xD3, 0xDB, 0x28,
0x00, 0xBA, 0x69, 0xD3, 0xD2, 0x68, 0xBB, 0x01, 0xB9, 0x03, 0xD0, 0x6A, 0x6B, 0xD1, 0x02, 0xB8, 0x00, 0x6F, 0xDE, 0xB1, 0xA1, 0xCE, 0x7F, 0x10, 0x5F, 0x30, 0x81, 0xEE, 0xFE, 0x91, 0x20, 0x4F,
0x00, 0xA7, 0x53, 0xF4, 0xA6, 0x01, 0xF5, 0x52, 0x51, 0xF6, 0x02, 0xA5, 0xF7, 0x50, 0xA4, 0x03, 0x00, 0xA2, 0x59, 0xFB, 0xB2, 0x10, 0xEB, 0x49, 0x79, 0xDB, 0x20, 0x82, 0xCB, 0x69, 0x92, 0x30,
0x00, 0x47, 0x8E, 0xC9, 0x01, 0x46, 0x8F, 0xC8, 0x02, 0x45, 0x8C, 0xCB, 0x03, 0x44, 0x8D, 0xCA, 0x00, 0x04, 0x08, 0x0C, 0x10, 0x14, 0x18, 0x1C, 0x20, 0x24, 0x28, 0x2C, 0x30, 0x34, 0x38, 0x3C,
0x00, 0xBA, 0x69, 0xD3, 0xD2, 0x68, 0xBB, 0x01, 0xB9, 0x03, 0xD0, 0x6A, 0x6B, 0xD1, 0x02, 0xB8, 0x00, 0x6F, 0xDE, 0xB1, 0xA1, 0xCE, 0x7F, 0x10, 0x5F, 0x30, 0x81, 0xEE, 0xFE, 0x91, 0x20, 0x4F,
0x00, 0x7A, 0xF4, 0x8E, 0xF5, 0x8F, 0x01, 0x7B, 0xF7, 0x8D, 0x03, 0x79, 0x02, 0x78, 0xF6, 0x8C, 0x00, 0xF3, 0xFB, 0x08, 0xEB, 0x18, 0x10, 0xE3, 0xCB, 0x38, 0x30, 0xC3, 0x20, 0xD3, 0xDB, 0x28,
];
let data: Vec<Vec<u8>> = vec![vec![1; len], vec![2; len], vec![3; len], vec![4; len]];
let orig_data = data.clone();
let data_ptrs = data.iter().map(Vec::as_slice).collect::<Vec<_>>();
let expected_coding: Vec<Vec<u8>> = vec![vec![0x48], vec![0x0F]];
let mut actual_coding = vec![vec![0; len], vec![0; len]];
let mut actual_coding_slices = actual_coding.iter_mut().collect::<Vec<_>>();
ec_encode_data(
len,
k,
nerrs,
&gftbls,
&data_ptrs,
&mut actual_coding_slices,
);
for (actual, expected) in data.iter().zip(orig_data.iter()) {
assert_eq!(actual, expected)
}
for (actual, expected) in actual_coding.iter().zip(expected_coding.iter()) {
assert_eq!(actual, expected);
}
let actual_coding = ec_encode_data_owned(len, k, nerrs, &gftbls, &data_ptrs);
for (actual, expected) in data.iter().zip(orig_data.iter()) {
assert_eq!(actual, expected)
}
for (actual, expected) in actual_coding.iter().zip(expected_coding.iter()) {
assert_eq!(actual, expected);
}
}
#[test]
fn test_gf_mul() {
let a = 0xBE;
let b = 0xEF;
let actual = gf_mul(a, b);
let expected = 0x03;
assert_eq!(actual, expected);
}
#[test]
fn test_gf_inv() {
let a = 0x42;
let actual = gf_inv(a);
let expected = 0xF8;
assert_eq!(actual, expected);
}
#[test]
fn test_gf_gen_rs_matrix() {
let k = 4;
let p = 2;
let m = k + p;
let actual_encode_matrix = gf_gen_rs_matrix(k, m);
#[rustfmt::skip]
let expected_encode_matrix: Vec<u8> = vec![
0x01, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x01, 0x01, 0x01, 0x01,
0x01, 0x02, 0x04, 0x08
];
assert_eq!(actual_encode_matrix, expected_encode_matrix);
}
#[test]
fn test_gf_gen_cauchy1_matrix() {
let k = 4;
let p = 2;
let m = k + p;
let actual_encode_matrix = gf_gen_cauchy1_matrix(k, m);
#[rustfmt::skip]
let expected_encode_matrix: Vec<u8> = vec![
0x01, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x47, 0xA7, 0x7A, 0xBA,
0xA7, 0x47, 0xBA, 0x7A,
];
assert_eq!(actual_encode_matrix, expected_encode_matrix);
}
#[test]
fn test_gf_invert_matrix() {
#[rustfmt::skip]
let input: Vec<u8> = vec![
0x01, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
];
let actual = gf_invert_matrix(input.clone());
let expected: Vec<u8> = input;
assert_eq!(actual, Some(expected));
#[rustfmt::skip]
let input: Vec<u8> = vec![
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x47, 0xA7, 0x7A, 0xBA,
0xA7, 0x47, 0xBA, 0x7A,
];
let actual = gf_invert_matrix(input);
#[rustfmt::skip]
let expected: Vec<u8> = vec![
0xD0, 0x6B, 0x44, 0x50,
0x6B, 0xD0, 0x50, 0x44,
0x01, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
];
assert_eq!(actual, Some(expected));
}
#[test]
fn test_ec_gen_decode_matrix_simple() {
#[rustfmt::skip]
let encode_matrix = vec![
0x01, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01,
0x47, 0xA7, 0x7A, 0xBA,
0xA7, 0x47, 0xBA, 0x7A,
];
let erased_idxs = vec![3, 4, 5];
let actual_decode_matrix = gf_gen_decode_matrix_simple(&encode_matrix, &erased_idxs, 4, 6);
assert_eq!(actual_decode_matrix, None);
let erased_idxs = vec![3, 4];
let actual_decode_matrix = gf_gen_decode_matrix_simple(&encode_matrix, &erased_idxs, 4, 6);
#[rustfmt::skip]
let expected_decode_matrix: Vec<u8> = vec![
0xF5, 0x8F, 0xBB, 0x06,
0x60, 0x40, 0xFE, 0xBB,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
assert_eq!(actual_decode_matrix, Some(expected_decode_matrix));
let erased_idxs = vec![3];
let actual_decode_matrix = gf_gen_decode_matrix_simple(&encode_matrix, &erased_idxs, 4, 6);
#[rustfmt::skip]
let expected_decode_matrix: Vec<u8> = vec![
0xC8, 0x52, 0x7B, 0x07,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
assert_eq!(actual_decode_matrix, Some(expected_decode_matrix));
let erased_idxs = vec![4];
let actual_decode_matrix = gf_gen_decode_matrix_simple(&encode_matrix, &erased_idxs, 4, 6);
#[rustfmt::skip]
let expected_decode_matrix: Vec<u8> = vec![
0x47, 0xA7, 0x7A, 0xBA,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
assert_eq!(actual_decode_matrix.unwrap(), expected_decode_matrix);
}
}