use super::reed_solomon;
use super::tables::{self, Ecc, MAX_VERSION, MIN_VERSION};
#[derive(Debug, Clone)]
pub struct Symbol {
pub version: usize,
pub ecc: Ecc,
pub mask: u8,
pub size: usize,
pub modules: Vec<Vec<bool>>,
}
const ALPHANUMERIC: &str = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
#[derive(Clone, Copy)]
enum Mode {
Numeric,
Alphanumeric,
Byte,
}
impl Mode {
fn of(text: &str) -> Self {
if !text.is_empty() && text.bytes().all(|b| b.is_ascii_digit()) {
Self::Numeric
} else if !text.is_empty() && text.chars().all(|c| ALPHANUMERIC.contains(c)) {
Self::Alphanumeric
} else {
Self::Byte
}
}
fn indicator(self) -> u32 {
match self {
Self::Numeric => 0b0001,
Self::Alphanumeric => 0b0010,
Self::Byte => 0b0100,
}
}
fn count_bits(self, version: usize) -> usize {
let band = if version <= 9 { 0 } else if version <= 26 { 1 } else { 2 };
match self {
Self::Numeric => [10, 12, 14][band],
Self::Alphanumeric => [9, 11, 13][band],
Self::Byte => [8, 16, 16][band],
}
}
}
struct Bits(Vec<bool>);
impl Bits {
fn push(&mut self, value: u32, len: usize) {
for i in (0..len).rev() {
self.0.push((value >> i) & 1 == 1);
}
}
}
fn payload(text: &str, mode: Mode) -> (Bits, usize) {
let mut bits = Bits(Vec::new());
match mode {
Mode::Numeric => {
let digits = text.as_bytes();
for chunk in digits.chunks(3) {
let value = chunk.iter().fold(0u32, |acc, d| acc * 10 + (d - b'0') as u32);
bits.push(value, chunk.len() * 3 + 1);
}
(bits, digits.len())
}
Mode::Alphanumeric => {
let codes: Vec<u32> = text.chars().map(|c| ALPHANUMERIC.find(c).unwrap_or(0) as u32).collect();
for pair in codes.chunks(2) {
match pair {
[a, b] => bits.push(a * 45 + b, 11),
[a] => bits.push(*a, 6),
_ => {}
}
}
(bits, codes.len())
}
Mode::Byte => {
for &byte in text.as_bytes() {
bits.push(byte as u32, 8);
}
(bits, text.len())
}
}
}
pub fn encode(text: &str, ecc: Ecc) -> Option<Symbol> {
let (version, codewords) = codewords(text, ecc)?;
Some(build(version, ecc, &codewords))
}
pub fn codewords(text: &str, ecc: Ecc) -> Option<(usize, Vec<u8>)> {
let mode = Mode::of(text);
let (body, count) = payload(text, mode);
let version = (MIN_VERSION..=MAX_VERSION).find(|&v| {
count < (1usize << mode.count_bits(v))
&& 4 + mode.count_bits(v) + body.0.len() <= tables::num_data_codewords(v, ecc) * 8
})?;
let capacity_bits = tables::num_data_codewords(version, ecc) * 8;
let mut bits = Bits(Vec::with_capacity(capacity_bits));
bits.push(mode.indicator(), 4);
bits.push(count as u32, mode.count_bits(version));
bits.0.extend(body.0);
let terminator = (capacity_bits - bits.0.len()).min(4);
bits.push(0, terminator);
let to_byte = (8 - bits.0.len() % 8) % 8;
bits.push(0, to_byte);
let mut data: Vec<u8> = bits.0.chunks(8).map(|b| b.iter().fold(0u8, |acc, &bit| (acc << 1) | bit as u8)).collect();
let mut pad = [0xEC, 0x11].into_iter().cycle();
while data.len() < capacity_bits / 8 {
data.push(pad.next().unwrap_or(0xEC));
}
Some((version, interleave_with_ecc(&data, version, ecc)))
}
fn interleave_with_ecc(data: &[u8], version: usize, ecc: Ecc) -> Vec<u8> {
let num_blocks = tables::num_blocks(ecc, version);
let block_ecc_len = tables::ecc_codewords_per_block(ecc, version);
let raw_codewords = tables::num_raw_data_modules(version) / 8;
let num_short_blocks = num_blocks - raw_codewords % num_blocks;
let short_block_len = raw_codewords / num_blocks;
let divisor = reed_solomon::divisor(block_ecc_len);
let mut blocks: Vec<Vec<u8>> = Vec::with_capacity(num_blocks);
let mut k = 0;
for i in 0..num_blocks {
let data_len = short_block_len - block_ecc_len + usize::from(i >= num_short_blocks);
let mut block = data[k..k + data_len].to_vec();
k += data_len;
let ecc_bytes = reed_solomon::remainder(&block, &divisor);
if i < num_short_blocks {
block.push(0); }
block.extend(ecc_bytes);
blocks.push(block);
}
let mut result = Vec::with_capacity(raw_codewords);
for i in 0..blocks[0].len() {
for (j, block) in blocks.iter().enumerate() {
if i != short_block_len - block_ecc_len || j >= num_short_blocks {
result.push(block[i]);
}
}
}
result
}
struct Grid {
size: usize,
modules: Vec<Vec<bool>>,
function: Vec<Vec<bool>>,
}
impl Grid {
fn set_function(&mut self, x: usize, y: usize, dark: bool) {
self.modules[y][x] = dark;
self.function[y][x] = true;
}
}
fn build(version: usize, ecc: Ecc, codewords: &[u8]) -> Symbol {
let size = tables::size(version);
let mut grid = Grid { size, modules: vec![vec![false; size]; size], function: vec![vec![false; size]; size] };
draw_function_patterns(&mut grid, version, ecc);
draw_codewords(&mut grid, codewords);
let mut best: Option<(u32, u8, Vec<Vec<bool>>)> = None;
for mask in 0..8u8 {
apply_mask(&mut grid, mask);
draw_format_bits(&mut grid, ecc, mask);
let score = super::penalty::score(&grid.modules);
if best.as_ref().is_none_or(|(s, ..)| score < *s) {
best = Some((score, mask, grid.modules.clone()));
}
apply_mask(&mut grid, mask); }
let (_, mask, modules) = best.expect("eight masks were tried");
Symbol { version, ecc, mask, size, modules }
}
fn draw_function_patterns(grid: &mut Grid, version: usize, ecc: Ecc) {
let size = grid.size;
for i in 0..size {
grid.set_function(6, i, i % 2 == 0);
grid.set_function(i, 6, i % 2 == 0);
}
for (cx, cy) in [(3, 3), (size - 4, 3), (3, size - 4)] {
for dy in -4i32..=4 {
for dx in -4i32..=4 {
let (x, y) = (cx as i32 + dx, cy as i32 + dy);
if (0..size as i32).contains(&x) && (0..size as i32).contains(&y) {
let dist = dx.abs().max(dy.abs());
grid.set_function(x as usize, y as usize, dist != 2 && dist != 4);
}
}
}
}
let positions = tables::alignment_positions(version);
let last = positions.len().saturating_sub(1);
for (i, &cx) in positions.iter().enumerate() {
for (j, &cy) in positions.iter().enumerate() {
let on_finder = (i == 0 && j == 0) || (i == 0 && j == last) || (i == last && j == 0);
if on_finder {
continue;
}
for dy in -2i32..=2 {
for dx in -2i32..=2 {
let dark = dx.abs().max(dy.abs()) != 1;
grid.set_function((cx as i32 + dx) as usize, (cy as i32 + dy) as usize, dark);
}
}
}
}
draw_format_bits(grid, ecc, 0); draw_version_bits(grid, version);
}
fn bit(value: u32, i: usize) -> bool {
(value >> i) & 1 == 1
}
fn draw_format_bits(grid: &mut Grid, ecc: Ecc, mask: u8) {
let data = (ecc.format_bits() << 3) | mask as u32;
let mut rem = data;
for _ in 0..10 {
rem = (rem << 1) ^ ((rem >> 9) * 0x537);
}
let bits = ((data << 10) | rem) ^ 0x5412;
let size = grid.size;
for i in 0..=5 {
grid.set_function(8, i, bit(bits, i));
}
grid.set_function(8, 7, bit(bits, 6));
grid.set_function(8, 8, bit(bits, 7));
grid.set_function(7, 8, bit(bits, 8));
for i in 9..15 {
grid.set_function(14 - i, 8, bit(bits, i));
}
for i in 0..8 {
grid.set_function(size - 1 - i, 8, bit(bits, i));
}
for i in 8..15 {
grid.set_function(8, size - 15 + i, bit(bits, i));
}
grid.set_function(8, size - 8, true);
}
fn draw_version_bits(grid: &mut Grid, version: usize) {
if version < 7 {
return;
}
let mut rem = version as u32;
for _ in 0..12 {
rem = (rem << 1) ^ ((rem >> 11) * 0x1F25);
}
let bits = ((version as u32) << 12) | rem;
for i in 0..18 {
let a = grid.size - 11 + i % 3;
let b = i / 3;
grid.set_function(a, b, bit(bits, i));
grid.set_function(b, a, bit(bits, i));
}
}
fn draw_codewords(grid: &mut Grid, codewords: &[u8]) {
let size = grid.size;
let total_bits = codewords.len() * 8;
let mut i = 0;
let mut right = size as i32 - 1;
while right >= 1 {
if right == 6 {
right = 5;
}
for vert in 0..size {
for j in 0..2 {
let x = (right - j) as usize;
let upward = (right + 1) & 2 == 0;
let y = if upward { size - 1 - vert } else { vert };
if !grid.function[y][x] && i < total_bits {
grid.modules[y][x] = (codewords[i >> 3] >> (7 - (i & 7))) & 1 == 1;
i += 1;
}
}
}
right -= 2;
}
}
fn apply_mask(grid: &mut Grid, mask: u8) {
for y in 0..grid.size {
for x in 0..grid.size {
let invert = match mask {
0 => (x + y) % 2 == 0,
1 => y % 2 == 0,
2 => x % 3 == 0,
3 => (x + y) % 3 == 0,
4 => (x / 3 + y / 2) % 2 == 0,
5 => x * y % 2 + x * y % 3 == 0,
6 => (x * y % 2 + x * y % 3) % 2 == 0,
_ => ((x + y) % 2 + x * y % 3) % 2 == 0,
};
if invert && !grid.function[y][x] {
grid.modules[y][x] ^= true;
}
}
}
}