mod decode;
mod encode;
pub mod gf;
mod tables;
pub use decode::MaxiCodeDecoder;
pub use encode::MaxiCodeEncoder;
use crate::output::BitMatrix;
const TOTAL_CW: usize = 144;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Carrier {
pub postcode: String,
pub country: u16,
pub service: u16,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaxiCodeMeta {
pub mode: u8,
pub carrier: Option<Carrier>,
pub body: Vec<u8>,
}
impl Default for MaxiCodeMeta {
fn default() -> Self {
MaxiCodeMeta {
mode: 4,
carrier: None,
body: vec![0; body_len(4)],
}
}
}
pub(crate) fn body_len(mode: u8) -> usize {
match mode {
5 => 77,
2 | 3 => 84,
_ => 93,
}
}
pub(crate) fn secondary_lengths(mode: u8) -> (usize, usize) {
if mode == 5 { (68, 56) } else { (84, 40) }
}
pub(crate) fn add_error_correction(cw: &mut [u8; TOTAL_CW], mode: u8) {
let primary_ec = gf::encode(&cw[0..10], 10);
cw[10..20].copy_from_slice(&primary_ec);
let (dlen, eclen) = secondary_lengths(mode);
let half = eclen / 2;
for parity in 0..2 {
let data: Vec<u8> = (0..dlen).step_by(2).map(|j| cw[20 + j + parity]).collect();
let ec = gf::encode(&data, half);
for (k, &e) in ec.iter().enumerate() {
cw[20 + dlen + 2 * k + parity] = e;
}
}
}
pub(crate) fn correct_primary(cw: &mut [u8; TOTAL_CW]) -> crate::error::Result<()> {
let fixed = gf::decode(&cw[0..20], 10).ok_or(crate::error::Error::ErrorCorrectionFailed)?;
cw[0..20].copy_from_slice(&fixed);
Ok(())
}
pub(crate) fn correct_secondary(cw: &mut [u8; TOTAL_CW], mode: u8) -> crate::error::Result<()> {
let (dlen, eclen) = secondary_lengths(mode);
let half = eclen / 2;
for parity in 0..2 {
let mut block: Vec<u8> = (0..dlen).step_by(2).map(|j| cw[20 + j + parity]).collect();
block.extend((0..half).map(|k| cw[20 + dlen + 2 * k + parity]));
let fixed = gf::decode(&block, half).ok_or(crate::error::Error::ErrorCorrectionFailed)?;
for (k, j) in (0..dlen).step_by(2).enumerate() {
cw[20 + j + parity] = fixed[k];
}
}
Ok(())
}
pub(crate) fn render_matrix(cw: &[u8; TOTAL_CW]) -> BitMatrix {
let mut m = BitMatrix::new(tables::WIDTH, tables::HEIGHT, 1);
for (row, cells) in tables::MAXI_GRID.iter().enumerate() {
for (col, &v) in cells.iter().enumerate() {
if let Some((idx, bit)) = tables::cell_target(v)
&& (cw[idx] >> bit) & 1 != 0
{
m.set(col, row, true);
}
}
}
for &(row, col) in &tables::ORIENTATION {
m.set(col, row, true);
}
m
}
pub(crate) fn read_codewords(m: &BitMatrix) -> [u8; TOTAL_CW] {
let mut cw = [0u8; TOTAL_CW];
for (row, cells) in tables::MAXI_GRID.iter().enumerate() {
for (col, &v) in cells.iter().enumerate() {
if let Some((idx, bit)) = tables::cell_target(v)
&& m.get(col, row)
{
cw[idx] |= 1 << bit;
}
}
}
cw
}