1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Provides features for parsing [Huffman code] table provided by the [HPACK]
//! documentation.
//! 
//! [HPACK] provides a pre-created [Huffman code] table for encoding [ASCII]
//! characters to the Huffman sequence. This Huffman code was generated from
//! statistics obtained on a large sample of HTTP headers.
//! 
//! The parser module is responsible for parsing the [Huffman code] table into
//! the static Rust source code. This module was used to create the ENCODE_TABLE
//! constant which can be found in the `encode::table` module.
//! 
//! You will probably never use this module while developing applications.
//! 
//! [ASCII]: https://en.wikipedia.org/wiki/ASCII
//! [HPACK]: https://tools.ietf.org/html/rfc7541
//! [Huffman code]: https://tools.ietf.org/html/rfc7541#appendix-B

/// Parses the HPACK's static Huffman table. The function expects data to be in
/// format as provided by the spec (7.2).
/// 
/// **Example:**
/// 
/// ```rust
/// use std::fs;
/// use std::path::Path;
/// use httlib_huffman::parser::parse;
/// 
/// let path = Path::new("assets/hpack-huffman.txt");
/// let data = fs::read_to_string(path).expect("Can't read file.");
/// let codings = parse(&data);
/// ```
pub fn parse(data: &str) -> Vec<(u16, u32)> {
    let lines = data.lines();
    let mut codings = vec![];

    for line in lines {
        let coding = parse_line(line);
        codings.push(coding);
    }

    codings
}

/// Parses a single line of the static Huffman table. The output returned
/// contains a tuple of the number of bits for the code representing the symbol
/// and Huffman LSB value.
fn parse_line(line: &str) -> (u16, u32) {

    let mut msb = vec![];
    for &b in &line.as_bytes()[12..45] {
        match b {
            b'1' => msb.push(true),
            b'0' => msb.push(false),
            b'|' | b' ' => {}
            _ => panic!("unexpected byte; {:?}", b),
        }
    }

    let lsb = u32::from_str_radix(&line[50..59].trim().to_string(), 16).expect("Invalid hex");
    let len = msb.len() as u16;

    (len, lsb)
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::path::Path;
    use super::*;

    /// Should read the text file `assets/hpack-huffman.txt` and parse the 
    /// content into 2-dimensional table that can be used in Rust code. 
    #[test]
    fn parses_huffman_table() { 
        let path = Path::new("assets/hpack-huffman.txt");
        let data = fs::read_to_string(path).expect("Can't read file.");
        let table = parse(&data);

        assert_eq!(table.len(), 257);

        let item = table[10];
        assert_eq!(item.0, 30);
        assert_eq!(item.1, 0x3ffffffc);

        let item = table[32];
        assert_eq!(item.0, 6);
        assert_eq!(item.1, 0x14);

        let item = table.last().unwrap();
        assert_eq!(item.0, 30);
        assert_eq!(item.1, 0x3fffffff);
    }
}