use super::Result;
use crate::h3::NameValue;
use super::INDEXED;
use super::LITERAL;
use super::LITERAL_WITH_NAME_REF;
#[derive(Default)]
pub struct Encoder {}
impl Encoder {
pub fn new() -> Encoder {
Encoder::default()
}
pub fn encode<T: NameValue>(
&mut self, headers: &[T], out: &mut [u8],
) -> Result<usize> {
let mut b = octets::OctetsMut::with_slice(out);
encode_int(0, 0, 8, &mut b)?;
encode_int(0, 0, 7, &mut b)?;
for h in headers {
match lookup_static(h) {
Some((idx, true)) => {
const STATIC: u8 = 0x40;
encode_int(idx, INDEXED | STATIC, 6, &mut b)?;
},
Some((idx, false)) => {
const STATIC: u8 = 0x10;
encode_int(idx, LITERAL_WITH_NAME_REF | STATIC, 4, &mut b)?;
encode_str::<false>(h.value(), 0, 7, &mut b)?;
},
None => {
encode_str::<true>(h.name(), LITERAL, 3, &mut b)?;
encode_str::<false>(h.value(), 0, 7, &mut b)?;
},
};
}
Ok(b.off())
}
}
fn lookup_static<T: NameValue>(h: &T) -> Option<(u64, bool)> {
let table_for_len =
super::static_table::STATIC_ENCODE_TABLE.get(h.name().len())?;
let cmp_lowercase = |a: &[u8], b: &[u8]| {
std::iter::zip(a, b).all(|(a, b)| a.eq(&b.to_ascii_lowercase()))
};
for (name, values) in table_for_len.iter() {
if cmp_lowercase(name, h.name()) {
for (value, enc) in values.iter() {
if value.is_empty() {
return Some((*enc, false));
}
if h.value() == *value {
return Some((*enc, true));
}
}
return Some((values.first()?.1, false));
}
}
None
}
pub fn encode_int(
mut v: u64, first: u8, prefix: usize, b: &mut octets::OctetsMut,
) -> Result<()> {
let mask = 2u64.pow(prefix as u32) - 1;
if v < mask {
b.put_u8(first | v as u8)?;
return Ok(());
}
b.put_u8(first | mask as u8)?;
v -= mask;
while v >= 128 {
b.put_u8((v % 128 + 128) as u8)?;
v >>= 7;
}
b.put_u8(v as u8)?;
Ok(())
}
#[inline]
pub fn encode_str<const LOWER_CASE: bool>(
v: &[u8], first: u8, prefix: usize, b: &mut octets::OctetsMut,
) -> Result<()> {
match octets::huffman_encoding_len::<LOWER_CASE>(v) {
Ok(len) => {
encode_int(len as u64, first | (1 << prefix), prefix, b)?;
b.put_huffman_encoded::<LOWER_CASE>(v)?;
},
Err(_) => {
encode_int(v.len() as u64, first, prefix, b)?;
if LOWER_CASE {
b.put_bytes(&v.to_ascii_lowercase())?;
} else {
b.put_bytes(v)?;
}
},
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_int1() {
let expected = [0b01010];
let mut encoded = [0; 1];
let mut b = octets::OctetsMut::with_slice(&mut encoded);
assert!(encode_int(10, 0, 5, &mut b).is_ok());
assert_eq!(expected, encoded);
}
#[test]
fn encode_int2() {
let expected = [0b11111, 0b10011010, 0b00001010];
let mut encoded = [0; 3];
let mut b = octets::OctetsMut::with_slice(&mut encoded);
assert!(encode_int(1337, 0, 5, &mut b).is_ok());
assert_eq!(expected, encoded);
}
#[test]
fn encode_int3() {
let expected = [0b101010];
let mut encoded = [0; 1];
let mut b = octets::OctetsMut::with_slice(&mut encoded);
assert!(encode_int(42, 0, 8, &mut b).is_ok());
assert_eq!(expected, encoded);
}
#[test]
fn encode_static_header() {
let mut encoded = [0; 3];
Encoder::default()
.encode(&[(b":method", b"GET")], &mut encoded)
.unwrap();
assert_eq!(encoded, [0, 0, INDEXED | 0x40 | 17]);
}
#[test]
fn encode_static_header_name_only() {
let mut encoded = [0; 11];
let mut expected = [0; 11];
let mut buf = octets::OctetsMut::with_slice(&mut expected[..]);
buf.put_u16(0).unwrap();
buf.put_u8(LITERAL_WITH_NAME_REF | 0x10 | 15).unwrap();
buf.put_u8(0).unwrap();
encode_str::<false>(b"FORGET", 0, 7, &mut buf).unwrap();
Encoder::default()
.encode(&[(b":method", b"FORGET")], &mut encoded)
.unwrap();
assert_eq!(encoded, expected);
}
}