use super::bch::{encode_format, encode_version, EcLevel};
use super::mask::{apply_mask, score};
use super::matrix::Matrix;
use super::reed_solomon;
use super::tables::{byte_mode_count_bits, byte_mode_max_capacity, ec_blocks, Version};
pub fn find_min_version(data_len: usize, level: EcLevel) -> Result<Version, &'static str> {
for v in 1u8..=40 {
let version = Version::new(v);
if data_len <= byte_mode_max_capacity(version, level) {
return Ok(version);
}
}
Err("data too large for QR (max version 40)")
}
fn build_data_codewords(data: &[u8], version: Version, level: EcLevel) -> Vec<u8> {
let total_data_bytes = ec_blocks(version, level).total_data_codewords() as usize;
let count_bits = byte_mode_count_bits(version);
let mut bw = BitWriter::new();
bw.write_bits(0b0100, 4); bw.write_bits(data.len() as u32, count_bits);
for &b in data {
bw.write_bits(b as u32, 8);
}
let total_bits = total_data_bytes * 8;
let remaining = total_bits.saturating_sub(bw.len());
let term_bits = remaining.min(4);
bw.write_bits(0, term_bits);
while bw.len() % 8 != 0 {
bw.write_bits(0, 1);
}
let mut toggle = false;
while bw.byte_len() < total_data_bytes {
bw.write_bits(if toggle { 0x11 } else { 0xEC }, 8);
toggle = !toggle;
}
bw.into_bytes()
}
fn build_blocks(
codewords: &[u8],
version: Version,
level: EcLevel,
) -> (Vec<Vec<u8>>, Vec<Vec<u8>>) {
let info = ec_blocks(version, level);
let mut data_blocks: Vec<Vec<u8>> = Vec::new();
let mut ec_block_vecs: Vec<Vec<u8>> = Vec::new();
let mut cursor = 0usize;
for &(n, d) in [Some(info.group1), info.group2].iter().flatten() {
for _ in 0..n {
let block = codewords[cursor..cursor + d as usize].to_vec();
cursor += d as usize;
let ec = reed_solomon::encode(&block, info.ec_per_block as usize);
data_blocks.push(block);
ec_block_vecs.push(ec);
}
}
debug_assert_eq!(cursor, codewords.len(), "数据 codeword 没用完");
(data_blocks, ec_block_vecs)
}
fn interleave_blocks(data_blocks: &[Vec<u8>], ec_blocks_vec: &[Vec<u8>]) -> Vec<u8> {
let mut out = Vec::new();
let max_data_len = data_blocks.iter().map(|b| b.len()).max().unwrap_or(0);
for i in 0..max_data_len {
for blk in data_blocks {
if i < blk.len() {
out.push(blk[i]);
}
}
}
let max_ec_len = ec_blocks_vec.iter().map(|b| b.len()).max().unwrap_or(0);
for i in 0..max_ec_len {
for blk in ec_blocks_vec {
if i < blk.len() {
out.push(blk[i]);
}
}
}
out
}
pub fn remainder_bits(version: Version) -> usize {
match version.0 {
1 => 0,
2..=6 => 7,
7..=13 => 0,
14..=20 => 3,
21..=27 => 4,
28..=34 => 3,
35..=40 => 0,
_ => unreachable!(),
}
}
pub fn write_data_zigzag(matrix: &mut Matrix, bits: &[bool]) {
let n = matrix.size;
let mut bit_iter = bits.iter();
let mut upward = true; let mut right = (n as i32) - 1;
while right > 0 {
if right == 6 {
right -= 1;
}
for step in 0..n {
let y = if upward { n - 1 - step } else { step };
for j in 0..2 {
let x = (right - j as i32) as usize;
if !matrix.is_reserved(y, x) {
if let Some(&b) = bit_iter.next() {
matrix.set_data(y, x, b);
}
}
}
}
upward = !upward;
right -= 2;
}
}
pub fn write_format_info_bits(matrix: &mut Matrix, fmt: u32) {
let n = matrix.size;
let get_bit = |k: u32| ((fmt >> k) & 1) == 1;
for i in 0u32..6 {
matrix.set_reserved_bit(i as usize, 8, get_bit(i));
}
matrix.set_reserved_bit(7, 8, get_bit(6));
matrix.set_reserved_bit(8, 8, get_bit(7));
matrix.set_reserved_bit(8, 7, get_bit(8));
for i in 9u32..15 {
let col = (14 - i) as usize;
matrix.set_reserved_bit(8, col, get_bit(i));
}
for i in 0u32..8 {
matrix.set_reserved_bit(8, n - 1 - i as usize, get_bit(i));
}
for i in 8u32..15 {
let row = n - 15 + i as usize;
matrix.set_reserved_bit(row, 8, get_bit(i));
}
}
pub fn write_version_info_bits(matrix: &mut Matrix, version: Version) {
if version.0 < 7 {
return;
}
let bits = encode_version(version.0);
let n = matrix.size;
let get_bit = |k: u32| ((bits >> k) & 1) == 1;
for i in 0u32..18 {
let b = get_bit(i);
let a = n - 11 + (i % 3) as usize;
let b_idx = (i / 3) as usize;
matrix.set_reserved_bit(a, b_idx, b); matrix.set_reserved_bit(b_idx, a, b); }
}
fn select_mask_and_write(matrix: &mut Matrix, level: EcLevel) -> u8 {
let mut best = (0u8, u32::MAX);
for m in 0u8..8 {
apply_mask(matrix, m);
write_format_info_bits(matrix, encode_format(level, m));
let s = score(matrix);
if s < best.1 {
best = (m, s);
}
apply_mask(matrix, m);
}
apply_mask(matrix, best.0);
write_format_info_bits(matrix, encode_format(level, best.0));
best.0
}
pub fn encode(data: &[u8], level: EcLevel) -> Result<(Matrix, Version, u8), &'static str> {
let version = find_min_version(data.len(), level)?;
let codewords = build_data_codewords(data, version, level);
let (data_blocks, ec_block_vecs) = build_blocks(&codewords, version, level);
let interleaved = interleave_blocks(&data_blocks, &ec_block_vecs);
let mut bits: Vec<bool> = Vec::with_capacity(interleaved.len() * 8 + 7);
for byte in interleaved {
for i in (0..8).rev() {
bits.push((byte >> i) & 1 == 1);
}
}
bits.extend(std::iter::repeat(false).take(remainder_bits(version)));
let mut matrix = Matrix::new(version);
write_data_zigzag(&mut matrix, &bits);
if version.0 >= 7 {
write_version_info_bits(&mut matrix, version);
}
let mask = select_mask_and_write(&mut matrix, level);
Ok((matrix, version, mask))
}
#[allow(dead_code)]
pub fn encode_at_version(
data: &[u8],
level: EcLevel,
version: Version,
) -> Result<(Matrix, u8), &'static str> {
if data.len() > byte_mode_max_capacity(version, level) {
return Err("data too large for given version");
}
let codewords = build_data_codewords(data, version, level);
let (data_blocks, ec_block_vecs) = build_blocks(&codewords, version, level);
let interleaved = interleave_blocks(&data_blocks, &ec_block_vecs);
let mut bits: Vec<bool> = Vec::with_capacity(interleaved.len() * 8 + 7);
for byte in interleaved {
for i in (0..8).rev() {
bits.push((byte >> i) & 1 == 1);
}
}
bits.extend(std::iter::repeat(false).take(remainder_bits(version)));
let mut matrix = Matrix::new(version);
write_data_zigzag(&mut matrix, &bits);
if version.0 >= 7 {
write_version_info_bits(&mut matrix, version);
}
let mask = select_mask_and_write(&mut matrix, level);
Ok((matrix, mask))
}
struct BitWriter {
buf: Vec<u8>,
bit_pos: usize, }
impl BitWriter {
fn new() -> Self {
Self {
buf: Vec::new(),
bit_pos: 0,
}
}
fn write_bits(&mut self, value: u32, n_bits: usize) {
for i in (0..n_bits).rev() {
let bit = (value >> i) & 1 == 1;
self.push_bit(bit);
}
}
fn push_bit(&mut self, bit: bool) {
let byte_idx = self.bit_pos / 8;
let bit_offset = 7 - (self.bit_pos % 8);
if byte_idx == self.buf.len() {
self.buf.push(0);
}
if bit {
self.buf[byte_idx] |= 1 << bit_offset;
}
self.bit_pos += 1;
}
fn len(&self) -> usize {
self.bit_pos
}
fn byte_len(&self) -> usize {
(self.bit_pos + 7) / 8
}
fn into_bytes(self) -> Vec<u8> {
self.buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn find_version_byte_mode() {
assert_eq!(find_min_version(17, EcLevel::L).unwrap().0, 1);
assert_eq!(find_min_version(18, EcLevel::L).unwrap().0, 2);
assert_eq!(find_min_version(32, EcLevel::L).unwrap().0, 2);
assert_eq!(find_min_version(33, EcLevel::L).unwrap().0, 3);
assert!(find_min_version(3000, EcLevel::H).is_err());
}
#[test]
fn encode_hello_world_v1q() {
let data = b"HELLO WORLD"; let (matrix, version, mask) = encode(data, EcLevel::Q).unwrap();
assert_eq!(version.0, 1);
assert!(mask < 8);
let bits = read_format_info_top_left(&matrix);
let (lvl, m, dist) = crate::bch::decode_format(bits as u32).unwrap();
assert_eq!(lvl, EcLevel::Q);
assert_eq!(m, mask);
assert_eq!(dist, 0);
}
fn read_format_info_top_left(matrix: &Matrix) -> u32 {
let mut bits = 0u32;
let set_if = |bit_idx: u32, on: bool| if on { 1u32 << bit_idx } else { 0u32 };
for i in 0u32..6 {
bits |= set_if(i, matrix.get(i as usize, 8));
}
bits |= set_if(6, matrix.get(7, 8));
bits |= set_if(7, matrix.get(8, 8));
bits |= set_if(8, matrix.get(8, 7));
for i in 9u32..15 {
bits |= set_if(i, matrix.get(8, (14 - i) as usize));
}
bits
}
#[test]
fn build_blocks_v1m_thonky() {
let codewords = vec![
0x10, 0x20, 0x0C, 0x56, 0x61, 0x80, 0xEC, 0x11, 0xEC, 0x11, 0xEC, 0x11, 0xEC, 0x11,
0xEC, 0x11,
];
let (data_blocks, ec_blocks_vec) =
build_blocks(&codewords, Version::new(1), EcLevel::M);
assert_eq!(data_blocks.len(), 1);
assert_eq!(data_blocks[0], codewords);
assert_eq!(ec_blocks_vec.len(), 1);
assert_eq!(
ec_blocks_vec[0],
vec![0xA5, 0x24, 0xD4, 0xC1, 0xED, 0x36, 0xC7, 0x87, 0x2C, 0x55]
);
}
#[test]
fn smoke_all_versions() {
for v in 1..=40u8 {
for level in [EcLevel::L, EcLevel::M, EcLevel::Q, EcLevel::H] {
let version = Version::new(v);
let _ = encode_at_version(b"x", level, version).unwrap();
}
}
}
}