fix: 格式信息 BCH 编码运算符优先级 — | 低于 ^ 导致 XOR 未覆盖数据位
Bug: ((data << 10) | (val & 0x3FF)) ^ 0x5412 被解析为 (data << 10) | ((val & 0x3FF) ^ 0x5412) 导致格式信息的 XOR mask 未覆盖前 5 个数据位,扫码器读到错误的 EC 级别和掩码编号
This commit is contained in:
@@ -153,8 +153,8 @@ pub fn encode_format_info(ec_bits: u8, mask: u8) -> u16 {
|
||||
}
|
||||
}
|
||||
|
||||
// XOR 掩码
|
||||
((data as u16) << 10) | (val & 0x3FF) ^ 0x5412
|
||||
// XOR 掩码 — 注意 ^ 优先级高于 |,必须加括号
|
||||
(((data as u16) << 10) | (val & 0x3FF)) ^ 0x5412
|
||||
}
|
||||
|
||||
/// 将格式信息写入矩阵(两处镜像放置)
|
||||
|
||||
@@ -1,6 +1,184 @@
|
||||
use qr_core::qr::{QrCode, QrConfig, VersionMode};
|
||||
use qr_core::version::EcLevel;
|
||||
|
||||
/// 诊断: 验证格式信息编码 + 解码是否正确
|
||||
#[test]
|
||||
fn test_format_info_roundtrip() {
|
||||
use qr_core::matrix::patterns::{encode_format_info, encode_version_info};
|
||||
|
||||
// M 级 (00) + mask 0 (000): data = 00000 = 0
|
||||
let fmt0 = encode_format_info(0b00, 0);
|
||||
// L 级 (01) + mask 3 (011): data = 01011 = 11
|
||||
let fmt1 = encode_format_info(0b01, 3);
|
||||
// H 级 (10) + mask 7 (111): data = 10111 = 23
|
||||
let fmt2 = encode_format_info(0b10, 7);
|
||||
|
||||
// 不同输入应产生不同输出
|
||||
assert_ne!(fmt0, fmt1);
|
||||
assert_ne!(fmt1, fmt2);
|
||||
assert_ne!(fmt0, fmt2);
|
||||
|
||||
// 应该在 15-bit 范围内
|
||||
assert!(fmt0 < 0x8000);
|
||||
assert!(fmt1 < 0x8000);
|
||||
|
||||
println!("格式信息 M+mask0: 0x{:04X}", fmt0);
|
||||
println!("格式信息 L+mask3: 0x{:04X}", fmt1);
|
||||
println!("格式信息 H+mask7: 0x{:04X}", fmt2);
|
||||
|
||||
// 版本信息编码
|
||||
let v7 = encode_version_info(7);
|
||||
println!("版本信息 v7: 0x{:06X}", v7);
|
||||
// 前 6 bit 是版本号
|
||||
assert_eq!((v7 >> 12) & 0x3F, 7);
|
||||
}
|
||||
|
||||
/// 诊断: 打印 QR 码的格式信息比特
|
||||
#[test]
|
||||
fn test_dump_format_info() {
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let m = qr.modules();
|
||||
|
||||
println!("\n=== 格式信息 15 bit (bit14 到 bit0) ===");
|
||||
println!("EC 级别: {:?}, 掩码: {}", qr.level, qr.mask);
|
||||
|
||||
// 格式信息位置 (按标准顺序 bit14→bit0)
|
||||
let coords = [
|
||||
(0, 8), (1, 8), (2, 8), (3, 8), (4, 8), (5, 8), (7, 8), (8, 8),
|
||||
(8, 7), (8, 5), (8, 4), (8, 3), (8, 2), (8, 1), (8, 0),
|
||||
];
|
||||
|
||||
let mut fmt_bits = 0u16;
|
||||
for (i, &(x, y)) in coords.iter().enumerate() {
|
||||
let bit = if m[y as usize][x as usize] { 1u16 } else { 0u16 };
|
||||
fmt_bits = (fmt_bits << 1) | bit;
|
||||
print!("{} ", if m[y as usize][x as usize] { '█' } else { '_' });
|
||||
}
|
||||
println!();
|
||||
println!("读取的格式信息 (原始, 含 XOR mask 0x5412): 0x{:04X}", fmt_bits);
|
||||
|
||||
// 去掉 XOR mask
|
||||
let unmasked = fmt_bits ^ 0x5412;
|
||||
println!("去 XOR mask 后: 0x{:04X}", unmasked);
|
||||
let ec_bits = (unmasked >> 13) & 0x03;
|
||||
let mask_bits = (unmasked >> 10) & 0x07;
|
||||
let bch = unmasked & 0x3FF;
|
||||
println!("EC bits: {:02b} 掩码 bits: {:03b} BCH: 0x{:03X}", ec_bits, mask_bits, bch);
|
||||
|
||||
// 期望值
|
||||
let expected = {
|
||||
use qr_core::matrix::patterns::encode_format_info;
|
||||
encode_format_info(qr.level.indicator_bits(), qr.mask)
|
||||
};
|
||||
println!("期望的格式信息: 0x{:04X}", expected);
|
||||
println!("匹配: {}", if fmt_bits == expected { "✅" } else { "❌ 不匹配!" });
|
||||
assert_eq!(fmt_bits, expected, "格式信息不匹配!");
|
||||
}
|
||||
#[test]
|
||||
fn test_finder_patterns_present() {
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let size = qr.size() as usize;
|
||||
let m = qr.modules();
|
||||
|
||||
// 三个定位图案: (0,0), (size-7,0), (0,size-7)
|
||||
let s = size as u8;
|
||||
let finders: [(u8, u8); 3] = [(0, 0), (s - 7, 0), (0, s - 7)];
|
||||
for (fx, fy) in finders {
|
||||
assert!(m[fy as usize][fx as usize], "定位({},{}): 左上角应为暗", fx, fy);
|
||||
assert!(m[fy as usize][(fx + 6) as usize], "定位({},{}): 右上角应为暗", fx, fy);
|
||||
assert!(m[(fy + 6) as usize][fx as usize], "定位({},{}): 左下角应为暗", fx, fy);
|
||||
// 内部 3×3 是暗色
|
||||
assert!(m[(fy + 2) as usize][(fx + 2) as usize], "定位({},{}): 中心3×3应为暗", fx, fy);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timing_pattern_alternates() {
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let m = qr.modules();
|
||||
let s = qr.size() as usize;
|
||||
|
||||
// 行6: 从列8到列s-8-1 应交替
|
||||
for x in (8..s - 8).step_by(2) {
|
||||
assert!(m[6][x], "时序 y=6 x={}: 偶数应为暗", x);
|
||||
if x + 1 < s - 8 {
|
||||
assert!(!m[6][x + 1], "时序 y=6 x={}: 奇数应为亮", x + 1);
|
||||
}
|
||||
}
|
||||
// 列6: 从行8到行s-8-1 应交替
|
||||
for y in (8..s - 8).step_by(2) {
|
||||
assert!(m[y][6], "时序 x=6 y={}: 偶数应为暗", y);
|
||||
if y + 1 < s - 8 {
|
||||
assert!(!m[y + 1][6], "时序 x=6 y={}: 奇数应为亮", y + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dark_module_present() {
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let m = qr.modules();
|
||||
let s = qr.size() as usize;
|
||||
// 暗模块总是位于 (8, size-8)
|
||||
assert!(m[s - 8][8], "暗模块 (8,{}) 缺失!", s - 8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_info_written() {
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let m = qr.modules();
|
||||
// 格式信息在定位图案旁,检查几个位置不是全亮
|
||||
assert!(m[8][0] || m[8][1] || m[8][2] || !m[8][0],
|
||||
"格式信息应已写入");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_svg_valid_structure() {
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let svg = qr.to_svg();
|
||||
// SVG 应有正确的结构
|
||||
assert!(svg.starts_with("<svg"), "SVG 应以 <svg 开头");
|
||||
assert!(svg.contains("rect"), "SVG 应包含 rect 元素");
|
||||
assert!(svg.contains("fill=\"black\""), "SVG 暗模块应是黑色");
|
||||
assert!(svg.ends_with("</svg>\n") || svg.ends_with("</svg>"),
|
||||
"SVG 应以 </svg> 结尾");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quiet_zone_is_white() {
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let m = qr.modules();
|
||||
let s = qr.size() as usize;
|
||||
// 左上角分隔符区域 (7,0..7) 和 (0..7,7) 应为白色
|
||||
for i in 0..8usize {
|
||||
assert!(!m[7][i], "定位分隔符 (7,{}) 应为白色", i);
|
||||
assert!(!m[i][7], "定位分隔符 ({},7) 应为白色", i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_qr_structure_dump() {
|
||||
// 打印矩阵到 stdout(用于调试)
|
||||
let qr = QrCode::encode("HELLO", QrConfig::default()).unwrap();
|
||||
let size = qr.size() as usize;
|
||||
let m = qr.modules();
|
||||
|
||||
println!("\n=== QR Matrix {}x{} v{} mask{} ===", size, size, qr.version.0, qr.mask);
|
||||
for y in 0..size {
|
||||
for x in 0..size {
|
||||
print!("{}", if m[y][x] { "##" } else { " " });
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
// 统计
|
||||
let dark: usize = m.iter().flatten().filter(|&&x| x).count();
|
||||
let total = size * size;
|
||||
println!("\n暗/总: {}/{} = {:.1}%", dark, total, dark as f64 / total as f64 * 100.0);
|
||||
println!("尺寸: {}×{}", size, size);
|
||||
println!("版本: {} 掩码: {} 纠错: {:?}", qr.version.0, qr.mask, qr.level);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_encode_simple_text() {
|
||||
let config = QrConfig::default();
|
||||
|
||||
Reference in New Issue
Block a user