use super::{MicroEcLevel, MicroVersion};
use crate::segment::Mode;
#[derive(Debug, Clone, Copy)]
pub struct MicroParams {
pub data_cw: usize,
pub ec_cw: usize,
}
pub fn ec_params(version: MicroVersion, ec: MicroEcLevel) -> Option<MicroParams> {
use MicroEcLevel::*;
use MicroVersion::*;
let (data_cw, ec_cw) = match (version, ec) {
(M1, Detection) => (3, 2),
(M2, L) => (5, 5),
(M2, M) => (4, 6),
(M3, L) => (11, 6),
(M3, M) => (9, 8),
(M4, L) => (16, 8),
(M4, M) => (14, 10),
(M4, Q) => (10, 14),
_ => return None,
};
Some(MicroParams { data_cw, ec_cw })
}
pub fn has_short_final_codeword(version: MicroVersion) -> bool {
matches!(version, MicroVersion::M1 | MicroVersion::M3)
}
pub fn data_bit_capacity(version: MicroVersion, ec: MicroEcLevel) -> Option<usize> {
let p = ec_params(version, ec)?;
Some(if has_short_final_codeword(version) {
(p.data_cw - 1) * 8 + 4
} else {
p.data_cw * 8
})
}
pub fn data_module_count(version: MicroVersion) -> usize {
match version {
MicroVersion::M1 => 36,
MicroVersion::M2 => 80,
MicroVersion::M3 => 132,
MicroVersion::M4 => 192,
}
}
pub fn symbol_number(version: MicroVersion, ec: MicroEcLevel) -> Option<u8> {
use MicroEcLevel::*;
use MicroVersion::*;
Some(match (version, ec) {
(M1, Detection) => 0,
(M2, L) => 1,
(M2, M) => 2,
(M3, L) => 3,
(M3, M) => 4,
(M4, L) => 5,
(M4, M) => 6,
(M4, Q) => 7,
_ => return None,
})
}
pub fn version_ec_from_symbol_number(s: u8) -> Option<(MicroVersion, MicroEcLevel)> {
use MicroEcLevel::*;
use MicroVersion::*;
Some(match s {
0 => (M1, Detection),
1 => (M2, L),
2 => (M2, M),
3 => (M3, L),
4 => (M3, M),
5 => (M4, L),
6 => (M4, M),
7 => (M4, Q),
_ => return None,
})
}
pub fn mode_indicator_bits(version: MicroVersion) -> usize {
version.number() as usize - 1
}
pub fn micro_mode_value(mode: &Mode) -> Option<u8> {
Some(match mode {
Mode::Numeric => 0,
Mode::Alphanumeric => 1,
Mode::Byte => 2,
Mode::Kanji => 3,
Mode::Eci(_) => return None,
})
}
pub fn mode_from_micro_value(v: u8) -> Option<Mode> {
Some(match v {
0 => Mode::Numeric,
1 => Mode::Alphanumeric,
2 => Mode::Byte,
3 => Mode::Kanji,
_ => return None,
})
}
pub fn char_count_bits(version: MicroVersion, mode: &Mode) -> Option<usize> {
use MicroVersion::*;
let v = match version {
M1 => 0,
M2 => 1,
M3 => 2,
M4 => 3,
};
let bits = match mode {
Mode::Numeric => [3, 4, 5, 6][v],
Mode::Alphanumeric => [0, 3, 4, 5][v],
Mode::Byte => [0, 0, 4, 5][v],
Mode::Kanji => [0, 0, 3, 4][v],
Mode::Eci(_) => return None,
};
if bits == 0 { None } else { Some(bits) }
}
pub fn terminator_bits(version: MicroVersion) -> usize {
2 * version.number() as usize + 1
}
pub fn format_bits(symbol_number: u8, mask: u8) -> u16 {
let data = (((symbol_number & 0x7) as u16) << 2) | (mask & 0x3) as u16;
let mut rem = data;
for _ in 0..10 {
rem = (rem << 1) ^ (((rem >> 9) & 1) * 0x537);
}
((data << 10) | (rem & 0x3FF)) ^ 0x4445
}
pub fn decode_format(raw: u16) -> Option<(u8, u8)> {
let mut best = None;
let mut best_dist = u32::MAX;
for s in 0u8..8 {
for m in 0u8..4 {
let candidate = format_bits(s, m);
let dist = (candidate ^ raw).count_ones();
if dist < best_dist {
best_dist = dist;
best = Some((s, m));
}
}
}
if best_dist <= 3 { best } else { None }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_codes_match_reference() {
const EXPECTED: [u16; 32] = [
0x4445, 0x4172, 0x4e2b, 0x4b1c, 0x55ae, 0x5099, 0x5fc0, 0x5af7, 0x6793, 0x62a4, 0x6dfd,
0x68ca, 0x7678, 0x734f, 0x7c16, 0x7921, 0x06de, 0x03e9, 0x0cb0, 0x0987, 0x1735, 0x1202,
0x1d5b, 0x186c, 0x2508, 0x203f, 0x2f66, 0x2a51, 0x34e3, 0x31d4, 0x3e8d, 0x3bba,
];
for (i, &want) in EXPECTED.iter().enumerate() {
let s = (i / 4) as u8;
let m = (i % 4) as u8;
assert_eq!(format_bits(s, m), want, "S={s} mask={m}");
assert_eq!(decode_format(want), Some((s, m)));
}
}
}