#![allow(clippy::identity_op,clippy::erasing_op)]
mod compact_generators;
mod compact_parity_checks;
#[repr(C)]
#[derive(Copy,Clone,Debug,Eq,PartialEq,Hash)]
pub enum LDPCCode {
TC128 = 0,
TC256 = 1,
TC512 = 2,
TM1280 = 3,
TM1536 = 4,
TM2048 = 5,
TM5120 = 6,
TM6144 = 7,
TM8192 = 8,
}
pub struct CodeParams {
pub n: usize,
pub k: usize,
pub punctured_bits: usize,
pub submatrix_size: usize,
pub circulant_size: usize,
pub paritycheck_sum: u32,
pub decode_bf_working_len: usize,
pub decode_ms_working_len: usize,
pub decode_ms_working_u8_len: usize,
pub output_len: usize,
}
pub const TC128_PARAMS: CodeParams = CodeParams {
n: 128,
k: 64,
punctured_bits: 0,
submatrix_size: 128/8,
circulant_size: 128/8,
paritycheck_sum: 512,
decode_bf_working_len: 128 + 0,
decode_ms_working_len: 2*512 + 3*128 + 3*0 - 2*64,
decode_ms_working_u8_len: (128 + 0 - 64)/8,
output_len: 128/8,
};
pub const TC256_PARAMS: CodeParams = CodeParams {
n: 256,
k: 128,
punctured_bits: 0,
submatrix_size: 256/8,
circulant_size: 256/8,
paritycheck_sum: 1024,
decode_bf_working_len: 256 + 0,
decode_ms_working_len: 2*1024 + 3*256 + 3*0 - 2*128,
decode_ms_working_u8_len: (256 + 0 - 128)/8,
output_len: 256/8,
};
pub const TC512_PARAMS: CodeParams = CodeParams {
n: 512,
k: 256,
punctured_bits: 0,
submatrix_size: 512/8,
circulant_size: 512/8,
paritycheck_sum: 2048,
decode_bf_working_len: 512 + 0,
decode_ms_working_len: 2*2048 + 3*512 + 3*0 - 2*256,
decode_ms_working_u8_len: (512 + 0 - 256)/8,
output_len: 512/8,
};
pub const TM1280_PARAMS: CodeParams = CodeParams {
n: 1280,
k: 1024,
punctured_bits: 128,
submatrix_size: 128,
circulant_size: 128/4,
paritycheck_sum: 4992,
decode_bf_working_len: 1280 + 128,
decode_ms_working_len: 2*4992 + 3*1280 + 3*128 - 2*1024,
decode_ms_working_u8_len: (1280 + 128 - 1024)/8,
output_len: (1280 + 128)/8,
};
pub const TM1536_PARAMS: CodeParams = CodeParams {
n: 1536,
k: 1024,
punctured_bits: 256,
submatrix_size: 256,
circulant_size: 256/4,
paritycheck_sum: 5888,
decode_bf_working_len: 1536 + 256,
decode_ms_working_len: 2*5888 + 3*1536 + 3*256 - 2*1024,
decode_ms_working_u8_len: (1536 + 256 - 1024)/8,
output_len: (1536 + 256)/8,
};
pub const TM2048_PARAMS: CodeParams = CodeParams {
n: 2048,
k: 1024,
punctured_bits: 512,
submatrix_size: 512,
circulant_size: 512/4,
paritycheck_sum: 7680,
decode_bf_working_len: 2048 + 512,
decode_ms_working_len: 2*7680 + 3*2048 + 3*512 - 2*1024,
decode_ms_working_u8_len: (2048 + 512 - 1024)/8,
output_len: (2048 + 512)/8,
};
pub const TM5120_PARAMS: CodeParams = CodeParams {
n: 5120,
k: 4096,
punctured_bits: 512,
submatrix_size: 512,
circulant_size: 512/4,
paritycheck_sum: 19968,
decode_bf_working_len: 5120 + 512,
decode_ms_working_len: 2*19968 + 3*5120 + 3*512 - 2*4096,
decode_ms_working_u8_len: (5120 + 512 - 4096)/8,
output_len: (5120 + 512)/8,
};
pub const TM6144_PARAMS: CodeParams = CodeParams {
n: 6144,
k: 4096,
punctured_bits: 1024,
submatrix_size: 1024,
circulant_size: 1024/4,
paritycheck_sum: 23552,
decode_bf_working_len: 6144 + 1024,
decode_ms_working_len: 2*23552 + 3*6144 + 3*1024 - 2*4096,
decode_ms_working_u8_len: (6144 + 1024 - 4096)/8,
output_len: (6144 + 1024)/8,
};
pub const TM8192_PARAMS: CodeParams = CodeParams {
n: 8192,
k: 4096,
punctured_bits: 2048,
submatrix_size: 2048,
circulant_size: 2048/4,
paritycheck_sum: 30720,
decode_bf_working_len: 8192 + 2048,
decode_ms_working_len: 2*30720 + 3*8192 + 3*2048 - 2*4096,
decode_ms_working_u8_len: (8192 + 2048 - 4096)/8,
output_len: (8192 + 2048)/8,
};
pub struct ParityIter {
phi: &'static [[u16; 26]; 4],
prototype: &'static [[[u8; 11]; 4]; 3],
m: usize,
logmd4: usize, modm: usize, modmd4: usize, rowidx: usize,
colidx: usize,
sub_mat_idx: usize,
sub_mat: u8,
sub_mat_val: usize,
check: usize,
}
impl Iterator for ParityIter {
type Item = (usize, usize);
#[allow(clippy::inline_always)]
#[inline(always)]
fn next(&mut self) -> Option<(usize, usize)> {
use self::compact_parity_checks::{HI, HP, THETA_K};
loop {
loop {
loop {
if self.check < self.m {
match self.sub_mat & (HP | HI) {
HI => { let chk = self.rowidx * self.m + self.check;
let var = self.colidx * self.m
+ ((self.check + self.sub_mat_val) & self.modm);
self.check += 1;
return Some((chk, var));
},
HP => { let pi =
(((THETA_K[self.sub_mat_val] as usize +
(self.check>>self.logmd4)) % 4) << self.logmd4)
+ ((self.phi[self.check>>self.logmd4][self.sub_mat_val]
as usize + self.check) & self.modmd4);
let chk = self.rowidx * self.m + self.check;
let var = self.colidx * self.m + pi;
self.check += 1;
return Some((chk, var));
},
_ => ()
}
}
self.check = 0;
if self.sub_mat != 0 && self.sub_mat_idx < 2 {
self.sub_mat_idx += 1;
self.sub_mat = self.prototype[self.sub_mat_idx][self.rowidx][self.colidx];
self.sub_mat_val = (self.sub_mat & 0x3F) as usize;
} else {
self.sub_mat_idx = 0;
break;
}
}
if self.colidx < 10 {
self.colidx += 1;
self.sub_mat = self.prototype[self.sub_mat_idx][self.rowidx][self.colidx];
self.sub_mat_val = (self.sub_mat & 0x3F) as usize;
} else {
self.colidx = 0;
break;
}
}
if self.rowidx < 3 {
self.rowidx += 1;
self.sub_mat = self.prototype[self.sub_mat_idx][self.rowidx][self.colidx];
self.sub_mat_val = (self.sub_mat & 0x3F) as usize;
} else {
return None;
}
}
}
}
impl LDPCCode {
pub const fn params(self) -> CodeParams {
match self {
LDPCCode::TC128 => TC128_PARAMS,
LDPCCode::TC256 => TC256_PARAMS,
LDPCCode::TC512 => TC512_PARAMS,
LDPCCode::TM1280 => TM1280_PARAMS,
LDPCCode::TM1536 => TM1536_PARAMS,
LDPCCode::TM2048 => TM2048_PARAMS,
LDPCCode::TM5120 => TM5120_PARAMS,
LDPCCode::TM6144 => TM6144_PARAMS,
LDPCCode::TM8192 => TM8192_PARAMS,
}
}
pub const fn n(self) -> usize {
self.params().n
}
pub const fn k(self) -> usize {
self.params().k
}
pub const fn punctured_bits(self) -> usize {
self.params().punctured_bits
}
pub const fn submatrix_size(self) -> usize {
self.params().submatrix_size
}
pub const fn circulant_size(self) -> usize {
self.params().circulant_size
}
pub const fn paritycheck_sum(self) -> u32 {
self.params().paritycheck_sum
}
pub fn compact_generator(self) -> &'static [u64] {
match self {
LDPCCode::TC128 => &compact_generators::TC128_G,
LDPCCode::TC256 => &compact_generators::TC256_G,
LDPCCode::TC512 => &compact_generators::TC512_G,
LDPCCode::TM1280 => &compact_generators::TM1280_G,
LDPCCode::TM1536 => &compact_generators::TM1536_G,
LDPCCode::TM2048 => &compact_generators::TM2048_G,
LDPCCode::TM5120 => &compact_generators::TM5120_G,
LDPCCode::TM6144 => &compact_generators::TM6144_G,
LDPCCode::TM8192 => &compact_generators::TM8192_G,
}
}
pub fn iter_paritychecks(self) -> ParityIter {
match self {
LDPCCode::TC128 | LDPCCode::TC256 | LDPCCode::TC512 => self.iter_paritychecks_tc(),
LDPCCode::TM1280 | LDPCCode::TM1536 | LDPCCode::TM2048 |
LDPCCode::TM5120 | LDPCCode::TM6144 | LDPCCode::TM8192 => self.iter_paritychecks_tm(),
}
}
fn iter_paritychecks_tc(self) -> ParityIter {
let prototype = match self {
LDPCCode::TC128 => &compact_parity_checks::TC128_H,
LDPCCode::TC256 => &compact_parity_checks::TC256_H,
LDPCCode::TC512 => &compact_parity_checks::TC512_H,
_ => unreachable!(),
};
let subm = prototype[0][0][0];
let m = self.submatrix_size();
let phi = &self::compact_parity_checks::PHI_J_K_M128;
ParityIter {
phi, prototype, m, logmd4: (m/4).trailing_zeros() as usize, modm: m-1, modmd4: (m/4)-1,
rowidx: 0, colidx: 0, sub_mat_idx: 0, sub_mat: subm, sub_mat_val: (subm & 0x3F) as usize, check: 0,
}
}
fn iter_paritychecks_tm(self) -> ParityIter {
let m = self.submatrix_size();
let phi = match m {
128 => &self::compact_parity_checks::PHI_J_K_M128,
256 => &self::compact_parity_checks::PHI_J_K_M256,
512 => &self::compact_parity_checks::PHI_J_K_M512,
1024 => &self::compact_parity_checks::PHI_J_K_M1024,
2048 => &self::compact_parity_checks::PHI_J_K_M2048,
4096 => &self::compact_parity_checks::PHI_J_K_M4096,
8192 => &self::compact_parity_checks::PHI_J_K_M8192,
_ => unreachable!(),
};
let prototype_cols = (self.n() + self.punctured_bits()) / m;
let prototype = match prototype_cols {
5 => &self::compact_parity_checks::TM_R12_H,
7 => &self::compact_parity_checks::TM_R23_H,
11 => &self::compact_parity_checks::TM_R45_H,
_ => unreachable!(),
};
let subm = prototype[0][0][0];
ParityIter {
phi, prototype, m, logmd4: (m/4).trailing_zeros() as usize, modm: m-1, modmd4: (m/4)-1,
rowidx: 0, colidx: 0, sub_mat_idx: 0, check: 0, sub_mat: subm, sub_mat_val: (subm & 0x3F) as usize,
}
}
}
#[cfg(test)]
mod tests {
use std::prelude::v1::*;
use super::{LDPCCode};
const CODES: [LDPCCode; 9] = [LDPCCode::TC128, LDPCCode::TC256, LDPCCode::TC512,
LDPCCode::TM1280, LDPCCode::TM1536, LDPCCode::TM2048,
LDPCCode::TM5120, LDPCCode::TM6144, LDPCCode::TM8192,
];
fn crc32_u16(crc: u32, data: u32) -> u32 {
let mut crc = crc ^ data;
for _ in 0..16 {
let mask = if crc & 1 == 0 { 0 } else { 0xFFFFFFFFu32 };
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
crc
}
#[test]
fn test_iter_parity() {
let crc_results = [0x13A9D28D, 0xC3CC7625, 0x66EA9A48,
0xB643C99E, 0x8169E0CF, 0x599A0807,
0xD0E794B1, 0xBD0AB764, 0x9003014C];
for (idx, code) in CODES.iter().enumerate() {
let mut count = 0;
let mut crc = 0xFFFFFFFFu32;
for (check, var) in code.iter_paritychecks() {
count += 1;
crc = crc32_u16(crc, check as u32);
crc = crc32_u16(crc, var as u32);
}
assert_eq!(count, code.paritycheck_sum() as usize);
assert_eq!(crc, crc_results[idx]);
}
}
}