fix: 4 个 CRITICAL bug 修复

- C1: placement.rs 删除列偏移特殊处理 (col==6→5),place_bit 已自动跳过保留区
- C2: version.rs V5-H 纠错表 h_g1: 4→2 (总码字数 200→134)
- C3: mode.rs Kanji 编码删除冗余 if/else 重复分支
- C4: galois.rs div() 返回 Option<u8> 替代 panic!
This commit is contained in:
2026-06-17 08:58:29 +08:00
parent 7c13fb8f1c
commit feb5ae709f
7 changed files with 124 additions and 33 deletions
+9 -10
View File
@@ -65,19 +65,19 @@ pub fn mul(a: u8, b: u8) -> u8 {
exp_table()[log_a + log_b]
}
/// GF(2⁸) 除法:a / b
/// GF(2⁸) 除法:a / bb == 0 时返回 None
#[inline]
pub fn div(a: u8, b: u8) -> u8 {
pub fn div(a: u8, b: u8) -> Option<u8> {
if a == 0 {
return 0;
return Some(0);
}
if b == 0 {
panic!("GF(2⁸) 除以零");
return None;
}
let log_a = log_table()[a as usize] as usize;
let log_b = log_table()[b as usize] as usize;
let diff = (log_a + 255 - log_b) % 255;
exp_table()[diff]
Some(exp_table()[diff])
}
/// GF(2⁸) 幂运算:base^exp
@@ -134,7 +134,7 @@ mod tests {
#[test]
fn test_div_inverse() {
for a in 1..=255u8 {
let inv = div(1, a);
let inv = div(1, a).unwrap();
assert_eq!(mul(a, inv), 1, "逆元失败: {:02X} * {:02X} != 1", a, inv);
}
}
@@ -143,7 +143,7 @@ mod tests {
fn test_div_mul_consistency() {
for a in 1..=255u8 {
for b in (1..=255u8).step_by(17) {
let q = div(a, b);
let q = div(a, b).unwrap();
assert_eq!(
mul(q, b),
a,
@@ -167,9 +167,8 @@ mod tests {
}
#[test]
#[should_panic(expected = "除以零")]
fn test_div_by_zero_panics() {
div(1, 0);
fn test_div_by_zero_returns_none() {
assert_eq!(div(1, 0), None);
}
#[test]
+2 -2
View File
@@ -31,10 +31,10 @@ pub fn build_codewords(text: &str, version: Version, level: EcLevel) -> Vec<u8>
// 2. 终止符(最多 4 bit 0
let total_capacity = get_data_capacity(version, level) as usize * 8;
let terminator_len = 4usize.min(total_capacity.saturating_sub(bits.len()));
bits.extend(std::iter::repeat(false).take(terminator_len));
bits.extend(std::iter::repeat_n(false, terminator_len));
// 3. 补零到 8-bit 边界
while bits.len() % 8 != 0 {
while !bits.len().is_multiple_of(8) {
bits.push(false);
}
+7 -15
View File
@@ -158,22 +158,14 @@ fn unicode_to_shift_jis(c: char) -> Option<u16> {
// 简化映射: 用 Unicode 码位偏移做近似
// 真实转换需要完整映射表,这里做合理近似
let base = code - 0x4E00;
let hi = 0x81 + (base / 0xBC) as u32;
let lo = 0x40 + (base % 0xBC) as u32;
let hi = 0x81 + (base / 0xBC);
let lo = 0x40 + (base % 0xBC);
let sjis = ((hi << 8) | lo) as u16;
// 映射到 13-bit 码字
let val = if sjis <= 0x9FFC {
let h = (sjis >> 8) as u16;
let l = (sjis & 0xFF) as u16;
if h >= 0x81 && h <= 0x9F {
(h - 0x81) * 0xBC + (l - 0x40)
} else {
(h - 0xC1) * 0xBC + (l - 0x40)
}
} else {
let h = (sjis >> 8) as u16;
let l = (sjis & 0xFF) as u16;
if h >= 0x81 && h <= 0x9F {
// 映射到 13-bit 码字(内层 if/else 已区分两个 Shift-JIS 区间)
let val = {
let h = (sjis >> 8);
let l = (sjis & 0xFF);
if (0x81..=0x9F).contains(&h) {
(h - 0x81) * 0xBC + (l - 0x40)
} else {
(h - 0xC1) * 0xBC + (l - 0x40)
+2 -2
View File
@@ -11,8 +11,8 @@ pub const MASK_FNS: [MaskFn; 8] = [
|x, y| (x + y) % 3 == 0,
|x, y| ((y / 2) + (x / 3)) % 2 == 0,
|x, y| (x as u32 * y as u32) % 2 + (x as u32 * y as u32) % 3 == 0,
|x, y| ((x as u32 * y as u32) % 2 + (x as u32 * y as u32) % 3) % 2 == 0,
|x, y| ((x as u32 + y as u32) % 2 + (x as u32 * y as u32) % 3) % 2 == 0,
|x, y| ((x as u32 * y as u32) % 2 + (x as u32 * y as u32) % 3).is_multiple_of(2),
|x, y| ((x as u32 + y as u32) % 2 + (x as u32 * y as u32) % 3).is_multiple_of(2),
];
/// 应用掩码到矩阵的数据区域(跳过功能图案保留区域)
+2 -2
View File
@@ -16,8 +16,8 @@ pub fn place_data(matrix: &mut Matrix, codewords: &[u8]) {
let mut going_up = true;
while col >= 0 && bit_idx < bits.len() {
// 跳过垂直时序线 (col=6)
let actual_col = if col == 6 { 5 } else { col as usize };
// 列 6 是垂直时序图案,place_bit 会自动跳过保留区
let actual_col = col as usize;
if going_up {
for row in (0..size).rev() {
+2 -2
View File
@@ -1,5 +1,5 @@
use std::sync::OnceLock;
use serde::Serialize;
use std::sync::OnceLock;
/// 纠错级别
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
@@ -272,7 +272,7 @@ static VERSION_TABLE: [VersionRow; 40] = [
q_g2_data: 16,
h_total: 134,
h_ec: 22,
h_g1: 4,
h_g1: 2,
h_g1_data: 11,
h_g2: 2,
h_g2_data: 12,