use std::collections::HashMap;
use std::fmt;
use std::hash::{BuildHasherDefault, Hasher};
use crate::helpers::huffman;
use crate::helpers::scan;
use crate::helpers::text::Text;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderField {
pub name: Text,
pub value: Text,
}
impl HeaderField {
pub const SENSITIVE: &'static [&'static str] = &["authorization", "proxy-authorization", "cookie", "set-cookie"];
pub const OVERHEAD: usize = 32;
pub const SECTION_FLOOR: usize = 8;
pub const SECTION_SPARE: usize = 1;
pub const SECTION_CEILING: usize = 128;
pub fn new(name: impl Into<Text>, value: impl Into<Text>) -> Self {
Self { name: name.into(), value: value.into() }
}
pub fn section_hint(previous: usize) -> usize {
previous.saturating_add(Self::SECTION_SPARE).clamp(Self::SECTION_FLOOR, Self::SECTION_CEILING)
}
pub fn size(&self) -> usize {
self.name.len() + self.value.len() + Self::OVERHEAD
}
pub fn sensitive(&self) -> bool {
let name = self.name.as_str();
let Some(first) = name.as_bytes().first() else {
return false;
};
match (name.len(), first) {
(6, b'c') => name == "cookie",
(10, b's') => name == "set-cookie",
(13, b'a') => name == "authorization",
(19, b'p') => name == "proxy-authorization",
_ => false,
}
}
pub const TOKEN: u8 = 1 << 0;
pub const LOWERCASE: u8 = 1 << 1;
pub const TOKENS: &'static [u8; 256] = &{
let mut octets = [0u8; 256];
let mut value = 0usize;
while value < 256 {
let byte = value as u8;
let token = byte.is_ascii_alphanumeric()
|| matches!(
byte,
b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~'
);
octets[value] = token as u8 | ((token && !byte.is_ascii_uppercase()) as u8) << 1;
value += 1;
}
octets
};
#[inline]
pub fn is_name(name: &str) -> bool {
!name.is_empty() && scan::all_in_class(name.as_bytes(), Self::TOKENS, Self::TOKEN)
}
#[inline]
pub fn is_lowercase_name(name: &str) -> bool {
!name.is_empty() && scan::all_in_class(name.as_bytes(), Self::TOKENS, Self::LOWERCASE)
}
#[inline]
pub fn is_value(value: &str) -> bool {
let octets = value.as_bytes();
if matches!(octets.first(), Some(b' ' | b'\t')) || matches!(octets.last(), Some(b' ' | b'\t')) {
return false;
}
scan::is_field_value(octets)
}
}
#[derive(Default)]
pub struct FieldHasher(u64);
impl FieldHasher {
pub const SEED: u64 = 0xcbf2_9ce4_8422_2325;
pub const FACTOR: u64 = 0x9e37_79b9_7f4a_7c15;
#[inline]
pub fn mix(hash: u64, word: u64) -> u64 {
(hash.rotate_left(5) ^ word).wrapping_mul(Self::FACTOR)
}
#[inline]
pub fn word(bytes: &[u8], offset: usize) -> u64 {
u64::from_le_bytes(bytes[offset..offset + 8].try_into().expect("eight octets are eight octets"))
}
#[inline]
pub fn short(bytes: &[u8]) -> u64 {
match bytes.len() {
0 => 0,
len @ 1..=3 => bytes[0] as u64 | (bytes[len / 2] as u64) << 8 | (bytes[len - 1] as u64) << 16,
len => {
let head = u32::from_le_bytes(bytes[..4].try_into().expect("four octets are four octets")) as u64;
let tail = u32::from_le_bytes(bytes[len - 4..].try_into().expect("four octets are four octets")) as u64;
head | tail << 32
}
}
}
}
impl Hasher for FieldHasher {
fn finish(&self) -> u64 {
let hash = self.0;
hash ^ hash >> 32
}
fn write(&mut self, bytes: &[u8]) {
let mut hash = match self.0 {
0 => Self::SEED,
held => held,
};
if bytes.len() >= size_of::<u64>() {
let mut offset = 0;
while offset + size_of::<u64>() <= bytes.len() {
hash = Self::mix(hash, Self::word(bytes, offset));
offset += size_of::<u64>();
}
if offset < bytes.len() {
hash = Self::mix(hash, Self::word(bytes, bytes.len() - size_of::<u64>()));
}
} else {
hash = Self::mix(hash, Self::short(bytes));
}
self.0 = Self::mix(hash, bytes.len() as u64);
}
}
pub type FieldMap<K, V> = HashMap<K, V, BuildHasherDefault<FieldHasher>>;
#[derive(Default)]
pub struct MarkHasher(u64);
impl Hasher for MarkHasher {
fn finish(&self) -> u64 {
self.0
}
fn write(&mut self, bytes: &[u8]) {
self.0 = bytes.iter().fold(self.0, |hash, octet| FieldHasher::mix(hash, *octet as u64));
}
fn write_u64(&mut self, value: u64) {
self.0 = value;
}
}
pub type MarkMap<V> = HashMap<Mark, V, BuildHasherDefault<MarkHasher>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Mark(pub u64);
impl Mark {
#[inline]
pub fn of(name: &str) -> Self {
let mut hasher = FieldHasher::default();
hasher.write(name.as_bytes());
Self(hasher.finish())
}
}
#[derive(Debug, Clone)]
pub struct Entry {
pub field: HeaderField,
pub mark: Mark,
}
impl Entry {
pub fn of(field: HeaderField) -> Self {
Self { mark: Mark::of(&field.name), field }
}
pub fn size(&self) -> usize {
self.field.size()
}
#[inline]
pub fn named(&self, mark: Mark, field: &HeaderField) -> bool {
self.mark == mark && self.field.name == field.name
}
#[inline]
pub fn valued(&self, field: &HeaderField) -> bool {
self.field.value == field.value
}
}
pub struct NameEntry {
pub name: &'static str,
pub first: usize,
pub values: Vec<(&'static str, usize)>,
}
pub struct StaticIndex {
by_name: MarkMap<NameEntry>,
}
impl StaticIndex {
pub fn new(entries: &'static [HeaderField], base: usize) -> Self {
let mut by_name: MarkMap<NameEntry> = MarkMap::default();
for (offset, entry) in entries.iter().enumerate() {
let index = base + offset;
let name = entry.name.as_str();
by_name
.entry(Mark::of(name))
.or_insert_with(|| NameEntry { name, first: index, values: Vec::new() })
.values
.push((entry.value.as_str(), index));
}
Self { by_name }
}
pub fn lookup(&self, name: &str, value: &str) -> (Option<usize>, Option<usize>) {
let Some(entry) = self.by_name.get(&Mark::of(name)).filter(|entry| scan::same(entry.name.as_bytes(), name.as_bytes())) else {
return (None, None);
};
let exact = entry.values.iter().find(|(candidate, _)| scan::same(candidate.as_bytes(), value.as_bytes())).map(|(_, index)| *index);
(Some(entry.first), exact)
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
IntegerOverflow,
Incomplete,
Huffman(huffman::DecodeError),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IntegerOverflow => write!(f, "integer representation overflowed"),
Self::Incomplete => write!(f, "representation ends before the input does"),
Self::Huffman(err) => write!(f, "huffman error: {err}"),
}
}
}
impl std::error::Error for Error {}
impl From<huffman::DecodeError> for Error {
fn from(err: huffman::DecodeError) -> Self {
Self::Huffman(err)
}
}
pub struct Integer;
impl Integer {
pub fn limit(prefix_bits: u8) -> u64 {
(1u64 << prefix_bits.min(63)) - 1
}
pub fn encode(out: &mut Vec<u8>, value: u64, prefix_bits: u8, flags: u8) {
let limit = Self::limit(prefix_bits);
if value < limit {
out.push(flags | value as u8);
return;
}
out.push(flags | limit as u8);
let mut rest = value - limit;
while rest >= 128 {
out.push((rest % 128) as u8 | 0x80);
rest /= 128;
}
out.push(rest as u8);
}
pub fn decode(input: &[u8], prefix_bits: u8) -> Result<(usize, u64), Error> {
let limit = Self::limit(prefix_bits);
let first = *input.first().ok_or(Error::Incomplete)?;
let mut value = first as u64 & limit;
if value < limit {
return Ok((1, value));
}
let mut consumed = 1;
let mut shift = 0;
loop {
let octet = *input.get(consumed).ok_or(Error::Incomplete)?;
consumed += 1;
value = (octet as u64 & 0x7f)
.checked_shl(shift)
.and_then(|part| value.checked_add(part))
.ok_or(Error::IntegerOverflow)?;
if octet & 0x80 == 0 {
return Ok((consumed, value));
}
shift += 7;
if shift >= 64 {
return Err(Error::IntegerOverflow);
}
}
}
}
pub struct StringLiteral;
impl StringLiteral {
pub const MAX_PREFIX_BITS: u8 = 7;
#[inline]
pub fn huffman_mark(prefix_bits: u8) -> u8 {
1u8 << prefix_bits.min(Self::MAX_PREFIX_BITS)
}
pub fn encode(out: &mut Vec<u8>, value: &[u8], prefix_bits: u8, flags: u8, huffman: bool) {
let prefix_bits = prefix_bits.min(Self::MAX_PREFIX_BITS);
if huffman {
let encoded = huffman::encoded_len(value);
Integer::encode(out, encoded as u64, prefix_bits, flags | Self::huffman_mark(prefix_bits));
huffman::encode_sized(value, encoded, out);
} else {
Integer::encode(out, value.len() as u64, prefix_bits, flags);
out.extend_from_slice(value);
}
}
pub fn encode_shorter(out: &mut Vec<u8>, value: &[u8], prefix_bits: u8, flags: u8) {
let prefix_bits = prefix_bits.min(Self::MAX_PREFIX_BITS);
let encoded = huffman::encoded_len(value);
if encoded < value.len() {
Integer::encode(out, encoded as u64, prefix_bits, flags | Self::huffman_mark(prefix_bits));
huffman::encode_sized(value, encoded, out);
} else {
Integer::encode(out, value.len() as u64, prefix_bits, flags);
out.extend_from_slice(value);
}
}
pub fn prefers_huffman(value: &[u8]) -> bool {
huffman::encoded_len(value) < value.len()
}
pub fn decode(input: &[u8], prefix_bits: u8) -> Result<(usize, Vec<u8>), Error> {
let mut value = Vec::new();
let consumed = Self::decode_into(input, prefix_bits, &mut value)?;
Ok((consumed, value))
}
pub fn decode_into(input: &[u8], prefix_bits: u8, scratch: &mut Vec<u8>) -> Result<usize, Error> {
Self::decode_into_ascii(input, prefix_bits, scratch).map(|(consumed, _)| consumed)
}
pub fn decode_into_ascii(input: &[u8], prefix_bits: u8, scratch: &mut Vec<u8>) -> Result<(usize, bool), Error> {
let prefix_bits = prefix_bits.min(Self::MAX_PREFIX_BITS);
let huffman = input.first().ok_or(Error::Incomplete)? & Self::huffman_mark(prefix_bits) != 0;
let (prefix, length) = Integer::decode(input, prefix_bits)?;
let length = length as usize;
let end = prefix.checked_add(length).ok_or(Error::Incomplete)?;
let octets = input.get(prefix..end).ok_or(Error::Incomplete)?;
scratch.clear();
let ascii = if huffman {
huffman::decode_into_ascii(octets, scratch)?
} else {
scratch.extend_from_slice(octets);
octets.is_ascii()
};
Ok((end, ascii))
}
pub fn decode_text(input: &[u8], prefix_bits: u8) -> Result<(usize, Text), Error> {
let mut scratch = Vec::new();
Self::decode_text_into(input, prefix_bits, &mut scratch)
}
pub fn decode_text_into(input: &[u8], prefix_bits: u8, scratch: &mut Vec<u8>) -> Result<(usize, Text), Error> {
let (consumed, ascii) = Self::decode_into_ascii(input, prefix_bits, scratch)?;
Ok((consumed, Self::text(scratch, ascii)))
}
#[inline]
pub fn text(octets: &[u8], ascii: bool) -> Text {
match ascii {
true => unsafe { Text::from_verified_ascii(octets) },
false => Text::from_utf8_lossy(octets),
}
}
}