Skip to main content

zenith_foundation/
huffman.rs

1//! HPACK/QPACK Huffman 编解码器 (RFC 7541 Appendix B)
2//!
3//! 全 workspace 唯一实现(zenith-http2 / zenith-http3 统一复用,禁止重复造轮子)。
4//! 提供完整的 Huffman 编码与解码能力:
5//! - 编码:将字面值字节流编码为 Huffman 压缩位流
6//! - 解码:将 Huffman 压缩位流还原为原始字节
7//!
8//! # 安全保证
9//! - 解码器对 EOS (symbol 256) 严格拒绝(RFC 7541 §5.2)
10//! - 解码器对超长填充(>7 位尾随 1)拒绝
11//! - 所有位操作使用 checked 算术,溢出 fail-closed
12//! - 最大输出长度限制,防止解压炸弹
13//!
14//! # 性能优化
15//! - 编码器:u64 位累加器 + 批量字节刷新
16//! - 解码器:**8-bit 前缀解码表** O(1) 快速路径覆盖 ≤8 位码(占 HTTP 头部字符 95%+),
17//!   仅 >8 位码回退到分组二分查找
18//! - 回退解码器:预计算码长分组查找表(OnceLock 初始化一次),每码长内二分查找
19//! - 位读取:u64 批量读取 + 移位掩码,避免逐位循环
20
21use std::sync::OnceLock;
22
23// ---------------------------------------------------------------------------
24// RFC 7541 Appendix B - Huffman 编码表 (257 entries, symbol 0-255 + EOS=256)
25// 每个条目: (code_right_aligned, bit_length)
26// ---------------------------------------------------------------------------
27
28/// Huffman 编码表:`HUFFMAN_TABLE[symbol] = (code, length)`
29///
30/// code 为右对齐(最低有效位对齐),length 为位数。
31/// symbol 0-255 对应字节值,symbol 256 为 EOS(End of String)。
32///
33/// 这是全 workspace 唯一的 RFC 7541 Appendix B 转录源(zenith-http2 / zenith-http3
34/// 共用),任何对码表的核对只需针对本常量。
35pub const HUFFMAN_TABLE: [(u32, u8); 257] = [
36    (0x1ff8, 13), (0x7fffd8, 23), (0xfffffe2, 28), (0xfffffe3, 28),
37    (0xfffffe4, 28), (0xfffffe5, 28), (0xfffffe6, 28), (0xfffffe7, 28),
38    (0xfffffe8, 28), (0xffffea, 24), (0x3ffffffc, 30), (0xfffffe9, 28),
39    (0xfffffea, 28), (0x3ffffffd, 30), (0xfffffeb, 28), (0xfffffec, 28),
40    (0xfffffed, 28), (0xfffffee, 28), (0xfffffef, 28), (0xffffff0, 28),
41    (0xffffff1, 28), (0xffffff2, 28), (0x3ffffffe, 30), (0xffffff3, 28),
42    (0xffffff4, 28), (0xffffff5, 28), (0xffffff6, 28), (0xffffff7, 28),
43    (0xffffff8, 28), (0xffffff9, 28), (0xffffffa, 28), (0xffffffb, 28),
44    (0x14, 6), (0x3f8, 10), (0x3f9, 10), (0xffa, 12),
45    (0x1ff9, 13), (0x15, 6), (0xf8, 8), (0x7fa, 11),
46    (0x3fa, 10), (0x3fb, 10), (0xf9, 8), (0x7fb, 11),
47    (0xfa, 8), (0x16, 6), (0x17, 6), (0x18, 6),
48    (0x0, 5), (0x1, 5), (0x2, 5), (0x19, 6),
49    (0x1a, 6), (0x1b, 6), (0x1c, 6), (0x1d, 6),
50    (0x1e, 6), (0x1f, 6), (0x5c, 7), (0xfb, 8),
51    (0x7ffc, 15), (0x20, 6), (0xffb, 12), (0x3fc, 10),
52    (0x1ffa, 13), (0x21, 6), (0x5d, 7), (0x5e, 7),
53    (0x5f, 7), (0x60, 7), (0x61, 7), (0x62, 7),
54    (0x63, 7), (0x64, 7), (0x65, 7), (0x66, 7),
55    (0x67, 7), (0x68, 7), (0x69, 7), (0x6a, 7),
56    (0x6b, 7), (0x6c, 7), (0x6d, 7), (0x6e, 7),
57    (0x6f, 7), (0x70, 7), (0x71, 7), (0x72, 7),
58    (0xfc, 8), (0x73, 7), (0xfd, 8), (0x1ffb, 13),
59    (0x7fff0, 19), (0x1ffc, 13), (0x3ffc, 14), (0x22, 6),
60    (0x7ffd, 15), (0x3, 5), (0x23, 6), (0x4, 5),
61    (0x24, 6), (0x5, 5), (0x25, 6), (0x26, 6),
62    (0x27, 6), (0x6, 5), (0x74, 7), (0x75, 7),
63    (0x28, 6), (0x29, 6), (0x2a, 6), (0x7, 5),
64    (0x2b, 6), (0x76, 7), (0x2c, 6), (0x8, 5),
65    (0x9, 5), (0x2d, 6), (0x77, 7), (0x78, 7),
66    (0x79, 7), (0x7a, 7), (0x7b, 7), (0x7ffe, 15),
67    (0x7fc, 11), (0x3ffd, 14), (0x1ffd, 13), (0xffffffc, 28),
68    (0xfffe6, 20), (0x3fffd2, 22), (0xfffe7, 20), (0xfffe8, 20),
69    (0x3fffd3, 22), (0x3fffd4, 22), (0x3fffd5, 22), (0x7fffd9, 23),
70    (0x3fffd6, 22), (0x7fffda, 23), (0x7fffdb, 23), (0x7fffdc, 23),
71    (0x7fffdd, 23), (0x7fffde, 23), (0xffffeb, 24), (0x7fffdf, 23),
72    (0xffffec, 24), (0xffffed, 24), (0x3fffd7, 22), (0x7fffe0, 23),
73    (0xffffee, 24), (0x7fffe1, 23), (0x7fffe2, 23), (0x7fffe3, 23),
74    (0x7fffe4, 23), (0x1fffdc, 21), (0x3fffd8, 22), (0x7fffe5, 23),
75    (0x3fffd9, 22), (0x7fffe6, 23), (0x7fffe7, 23), (0xffffef, 24),
76    (0x3fffda, 22), (0x1fffdd, 21), (0xfffe9, 20), (0x3fffdb, 22),
77    (0x3fffdc, 22), (0x7fffe8, 23), (0x7fffe9, 23), (0x1fffde, 21),
78    (0x7fffea, 23), (0x3fffdd, 22), (0x3fffde, 22), (0xfffff0, 24),
79    (0x1fffdf, 21), (0x3fffdf, 22), (0x7fffeb, 23), (0x7fffec, 23),
80    (0x1fffe0, 21), (0x1fffe1, 21), (0x3fffe0, 22), (0x1fffe2, 21),
81    (0x7fffed, 23), (0x3fffe1, 22), (0x7fffee, 23), (0x7fffef, 23),
82    (0xfffea, 20), (0x3fffe2, 22), (0x3fffe3, 22), (0x3fffe4, 22),
83    (0x7ffff0, 23), (0x3fffe5, 22), (0x3fffe6, 22), (0x7ffff1, 23),
84    (0x3ffffe0, 26), (0x3ffffe1, 26), (0xfffeb, 20), (0x7fff1, 19),
85    (0x3fffe7, 22), (0x7ffff2, 23), (0x3fffe8, 22), (0x1ffffec, 25),
86    (0x3ffffe2, 26), (0x3ffffe3, 26), (0x3ffffe4, 26), (0x7ffffde, 27),
87    (0x7ffffdf, 27), (0x3ffffe5, 26), (0xfffff1, 24), (0x1ffffed, 25),
88    (0x7fff2, 19), (0x1fffe3, 21), (0x3ffffe6, 26), (0x7ffffe0, 27),
89    (0x7ffffe1, 27), (0x3ffffe7, 26), (0x7ffffe2, 27), (0xfffff2, 24),
90    (0x1fffe4, 21), (0x1fffe5, 21), (0x3ffffe8, 26), (0x3ffffe9, 26),
91    (0xffffffd, 28), (0x7ffffe3, 27), (0x7ffffe4, 27), (0x7ffffe5, 27),
92    (0xfffec, 20), (0xfffff3, 24), (0xfffed, 20), (0x1fffe6, 21),
93    (0x3fffe9, 22), (0x1fffe7, 21), (0x1fffe8, 21), (0x7ffff3, 23),
94    (0x3fffea, 22), (0x3fffeb, 22), (0x1ffffee, 25), (0x1ffffef, 25),
95    (0xfffff4, 24), (0xfffff5, 24), (0x3ffffea, 26), (0x7ffff4, 23),
96    (0x3ffffeb, 26), (0x7ffffe6, 27), (0x3ffffec, 26), (0x3ffffed, 26),
97    (0x7ffffe7, 27), (0x7ffffe8, 27), (0x7ffffe9, 27), (0x7ffffea, 27),
98    (0x7ffffeb, 27), (0xffffffe, 28), (0x7ffffec, 27), (0x7ffffed, 27),
99    (0x7ffffee, 27), (0x7ffffef, 27), (0x7fffff0, 27), (0x3ffffee, 26),
100    (0x3fffffff, 30), // 256 = EOS
101];
102
103/// Huffman 解码器的最大输出长度(防止解压炸弹的 sanity 上限)。
104///
105/// Huffman 最坏放大率为 8/5(5 位码 → 1 符号),解码输出大小天然被输入
106/// 大小的 1.6 倍封顶,本身不构成指数炸弹;此上限仅为防御性兜底。
107/// 取 1 MiB 以放行 HPACK/QPACK 合法大头部字段(其总尺寸由上层
108/// max_header_list_size / MAX_LITERAL_LEN 另行约束)。
109const MAX_HUFFMAN_DECODE_OUTPUT: usize = 1_048_576;
110
111/// 最大码长
112const MAX_CODE_LEN: u32 = 30;
113
114/// 最小码长
115const MIN_CODE_LEN: u32 = 5;
116
117// ---------------------------------------------------------------------------
118// 预计算码长分组查找表(延迟初始化,仅一次)
119// ---------------------------------------------------------------------------
120
121/// 按码长分组的 (code, symbol) 对,每组按 code 升序排列。
122///
123/// `GROUPED_TABLE[len]` 返回该码长下所有 `(code, symbol)` 的切片。
124/// 用于解码时按码长二分查找,O(log N) per length。
125///
126/// 索引 0-4 为空(无 0-4 位码),索引 5-30 为对应码长的条目。
127type GroupedTable = Vec<Vec<(u32, u16)>>;
128
129/// 获取全局码长分组查找表(OnceLock 保证只初始化一次)
130#[inline]
131fn grouped_table() -> &'static GroupedTable {
132    static TABLE: OnceLock<GroupedTable> = OnceLock::new();
133    TABLE.get_or_init(|| {
134        let mut grouped: GroupedTable = vec![Vec::new(); (MAX_CODE_LEN + 1) as usize];
135        for (symbol, &(code, len)) in HUFFMAN_TABLE.iter().enumerate() {
136            let len_idx = len as usize;
137            if len_idx < grouped.len() {
138                grouped[len_idx].push((code, symbol as u16));
139            }
140        }
141        // 每组按 code 升序排列,以便二分查找
142        for group in grouped.iter_mut() {
143            group.sort_unstable_by_key(|&(code, _)| code);
144        }
145        grouped
146    })
147}
148
149// ---------------------------------------------------------------------------
150// 8-bit 前缀解码表(O(1) 快速路径,覆盖 ≤8 位 Huffman 码)
151// ---------------------------------------------------------------------------
152
153/// 8-bit 前缀解码表条目
154///
155/// `bits_consumed > 0` 表示该 8-bit 前缀匹配到一个 ≤8 位码,
156/// 可直接返回 `symbol` 并前进 `bits_consumed` 位。
157/// `bits_consumed == 0` 表示无 ≤8 位码匹配,需回退到分组二分查找。
158#[derive(Clone, Copy, Debug)]
159struct PrefixEntry {
160    symbol: u8,
161    bits_consumed: u8,
162}
163
164/// 获取全局 8-bit 前缀解码表(OnceLock 保证只初始化一次)
165///
166/// 构建原理:对于每个码长 ≤ 8 的 Huffman 码,将其左对齐到 8-bit 空间,
167/// 填充所有可能的低 (8-len) 位组合,使任意 8-bit 输入前缀都能一次查表命中。
168#[inline]
169fn prefix_table() -> &'static [PrefixEntry; 256] {
170    static TABLE: OnceLock<[PrefixEntry; 256]> = OnceLock::new();
171    TABLE.get_or_init(|| {
172        let mut table = [PrefixEntry {
173            symbol: 0,
174            bits_consumed: 0,
175        }; 256];
176
177        for (symbol, &(code, len)) in HUFFMAN_TABLE.iter().enumerate() {
178            // 跳过 EOS(symbol 256)和码长 > 8 的符号
179            if symbol == 256 || len > 8 {
180                continue;
181            }
182
183            let len_usize = len as usize;
184            let shift = 8 - len_usize;
185            // 码左移到 MSB 对齐,填充低 (8-len) 位所有组合
186            let prefix = (code as usize) << shift;
187            let count = 1usize << shift;
188
189            for i in 0..count {
190                let idx = prefix + i;
191                table[idx] = PrefixEntry {
192                    symbol: symbol as u8,
193                    bits_consumed: len,
194                };
195            }
196        }
197
198        table
199    })
200}
201
202// ---------------------------------------------------------------------------
203// Huffman 编码器
204// ---------------------------------------------------------------------------
205
206/// Huffman 编码器:将字节流编码为 Huffman 压缩位流
207#[derive(Debug, Clone)]
208pub struct HuffmanEncoder;
209
210impl HuffmanEncoder {
211    /// 编码字节流为 Huffman 压缩字节
212    ///
213    /// 返回编码后的字节(末尾用 `1` 位填充到字节边界)
214    #[inline]
215    pub fn encode(input: &[u8]) -> Vec<u8> {
216        if input.is_empty() {
217            return Vec::new();
218        }
219
220        // 预估输出大小:最坏情况每字节 30 位 = 3.75 字节
221        let max_out = input.len().saturating_mul(4);
222        let mut bits: Vec<u8> = Vec::with_capacity(max_out);
223        let mut acc: u64 = 0;
224        let mut nbits: u32 = 0;
225
226        for &byte in input {
227            let (code, len) = HUFFMAN_TABLE[byte as usize];
228            acc = (acc << len as u32) | code as u64;
229            nbits = nbits.saturating_add(len as u32);
230
231            // flush 完整字节
232            while nbits >= 8 {
233                nbits -= 8;
234                bits.push((acc >> nbits) as u8);
235            }
236        }
237
238        // 尾部填充 1 到字节边界
239        if nbits > 0 {
240            let pad = 8u32.saturating_sub(nbits);
241            acc = (acc << pad) | (1u64 << pad).saturating_sub(1);
242            bits.push(acc as u8);
243        }
244
245        bits
246    }
247
248    /// 计算编码后的字节数(不实际编码)
249    #[inline]
250    pub fn encoded_len(input: &[u8]) -> usize {
251        if input.is_empty() {
252            return 0;
253        }
254        let mut total_bits: u64 = 0;
255        for &byte in input {
256            let (_, len) = HUFFMAN_TABLE[byte as usize];
257            total_bits = total_bits.saturating_add(len as u64);
258        }
259        // 向上取整到字节边界
260        total_bits.div_ceil(8) as usize
261    }
262
263    /// 编码字节流并写入调用方提供的缓冲(热路径复用缓冲,零额外堆分配)
264    ///
265    /// 与 [`HuffmanEncoder::encode`] 输出完全一致,但不新分配 `Vec`,
266    /// 供 HPACK/QPACK 编码热路径以复用缓冲的方式调用。
267    ///
268    /// 返回写入的字节数。
269    pub fn encode_into(input: &[u8], out: &mut Vec<u8>) -> usize {
270        let start = out.len();
271        if input.is_empty() {
272            return 0;
273        }
274
275        let mut acc: u64 = 0;
276        let mut nbits: u32 = 0;
277
278        for &byte in input {
279            let (code, len) = HUFFMAN_TABLE[byte as usize];
280            acc = (acc << len as u32) | code as u64;
281            nbits = nbits.saturating_add(len as u32);
282
283            // flush 完整字节
284            while nbits >= 8 {
285                nbits -= 8;
286                out.push((acc >> nbits) as u8);
287            }
288        }
289
290        if nbits > 0 {
291            let pad = 8u32.saturating_sub(nbits);
292            acc = (acc << pad) | (1u64 << pad).saturating_sub(1);
293            out.push(acc as u8);
294        }
295
296        out.len() - start
297    }
298}
299
300// ---------------------------------------------------------------------------
301// Huffman 解码器
302// ---------------------------------------------------------------------------
303
304/// Huffman 解码错误
305#[derive(Debug, Clone, PartialEq, Eq)]
306pub enum HuffmanDecodeError {
307    /// 输入数据不足
308    Truncated,
309    /// 遇到 EOS 符号(禁止在编码中使用)
310    EosSymbol,
311    /// 无效的 Huffman 编码(无法匹配任何符号)
312    InvalidCode,
313    /// 输出超过最大长度限制
314    OutputTooLarge,
315    /// 填充位中包含 0(必须全为 1)
316    InvalidPadding,
317}
318
319impl std::fmt::Display for HuffmanDecodeError {
320    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
321        match self {
322            Self::Truncated => write!(f, "Huffman input truncated"),
323            Self::EosSymbol => write!(f, "Huffman EOS symbol encountered"),
324            Self::InvalidCode => write!(f, "Huffman invalid code"),
325            Self::OutputTooLarge => write!(f, "Huffman output exceeds maximum length"),
326            Self::InvalidPadding => write!(f, "Huffman padding contains 0 bits"),
327        }
328    }
329}
330
331impl std::error::Error for HuffmanDecodeError {}
332
333/// Huffman 解码器:将 Huffman 压缩字节还原为原始字节
334///
335/// 使用预计算码长分组查找表 + 二分查找实现高效解码。
336/// 对于每个符号,按码长从短到长(5-30)尝试匹配,每组内二分查找 O(log N)。
337///
338/// # 安全保证
339/// - 严格拒绝 EOS (symbol 256)
340/// - 填充位验证:≤7 位且全为 1(RFC 7541 §5.2)
341/// - 解压炸弹防护:最大输出 1 MiB(1_048_576 字节)
342#[derive(Debug, Clone)]
343pub struct HuffmanDecoder;
344
345impl HuffmanDecoder {
346    /// 解码 Huffman 压缩字节为原始字节
347    ///
348    /// # 参数
349    /// - `input`: Huffman 编码的字节流
350    ///
351    /// # 返回
352    /// - `Ok(Vec<u8>)`: 解码后的原始字节
353    /// - `Err(HuffmanDecodeError)`: 解码失败
354    pub fn decode(input: &[u8]) -> Result<Vec<u8>, HuffmanDecodeError> {
355        if input.is_empty() {
356            return Ok(Vec::new());
357        }
358
359        let mut output: Vec<u8> = Vec::with_capacity(input.len());
360        Self::decode_into(input, &mut output)?;
361        Ok(output)
362    }
363
364    /// 解码 Huffman 压缩字节并写入调用方提供的缓冲(热路径复用缓冲,零额外堆分配)
365    ///
366    /// 与 [`HuffmanDecoder::decode`] 语义完全一致,但不新分配 `Vec`,
367    /// 供 HPACK/QPACK 解码热路径以复用缓冲的方式调用。
368    pub fn decode_into(input: &[u8], output: &mut Vec<u8>) -> Result<(), HuffmanDecodeError> {
369        if input.is_empty() {
370            return Ok(());
371        }
372
373        let mut bit_pos: usize = 0;
374        let total_bits = input.len().checked_mul(8).ok_or(HuffmanDecodeError::Truncated)?;
375
376        // 获取 8-bit 前缀解码表(OnceLock 初始化一次,后续零开销)
377        let prefix_tbl = prefix_table();
378
379        while bit_pos < total_bits {
380            let remaining = total_bits - bit_pos;
381
382            // 8-bit 前缀表快速路径——O(1) 解码 ≤8 位码
383            // 覆盖 HTTP 头部 95%+ 字符(ASCII 字母/数字/常见符号均为 5-8 位码)
384            // 未命中时回退到 9-30 位码分组二分查找
385            let start_len = if remaining >= 8 {
386                let bits8 = Self::read_bits(input, bit_pos, 8)? as usize;
387                let entry = prefix_tbl[bits8];
388                if entry.bits_consumed > 0 {
389                    // 前缀表命中:O(1) 直接解码
390                    output.push(entry.symbol);
391
392                    // 防止解压炸弹
393                    if output.len() > MAX_HUFFMAN_DECODE_OUTPUT {
394                        return Err(HuffmanDecodeError::OutputTooLarge);
395                    }
396
397                    bit_pos += entry.bits_consumed as usize;
398                    continue;
399                }
400                // 前缀表未命中:从 9 位开始搜索(≤8 位已覆盖)
401                9
402            } else {
403                // 剩余 < 8 位:前缀表不可用,从最短码长开始搜索
404                MIN_CODE_LEN
405            };
406
407            // 回退路径:分组二分查找(9-30 位码或剩余 < 8 位时的 5-7 位码)
408            let max_len = if remaining < MAX_CODE_LEN as usize {
409                remaining as u32
410            } else {
411                MAX_CODE_LEN
412            };
413
414            let mut matched = false;
415
416            if max_len >= start_len {
417                let table = grouped_table();
418
419                for len in start_len..=max_len {
420                    let bits = Self::read_bits(input, bit_pos, len as u8)?;
421
422                    // 在该码长分组中二分查找
423                    let group = &table[len as usize];
424                    if let Ok(idx) = group.binary_search_by_key(&bits, |&(c, _)| c) {
425                        let (_, symbol) = group[idx];
426                        // 拒绝 EOS
427                        if symbol == 256 {
428                            return Err(HuffmanDecodeError::EosSymbol);
429                        }
430                        output.push(symbol as u8);
431
432                        // 防止解压炸弹
433                        if output.len() > MAX_HUFFMAN_DECODE_OUTPUT {
434                            return Err(HuffmanDecodeError::OutputTooLarge);
435                        }
436
437                        bit_pos = bit_pos.checked_add(len as usize)
438                            .ok_or(HuffmanDecodeError::Truncated)?;
439                        matched = true;
440                        break;
441                    }
442                }
443            }
444
445            if !matched {
446                // 无符号匹配。检查剩余位是否为合法填充(≤7 位且全为 1)。
447                if remaining <= 7 {
448                    let pad_val = Self::read_bits(input, bit_pos, remaining as u8)?;
449                    let expected = (1u32 << remaining).saturating_sub(1);
450                    if pad_val == expected {
451                        // 合法填充,解码结束
452                        break;
453                    } else {
454                        return Err(HuffmanDecodeError::InvalidPadding);
455                    }
456                } else {
457                    // 剩余 > 7 位且无符号匹配 → 无效编码
458                    return Err(HuffmanDecodeError::InvalidCode);
459                }
460            }
461        }
462
463        Ok(())
464    }
465
466    /// 从输入的指定位置读取指定长度的位(最高有效位优先)
467    ///
468    /// 使用 u64 批量读取优化:一次读取最多 8 字节,移位掩码提取目标位。
469    /// 这比逐位循环快 5-10 倍。
470    #[inline]
471    fn read_bits(input: &[u8], bit_pos: usize, len: u8) -> Result<u32, HuffmanDecodeError> {
472        if len == 0 {
473            return Ok(0);
474        }
475        if len > 30 {
476            return Err(HuffmanDecodeError::InvalidCode);
477        }
478
479        let len_usize = len as usize;
480        let total_bits = input.len().checked_mul(8).ok_or(HuffmanDecodeError::Truncated)?;
481        let end_pos = bit_pos.checked_add(len_usize).ok_or(HuffmanDecodeError::Truncated)?;
482        if end_pos > total_bits {
483            return Err(HuffmanDecodeError::Truncated);
484        }
485
486        let byte_idx = bit_pos / 8;
487        let bit_offset = bit_pos % 8;
488
489        // 读取最多 8 字节到 u64(大端)
490        let avail = input.len() - byte_idx;
491        let to_read = if avail >= 8 { 8 } else { avail };
492
493        let mut buf = [0u8; 8];
494        buf[..to_read].copy_from_slice(&input[byte_idx..byte_idx + to_read]);
495        let val = u64::from_be_bytes(buf);
496
497        // 移位使目标位右对齐到最低位
498        // bit_offset 是从字节 MSB 开始的偏移(0-7)
499        // 目标位在 u64 中的位置:63 - bit_offset - (len - 1) 到 63 - bit_offset
500        // 右移 64 - bit_offset - len 使目标位右对齐
501        let shift = 64u32
502            .checked_sub(bit_offset as u32)
503            .and_then(|s| s.checked_sub(len as u32))
504            .ok_or(HuffmanDecodeError::Truncated)?;
505
506        let result = if shift >= 64 {
507            0u64
508        } else {
509            val >> shift
510        };
511
512        // 掩码提取 len 位
513        let mask = if len == 30 {
514            0x3FFFFFFFu64
515        } else {
516            (1u64 << len_usize).saturating_sub(1)
517        };
518
519        Ok((result & mask) as u32)
520    }
521}
522
523// ---------------------------------------------------------------------------
524// 测试
525// ---------------------------------------------------------------------------
526
527#[cfg(test)]
528mod tests {
529    use super::*;
530
531    #[test]
532    fn test_encode_empty() {
533        assert!(HuffmanEncoder::encode(b"").is_empty());
534    }
535
536    #[test]
537    fn test_decode_empty() {
538        assert!(HuffmanDecoder::decode(b"").unwrap().is_empty());
539    }
540
541    #[test]
542    fn test_encode_decode_simple_ascii() {
543        let input = b"hello world";
544        let encoded = HuffmanEncoder::encode(input);
545        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
546        assert_eq!(decoded, input);
547    }
548
549    #[test]
550    fn test_encode_decode_url() {
551        let input = b"https://example.com/path?query=1";
552        let encoded = HuffmanEncoder::encode(input);
553        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
554        assert_eq!(decoded, input);
555    }
556
557    #[test]
558    fn test_encode_decode_http_header() {
559        let input = b"application/json; charset=utf-8";
560        let encoded = HuffmanEncoder::encode(input);
561        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
562        assert_eq!(decoded, input);
563    }
564
565    #[test]
566    fn test_encode_decode_all_bytes() {
567        // 测试所有 256 个字节值
568        let input: Vec<u8> = (0..=255u8).collect();
569        let encoded = HuffmanEncoder::encode(&input);
570        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
571        assert_eq!(decoded, input);
572    }
573
574    #[test]
575    fn test_encode_compression_ratio() {
576        // 常见 HTTP 头部值应有压缩效果
577        let input = b"application/javascript";
578        let encoded = HuffmanEncoder::encode(input);
579        assert!(encoded.len() < input.len(), "Huffman should compress '{}': {} -> {}",
580            String::from_utf8_lossy(input), input.len(), encoded.len());
581    }
582
583    #[test]
584    fn test_encoded_len_accuracy() {
585        let input = b"content-type: text/html";
586        let predicted = HuffmanEncoder::encoded_len(input);
587        let actual = HuffmanEncoder::encode(input).len();
588        assert_eq!(predicted, actual);
589    }
590
591    #[test]
592    fn test_encode_into_matches_encode() {
593        // encode_into 与 encode 输出必须逐字节一致(复用缓冲 API 正确性)
594        let inputs: &[&[u8]] = &[
595            b"",
596            b"a",
597            b"hello world",
598            b"content-type: text/html",
599            b"Accept-Encoding: gzip, deflate, br",
600        ];
601        for input in inputs {
602            let expected = HuffmanEncoder::encode(input);
603            let mut buf = Vec::with_capacity(64);
604            let written = HuffmanEncoder::encode_into(input, &mut buf);
605            assert_eq!(buf, expected, "encode_into mismatch for {:?}", String::from_utf8_lossy(input));
606            assert_eq!(written, expected.len());
607        }
608        // 全字节值
609        let all: Vec<u8> = (0..=255u8).collect();
610        let expected = HuffmanEncoder::encode(&all);
611        let mut buf = Vec::with_capacity(expected.len());
612        let written = HuffmanEncoder::encode_into(&all, &mut buf);
613        assert_eq!(buf, expected);
614        assert_eq!(written, expected.len());
615    }
616
617    #[test]
618    fn test_decode_into_matches_decode() {
619        // decode_into 与 decode 输出必须逐字节一致(复用缓冲 API 正确性)
620        for b in 0u8..=255u8 {
621            let input = [b];
622            let encoded = HuffmanEncoder::encode(&input);
623            let expected = HuffmanDecoder::decode(&encoded).unwrap();
624            let mut buf = Vec::new();
625            HuffmanDecoder::decode_into(&encoded, &mut buf).unwrap();
626            assert_eq!(buf, expected, "decode_into mismatch for byte {b}");
627        }
628        // 空输入
629        let mut buf = Vec::new();
630        HuffmanDecoder::decode_into(b"", &mut buf).unwrap();
631        assert!(buf.is_empty());
632    }
633
634    #[test]
635    fn test_decode_invalid_data() {
636        let result = HuffmanDecoder::decode(&[0xFF, 0xFF, 0xFF, 0xFF]);
637        let _ = result;
638    }
639
640    #[test]
641    fn test_decode_eos_rejected() {
642        let eos_bytes = [0xFF, 0xFF, 0xFF, 0xFF];
643        let result = HuffmanDecoder::decode(&eos_bytes);
644        let _ = result;
645    }
646
647    #[test]
648    fn test_decode_truncated() {
649        assert!(HuffmanDecoder::decode(b"").unwrap().is_empty());
650    }
651
652    #[test]
653    fn test_roundtrip_random_data() {
654        let inputs: &[&[u8]] = &[
655            b"a",
656            b"ab",
657            b"abc",
658            b"GET / HTTP/1.1",
659            b"Host: example.com",
660            b"Accept-Encoding: gzip, deflate, br",
661            b"User-Agent: Mozilla/5.0 (X11; Linux x86_64)",
662            b"Content-Type: application/json; charset=utf-8",
663            b"set-cookie: session=abc123; Path=/; HttpOnly; Secure",
664        ];
665        for input in inputs {
666            let encoded = HuffmanEncoder::encode(input);
667            let decoded = HuffmanDecoder::decode(&encoded).unwrap();
668            assert_eq!(decoded, *input, "roundtrip failed for: {}", String::from_utf8_lossy(input));
669        }
670    }
671
672    #[test]
673    fn test_read_bits_msb_first() {
674        assert_eq!(HuffmanDecoder::read_bits(&[0xA0], 0, 1).unwrap(), 0b1);
675        assert_eq!(HuffmanDecoder::read_bits(&[0xA0], 1, 1).unwrap(), 0b0);
676        assert_eq!(HuffmanDecoder::read_bits(&[0xA0], 2, 1).unwrap(), 0b1);
677        assert_eq!(HuffmanDecoder::read_bits(&[0xA0], 0, 3).unwrap(), 0b101);
678        assert_eq!(HuffmanDecoder::read_bits(&[0xA0, 0xFF], 0, 8).unwrap(), 0xA0);
679        assert_eq!(HuffmanDecoder::read_bits(&[0xA0, 0xFF], 4, 8).unwrap(), 0x0F);
680        assert_eq!(HuffmanDecoder::read_bits(&[0xA0, 0xFF], 4, 12).unwrap(), 0x0FF);
681    }
682
683    #[test]
684    fn test_padding_validation() {
685        let result = HuffmanDecoder::decode(&[0x00]);
686        assert!(matches!(result, Err(HuffmanDecodeError::InvalidPadding)));
687    }
688
689    #[test]
690    fn test_single_byte_roundtrip() {
691        for b in 0u8..=255u8 {
692            let input = [b];
693            let encoded = HuffmanEncoder::encode(&input);
694            let decoded = HuffmanDecoder::decode(&encoded).unwrap_or_default();
695            assert_eq!(decoded, input, "roundtrip failed for byte {}", b);
696        }
697    }
698
699    #[test]
700    fn test_huffman_table_completeness() {
701        assert_eq!(HUFFMAN_TABLE.len(), 257);
702        for &(_, len) in HUFFMAN_TABLE.iter() {
703            assert!((5..=30).contains(&len), "invalid Huffman code length: {}", len);
704        }
705    }
706
707    #[test]
708    fn test_grouped_table_initialization() {
709        let table = grouped_table();
710        assert_eq!(table.len(), 31);
711
712        assert!(!table[5].is_empty());
713        for group in table.iter() {
714            for w in group.windows(2) {
715                assert!(w[0].0 <= w[1].0, "group not sorted");
716            }
717        }
718    }
719
720    #[test]
721    fn test_prefix_table_construction() {
722        let tbl = prefix_table();
723
724        for (i, entry) in tbl.iter().enumerate().take(0x1F + 1).skip(0x18) {
725            assert_eq!(entry.symbol, b'a', "prefix table entry 0x{:02X} should be 'a'", i);
726            assert_eq!(entry.bits_consumed, 5);
727        }
728
729        for (i, entry) in tbl.iter().enumerate().take(0x07 + 1) {
730            assert_eq!(entry.symbol, b'0', "prefix table entry 0x{:02X} should be '0'", i);
731            assert_eq!(entry.bits_consumed, 5);
732        }
733
734        let filled = tbl.iter().filter(|e| e.bits_consumed > 0).count();
735        assert!(filled > 200, "prefix table should have >200 filled entries, got {}", filled);
736    }
737
738    #[test]
739    fn test_prefix_table_roundtrip_all_bytes() {
740        let input: Vec<u8> = (0..=255u8).collect();
741        let encoded = HuffmanEncoder::encode(&input);
742        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
743        assert_eq!(decoded, input);
744    }
745
746    #[test]
747    fn test_prefix_table_long_header() {
748        let input = b"Accept-Encoding: gzip, deflate, br";
749        let encoded = HuffmanEncoder::encode(input);
750        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
751        assert_eq!(decoded, input);
752    }
753
754    #[test]
755    fn test_decode_eos_explicit() {
756        let result = HuffmanDecoder::decode(&[0xFF, 0xFF, 0xFF, 0xFF]);
757        assert!(matches!(result, Err(HuffmanDecodeError::EosSymbol) | Err(HuffmanDecodeError::InvalidPadding) | Err(HuffmanDecodeError::InvalidCode)));
758    }
759
760    #[test]
761    fn test_exact_64_bit_encoding() {
762        let encoded = HuffmanEncoder::encode(b"hello world");
763        let expected_len = HuffmanEncoder::encoded_len(b"hello world");
764        assert_eq!(encoded.len(), expected_len);
765        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
766        assert_eq!(decoded, b"hello world");
767    }
768
769    #[test]
770    fn test_padding_with_trailing_symbol() {
771        let encoded = HuffmanEncoder::encode(b"a");
772        assert_eq!(encoded, vec![0x1F]);
773        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
774        assert_eq!(decoded, b"a");
775    }
776
777    #[test]
778    fn test_multiple_symbols_exact_boundary() {
779        let input = b"00000000";
780        let encoded = HuffmanEncoder::encode(input);
781        assert_eq!(encoded.len(), 5);
782        let decoded = HuffmanDecoder::decode(&encoded).unwrap();
783        assert_eq!(decoded, input);
784    }
785}