const STRUCTURE: [(u16, u8, u8); 40] = [
(26, 10, 1),
(44, 16, 1),
(70, 26, 1),
(100, 18, 2),
(134, 24, 2),
(172, 16, 4),
(196, 18, 4),
(242, 22, 4),
(292, 22, 5),
(346, 26, 5),
(404, 30, 5),
(466, 22, 8),
(532, 22, 9),
(581, 24, 9),
(655, 24, 10),
(733, 28, 10),
(815, 28, 11),
(901, 26, 13),
(991, 26, 14),
(1085, 26, 16),
(1156, 26, 17),
(1258, 28, 17),
(1364, 28, 18),
(1474, 28, 20),
(1588, 28, 21),
(1706, 28, 23),
(1828, 28, 25),
(1921, 28, 26),
(2051, 28, 28),
(2185, 28, 29),
(2323, 28, 31),
(2465, 28, 33),
(2611, 28, 35),
(2761, 28, 37),
(2876, 28, 38),
(3034, 28, 40),
(3196, 28, 43),
(3362, 28, 45),
(3532, 28, 47),
(3706, 28, 49),
];
pub(super) const BYTE_MODE: u32 = 0b0100;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Plan {
pub(super) version: usize,
pub(super) data: usize,
pub(super) check: usize,
pub(super) blocks: usize,
}
impl Plan {
pub(super) fn smallest_for(len: usize) -> Option<Self> {
(1..=STRUCTURE.len()).find_map(|version| {
let plan = Self::at(version)?;
let needed = 4 + plan.count_bits() + len.checked_mul(8)?;
(needed <= plan.data.checked_mul(8)?).then_some(plan)
})
}
pub(super) fn at(version: usize) -> Option<Self> {
let &(total, check, blocks) = STRUCTURE.get(version.checked_sub(1)?)?;
let (check, blocks) = (usize::from(check), usize::from(blocks));
Some(Self {
version,
data: usize::from(total).checked_sub(check.checked_mul(blocks)?)?,
check,
blocks,
})
}
pub(super) fn count_bits(self) -> usize {
if self.version <= 9 { 8 } else { 16 }
}
pub(super) fn side(self) -> usize {
17 + 4 * self.version
}
pub(super) fn block_lengths(self) -> Vec<usize> {
let short = self.data.checked_div(self.blocks).unwrap_or(0);
let long = self.data.checked_rem(self.blocks).unwrap_or(0);
(0..self.blocks)
.map(|at| {
if at + long < self.blocks {
short
} else {
short + 1
}
})
.collect()
}
pub(super) fn alignment(self) -> Vec<usize> {
if self.version < 2 {
return Vec::new();
}
if self.version == 32 {
return vec![6, 34, 60, 86, 112, 138];
}
let count = self.version / 7 + 2;
let last = 4 * self.version + 10;
let span = last.saturating_sub(6);
let gaps = count.saturating_sub(1);
let step = 2 * span.div_ceil(2 * gaps);
std::iter::once(6)
.chain((0..gaps).map(|at| last - (gaps - 1 - at) * step))
.collect()
}
pub(super) fn version_bits(self) -> Option<u32> {
let version = u32::try_from(self.version).ok()?;
(self.version >= 7).then(|| bch(version, 0x1f25, 12))
}
}
pub(super) fn format_bits(mask: u32) -> u32 {
bch(mask & 0b111, 0x537, 10) ^ 0x5412
}
fn bch(value: u32, generator: u32, bits: u32) -> u32 {
let width = |of: u32| 32 - of.leading_zeros();
let mut rem = value << bits;
while width(rem) >= width(generator) {
rem ^= generator << (width(rem) - width(generator));
}
(value << bits) | rem
}