750 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			750 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use super::Header;
 | |
| 
 | |
| use fnv::FnvHasher;
 | |
| use http::header;
 | |
| use http::method::Method;
 | |
| 
 | |
| use std::{cmp, mem, usize};
 | |
| use std::collections::VecDeque;
 | |
| use std::hash::{Hash, Hasher};
 | |
| 
 | |
| /// HPACK encoder table
 | |
| #[derive(Debug)]
 | |
| pub struct Table {
 | |
|     mask: usize,
 | |
|     indices: Vec<Option<Pos>>,
 | |
|     slots: VecDeque<Slot>,
 | |
|     inserted: usize,
 | |
|     // Size is in bytes
 | |
|     size: usize,
 | |
|     max_size: usize,
 | |
| }
 | |
| 
 | |
| #[derive(Debug)]
 | |
| pub enum Index {
 | |
|     // The header is already fully indexed
 | |
|     Indexed(usize, Header),
 | |
| 
 | |
|     // The name is indexed, but not the value
 | |
|     Name(usize, Header),
 | |
| 
 | |
|     // The full header has been inserted into the table.
 | |
|     Inserted(usize),
 | |
| 
 | |
|     // Only the value has been inserted (hpack table idx, slots idx)
 | |
|     InsertedValue(usize, usize),
 | |
| 
 | |
|     // The header is not indexed by this table
 | |
|     NotIndexed(Header),
 | |
| }
 | |
| 
 | |
| #[derive(Debug)]
 | |
| struct Slot {
 | |
|     hash: HashValue,
 | |
|     header: Header,
 | |
|     next: Option<usize>,
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Clone, Copy, Eq, PartialEq)]
 | |
| struct Pos {
 | |
|     index: usize,
 | |
|     hash: HashValue,
 | |
| }
 | |
| 
 | |
| #[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | |
| struct HashValue(usize);
 | |
| 
 | |
| const MAX_SIZE: usize = (1 << 16);
 | |
| const DYN_OFFSET: usize = 62;
 | |
| 
 | |
| macro_rules! probe_loop {
 | |
|     ($probe_var: ident < $len: expr, $body: expr) => {
 | |
|         debug_assert!($len > 0);
 | |
|         loop {
 | |
|             if $probe_var < $len {
 | |
|                 $body
 | |
|                 $probe_var += 1;
 | |
|             } else {
 | |
|                 $probe_var = 0;
 | |
|             }
 | |
|         }
 | |
|     };
 | |
| }
 | |
| 
 | |
