use super::dynamic_table::DynamicTable;
use super::huffman::huffman_encode_if_smaller;
use super::integer::encode_integer;
use super::static_table::{find_static_entry, find_static_entry_by_name};
const STATIC_TABLE_SIZE: usize = 61;
pub struct Encoder {
dynamic_table: DynamicTable,
}
impl Encoder {
pub fn new() -> Self {
Self {
dynamic_table: DynamicTable::new(4096),
}
}
pub fn set_max_table_size(&mut self, size: usize) {
self.dynamic_table.set_max_size(size);
}
pub fn encode(&mut self, headers: &[(&[u8], &[u8])]) -> Vec<u8> {
let mut output = Vec::new();
for (name, value) in headers {
self.encode_header(name, value, &mut output);
}
output
}
fn encode_header(&mut self, name: &[u8], value: &[u8], output: &mut Vec<u8>) {
if let Some(static_idx) = find_static_entry(name, value) {
output.push(0x80); encode_integer(static_idx, 7, 0x7F, output).unwrap();
return;
}
if let Some(dynamic_idx) = self.dynamic_table.find(name, value) {
let combined_idx = STATIC_TABLE_SIZE + dynamic_idx;
output.push(0x80);
encode_integer(combined_idx, 7, 0x7F, output).unwrap();
return;
}
if let Some(static_name_idx) = find_static_entry_by_name(name) {
output.push(0x40); encode_integer(static_name_idx, 6, 0x3F, output).unwrap();
self.encode_string_literal(value, output);
self.dynamic_table.add(name.to_vec(), value.to_vec());
return;
}
if let Some(dynamic_name_idx) = self.dynamic_table.find_by_name(name) {
let combined_name_idx = STATIC_TABLE_SIZE + dynamic_name_idx;
output.push(0x40);
encode_integer(combined_name_idx, 6, 0x3F, output).unwrap();
self.encode_string_literal(value, output);
self.dynamic_table.add(name.to_vec(), value.to_vec());
return;
}
output.push(0x40);
encode_integer(0, 6, 0x3F, output).unwrap();
self.encode_string_literal(name, output);
self.encode_string_literal(value, output);
self.dynamic_table.add(name.to_vec(), value.to_vec());
}
fn encode_string_literal(&self, input: &[u8], output: &mut Vec<u8>) {
let (encoded, use_huffman) = huffman_encode_if_smaller(input);
output.push(if use_huffman { 0x80 } else { 0x00 });
encode_integer(encoded.len(), 7, 0x7F, output).unwrap();
output.extend_from_slice(&encoded);
}
}
impl Default for Encoder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_static_entry() {
let mut encoder = Encoder::new();
let headers = [(b":method".as_slice(), b"GET".as_slice())];
let encoded = encoder.encode(&headers);
assert!(!encoded.is_empty());
assert_eq!(encoded[0] & 0x80, 0x80); }
#[test]
fn test_encode_literal() {
let mut encoder = Encoder::new();
let headers = [(b"custom-key".as_slice(), b"custom-value".as_slice())];
let encoded = encoder.encode(&headers);
assert!(!encoded.is_empty());
assert_eq!(encoded[0] & 0xC0, 0x40); }
#[test]
fn test_encode_multiple_headers() {
let mut encoder = Encoder::new();
let headers = [
(b":method".as_slice(), b"GET".as_slice()),
(b":scheme".as_slice(), b"http".as_slice()),
(b":path".as_slice(), b"/".as_slice()),
];
let encoded = encoder.encode(&headers);
assert!(!encoded.is_empty());
}
}