diff --git a/core/src/version.rs b/core/src/version.rs index 726fb3c..7276efc 100644 --- a/core/src/version.rs +++ b/core/src/version.rs @@ -1 +1,295 @@ -// FIXME: 版本参数表 — Task 3 +use std::sync::OnceLock; + +/// 纠错级别 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EcLevel { + L, // 约 7% + M, // 约 15% + Q, // 约 25% + H, // 约 30% +} + +impl EcLevel { + /// 格式信息中使用的指示位 + pub fn indicator_bits(self) -> u8 { + match self { + EcLevel::L => 0b01, + EcLevel::M => 0b00, + EcLevel::Q => 0b11, + EcLevel::H => 0b10, + } + } +} + +/// 版本号 1~40 +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Version(pub u8); + +impl Version { + pub fn new(v: u8) -> Option { + if (1..=40).contains(&v) { Some(Version(v)) } else { None } + } + + /// QR 码边长(模块数) + pub fn size(self) -> u8 { + 17 + self.0 * 4 + } + + /// 对齐图案位置列表 + pub fn alignment_positions(self) -> &'static [u8] { + &ALIGNMENT_POSITIONS[self.0 as usize - 1] + } + + /// 获取该版本+级别的纠错信息 + pub fn ec_info(self, level: EcLevel) -> EcInfo { + let row = &VERSION_TABLE[self.0 as usize - 1]; + let (total, ec_per_block, g1_blocks, g1_data, g2_blocks, g2_data) = match level { + EcLevel::L => (row.l_total, row.l_ec, row.l_g1, row.l_g1_data, row.l_g2, row.l_g2_data), + EcLevel::M => (row.m_total, row.m_ec, row.m_g1, row.m_g1_data, row.m_g2, row.m_g2_data), + EcLevel::Q => (row.q_total, row.q_ec, row.q_g1, row.q_g1_data, row.q_g2, row.q_g2_data), + EcLevel::H => (row.h_total, row.h_ec, row.h_g1, row.h_g1_data, row.h_g2, row.h_g2_data), + }; + EcInfo { + total_codewords: total, + ec_per_block, + blocks: vec![ + BlockInfo { count: g1_blocks, data_codewords: g1_data }, + BlockInfo { count: g2_blocks, data_codewords: g2_data }, + ], + } + } +} + +pub struct EcInfo { + pub total_codewords: u16, + pub ec_per_block: u8, + pub blocks: Vec, +} + +pub struct BlockInfo { + pub count: u16, + pub data_codewords: u16, +} + +/// 单行版本数据 +struct VersionRow { + l_total: u16, l_ec: u8, l_g1: u16, l_g1_data: u16, l_g2: u16, l_g2_data: u16, + m_total: u16, m_ec: u8, m_g1: u16, m_g1_data: u16, m_g2: u16, m_g2_data: u16, + q_total: u16, q_ec: u8, q_g1: u16, q_g1_data: u16, q_g2: u16, q_g2_data: u16, + h_total: u16, h_ec: u8, h_g1: u16, h_g1_data: u16, h_g2: u16, h_g2_data: u16, +} + +/// ISO 18004 附录 I — 40 版本 × 4 纠错级别的容量表 +/// 格式: (总码字数, EC码字/块, 组1块数, 组1数据码字数, 组2块数, 组2数据码字数) +/// 当 g2_data == 0 时,表示仅有一组 +static VERSION_TABLE: [VersionRow; 40] = [ + // 版本 1 + VersionRow { l_total:26, l_ec:7, l_g1:1, l_g1_data:19, l_g2:0, l_g2_data:0, m_total:26, m_ec:10, m_g1:1, m_g1_data:16, m_g2:0, m_g2_data:0, q_total:26, q_ec:13, q_g1:1, q_g1_data:13, q_g2:0, q_g2_data:0, h_total:26, h_ec:17, h_g1:1, h_g1_data:9, h_g2:0, h_g2_data:0 }, + // 版本 2 + VersionRow { l_total:44, l_ec:10, l_g1:1, l_g1_data:34, l_g2:0, l_g2_data:0, m_total:44, m_ec:16, m_g1:1, m_g1_data:28, m_g2:0, m_g2_data:0, q_total:44, q_ec:22, q_g1:1, q_g1_data:22, q_g2:0, q_g2_data:0, h_total:44, h_ec:28, h_g1:1, h_g1_data:16, h_g2:0, h_g2_data:0 }, + // 版本 3 + VersionRow { l_total:70, l_ec:15, l_g1:1, l_g1_data:55, l_g2:0, l_g2_data:0, m_total:70, m_ec:26, m_g1:1, m_g1_data:44, m_g2:0, m_g2_data:0, q_total:70, q_ec:18, q_g1:2, q_g1_data:17, q_g2:0, q_g2_data:0, h_total:70, h_ec:22, h_g1:2, h_g1_data:13, h_g2:0, h_g2_data:0 }, + // 版本 4 + VersionRow { l_total:100, l_ec:20, l_g1:1, l_g1_data:80, l_g2:0, l_g2_data:0, m_total:100, m_ec:18, m_g1:2, m_g1_data:32, m_g2:0, m_g2_data:0, q_total:100, q_ec:26, q_g1:2, q_g1_data:24, q_g2:0, q_g2_data:0, h_total:100, h_ec:16, h_g1:4, h_g1_data:9, h_g2:0, h_g2_data:0 }, + // 版本 5 + VersionRow { l_total:134, l_ec:26, l_g1:1, l_g1_data:108, l_g2:0, l_g2_data:0, m_total:134, m_ec:24, m_g1:2, m_g1_data:43, m_g2:0, m_g2_data:0, q_total:134, q_ec:18, q_g1:2, q_g1_data:15, q_g2:2, q_g2_data:16, h_total:134, h_ec:22, h_g1:4, h_g1_data:11, h_g2:2, h_g2_data:12 }, + // 版本 6 + VersionRow { l_total:172, l_ec:18, l_g1:2, l_g1_data:68, l_g2:0, l_g2_data:0, m_total:172, m_ec:16, m_g1:4, m_g1_data:27, m_g2:0, m_g2_data:0, q_total:172, q_ec:24, q_g1:4, q_g1_data:19, q_g2:0, q_g2_data:0, h_total:172, h_ec:28, h_g1:4, h_g1_data:15, h_g2:0, h_g2_data:0 }, + // 版本 7 + VersionRow { l_total:196, l_ec:20, l_g1:2, l_g1_data:78, l_g2:0, l_g2_data:0, m_total:196, m_ec:18, m_g1:4, m_g1_data:31, m_g2:0, m_g2_data:0, q_total:196, q_ec:18, q_g1:2, q_g1_data:14, q_g2:4, q_g2_data:15, h_total:196, h_ec:26, h_g1:4, h_g1_data:13, h_g2:1, h_g2_data:14 }, + // 版本 8 + VersionRow { l_total:242, l_ec:24, l_g1:2, l_g1_data:97, l_g2:0, l_g2_data:0, m_total:242, m_ec:22, m_g1:2, m_g1_data:38, m_g2:2, m_g2_data:39, q_total:242, q_ec:22, q_g1:4, q_g1_data:18, q_g2:2, q_g2_data:19, h_total:242, h_ec:26, h_g1:4, h_g1_data:14, h_g2:2, h_g2_data:15 }, + // 版本 9 + VersionRow { l_total:292, l_ec:30, l_g1:2, l_g1_data:116, l_g2:0, l_g2_data:0, m_total:292, m_ec:22, m_g1:3, m_g1_data:36, m_g2:2, m_g2_data:37, q_total:292, q_ec:20, q_g1:4, q_g1_data:16, q_g2:4, q_g2_data:17, h_total:292, h_ec:24, h_g1:4, h_g1_data:12, h_g2:4, h_g2_data:13 }, + // 版本 10 + VersionRow { l_total:346, l_ec:18, l_g1:2, l_g1_data:68, l_g2:2, l_g2_data:69, m_total:346, m_ec:26, m_g1:4, m_g1_data:43, m_g2:1, m_g2_data:44, q_total:346, q_ec:24, q_g1:6, q_g1_data:19, q_g2:2, q_g2_data:20, h_total:346, h_ec:28, h_g1:6, h_g1_data:15, h_g2:2, h_g2_data:16 }, + // 版本 11 + VersionRow { l_total:404, l_ec:20, l_g1:4, l_g1_data:81, l_g2:0, l_g2_data:0, m_total:404, m_ec:30, m_g1:1, m_g1_data:50, m_g2:4, m_g2_data:51, q_total:404, q_ec:28, q_g1:4, q_g1_data:22, q_g2:4, q_g2_data:23, h_total:404, h_ec:24, h_g1:3, h_g1_data:12, h_g2:8, h_g2_data:13 }, + // 版本 12 + VersionRow { l_total:466, l_ec:24, l_g1:2, l_g1_data:92, l_g2:2, l_g2_data:93, m_total:466, m_ec:22, m_g1:6, m_g1_data:36, m_g2:2, m_g2_data:37, q_total:466, q_ec:26, q_g1:4, q_g1_data:20, q_g2:6, q_g2_data:21, h_total:466, h_ec:28, h_g1:7, h_g1_data:14, h_g2:4, h_g2_data:15 }, + // 版本 13 + VersionRow { l_total:532, l_ec:26, l_g1:4, l_g1_data:107, l_g2:0, l_g2_data:0, m_total:532, m_ec:22, m_g1:8, m_g1_data:37, m_g2:1, m_g2_data:38, q_total:532, q_ec:24, q_g1:8, q_g1_data:20, q_g2:4, q_g2_data:21, h_total:532, h_ec:22, h_g1:12, h_g1_data:11, h_g2:4, h_g2_data:12 }, + // 版本 14 + VersionRow { l_total:581, l_ec:30, l_g1:3, l_g1_data:115, l_g2:1, l_g2_data:116, m_total:581, m_ec:24, m_g1:4, m_g1_data:40, m_g2:5, m_g2_data:41, q_total:581, q_ec:20, q_g1:11, q_g1_data:16, q_g2:5, q_g2_data:17, h_total:581, h_ec:24, h_g1:11, h_g1_data:12, h_g2:5, h_g2_data:13 }, + // 版本 15 + VersionRow { l_total:655, l_ec:22, l_g1:5, l_g1_data:87, l_g2:1, l_g2_data:88, m_total:655, m_ec:24, m_g1:5, m_g1_data:41, m_g2:5, m_g2_data:42, q_total:655, q_ec:30, q_g1:5, q_g1_data:24, q_g2:7, q_g2_data:25, h_total:655, h_ec:24, h_g1:11, h_g1_data:12, h_g2:7, h_g2_data:13 }, + // 版本 16 + VersionRow { l_total:733, l_ec:24, l_g1:5, l_g1_data:98, l_g2:1, l_g2_data:99, m_total:733, m_ec:28, m_g1:7, m_g1_data:45, m_g2:3, m_g2_data:46, q_total:733, q_ec:24, q_g1:15, q_g1_data:19, q_g2:2, q_g2_data:20, h_total:733, h_ec:30, h_g1:3, h_g1_data:15, h_g2:13, h_g2_data:16 }, + // 版本 17 + VersionRow { l_total:815, l_ec:28, l_g1:1, l_g1_data:107, l_g2:5, l_g2_data:108, m_total:815, m_ec:28, m_g1:10, m_g1_data:46, m_g2:1, m_g2_data:47, q_total:815, q_ec:28, q_g1:1, q_g1_data:22, q_g2:15, q_g2_data:23, h_total:815, h_ec:28, h_g1:2, h_g1_data:14, h_g2:17, h_g2_data:15 }, + // 版本 18 + VersionRow { l_total:901, l_ec:30, l_g1:5, l_g1_data:120, l_g2:1, l_g2_data:121, m_total:901, m_ec:26, m_g1:9, m_g1_data:43, m_g2:4, m_g2_data:44, q_total:901, q_ec:28, q_g1:17, q_g1_data:22, q_g2:1, q_g2_data:23, h_total:901, h_ec:28, h_g1:2, h_g1_data:14, h_g2:19, h_g2_data:15 }, + // 版本 19 + VersionRow { l_total:991, l_ec:28, l_g1:3, l_g1_data:113, l_g2:4, l_g2_data:114, m_total:991, m_ec:26, m_g1:3, m_g1_data:44, m_g2:11, m_g2_data:45, q_total:991, q_ec:26, q_g1:17, q_g1_data:21, q_g2:4, q_g2_data:22, h_total:991, h_ec:26, h_g1:9, h_g1_data:13, h_g2:16, h_g2_data:14 }, + // 版本 20 + VersionRow { l_total:1085, l_ec:28, l_g1:3, l_g1_data:107, l_g2:5, l_g2_data:108, m_total:1085, m_ec:26, m_g1:3, m_g1_data:41, m_g2:13, m_g2_data:42, q_total:1085, q_ec:30, q_g1:15, q_g1_data:24, q_g2:5, q_g2_data:25, h_total:1085, h_ec:28, h_g1:15, h_g1_data:15, h_g2:10, h_g2_data:16 }, + // 版本 21 + VersionRow { l_total:1156, l_ec:28, l_g1:4, l_g1_data:116, l_g2:4, l_g2_data:117, m_total:1156, m_ec:26, m_g1:17, m_g1_data:42, m_g2:0, m_g2_data:0, q_total:1156, q_ec:28, q_g1:17, q_g1_data:22, q_g2:6, q_g2_data:23, h_total:1156, h_ec:30, h_g1:19, h_g1_data:16, h_g2:6, h_g2_data:17 }, + // 版本 22 + VersionRow { l_total:1258, l_ec:28, l_g1:2, l_g1_data:111, l_g2:7, l_g2_data:112, m_total:1258, m_ec:28, m_g1:17, m_g1_data:46, m_g2:0, m_g2_data:0, q_total:1258, q_ec:30, q_g1:7, q_g1_data:24, q_g2:16, q_g2_data:25, h_total:1258, h_ec:24, h_g1:34, h_g1_data:13, h_g2:0, h_g2_data:0 }, + // 版本 23 + VersionRow { l_total:1364, l_ec:30, l_g1:4, l_g1_data:121, l_g2:5, l_g2_data:122, m_total:1364, m_ec:28, m_g1:4, m_g1_data:47, m_g2:14, m_g2_data:48, q_total:1364, q_ec:30, q_g1:11, q_g1_data:24, q_g2:14, q_g2_data:25, h_total:1364, h_ec:30, h_g1:16, h_g1_data:15, h_g2:14, h_g2_data:16 }, + // 版本 24 + VersionRow { l_total:1474, l_ec:30, l_g1:6, l_g1_data:117, l_g2:4, l_g2_data:118, m_total:1474, m_ec:28, m_g1:6, m_g1_data:45, m_g2:14, m_g2_data:46, q_total:1474, q_ec:30, q_g1:11, q_g1_data:24, q_g2:16, q_g2_data:25, h_total:1474, h_ec:30, h_g1:30, h_g1_data:16, h_g2:2, h_g2_data:17 }, + // 版本 25 + VersionRow { l_total:1588, l_ec:26, l_g1:8, l_g1_data:106, l_g2:4, l_g2_data:107, m_total:1588, m_ec:28, m_g1:8, m_g1_data:47, m_g2:13, m_g2_data:48, q_total:1588, q_ec:30, q_g1:7, q_g1_data:24, q_g2:22, q_g2_data:25, h_total:1588, h_ec:30, h_g1:22, h_g1_data:15, h_g2:13, h_g2_data:16 }, + // 版本 26 + VersionRow { l_total:1706, l_ec:28, l_g1:10, l_g1_data:114, l_g2:2, l_g2_data:115, m_total:1706, m_ec:28, m_g1:19, m_g1_data:46, m_g2:4, m_g2_data:47, q_total:1706, q_ec:28, q_g1:28, q_g1_data:22, q_g2:6, q_g2_data:23, h_total:1706, h_ec:30, h_g1:33, h_g1_data:16, h_g2:4, h_g2_data:17 }, + // 版本 27 + VersionRow { l_total:1828, l_ec:30, l_g1:8, l_g1_data:122, l_g2:4, l_g2_data:123, m_total:1828, m_ec:28, m_g1:22, m_g1_data:45, m_g2:3, m_g2_data:46, q_total:1828, q_ec:30, q_g1:8, q_g1_data:23, q_g2:26, q_g2_data:24, h_total:1828, h_ec:30, h_g1:12, h_g1_data:15, h_g2:28, h_g2_data:16 }, + // 版本 28 + VersionRow { l_total:1921, l_ec:30, l_g1:3, l_g1_data:117, l_g2:10, l_g2_data:118, m_total:1921, m_ec:28, m_g1:3, m_g1_data:45, m_g2:23, m_g2_data:46, q_total:1921, q_ec:30, q_g1:4, q_g1_data:24, q_g2:31, q_g2_data:25, h_total:1921, h_ec:30, h_g1:11, h_g1_data:15, h_g2:31, h_g2_data:16 }, + // 版本 29 + VersionRow { l_total:2051, l_ec:30, l_g1:7, l_g1_data:116, l_g2:7, l_g2_data:117, m_total:2051, m_ec:28, m_g1:21, m_g1_data:45, m_g2:7, m_g2_data:46, q_total:2051, q_ec:30, q_g1:1, q_g1_data:23, q_g2:37, q_g2_data:24, h_total:2051, h_ec:30, h_g1:19, h_g1_data:15, h_g2:26, h_g2_data:16 }, + // 版本 30 + VersionRow { l_total:2185, l_ec:30, l_g1:5, l_g1_data:115, l_g2:10, l_g2_data:116, m_total:2185, m_ec:28, m_g1:19, m_g1_data:47, m_g2:10, m_g2_data:48, q_total:2185, q_ec:30, q_g1:15, q_g1_data:24, q_g2:25, q_g2_data:25, h_total:2185, h_ec:30, h_g1:23, h_g1_data:15, h_g2:25, h_g2_data:16 }, + // 版本 31 + VersionRow { l_total:2323, l_ec:30, l_g1:13, l_g1_data:115, l_g2:3, l_g2_data:116, m_total:2323, m_ec:28, m_g1:2, m_g1_data:46, m_g2:29, m_g2_data:47, q_total:2323, q_ec:30, q_g1:42, q_g1_data:24, q_g2:1, q_g2_data:25, h_total:2323, h_ec:30, h_g1:23, h_g1_data:15, h_g2:28, h_g2_data:16 }, + // 版本 32 + VersionRow { l_total:2465, l_ec:30, l_g1:17, l_g1_data:115, l_g2:0, l_g2_data:0, m_total:2465, m_ec:28, m_g1:10, m_g1_data:46, m_g2:23, m_g2_data:47, q_total:2465, q_ec:30, q_g1:10, q_g1_data:24, q_g2:35, q_g2_data:25, h_total:2465, h_ec:30, h_g1:19, h_g1_data:15, h_g2:35, h_g2_data:16 }, + // 版本 33 + VersionRow { l_total:2611, l_ec:30, l_g1:17, l_g1_data:115, l_g2:1, l_g2_data:116, m_total:2611, m_ec:28, m_g1:14, m_g1_data:46, m_g2:21, m_g2_data:47, q_total:2611, q_ec:30, q_g1:29, q_g1_data:24, q_g2:19, q_g2_data:25, h_total:2611, h_ec:30, h_g1:11, h_g1_data:15, h_g2:46, h_g2_data:16 }, + // 版本 34 + VersionRow { l_total:2761, l_ec:30, l_g1:13, l_g1_data:115, l_g2:6, l_g2_data:116, m_total:2761, m_ec:28, m_g1:14, m_g1_data:46, m_g2:23, m_g2_data:47, q_total:2761, q_ec:30, q_g1:44, q_g1_data:24, q_g2:7, q_g2_data:25, h_total:2761, h_ec:30, h_g1:59, h_g1_data:16, h_g2:1, h_g2_data:17 }, + // 版本 35 + VersionRow { l_total:2876, l_ec:30, l_g1:12, l_g1_data:121, l_g2:7, l_g2_data:122, m_total:2876, m_ec:28, m_g1:12, m_g1_data:47, m_g2:26, m_g2_data:48, q_total:2876, q_ec:30, q_g1:39, q_g1_data:24, q_g2:14, q_g2_data:25, h_total:2876, h_ec:30, h_g1:22, h_g1_data:15, h_g2:41, h_g2_data:16 }, + // 版本 36 + VersionRow { l_total:3034, l_ec:30, l_g1:6, l_g1_data:121, l_g2:14, l_g2_data:122, m_total:3034, m_ec:28, m_g1:6, m_g1_data:47, m_g2:34, m_g2_data:48, q_total:3034, q_ec:30, q_g1:46, q_g1_data:24, q_g2:10, q_g2_data:25, h_total:3034, h_ec:30, h_g1:2, h_g1_data:15, h_g2:64, h_g2_data:16 }, + // 版本 37 + VersionRow { l_total:3196, l_ec:30, l_g1:17, l_g1_data:122, l_g2:4, l_g2_data:123, m_total:3196, m_ec:28, m_g1:29, m_g1_data:46, m_g2:14, m_g2_data:47, q_total:3196, q_ec:30, q_g1:49, q_g1_data:24, q_g2:10, q_g2_data:25, h_total:3196, h_ec:30, h_g1:24, h_g1_data:15, h_g2:46, h_g2_data:16 }, + // 版本 38 + VersionRow { l_total:3362, l_ec:30, l_g1:4, l_g1_data:122, l_g2:18, l_g2_data:123, m_total:3362, m_ec:28, m_g1:13, m_g1_data:46, m_g2:32, m_g2_data:47, q_total:3362, q_ec:30, q_g1:48, q_g1_data:24, q_g2:14, q_g2_data:25, h_total:3362, h_ec:30, h_g1:42, h_g1_data:15, h_g2:32, h_g2_data:16 }, + // 版本 39 + VersionRow { l_total:3532, l_ec:30, l_g1:20, l_g1_data:117, l_g2:4, l_g2_data:118, m_total:3532, m_ec:28, m_g1:40, m_g1_data:47, m_g2:7, m_g2_data:48, q_total:3532, q_ec:30, q_g1:43, q_g1_data:24, q_g2:22, q_g2_data:25, h_total:3532, h_ec:30, h_g1:10, h_g1_data:15, h_g2:67, h_g2_data:16 }, + // 版本 40 + VersionRow { l_total:3706, l_ec:30, l_g1:19, l_g1_data:118, l_g2:6, l_g2_data:119, m_total:3706, m_ec:28, m_g1:18, m_g1_data:47, m_g2:31, m_g2_data:48, q_total:3706, q_ec:30, q_g1:34, q_g1_data:24, q_g2:34, q_g2_data:25, h_total:3706, h_ec:30, h_g1:20, h_g1_data:15, h_g2:61, h_g2_data:16 }, +]; + +// 对齐图案中心位置表(每个版本的坐标数组) +static ALIGNMENT_POSITIONS: [&[u8]; 40] = [ + &[], // v1 (无对齐图案) + &[6, 18], // v2 + &[6, 22], // v3 + &[6, 26], // v4 + &[6, 30], // v5 + &[6, 34], // v6 + &[6, 22, 38], // v7 + &[6, 24, 42], // v8 + &[6, 26, 46], // v9 + &[6, 28, 50], // v10 + &[6, 30, 54], // v11 + &[6, 32, 58], // v12 + &[6, 34, 62], // v13 + &[6, 26, 46, 66], // v14 + &[6, 26, 48, 70], // v15 + &[6, 26, 50, 74], // v16 + &[6, 30, 54, 78], // v17 + &[6, 30, 56, 82], // v18 + &[6, 30, 58, 86], // v19 + &[6, 34, 62, 90], // v20 + &[6, 28, 50, 72, 94], // v21 + &[6, 26, 50, 74, 98], // v22 + &[6, 30, 54, 78, 102], // v23 + &[6, 28, 54, 80, 106], // v24 + &[6, 32, 58, 84, 110], // v25 + &[6, 30, 58, 86, 114], // v26 + &[6, 34, 62, 90, 118], // v27 + &[6, 26, 50, 74, 98, 122], // v28 + &[6, 30, 54, 78, 102, 126], // v29 + &[6, 26, 52, 78, 104, 130], // v30 + &[6, 30, 56, 82, 108, 134], // v31 + &[6, 34, 60, 86, 112, 138], // v32 + &[6, 30, 58, 86, 114, 142], // v33 + &[6, 34, 62, 90, 118, 146], // v34 + &[6, 30, 54, 78, 102, 126, 150], // v35 + &[6, 24, 50, 76, 102, 128, 154], // v36 + &[6, 28, 54, 80, 106, 132, 158], // v37 + &[6, 32, 58, 84, 110, 136, 162], // v38 + &[6, 26, 54, 82, 110, 138, 166], // v39 + &[6, 30, 58, 86, 114, 142, 170], // v40 +]; + +/// 获取某版本+级别的数据码字容量 +fn init_capacity_table() -> [[u16; 4]; 40] { + let mut table = [[0u16; 4]; 40]; + for v in 1..=40u8 { + let ver = Version(v); + let li: [EcLevel; 4] = [EcLevel::L, EcLevel::M, EcLevel::Q, EcLevel::H]; + for (idx, &level) in li.iter().enumerate() { + let info = ver.ec_info(level); + let total_data: u16 = info.blocks.iter() + .map(|b| b.count * b.data_codewords) + .sum(); + table[v as usize - 1][idx] = total_data; + } + } + table +} + +pub fn get_data_capacity(version: Version, level: EcLevel) -> u16 { + static CAPACITY: OnceLock<[[u16; 4]; 40]> = OnceLock::new(); + let cap = CAPACITY.get_or_init(init_capacity_table); + cap[version.0 as usize - 1][level as usize] +} + +/// 自动选择最小版本:返回能容纳 data_bits 比特的最小版本 +pub fn pick_version(data_bits: u16, level: EcLevel) -> Option { + for v in 1..=40 { + let cap_bits = get_data_capacity(Version(v), level) * 8; + if cap_bits >= data_bits { + return Some(Version(v)); + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_version_size() { + assert_eq!(Version(1).size(), 21); + assert_eq!(Version(40).size(), 177); + assert_eq!(Version(2).size(), 25); + } + + #[test] + fn test_version_new_out_of_range() { + assert!(Version::new(0).is_none()); + assert!(Version::new(41).is_none()); + } + + #[test] + fn test_pick_version_l() { + // Version 1 L 级: 19 数据码字 = 152 bits + let v = pick_version(152, EcLevel::L); + assert_eq!(v, Some(Version(1))); + } + + #[test] + fn test_pick_version_none() { + // 超过 version 40 容量 + let v = pick_version(30000, EcLevel::H); + assert!(v.is_none()); + } + + #[test] + fn test_ec_info_blocks() { + let info = Version(1).ec_info(EcLevel::M); + // Version 1 M: 1 block × 16 data codewords, 10 ec per block + assert_eq!(info.total_codewords, 26); + assert_eq!(info.ec_per_block, 10); + assert_eq!(info.blocks.len(), 2); + assert_eq!(info.blocks[0].count, 1); + assert_eq!(info.blocks[0].data_codewords, 16); + } + + #[test] + fn test_indicator_bits() { + assert_eq!(EcLevel::L.indicator_bits(), 0b01); + assert_eq!(EcLevel::M.indicator_bits(), 0b00); + assert_eq!(EcLevel::Q.indicator_bits(), 0b11); + assert_eq!(EcLevel::H.indicator_bits(), 0b10); + } +}