| impl Table {
 | |
|     pub fn new(max_size: usize, capacity: usize) -> Table {
 | |
|         if capacity == 0 {
 | |
|             Table {
 | |
|                 mask: 0,
 | |
|                 indices: vec![],
 | |
|                 slots: VecDeque::new(),
 | |
|                 inserted: 0,
 | |
|                 size: 0,
 | |
|                 max_size: max_size,
 | |
|             }
 | |
|         } else {
 | |
|             let capacity = cmp::max(to_raw_capacity(capacity).next_power_of_two(), 8);
 | |
| 
 | |
|             Table {
 | |
|                 mask: capacity.wrapping_sub(1),
 | |
|                 indices: vec![None; capacity],
 | |
|                 slots: VecDeque::with_capacity(usable_capacity(capacity)),
 | |
|                 inserted: 0,
 | |
|                 size: 0,
 | |
|                 max_size: max_size,
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[inline]
 | |
|     pub fn capacity(&self) -> usize {
 | |
|         usable_capacity(self.indices.len())
 | |
|     }
 | |
| 
 | |
|     pub fn max_size(&self) -> usize {
 | |
|         self.max_size
 | |
|     }
 | |
| 
 | |
|     /// Gets the header stored in the table
 | |
|     pub fn resolve<'a>(&'a self, index: &'a Index) -> &'a Header {
 | |
|         use self::Index::*;
 | |
| 
 | |
|         match *index {
 | |
|             Indexed(_, ref h) => h,
 | |
|             Name(_, ref h) => h,
 | |
|             Inserted(idx) => &self.slots[idx].header,
 | |
|             InsertedValue(_, idx) => &self.slots[idx].header,
 | |
|             NotIndexed(ref h) => h,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn resolve_idx(&self, index: &Index) -> usize {
 | |
|         use self::Index::*;
 | |
| 
 | |
|         match *index {
 | |
|             Indexed(idx, ..) => idx,
 | |
|             Name(idx, ..) => idx,
 | |
|             Inserted(idx) => idx + DYN_OFFSET,
 | |
|             InsertedValue(idx, _) => idx,
 | |
|             NotIndexed(_) => panic!("cannot resolve index"),
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// Index the header in the HPACK table.
 | |
|     pub fn index(&mut self, header: Header) -> Index {
 | |
|         // Check the static table
 | |
|         let statik = index_static(&header);
 | |
| 
 | |
|         // Don't index certain headers. This logic is borrowed from nghttp2.
 | |
|         if header.skip_value_index() {
 | |
|             // Right now, if this is true, the header name is always in the
 | |
|             // static table. At some point in the future, this might not be true
 | |
|             // and this logic will need to be updated.
 | |
|             return Index::new(statik, header);
 | |
|         }
 | |
| 
 | |
|         // If the header is already indexed by the static table, return that
 | |
|         if let Some((n, true)) = statik {
 | |
|             return Index::Indexed(n, header);
 | |
|         }
 | |
| 
 | |
|         // Don't index large headers
 | |
|         if header.len() * 4 > self.max_size * 3 {
 | |
|             return Index::new(statik, header);
 | |
|         }
 | |
| 
 | |
|         self.index_dynamic(header, statik)
 | |
|     }
 | |
| 
 | |
|     fn index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index {
 | |
|         debug_assert!(self.assert_valid_state("one"));
 | |
| 
 | |
|         if header.len() + self.size < self.max_size || !header.is_sensitive() {
 | |
|             // Only grow internal storage if needed
 | |
|             self.reserve_one();
 | |
|         }
 | |
| 
 | |
|         if self.indices.is_empty() {
 | |
|             // If `indices` is not empty, then it is impossible for all
 | |
|             // `indices` entries to be `Some`. So, we only need to check for the
 | |
|             // empty case.
 | |
|             return Index::new(statik, header);
 | |
|         }
 | |
| 
 | |
|         let hash = hash_header(&header);
 | |
| 
 | |
|         let desired_pos = desired_pos(self.mask, hash);
 | |
|         let mut probe = desired_pos;
 | |
|         let mut dist = 0;
 | |
| 
 | |
|         // Start at the ideal position, checking all slots
 | |
|         probe_loop!(probe < self.indices.len(), {
 | |
|             if let Some(pos) = self.indices[probe] {
 | |
|                 // The slot is already occupied, but check if it has a lower
 | |
|                 // displacement.
 | |
|                 let their_dist = probe_distance(self.mask, pos.hash, probe);
 | |
| 
 | |
|                 let slot_idx = pos.index.wrapping_add(self.inserted);
 | |
| 
 | |
|                 if their_dist < dist {
 | |
|                     // Index robinhood
 | |
|                     return self.index_vacant(header, hash, dist, probe, statik);
 | |
|                 } else if pos.hash == hash && self.slots[slot_idx].header.name() == header.name() {
 | |
|                     // Matching name, check values
 | |
|                     return self.index_occupied(header, hash, pos.index);
 | |
|                 }
 | |
|             } else {
 | |
|                 return self.index_vacant(header, hash, dist, probe, statik);
 | |
|             }
 | |
| 
 | |
|             dist += 1;
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     fn index_occupied(&mut self, header: Header, hash: HashValue, mut index: usize) -> Index {
 | |
|         debug_assert!(self.assert_valid_state("top"));
 | |
| 
 | |
|         // There already is a match for the given header name. Check if a value
 | |
|         // matches. The header will also only be inserted if the table is not at
 | |
|         // capacity.
 | |
|         loop {
 | |
|             // Compute the real index into the VecDeque
 | |
|             let real_idx = index.wrapping_add(self.inserted);
 | |
| 
 | |
|             if self.slots[real_idx].header.value_eq(&header) {
 | |
|                 // We have a full match!
 | |
|                 return Index::Indexed(real_idx + DYN_OFFSET, header);
 | |
|             }
 | |
| 
 | |
|             if let Some(next) = self.slots[real_idx].next {
 | |
|                 index = next;
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             if header.is_sensitive() {
 | |
|                 return Index::Name(real_idx + DYN_OFFSET, header);
 | |
|             }
 | |
| 
 | |
|             self.update_size(header.len(), Some(index));
 | |
| 
 | |
|             // Insert the new header
 | |
|             self.insert(header, hash);
 | |
| 
 | |
|             // Recompute real_idx as it just changed.
 | |
|             let new_real_idx = index.wrapping_add(self.inserted);
 | |
| 
 | |
|             // The previous node in the linked list may have gotten evicted
 | |
|             // while making room for this header.
 | |
|             if new_real_idx < self.slots.len() {
 | |
|                 let idx = 0usize.wrapping_sub(self.inserted);
 | |
| 
 | |
|                 self.slots[new_real_idx].next = Some(idx);
 | |
|             }
 | |
| 
 | |
|             debug_assert!(self.assert_valid_state("bottom"));
 | |
| 
 | |
|             // Even if the previous header was evicted, we can still reference
 | |
|             // it when inserting the new one...
 | |
|             return Index::InsertedValue(real_idx + DYN_OFFSET, 0);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn index_vacant(
 | |
|         &mut self,
 | |
|         header: Header,
 | |
|         hash: HashValue,
 | |
|         mut dist: usize,
 | |
|         mut probe: usize,
 | |
|         statik: Option<(usize, bool)>,
 | |
|     ) -> Index {
 | |
|         if header.is_sensitive() {
 | |
|             return Index::new(statik, header);
 | |
|         }
 | |
| 
 | |
|         debug_assert!(self.assert_valid_state("top"));
 | |
|         debug_assert!(dist == 0 || self.indices[probe.wrapping_sub(1) & self.mask].is_some());
 | |
| 
 | |
|         // Passing in `usize::MAX` for prev_idx since there is no previous
 | |
|         // header in this case.
 | |
|         if self.update_size(header.len(), None) {
 | |
|             while dist != 0 {
 | |
|                 let back = probe.wrapping_sub(1) & self.mask;
 | |
| 
 | |
|                 if let Some(pos) = self.indices[back] {
 | |
|                     let their_dist = probe_distance(self.mask, pos.hash, back);
 | |
| 
 | |
|                     if their_dist < (dist - 1) {
 | |
|                         probe = back;
 | |
|                         dist -= 1;
 | |
|                     } else {
 | |
|                         break;
 | |
|                     }
 | |
|                 } else {
 | |
|                     probe = back;
 | |
|                     dist -= 1;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         debug_assert!(self.assert_valid_state("after update"));
 | |
| 
 | |
|         self.insert(header, hash);
 | |
| 
 | |
|         let pos_idx = 0usize.wrapping_sub(self.inserted);
 | |
| 
 | |
|         let prev = mem::replace(
 | |
|             &mut self.indices[probe],
 | |
|             Some(Pos {
 | |
|                 index: pos_idx,
 | |
|                 hash: hash,
 | |
|             }),
 | |
|         );
 | |
| 
 | |
|         if let Some(mut prev) = prev {
 | |
|             // Shift forward
 | |
|             let mut probe = probe + 1;
 | |
| 
 | |
|             probe_loop!(probe < self.indices.len(), {
 | |
|                 let pos = &mut self.indices[probe as usize];
 | |
| 
 | |
|                 prev = match mem::replace(pos, Some(prev)) {
 | |
|                     Some(p) => p,
 | |
|                     None => break,
 | |
|                 };
 | |
|             });
 | |
|         }
 | |
| 
 | |
|         debug_assert!(self.assert_valid_state("bottom"));
 | |
| 
 | |
|         if let Some((n, _)) = statik {
 | |
|             Index::InsertedValue(n, 0)
 | |
|         } else {
 | |
|             Index::Inserted(0)
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn insert(&mut self, header: Header, hash: HashValue) {
 | |
|         self.inserted = self.inserted.wrapping_add(1);
 | |
| 
 | |
|         self.slots.push_front(Slot {
 | |
|             hash: hash,
 | |
|             header: header,
 | |
|             next: None,
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     pub fn resize(&mut self, size: usize) {
 | |
|         self.max_size = size;
 | |
| 
 | |
|         if size == 0 {
 | |
|             self.size = 0;
 | |
| 
 | |
|             for i in &mut self.indices {
 | |
|                 *i = None;
 | |
|             }
 | |
| 
 | |
|             self.slots.clear();
 | |
|             self.inserted = 0;
 | |
|         } else {
 | |
|             self.converge(None);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fn update_size(&mut self, len: usize, prev_idx: Option<usize>) -> bool {
 | |
|         self.size += len;
 | |
|         self.converge(prev_idx)
 | |
|     }
 | |
| 
 | |
|     fn converge(&mut self, prev_idx: Option<usize>) -> bool {
 | |
|         let mut ret = false;
 | |
| 
 | |
|         while self.size > self.max_size {
 | |
|             ret = true;
 | |
|             self.evict(prev_idx);
 | |
|         }
 | |
| 
 | |
|         ret
 | |
|     }
 | |
| 
 | |
|     fn evict(&mut self, prev_idx: Option<usize>) {
 | |
|         let pos_idx = (self.slots.len() - 1).wrapping_sub(self.inserted);
 | |
| 
 | |
|         debug_assert!(!self.slots.is_empty());
 | |
|         debug_assert!(self.assert_valid_state("one"));
 | |
| 
 | |
|         // Remove the header
 | |
|         let slot = self.slots.pop_back().unwrap();
 | |
|         let mut probe = desired_pos(self.mask, slot.hash);
 | |
| 
 | |
|         // Update the size
 | |
|         self.size -= slot.header.len();
 | |
| 
 | |
|         debug_assert_eq!(
 | |
|             self.indices
 | |
|                 .iter()
 | |
|                 .filter_map(|p| *p)
 | |
|                 .filter(|p| p.index == pos_idx)
 | |
|                 .count(),
 | |
|             1
 | |
|         );
 | |
| 
 | |
|         // Find the associated position
 | |
|         probe_loop!(probe < self.indices.len(), {
 | |
|             debug_assert!(!self.indices[probe].is_none());
 | |
| 
 | |
|             let mut pos = self.indices[probe].unwrap();
 | |
| 
 | |
|             if pos.index == pos_idx {
 | |
|                 if let Some(idx) = slot.next {
 | |
|                     pos.index = idx;
 | |
|                     self.indices[probe] = Some(pos);
 | |
|                 } else if Some(pos.index) == prev_idx {
 | |
|                     pos.index = 0usize.wrapping_sub(self.inserted + 1);
 | |
|                     self.indices[probe] = Some(pos);
 | |
|                 } else {
 | |
|                     self.indices[probe] = None;
 | |
|                     self.remove_phase_two(probe);
 | |
|                 }
 | |
| 
 | |
|                 break;
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         debug_assert!(self.assert_valid_state("two"));
 | |
|     }
 | |
| 
 | |
|     // Shifts all indices that were displaced by the header that has just been
 | |
|     // removed.
 | |
|     fn remove_phase_two(&mut self, probe: usize) {
 | |
|         let mut last_probe = probe;
 | |
|         let mut probe = probe + 1;
 | |
| 
 | |
|         probe_loop!(probe < self.indices.len(), {
 | |
|             if let Some(pos) = self.indices[probe] {
 | |
|                 if probe_distance(self.mask, pos.hash, probe) > 0 {
 | |
|                     self.indices[last_probe] = self.indices[probe].take();
 | |
|                 } else {
 | |
|                     break;
 | |
|                 }
 | |
|             } else {
 | |
|                 break;
 | |
|             }
 | |
| 
 | |
|             last_probe = probe;
 | |
|         });
 | |
| 
 | |
|         debug_assert!(self.assert_valid_state("two"));
 | |
|     }
 | |
| 
 | |
|     fn reserve_one(&mut self) {
 | |
|         let len = self.slots.len();
 | |
| 
 | |
|         if len == self.capacity() {
 | |
|             if len == 0 {
 | |
|                 let new_raw_cap = 8;
 | |
|                 self.mask = 8 - 1;
 | |
|                 self.indices = vec![None; new_raw_cap];
 | |
|             } else {
 | |
|                 let raw_cap = self.indices.len();
 | |
|                 self.grow(raw_cap << 1);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[inline]
 | |
|     fn grow(&mut self, new_raw_cap: usize) {
 | |
|         // This path can never be reached when handling the first allocation in
 | |
|         // the map.
 | |
| 
 | |
|         debug_assert!(self.assert_valid_state("top"));
 | |
| 
 | |
|         // find first ideally placed element -- start of cluster
 | |
|         let mut first_ideal = 0;
 | |
| 
 | |
|         for (i, pos) in self.indices.iter().enumerate() {
 | |
|             if let Some(pos) = *pos {
 | |
|                 if 0 == probe_distance(self.mask, pos.hash, i) {
 | |
|                     first_ideal = i;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // visit the entries in an order where we can simply reinsert them
 | |
|         // into self.indices without any bucket stealing.
 | |
|         let old_indices = mem::replace(&mut self.indices, vec![None; new_raw_cap]);
 | |
|         self.mask = new_raw_cap.wrapping_sub(1);
 | |
| 
 | |
|         for &pos in &old_indices[first_ideal..] {
 | |
|             self.reinsert_entry_in_order(pos);
 | |
|         }
 | |
| 
 | |
|         for &pos in &old_indices[..first_ideal] {
 | |
|             self.reinsert_entry_in_order(pos);
 | |
|         }
 | |
| 
 | |
|         debug_assert!(self.assert_valid_state("bottom"));
 | |
|     }
 | |
| 
 | |
|     fn reinsert_entry_in_order(&mut self, pos: Option<Pos>) {
 | |
|         if let Some(pos) = pos {
 | |
|             // Find first empty bucket and insert there
 | |
|             let mut probe = desired_pos(self.mask, pos.hash);
 | |
| 
 | |
|             probe_loop!(probe < self.indices.len(), {
 | |
|                 if self.indices[probe].is_none() {
 | |
|                     // empty bucket, insert here
 | |
|                     self.indices[probe] = Some(pos);
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 debug_assert!({
 | |
|                     let them = self.indices[probe].unwrap();
 | |
|                     let their_distance = probe_distance(self.mask, them.hash, probe);
 | |
|                     let our_distance = probe_distance(self.mask, pos.hash, probe);
 | |
| 
 | |
|                     their_distance >= our_distance
 | |
|                 });
 | |
|             });
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     #[cfg(not(test))]
 | |
|     fn assert_valid_state(&self, _: &'static str) -> bool {
 | |
|         true
 | |
|     }
 | |
| 
 | |
|     #[cfg(test)]
 | |
|     fn assert_valid_state(&self, _msg: &'static str) -> bool {
 | |
|         /*
 | |
|         // Checks that the internal map structure is valid
 | |
|         //
 | |
|         // Ensure all hash codes in indices match the associated slot
 | |
|         for pos in &self.indices {
 | |
|             if let Some(pos) = *pos {
 | |
|                 let real_idx = pos.index.wrapping_add(self.inserted);
 | |
| 
 | |
|                 if real_idx.wrapping_add(1) != 0 {
 | |
|                     assert!(real_idx < self.slots.len(),
 | |
|                             "out of index; real={}; len={}, msg={}",
 | |
|                             real_idx, self.slots.len(), msg);
 | |
| 
 | |
|                     assert_eq!(pos.hash, self.slots[real_idx].hash,
 | |
|                                "index hash does not match slot; msg={}", msg);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Every index is only available once
 | |
|         for i in 0..self.indices.len() {
 | |
|             if self.indices[i].is_none() {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             for j in i+1..self.indices.len() {
 | |
|                 assert_ne!(self.indices[i], self.indices[j],
 | |
|                             "duplicate indices; msg={}", msg);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         for (index, slot) in self.slots.iter().enumerate() {
 | |
|             let mut indexed = None;
 | |
| 
 | |
|             // First, see if the slot is indexed
 | |
|             for (i, pos) in self.indices.iter().enumerate() {
 | |
|                 if let Some(pos) = *pos {
 | |
|                     let real_idx = pos.index.wrapping_add(self.inserted);
 | |
|                     if real_idx == index {
 | |
|                         indexed = Some(i);
 | |
|                         // Already know that there is no dup, so break
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if let Some(actual) = indexed {
 | |
|                 // Ensure that it is accessible..
 | |
|                 let desired = desired_pos(self.mask, slot.hash);
 | |
|                 let mut probe = desired;
 | |
|                 let mut dist = 0;
 | |
| 
 | |
|                 probe_loop!(probe < self.indices.len(), {
 | |
|                     assert!(self.indices[probe].is_some(),
 | |
|                             "unexpected empty slot; probe={}; hash={:?}; msg={}",
 | |
|                             probe, slot.hash, msg);
 | |
| 
 | |
|                     let pos = self.indices[probe].unwrap();
 | |
| 
 | |
|                     let their_dist = probe_distance(self.mask, pos.hash, probe);
 | |
|                     let real_idx = pos.index.wrapping_add(self.inserted);
 | |
| 
 | |
|                     if real_idx == index {
 | |
|                         break;
 | |
|                     }
 | |
| 
 | |
|                     assert!(dist <= their_dist,
 | |
|                             "could not find entry; actual={}; desired={};" +
 | |
|                             "probe={}, dist={}; their_dist={}; index={}; msg={}",
 | |
|                             actual, desired, probe, dist, their_dist,
 | |
|                             index.wrapping_sub(self.inserted), msg);
 | |
| 
 | |
|                     dist += 1;
 | |
|                 });
 | |
|             } else {
 | |
|                 // There is exactly one next link
 | |
|                 let cnt = self.slots.iter().map(|s| s.next)
 | |
|                     .filter(|n| *n == Some(index.wrapping_sub(self.inserted)))
 | |
|                     .count();
 | |
| 
 | |
|                 assert_eq!(1, cnt, "more than one node pointing here; msg={}", msg);
 | |
|             }
 | |
|         }
 | |
|     */
 | |
| 
 | |
|         // TODO: Ensure linked lists are correct: no cycles, etc...
 | |
| 
 | |
|         true
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[cfg(test)]
 | |
| impl Table {
 | |
|     /// Returns the number of headers in the table
 | |
|     pub fn len(&self) -> usize {
 | |
|         self.slots.len()
 | |
|     }
 | |
| 
 | |
|     /// Returns the table size
 | |
|     pub fn size(&self) -> usize {
 | |
|         self.size
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl Index {
 | |
|     fn new(v: Option<(usize, bool)>, e: Header) -> Index {
 | |
|         match v {
 | |
|             None => Index::NotIndexed(e),
 | |
|             Some((n, true)) => Index::Indexed(n, e),
 | |
|             Some((n, false)) => Index::Name(n, e),
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| fn usable_capacity(cap: usize) -> usize {
 | |
|     cap - cap / 4
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| fn to_raw_capacity(n: usize) -> usize {
 | |
|     n + n / 3
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| fn desired_pos(mask: usize, hash: HashValue) -> usize {
 | |
|     (hash.0 & mask) as usize
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize {
 | |
|     current.wrapping_sub(desired_pos(mask, hash)) & mask as usize
 | |
| }
 | |
| 
 | |
| fn hash_header(header: &Header) -> HashValue {
 | |
|     const MASK: u64 = (MAX_SIZE as u64) - 1;
 | |
| 
 | |
|     let mut h = FnvHasher::default();
 | |
|     header.name().hash(&mut h);
 | |
|     HashValue((h.finish() & MASK) as usize)
 | |
| }
 | |
| 
 | |
| /// Checks the static table for the header. If found, returns the index and a
 | |
| /// boolean representing if the value matched as well.
 | |
| fn index_static(header: &Header) -> Option<(usize, bool)> {
 | |
|     match *header {
 | |
|         Header::Field {
 | |
|             ref name,
 | |
|             ref value,
 | |
|         } => match *name {
 | |
|             header::ACCEPT_CHARSET => Some((15, false)),
 | |
|             header::ACCEPT_ENCODING => if value == "gzip, deflate" {
 | |
|                 Some((16, true))
 | |
|             } else {
 | |
|                 Some((16, false))
 | |
|             },
 | |
|             header::ACCEPT_LANGUAGE => Some((17, false)),
 | |
|             header::ACCEPT_RANGES => Some((18, false)),
 | |
|             header::ACCEPT => Some((19, false)),
 | |
|             header::ACCESS_CONTROL_ALLOW_ORIGIN => Some((20, false)),
 | |
|             header::AGE => Some((21, false)),
 | |
|             header::ALLOW => Some((22, false)),
 | |
|             header::AUTHORIZATION => Some((23, false)),
 | |
|             header::CACHE_CONTROL => Some((24, false)),
 | |
|             header::CONTENT_DISPOSITION => Some((25, false)),
 | |
|             header::CONTENT_ENCODING => Some((26, false)),
 | |
|             header::CONTENT_LANGUAGE => Some((27, false)),
 | |
|             header::CONTENT_LENGTH => Some((28, false)),
 | |
|             header::CONTENT_LOCATION => Some((29, false)),
 | |
|             header::CONTENT_RANGE => Some((30, false)),
 | |
|             header::CONTENT_TYPE => Some((31, false)),
 | |
|             header::COOKIE => Some((32, false)),
 | |
|             header::DATE => Some((33, false)),
 | |
|             header::ETAG => Some((34, false)),
 | |
|             header::EXPECT => Some((35, false)),
 | |
|             header::EXPIRES => Some((36, false)),
 | |
|             header::FROM => Some((37, false)),
 | |
|             header::HOST => Some((38, false)),
 | |
|             header::IF_MATCH => Some((39, false)),
 | |
|             header::IF_MODIFIED_SINCE => Some((40, false)),
 | |
|             header::IF_NONE_MATCH => Some((41, false)),
 | |
|             header::IF_RANGE => Some((42, false)),
 | |
|             header::IF_UNMODIFIED_SINCE => Some((43, false)),
 | |
|             header::LAST_MODIFIED => Some((44, false)),
 | |
|             header::LINK => Some((45, false)),
 | |
|             header::LOCATION => Some((46, false)),
 | |
|             header::MAX_FORWARDS => Some((47, false)),
 | |
|             header::PROXY_AUTHENTICATE => Some((48, false)),
 | |
|             header::PROXY_AUTHORIZATION => Some((49, false)),
 | |
|             header::RANGE => Some((50, false)),
 | |
|             header::REFERER => Some((51, false)),
 | |
|             header::REFRESH => Some((52, false)),
 | |
|             header::RETRY_AFTER => Some((53, false)),
 | |
|             header::SERVER => Some((54, false)),
 | |
|             header::SET_COOKIE => Some((55, false)),
 | |
|             header::STRICT_TRANSPORT_SECURITY => Some((56, false)),
 | |
|             header::TRANSFER_ENCODING => Some((57, false)),
 | |
|             header::USER_AGENT => Some((58, false)),
 | |
|             header::VARY => Some((59, false)),
 | |
|             header::VIA => Some((60, false)),
 | |
|             header::WWW_AUTHENTICATE => Some((61, false)),
 | |
|             _ => None,
 | |
|         },
 | |
|         Header::Authority(_) => Some((1, false)),
 | |
|         Header::Method(ref v) => match *v {
 | |
|             Method::GET => Some((2, true)),
 | |
|             Method::POST => Some((3, true)),
 | |
|             _ => Some((2, false)),
 | |
|         },
 | |
|         Header::Scheme(ref v) => match &**v {
 | |
|             "http" => Some((6, true)),
 | |
|             "https" => Some((7, true)),
 | |
|             _ => Some((6, false)),
 | |
|         },
 | |
|         Header::Path(ref v) => match &**v {
 | |
|             "/" => Some((4, true)),
 | |
|             "/index.html" => Some((5, true)),
 | |
|             _ => Some((4, false)),
 | |
|         },
 | |
|         Header::Status(ref v) => match u16::from(*v) {
 | |
|             200 => Some((8, true)),
 | |
|             204 => Some((9, true)),
 | |
|             206 => Some((10, true)),
 | |
|             304 => Some((11, true)),
 | |
|             400 => Some((12, true)),
 | |
|             404 => Some((13, true)),
 | |
|             500 => Some((14, true)),
 | |
|             _ => Some((8, false)),
 | |
|         },
 | |
|     }
 | |
| }
 |