use crate::hpack::table::{HeaderField, STATIC_TABLE_SIZE};
#[derive(Debug, Clone)]
pub struct DynamicTable {
entries: Vec<HeaderField>,
size: usize,
max_size: usize,
}
impl DynamicTable {
#[must_use]
pub fn new(max_size: usize) -> Self {
Self {
entries: Vec::new(),
size: 0,
max_size,
}
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn size(&self) -> usize {
self.size
}
#[must_use]
pub fn max_size(&self) -> usize {
self.max_size
}
pub fn set_max_size(&mut self, max_size: usize) {
self.max_size = max_size;
self.evict();
}
pub fn insert(&mut self, name: Vec<u8>, value: Vec<u8>) {
let entry = HeaderField::new(name, value);
let entry_size = entry.size();
if entry_size > self.max_size {
self.clear();
return;
}
while self.size + entry_size > self.max_size {
if let Some(removed) = self.entries.pop() {
self.size -= removed.size();
} else {
break;
}
}
self.entries.insert(0, entry);
self.size += entry_size;
}
#[must_use]
pub fn get(&self, index: usize) -> Option<&HeaderField> {
self.entries.get(index)
}
#[must_use]
pub fn get_by_absolute_index(&self, index: usize) -> Option<&HeaderField> {
if index <= STATIC_TABLE_SIZE {
None
} else {
self.get(index - STATIC_TABLE_SIZE - 1)
}
}
#[must_use]
pub fn find(&self, name: &[u8], value: &[u8]) -> Option<(usize, bool)> {
let mut name_match = None;
for (i, entry) in self.entries.iter().enumerate() {
if entry.name == name {
if entry.value == value {
return Some((i, true));
}
if name_match.is_none() {
name_match = Some(i);
}
}
}
name_match.map(|i| (i, false))
}
pub fn clear(&mut self) {
self.entries.clear();
self.size = 0;
}
fn evict(&mut self) {
while self.size > self.max_size {
if let Some(removed) = self.entries.pop() {
self.size -= removed.size();
} else {
break;
}
}
}
}
impl Default for DynamicTable {
fn default() -> Self {
Self::new(crate::settings::DEFAULT_HEADER_TABLE_SIZE as usize)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_insert_and_get() {
let mut table = DynamicTable::new(4096);
table.insert(b"content-type".to_vec(), b"text/html".to_vec());
assert_eq!(table.len(), 1);
let entry = table.get(0).unwrap();
assert_eq!(entry.name, b"content-type");
assert_eq!(entry.value, b"text/html");
}
#[test]
fn test_fifo_order() {
let mut table = DynamicTable::new(4096);
table.insert(b"first".to_vec(), b"1".to_vec());
table.insert(b"second".to_vec(), b"2".to_vec());
table.insert(b"third".to_vec(), b"3".to_vec());
assert_eq!(table.get(0).unwrap().name, b"third");
assert_eq!(table.get(1).unwrap().name, b"second");
assert_eq!(table.get(2).unwrap().name, b"first");
}
#[test]
fn test_eviction() {
let mut table = DynamicTable::new(100);
table.insert(b"name1".to_vec(), b"1".to_vec());
table.insert(b"name2".to_vec(), b"2".to_vec());
assert_eq!(table.len(), 2);
table.insert(b"name3".to_vec(), b"3".to_vec());
assert_eq!(table.len(), 2);
assert_eq!(table.get(0).unwrap().name, b"name3");
assert_eq!(table.get(1).unwrap().name, b"name2");
}
#[test]
fn test_set_max_size() {
let mut table = DynamicTable::new(4096);
table.insert(b"name1".to_vec(), b"value1".to_vec());
table.insert(b"name2".to_vec(), b"value2".to_vec());
assert_eq!(table.len(), 2);
table.set_max_size(0);
assert_eq!(table.len(), 0);
}
#[test]
fn test_find() {
let mut table = DynamicTable::new(4096);
table.insert(b"content-type".to_vec(), b"text/html".to_vec());
table.insert(b"content-type".to_vec(), b"application/json".to_vec());
let result = table.find(b"content-type", b"application/json");
assert_eq!(result, Some((0, true)));
let result = table.find(b"content-type", b"text/plain");
assert_eq!(result, Some((0, false)));
let result = table.find(b"x-custom", b"value");
assert_eq!(result, None);
}
#[test]
fn test_entry_too_large() {
let mut table = DynamicTable::new(50);
table.insert(b"very-long-name".to_vec(), b"very-long-value".to_vec());
assert!(table.is_empty());
}
}