#[derive(Clone)]
pub struct DynamicEntry {
name: Vec<u8>,
value: Vec<u8>,
}
impl DynamicEntry {
pub fn new(name: Vec<u8>, value: Vec<u8>) -> Self {
Self { name, value }
}
pub fn name(&self) -> &[u8] {
&self.name
}
pub fn value(&self) -> &[u8] {
&self.value
}
pub fn size(&self) -> usize {
32 + self.name.len() + self.value.len()
}
}
pub struct DynamicTable {
entries: Vec<DynamicEntry>,
max_size: usize,
current_size: usize,
}
impl DynamicTable {
pub fn new(max_size: usize) -> Self {
Self {
entries: Vec::new(),
max_size,
current_size: 0,
}
}
pub fn set_max_size(&mut self, max_size: usize) {
self.max_size = max_size;
self.evict_to_max_size();
}
#[allow(dead_code)]
pub fn max_size(&self) -> usize {
self.max_size
}
#[allow(dead_code)]
pub fn current_size(&self) -> usize {
self.current_size
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.entries.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn get(&self, index: usize) -> Option<&DynamicEntry> {
if index == 0 || index > self.entries.len() {
return None;
}
self.entries.get(index - 1)
}
pub fn find(&self, name: &[u8], value: &[u8]) -> Option<usize> {
self.entries
.iter()
.position(|e| e.name() == name && e.value() == value)
.map(|idx| idx + 1)
}
pub fn find_by_name(&self, name: &[u8]) -> Option<usize> {
self.entries
.iter()
.position(|e| e.name() == name)
.map(|idx| idx + 1)
}
pub fn add(&mut self, name: Vec<u8>, value: Vec<u8>) {
let entry = DynamicEntry::new(name, value);
let entry_size = entry.size();
while self.current_size + entry_size > self.max_size && !self.entries.is_empty() {
self.evict_oldest();
}
if entry_size > self.max_size {
self.entries.clear();
self.current_size = 0;
return;
}
self.entries.insert(0, entry);
self.current_size += entry_size;
}
fn evict_to_max_size(&mut self) {
while self.current_size > self.max_size && !self.entries.is_empty() {
self.evict_oldest();
}
}
fn evict_oldest(&mut self) {
if let Some(entry) = self.entries.pop() {
self.current_size -= entry.size();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dynamic_table_add() {
let mut table = DynamicTable::new(4096);
table.add(b"custom-key".to_vec(), b"custom-value".to_vec());
assert_eq!(table.len(), 1);
assert!(!table.is_empty());
let entry = table.get(1).unwrap();
assert_eq!(entry.name(), b"custom-key");
assert_eq!(entry.value(), b"custom-value");
}
#[test]
fn test_dynamic_table_fifo() {
let mut table = DynamicTable::new(4096);
table.add(b"first".to_vec(), b"value1".to_vec());
table.add(b"second".to_vec(), b"value2".to_vec());
assert_eq!(table.get(1).unwrap().name(), b"second");
assert_eq!(table.get(2).unwrap().name(), b"first");
}
#[test]
fn test_dynamic_table_eviction() {
let mut table = DynamicTable::new(100);
for i in 0..10 {
table.add(
format!("name{}", i).into_bytes(),
format!("value{}", i).into_bytes(),
);
}
assert!(table.current_size() <= 100);
}
#[test]
fn test_dynamic_table_find() {
let mut table = DynamicTable::new(4096);
table.add(b"custom-key".to_vec(), b"custom-value".to_vec());
assert_eq!(table.find(b"custom-key", b"custom-value"), Some(1));
assert_eq!(table.find(b"custom-key", b"other-value"), None);
assert_eq!(table.find_by_name(b"custom-key"), Some(1));
}
#[test]
fn test_dynamic_table_size_reduction() {
let mut table = DynamicTable::new(4096);
assert_eq!(table.max_size(), 4096);
table.add(b"name1".to_vec(), b"value1".to_vec());
table.add(b"name2".to_vec(), b"value2".to_vec());
let initial_size = table.current_size();
table.set_max_size(50);
assert_eq!(table.max_size(), 50);
assert!(table.current_size() <= 50);
assert!(table.current_size() < initial_size);
}
}