More encoding work

This commit is contained in:
Carl Lerche
2017-06-02 14:33:09 -07:00
parent 2f8095e71a
commit a7da819e45
6 changed files with 310 additions and 194 deletions

View File

@@ -1,4 +1,4 @@
use super::{huffman, Entry, Key};
use super::{huffman, header as h2_header, Header};
use util::byte_str::FromUtf8Error;
use http::{method, header, status, StatusCode, Method};
@@ -124,7 +124,7 @@ enum Representation {
}
struct Table {
entries: VecDeque<Entry>,
entries: VecDeque<Header>,
size: usize,
max_size: usize,
}
@@ -152,7 +152,7 @@ impl Decoder {
/// Decodes the headers found in the given buffer.
pub fn decode<F>(&mut self, src: &Bytes, mut f: F) -> Result<(), DecoderError>
where F: FnMut(Entry)
where F: FnMut(Header)
{
use self::Representation::*;
@@ -227,14 +227,14 @@ impl Decoder {
}
fn decode_indexed(&self, buf: &mut Cursor<&Bytes>)
-> Result<Entry, DecoderError>
-> Result<Header, DecoderError>
{
let index = try!(decode_int(buf, 7));
self.table.get(index)
}
fn decode_literal(&self, buf: &mut Cursor<&Bytes>, index: bool)
-> Result<Entry, DecoderError>
-> Result<Header, DecoderError>
{
let prefix = if index {
6
@@ -251,12 +251,12 @@ impl Decoder {
let name = try!(decode_string(buf));
let value = try!(decode_string(buf));
Entry::new(name, value)
Header::new(name, value)
} else {
let e = try!(self.table.get(table_idx));
let value = try!(decode_string(buf));
e.key().into_entry(value)
e.name().into_entry(value)
}
}
}
@@ -419,7 +419,7 @@ impl Table {
///
/// This is according to the [HPACK spec, section 2.3.3.]
/// (http://http2.github.io/http2-spec/compression.html#index.address.space)
pub fn get(&self, index: usize) -> Result<Entry, DecoderError> {
pub fn get(&self, index: usize) -> Result<Header, DecoderError> {
if index == 0 {
return Err(DecoderError::InvalidTableIndex);
}
@@ -435,7 +435,7 @@ impl Table {
}
}
fn insert(&mut self, entry: Entry) {
fn insert(&mut self, entry: Header) {
let len = entry.len();
self.reserve(len);
@@ -518,211 +518,211 @@ impl From<status::FromStrError> for DecoderError {
}
/// Get an entry from the static table
pub fn get_static(idx: usize) -> Entry {
pub fn get_static(idx: usize) -> Header {
use http::{status, method, header};
use http::header::HeaderValue;
use util::byte_str::ByteStr;
match idx {
1 => Entry::Authority(ByteStr::from_static("")),
2 => Entry::Method(method::GET),
3 => Entry::Method(method::POST),
4 => Entry::Path(ByteStr::from_static("/")),
5 => Entry::Path(ByteStr::from_static("/index.html")),
6 => Entry::Scheme(ByteStr::from_static("http")),
7 => Entry::Scheme(ByteStr::from_static("https")),
8 => Entry::Status(status::OK),
9 => Entry::Status(status::NO_CONTENT),
10 => Entry::Status(status::PARTIAL_CONTENT),
11 => Entry::Status(status::NOT_MODIFIED),
12 => Entry::Status(status::BAD_REQUEST),
13 => Entry::Status(status::NOT_FOUND),
14 => Entry::Status(status::INTERNAL_SERVER_ERROR),
15 => Entry::Header {
1 => Header::Authority(ByteStr::from_static("")),
2 => Header::Method(method::GET),
3 => Header::Method(method::POST),
4 => Header::Path(ByteStr::from_static("/")),
5 => Header::Path(ByteStr::from_static("/index.html")),
6 => Header::Scheme(ByteStr::from_static("http")),
7 => Header::Scheme(ByteStr::from_static("https")),
8 => Header::Status(status::OK),
9 => Header::Status(status::NO_CONTENT),
10 => Header::Status(status::PARTIAL_CONTENT),
11 => Header::Status(status::NOT_MODIFIED),
12 => Header::Status(status::BAD_REQUEST),
13 => Header::Status(status::NOT_FOUND),
14 => Header::Status(status::INTERNAL_SERVER_ERROR),
15 => Header::Field {
name: header::ACCEPT_CHARSET,
value: HeaderValue::from_static(""),
},
16 => Entry::Header {
16 => Header::Field {
name: header::ACCEPT_ENCODING,
value: HeaderValue::from_static("gzip, deflate"),
},
17 => Entry::Header {
17 => Header::Field {
name: header::ACCEPT_LANGUAGE,
value: HeaderValue::from_static(""),
},
18 => Entry::Header {
18 => Header::Field {
name: header::ACCEPT_RANGES,
value: HeaderValue::from_static(""),
},
19 => Entry::Header {
19 => Header::Field {
name: header::ACCEPT,
value: HeaderValue::from_static(""),
},
20 => Entry::Header {
20 => Header::Field {
name: header::ACCESS_CONTROL_ALLOW_ORIGIN,
value: HeaderValue::from_static(""),
},
21 => Entry::Header {
21 => Header::Field {
name: header::AGE,
value: HeaderValue::from_static(""),
},
22 => Entry::Header {
22 => Header::Field {
name: header::ALLOW,
value: HeaderValue::from_static(""),
},
23 => Entry::Header {
23 => Header::Field {
name: header::AUTHORIZATION,
value: HeaderValue::from_static(""),
},
24 => Entry::Header {
24 => Header::Field {
name: header::CACHE_CONTROL,
value: HeaderValue::from_static(""),
},
25 => Entry::Header {
25 => Header::Field {
name: header::CONTENT_DISPOSITION,
value: HeaderValue::from_static(""),
},
26 => Entry::Header {
26 => Header::Field {
name: header::CONTENT_ENCODING,
value: HeaderValue::from_static(""),
},
27 => Entry::Header {
27 => Header::Field {
name: header::CONTENT_LANGUAGE,
value: HeaderValue::from_static(""),
},
28 => Entry::Header {
28 => Header::Field {
name: header::CONTENT_LENGTH,
value: HeaderValue::from_static(""),
},
29 => Entry::Header {
29 => Header::Field {
name: header::CONTENT_LOCATION,
value: HeaderValue::from_static(""),
},
30 => Entry::Header {
30 => Header::Field {
name: header::CONTENT_RANGE,
value: HeaderValue::from_static(""),
},
31 => Entry::Header {
31 => Header::Field {
name: header::CONTENT_TYPE,
value: HeaderValue::from_static(""),
},
32 => Entry::Header {
32 => Header::Field {
name: header::COOKIE,
value: HeaderValue::from_static(""),
},
33 => Entry::Header {
33 => Header::Field {
name: header::DATE,
value: HeaderValue::from_static(""),
},
34 => Entry::Header {
34 => Header::Field {
name: header::ETAG,
value: HeaderValue::from_static(""),
},
35 => Entry::Header {
35 => Header::Field {
name: header::EXPECT,
value: HeaderValue::from_static(""),
},
36 => Entry::Header {
36 => Header::Field {
name: header::EXPIRES,
value: HeaderValue::from_static(""),
},
37 => Entry::Header {
37 => Header::Field {
name: header::FROM,
value: HeaderValue::from_static(""),
},
38 => Entry::Header {
38 => Header::Field {
name: header::HOST,
value: HeaderValue::from_static(""),
},
39 => Entry::Header {
39 => Header::Field {
name: header::IF_MATCH,
value: HeaderValue::from_static(""),
},
40 => Entry::Header {
40 => Header::Field {
name: header::IF_MODIFIED_SINCE,
value: HeaderValue::from_static(""),
},
41 => Entry::Header {
41 => Header::Field {
name: header::IF_NONE_MATCH,
value: HeaderValue::from_static(""),
},
42 => Entry::Header {
42 => Header::Field {
name: header::IF_RANGE,
value: HeaderValue::from_static(""),
},
43 => Entry::Header {
43 => Header::Field {
name: header::IF_UNMODIFIED_SINCE,
value: HeaderValue::from_static(""),
},
44 => Entry::Header {
44 => Header::Field {
name: header::LAST_MODIFIED,
value: HeaderValue::from_static(""),
},
45 => Entry::Header {
45 => Header::Field {
name: header::LINK,
value: HeaderValue::from_static(""),
},
46 => Entry::Header {
46 => Header::Field {
name: header::LOCATION,
value: HeaderValue::from_static(""),
},
47 => Entry::Header {
47 => Header::Field {
name: header::MAX_FORWARDS,
value: HeaderValue::from_static(""),
},
48 => Entry::Header {
48 => Header::Field {
name: header::PROXY_AUTHENTICATE,
value: HeaderValue::from_static(""),
},
49 => Entry::Header {
49 => Header::Field {
name: header::PROXY_AUTHORIZATION,
value: HeaderValue::from_static(""),
},
50 => Entry::Header {
50 => Header::Field {
name: header::RANGE,
value: HeaderValue::from_static(""),
},
51 => Entry::Header {
51 => Header::Field {
name: header::REFERER,
value: HeaderValue::from_static(""),
},
52 => Entry::Header {
52 => Header::Field {
name: header::REFRESH,
value: HeaderValue::from_static(""),
},
53 => Entry::Header {
53 => Header::Field {
name: header::RETRY_AFTER,
value: HeaderValue::from_static(""),
},
54 => Entry::Header {
54 => Header::Field {
name: header::SERVER,
value: HeaderValue::from_static(""),
},
55 => Entry::Header {
55 => Header::Field {
name: header::SET_COOKIE,
value: HeaderValue::from_static(""),
},
56 => Entry::Header {
56 => Header::Field {
name: header::STRICT_TRANSPORT_SECURITY,
value: HeaderValue::from_static(""),
},
57 => Entry::Header {
57 => Header::Field {
name: header::TRANSFER_ENCODING,
value: HeaderValue::from_static(""),
},
58 => Entry::Header {
58 => Header::Field {
name: header::USER_AGENT,
value: HeaderValue::from_static(""),
},
59 => Entry::Header {
59 => Header::Field {
name: header::VARY,
value: HeaderValue::from_static(""),
},
60 => Entry::Header {
60 => Header::Field {
name: header::VIA,
value: HeaderValue::from_static(""),
},
61 => Entry::Header {
61 => Header::Field {
name: header::WWW_AUTHENTICATE,
value: HeaderValue::from_static(""),
},

View File

@@ -1,8 +1,8 @@
use super::entry;
use super::table::Table;
use super::{huffman, Header};
use super::table::{Table, Index};
use http::header::{HeaderName, HeaderValue};
use bytes::BytesMut;
use bytes::{BytesMut, BufMut};
pub struct Encoder {
table: Table,
@@ -24,7 +24,7 @@ impl Encoder {
}
pub fn encode<'a, I>(&mut self, headers: I, dst: &mut BytesMut) -> Result<(), EncoderError>
where I: IntoIterator<Item=Entry>,
where I: IntoIterator<Item=Header>,
{
if let Some(max_size_update) = self.max_size_update.take() {
// Write size update frame
@@ -38,31 +38,118 @@ impl Encoder {
Ok(())
}
fn encode_header(&mut self, entry: Entry, dst: &mut BytesMut)
fn encode_header(&mut self, header: Header, dst: &mut BytesMut)
-> Result<(), EncoderError>
{
if is_sensitive(&e) {
if header.is_sensitive() {
unimplemented!();
}
/*
match self.table.entry(name, val) {
Entry::Indexed(idx) => {
unimplemented!();
match self.table.index(header) {
Index::Indexed(idx, _header) => {
encode_int(idx, 7, 0x80, dst);
}
Entry::Name(idx) => {
unimplemented!();
Index::Name(idx, header) => {
encode_int(idx, 4, 0, dst);
encode_str(header.value_slice(), dst);
}
Entry::NotIndexed => {
unimplemented!();
Index::Inserted(header) => {
dst.put_u8(0b01000000);
encode_str(header.name().as_slice(), dst);
encode_str(header.value_slice(), dst);
}
Index::InsertedValue(idx, header) => {
encode_int(idx, 6, 0b01000000, dst);
encode_str(header.value_slice(), dst);
}
Index::NotIndexed(header) => {
dst.put_u8(0);
encode_str(header.name().as_slice(), dst);
encode_str(header.value_slice(), dst);
}
}
*/
unimplemented!();
Ok(())
}
}
fn is_sensitive(e: &Entry) -> bool {
false
fn encode_str(val: &[u8], dst: &mut BytesMut) {
use std::io::Cursor;
if val.len() != 0 {
let idx = dst.len();
// Push a placeholder byte for the length header
dst.put_u8(0);
// Encode with huffman
huffman::encode(val, dst);
let huff_len = dst.len() - (idx + 1);
if encode_int_one_byte(huff_len, 7) {
// Write the string head
dst[idx] = (0x80 | huff_len as u8);
} else {
// Write the head to a placeholer
let mut buf = [0; 8];
let head_len = {
let mut head_dst = Cursor::new(&mut buf);
encode_int(huff_len, 7, 0x80, &mut head_dst);
head_dst.position() as usize
};
// This is just done to reserve space in the destination
dst.put_slice(&buf[1..head_len]);
// Shift the header forward
for i in 0..huff_len {
dst[idx + head_len + (huff_len - i)] = dst[idx + 1 + (huff_len - 1)];
}
// Copy in the head
for i in 0..head_len {
dst[idx + i] = buf[i];
}
}
} else {
// Write an empty string
dst.put_u8(0);
}
}
/// Encode an integer into the given destination buffer
fn encode_int<B: BufMut>(
mut value: usize, // The integer to encode
prefix_bits: usize, // The number of bits in the prefix
first_byte: u8, // The base upon which to start encoding the int
dst: &mut B) // The destination buffer
{
if encode_int_one_byte(value, prefix_bits) {
dst.put_u8(first_byte | value as u8);
return;
}
let low = (1 << prefix_bits) - 1;
value -= low;
if value > 0x0fffffff {
panic!("value out of range");
}
dst.put_u8(first_byte | low as u8);
while value >= 128 {
dst.put_u8(0b10000000 | value as u8);
value = value >> 7;
}
dst.put_u8(value as u8);
}
/// Returns true if the in the int can be fully encoded in the first byte.
fn encode_int_one_byte(value: usize, prefix_bits: usize) -> bool {
value < (1 << prefix_bits) - 1
}

View File

@@ -5,10 +5,10 @@ use http::{Method, StatusCode};
use http::header::{HeaderName, HeaderValue};
use bytes::Bytes;
/// HPack table entry
/// HTTP/2.0 Header
#[derive(Debug, Clone)]
pub enum Entry {
Header {
pub enum Header {
Field {
name: HeaderName,
value: HeaderValue,
},
@@ -19,10 +19,10 @@ pub enum Entry {
Status(StatusCode),
}
/// The name component of an Entry
/// The header field name
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum Key<'a> {
Header(&'a HeaderName),
pub enum Name<'a> {
Field(&'a HeaderName),
Authority,
Method,
Scheme,
@@ -35,29 +35,29 @@ pub fn len(name: &HeaderName, value: &HeaderValue) -> usize {
32 + n.len() + value.len()
}
impl Entry {
pub fn new(name: Bytes, value: Bytes) -> Result<Entry, DecoderError> {
impl Header {
pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
if name[0] == b':' {
match &name[1..] {
b"authority" => {
let value = try!(ByteStr::from_utf8(value));
Ok(Entry::Authority(value))
Ok(Header::Authority(value))
}
b"method" => {
let method = try!(Method::from_bytes(&value));
Ok(Entry::Method(method))
Ok(Header::Method(method))
}
b"scheme" => {
let value = try!(ByteStr::from_utf8(value));
Ok(Entry::Scheme(value))
Ok(Header::Scheme(value))
}
b"path" => {
let value = try!(ByteStr::from_utf8(value));
Ok(Entry::Path(value))
Ok(Header::Path(value))
}
b"status" => {
let status = try!(StatusCode::from_slice(&value));
Ok(Entry::Status(status))
Ok(Header::Status(status))
}
_ => {
Err(DecoderError::InvalidPseudoheader)
@@ -67,91 +67,107 @@ impl Entry {
let name = try!(HeaderName::from_bytes(&name));
let value = try!(HeaderValue::try_from_slice(&value));
Ok(Entry::Header { name: name, value: value })
Ok(Header::Field { name: name, value: value })
}
}
pub fn len(&self) -> usize {
match *self {
Entry::Header { ref name, ref value } => {
Header::Field { ref name, ref value } => {
len(name, value)
}
Entry::Authority(ref v) => {
Header::Authority(ref v) => {
32 + 10 + v.len()
}
Entry::Method(ref v) => {
Header::Method(ref v) => {
32 + 7 + v.as_ref().len()
}
Entry::Scheme(ref v) => {
Header::Scheme(ref v) => {
32 + 7 + v.len()
}
Entry::Path(ref v) => {
Header::Path(ref v) => {
32 + 5 + v.len()
}
Entry::Status(ref v) => {
Header::Status(ref v) => {
32 + 7 + 3
}
}
}
pub fn key(&self) -> Key {
/// Returns the header name
pub fn name(&self) -> Name {
match *self {
Entry::Header { ref name, .. } => Key::Header(name),
Entry::Authority(..) => Key::Authority,
Entry::Method(..) => Key::Method,
Entry::Scheme(..) => Key::Scheme,
Entry::Path(..) => Key::Path,
Entry::Status(..) => Key::Status,
Header::Field { ref name, .. } => Name::Field(name),
Header::Authority(..) => Name::Authority,
Header::Method(..) => Name::Method,
Header::Scheme(..) => Name::Scheme,
Header::Path(..) => Name::Path,
Header::Status(..) => Name::Status,
}
}
pub fn value_eq(&self, other: &Entry) -> bool {
pub fn value_slice(&self) -> &[u8] {
match *self {
Entry::Header { ref value, .. } => {
Header::Field { ref value, .. } => value.as_ref(),
Header::Authority(ref v) => v.as_ref(),
Header::Method(ref v) => v.as_ref().as_ref(),
Header::Scheme(ref v) => v.as_ref(),
Header::Path(ref v) => v.as_ref(),
Header::Status(ref v) => v.as_str().as_ref(),
}
}
pub fn value_eq(&self, other: &Header) -> bool {
match *self {
Header::Field { ref value, .. } => {
let a = value;
match *other {
Entry::Header { ref value, .. } => a == value,
Header::Field { ref value, .. } => a == value,
_ => false,
}
}
Entry::Authority(ref a) => {
Header::Authority(ref a) => {
match *other {
Entry::Authority(ref b) => a == b,
Header::Authority(ref b) => a == b,
_ => false,
}
}
Entry::Method(ref a) => {
Header::Method(ref a) => {
match *other {
Entry::Method(ref b) => a == b,
Header::Method(ref b) => a == b,
_ => false,
}
}
Entry::Scheme(ref a) => {
Header::Scheme(ref a) => {
match *other {
Entry::Scheme(ref b) => a == b,
Header::Scheme(ref b) => a == b,
_ => false,
}
}
Entry::Path(ref a) => {
Header::Path(ref a) => {
match *other {
Entry::Path(ref b) => a == b,
Header::Path(ref b) => a == b,
_ => false,
}
}
Entry::Status(ref a) => {
Header::Status(ref a) => {
match *other {
Entry::Status(ref b) => a == b,
Header::Status(ref b) => a == b,
_ => false,
}
}
}
}
pub fn is_sensitive(&self) -> bool {
false
}
pub fn skip_value_index(&self) -> bool {
use http::header;
match *self {
Entry::Header { ref name, .. } => {
Header::Field { ref name, .. } => {
match *name {
header::AGE |
header::AUTHORIZATION |
@@ -165,40 +181,51 @@ impl Entry {
_ => false,
}
}
Entry::Path(..) => true,
Header::Path(..) => true,
_ => false,
}
}
}
impl<'a> Key<'a> {
pub fn into_entry(self, value: Bytes) -> Result<Entry, DecoderError> {
impl<'a> Name<'a> {
pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
match self {
Key::Header(name) => {
Ok(Entry::Header {
Name::Field(name) => {
Ok(Header::Field {
name: name.clone(),
value: try!(HeaderValue::try_from_slice(&*value)),
})
}
Key::Authority => {
Ok(Entry::Authority(try!(ByteStr::from_utf8(value))))
Name::Authority => {
Ok(Header::Authority(try!(ByteStr::from_utf8(value))))
}
Key::Method => {
Ok(Entry::Scheme(try!(ByteStr::from_utf8(value))))
Name::Method => {
Ok(Header::Scheme(try!(ByteStr::from_utf8(value))))
}
Key::Scheme => {
Ok(Entry::Scheme(try!(ByteStr::from_utf8(value))))
Name::Scheme => {
Ok(Header::Scheme(try!(ByteStr::from_utf8(value))))
}
Key::Path => {
Ok(Entry::Path(try!(ByteStr::from_utf8(value))))
Name::Path => {
Ok(Header::Path(try!(ByteStr::from_utf8(value))))
}
Key::Status => {
Name::Status => {
match StatusCode::from_slice(&value) {
Ok(status) => Ok(Entry::Status(status)),
Ok(status) => Ok(Header::Status(status)),
// TODO: better error handling
Err(_) => Err(DecoderError::InvalidStatusCode),
}
}
}
}
pub fn as_slice(&self) -> &[u8] {
match *self {
Name::Field(ref name) => name.as_ref(),
Name::Authority => b":authority",
Name::Method => b":method",
Name::Scheme => b":scheme",
Name::Path => b":path",
Name::Status => b":status",
}
}
}

View File

@@ -42,8 +42,7 @@ pub fn decode(src: &[u8]) -> Result<BytesMut, DecoderError> {
Ok(dst)
}
// To avoid panics, the destination buffer must have src.len() remaining
// capacity.
// TODO: return error when there is not enough room to encode the value
pub fn encode<B: BufMut>(src: &[u8], dst: &mut B) {
let mut bits: u64 = 0;
let mut bits_left = 40;

View File

@@ -1,6 +1,6 @@
mod encoder;
mod decoder;
mod entry;
mod header;
mod huffman;
mod table;
@@ -8,5 +8,5 @@ mod table;
mod test;
pub use self::encoder::Encoder;
pub use self::entry::{Entry, Key};
pub use self::header::Header;
pub use self::decoder::{Decoder, DecoderError};

View File

@@ -1,4 +1,4 @@
use super::Entry;
use super::Header;
use fnv::FnvHasher;
use http::method;
@@ -22,22 +22,25 @@ pub struct Table {
#[derive(Debug)]
pub enum Index<'a> {
// The entry is already fully indexed
Indexed(usize),
// The header is already fully indexed
Indexed(usize, Header),
// The name is indexed, but not the value
Name(usize, Entry),
Name(usize, Header),
// The full entry has been inserted into the table.
Inserted(&'a Entry),
// The full header has been inserted into the table.
Inserted(&'a Header),
// The entry is not indexed by this table
NotIndexed(Entry),
// Only the value has been inserted
InsertedValue(usize, Header),
// The header is not indexed by this table
NotIndexed(Header),
}
struct Slot {
hash: HashValue,
entry: Entry,
header: Header,
next: Option<usize>,
}
@@ -94,33 +97,33 @@ impl Table {
usable_capacity(self.indices.len())
}
/// Index the entry in the HPACK table.
pub fn index(&mut self, entry: Entry) -> Index {
/// Index the header in the HPACK table.
pub fn index(&mut self, header: Header) -> Index {
// Check the static table
let statik = index_static(&entry);
let statik = index_static(&header);
// Don't index certain headers. This logic is borrowed from nghttp2.
if entry.skip_value_index() {
return Index::new(statik, entry);
if header.skip_value_index() {
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);
return Index::Indexed(n, header);
}
// Don't index large headers
if entry.len() * 4 > self.max_size * 3 {
return Index::new(statik, entry);
if header.len() * 4 > self.max_size * 3 {
return Index::new(statik, header);
}
self.index_dynamic(entry, statik)
self.index_dynamic(header, statik)
}
fn index_dynamic(&mut self, entry: Entry, statik: Option<(usize, bool)>) -> Index {
fn index_dynamic(&mut self, header: Header, statik: Option<(usize, bool)>) -> Index {
self.reserve_one();
let hash = hash_entry(&entry);
let hash = hash_header(&header);
let desired_pos = desired_pos(self.mask, hash);
let mut probe = desired_pos;
@@ -135,20 +138,20 @@ impl Table {
if their_dist < dist {
// Index robinhood
return self.index_vacant(entry, hash, desired_pos, probe);
} else if pos.hash == hash && self.slots[pos.index].entry.key() == entry.key() {
// Matching key, check values
return self.index_occupied(entry, pos.index, statik);
return self.index_vacant(header, hash, desired_pos, probe);
} else if pos.hash == hash && self.slots[pos.index].header.name() == header.name() {
// Matching name, check values
return self.index_occupied(header, pos.index, statik);
}
} else {
return self.index_vacant(entry, hash, desired_pos, probe);
return self.index_vacant(header, hash, desired_pos, probe);
}
dist += 1;
});
}
fn index_occupied(&mut self, entry: Entry, mut index: usize, statik: Option<(usize, bool)>)
fn index_occupied(&mut self, header: Header, mut index: usize, statik: Option<(usize, bool)>)
-> Index
{
// There already is a match for the given header name. Check if a value
@@ -158,13 +161,13 @@ impl Table {
}
fn index_vacant(&mut self,
entry: Entry,
header: Header,
hash: HashValue,
desired: usize,
probe: usize)
-> Index
{
if self.maybe_evict(entry.len()) {
if self.maybe_evict(header.len()) {
// Maybe step back
unimplemented!();
}
@@ -175,7 +178,7 @@ impl Table {
self.slots.push_back(Slot {
hash: hash,
entry: entry,
header: header,
next: None,
});
@@ -199,7 +202,7 @@ impl Table {
});
}
Index::Inserted(&self.slots[slot_idx].entry)
Index::Inserted(&self.slots[slot_idx].header)
}
fn maybe_evict(&mut self, len: usize) -> bool {
@@ -217,7 +220,7 @@ impl Table {
fn evict(&mut self) {
debug_assert!(!self.slots.is_empty());
// Remove the entry
// Remove the header
let slot = self.slots.pop_front().unwrap();
let mut probe = desired_pos(self.mask, slot.hash);
@@ -242,7 +245,7 @@ impl Table {
self.evicted = self.evicted.wrapping_add(1);
}
// Shifts all indices that were displaced by the entry that has just been
// 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;
@@ -326,10 +329,10 @@ impl Table {
}
impl<'a> Index<'a> {
fn new(v: Option<(usize, bool)>, e: Entry) -> Index<'a> {
fn new(v: Option<(usize, bool)>, e: Header) -> Index<'a> {
match v {
None => Index::NotIndexed(e),
Some((n, true)) => Index::Indexed(n),
Some((n, true)) => Index::Indexed(n, e),
Some((n, false)) => Index::Name(n, e),
}
}
@@ -355,19 +358,19 @@ fn probe_distance(mask: usize, hash: HashValue, current: usize) -> usize {
current.wrapping_sub(desired_pos(mask, hash)) & mask as usize
}
fn hash_entry(entry: &Entry) -> HashValue {
fn hash_header(header: &Header) -> HashValue {
const MASK: u64 = (MAX_SIZE as u64) - 1;
let mut h = FnvHasher::default();
entry.key().hash(&mut h);
header.name().hash(&mut h);
HashValue((h.finish() & MASK) as usize)
}
/// Checks the static table for the entry. If found, returns the index and a
/// 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(entry: &Entry) -> Option<(usize, bool)> {
match *entry {
Entry::Header { ref name, ref value } => {
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 => {
@@ -425,29 +428,29 @@ fn index_static(entry: &Entry) -> Option<(usize, bool)> {
_ => None,
}
}
Entry::Authority(ref v) => Some((1, false)),
Entry::Method(ref v) => {
Header::Authority(ref v) => Some((1, false)),
Header::Method(ref v) => {
match *v {
method::GET => Some((2, true)),
method::POST => Some((3, true)),
_ => Some((2, false)),
}
}
Entry::Scheme(ref v) => {
Header::Scheme(ref v) => {
match &**v {
"http" => Some((6, true)),
"https" => Some((7, true)),
_ => Some((6, false)),
}
}
Entry::Path(ref v) => {
Header::Path(ref v) => {
match &**v {
"/" => Some((4, true)),
"/index.html" => Some((5, true)),
_ => Some((4, false)),
}
}
Entry::Status(ref v) => {
Header::Status(ref v) => {
match u16::from(*v) {
200 => Some((8, true)),
204 => Some((9, true)),