specter/transport/h2/hpack_impl/
encoder.rs1use super::dynamic_table::DynamicTable;
4use super::huffman::huffman_encode_if_smaller;
5use super::integer::encode_integer;
6use super::static_table::{find_static_entry, find_static_entry_by_name};
7
8const STATIC_TABLE_SIZE: usize = 61;
9
10pub struct Encoder {
12 dynamic_table: DynamicTable,
13}
14
15impl Encoder {
16 pub fn new() -> Self {
18 Self {
19 dynamic_table: DynamicTable::new(4096),
20 }
21 }
22
23 pub fn set_max_table_size(&mut self, size: usize) {
25 self.dynamic_table.set_max_size(size);
26 }
27
28 pub fn encode(&mut self, headers: &[(&[u8], &[u8])]) -> Vec<u8> {
32 let mut output = Vec::new();
33
34 for (name, value) in headers {
35 self.encode_header(name, value, &mut output);
36 }
37
38 output
39 }
40
41 fn encode_header(&mut self, name: &[u8], value: &[u8], output: &mut Vec<u8>) {
43 if let Some(static_idx) = find_static_entry(name, value) {
45 output.push(0x80); encode_integer(static_idx, 7, 0x7F, output).unwrap();
50 return;
51 }
52
53 if let Some(dynamic_idx) = self.dynamic_table.find(name, value) {
55 let combined_idx = STATIC_TABLE_SIZE + dynamic_idx;
56 output.push(0x80);
59 encode_integer(combined_idx, 7, 0x7F, output).unwrap();
60 return;
61 }
62
63 if let Some(static_name_idx) = find_static_entry_by_name(name) {
65 output.push(0x40); encode_integer(static_name_idx, 6, 0x3F, output).unwrap();
70 self.encode_string_literal(value, output);
71
72 self.dynamic_table.add(name.to_vec(), value.to_vec());
74 return;
75 }
76
77 if let Some(dynamic_name_idx) = self.dynamic_table.find_by_name(name) {
79 let combined_name_idx = STATIC_TABLE_SIZE + dynamic_name_idx;
80 output.push(0x40);
83 encode_integer(combined_name_idx, 6, 0x3F, output).unwrap();
84 self.encode_string_literal(value, output);
85
86 self.dynamic_table.add(name.to_vec(), value.to_vec());
88 return;
89 }
90
91 output.push(0x40);
95 encode_integer(0, 6, 0x3F, output).unwrap();
96 self.encode_string_literal(name, output);
97 self.encode_string_literal(value, output);
98
99 self.dynamic_table.add(name.to_vec(), value.to_vec());
101 }
102
103 fn encode_string_literal(&self, input: &[u8], output: &mut Vec<u8>) {
105 let (encoded, use_huffman) = huffman_encode_if_smaller(input);
106
107 output.push(if use_huffman { 0x80 } else { 0x00 });
110 encode_integer(encoded.len(), 7, 0x7F, output).unwrap();
111 output.extend_from_slice(&encoded);
112 }
113}
114
115impl Default for Encoder {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_encode_static_entry() {
127 let mut encoder = Encoder::new();
128 let headers = [(b":method".as_slice(), b"GET".as_slice())];
129 let encoded = encoder.encode(&headers);
130 assert!(!encoded.is_empty());
132 assert_eq!(encoded[0] & 0x80, 0x80); }
134
135 #[test]
136 fn test_encode_literal() {
137 let mut encoder = Encoder::new();
138 let headers = [(b"custom-key".as_slice(), b"custom-value".as_slice())];
139 let encoded = encoder.encode(&headers);
140 assert!(!encoded.is_empty());
142 assert_eq!(encoded[0] & 0xC0, 0x40); }
144
145 #[test]
146 fn test_encode_multiple_headers() {
147 let mut encoder = Encoder::new();
148 let headers = [
149 (b":method".as_slice(), b"GET".as_slice()),
150 (b":scheme".as_slice(), b"http".as_slice()),
151 (b":path".as_slice(), b"/".as_slice()),
152 ];
153 let encoded = encoder.encode(&headers);
154 assert!(!encoded.is_empty());
155 }
156}