QPACK: split stream inserter / bloc decoder interface:

- ownership usage for future concurrent decoding
- refactor vas inside, preparing encoder implementation
This commit is contained in:
stammw
2019-01-12 17:38:03 +01:00
committed by Dirkjan Ochtman
parent f2776030d2
commit 79de3ca989
3 changed files with 401 additions and 275 deletions
+216 -214
View File
@@ -6,7 +6,10 @@ use bytes::{Buf, BufMut};
use std::borrow::Cow;
use std::io::Cursor;
use super::table::{dynamic, static_, DynamicTable, HeaderField, StaticTable};
use super::table::{
dynamic, static_, DynamicTable, DynamicTableDecoder, DynamicTableError, DynamicTableInserter,
HeaderField, StaticTable,
};
use super::vas::{self, VirtualAddressSpace};
use super::bloc::{
@@ -31,6 +34,7 @@ pub enum Error {
BadNameIndexOnStaticTable,
BadDuplicateIndex,
InvalidIndex(vas::Error),
DynamicTableError(DynamicTableError),
InvalidStaticIndex(usize),
UnknownPrefix,
MissingRefs,
@@ -38,180 +42,166 @@ pub enum Error {
UnexpectedEnd,
}
pub struct Decoder {
table: DynamicTable,
vas: VirtualAddressSpace,
// Decode a header bloc received on Request of Push stream. (draft: 4.5)
pub fn decode_header<T: Buf>(table: &DynamicTable, buf: &mut T) -> Result<Vec<HeaderField>, Error> {
let (_, encoded_largest_ref) = prefix_int::decode(8, buf)?;
let (sign, encoded_base_index) = prefix_int::decode(7, buf)?;
let remote_largest_ref = largest_ref(
encoded_largest_ref,
table.total_inserted(),
table.max_mem_size(),
);
if remote_largest_ref > table.total_inserted() {
// TODO here the header block cannot be decoded because it contains references to
// dynamic table entries that have not been recieved yet. It should be saved
// and then be decoded when the missing dynamic entries arrive on encoder
// stream.
return Err(Error::MissingRefs);
}
let decoder_table = if sign == 0 {
table.decoder(remote_largest_ref + encoded_base_index)
} else {
if encoded_base_index > remote_largest_ref - 1 {
return Err(Error::BadBaseIndex(
remote_largest_ref as isize - encoded_base_index as isize - 1,
));
}
table.decoder(remote_largest_ref - encoded_base_index - 1)
};
let mut fields = Vec::new();
while buf.has_remaining() {
fields.push(parse_header_field(&decoder_table, buf)?);
}
Ok(fields)
}
impl Decoder {
pub fn new() -> Decoder {
Decoder {
table: DynamicTable::new(),
vas: VirtualAddressSpace::new(),
fn parse_header_field<R: Buf>(
table: &DynamicTableDecoder,
buf: &mut R,
) -> Result<HeaderField, Error> {
let first = buf.bytes()[0];
let field = match HeaderBlocField::decode(first) {
HeaderBlocField::Indexed => match Indexed::decode(buf)? {
Indexed::Static(index) => StaticTable::get(index)?.clone(),
Indexed::Dynamic(index) => table.get_relative(index)?.clone(),
},
HeaderBlocField::IndexedWithPostBase => {
let index = IndexedWithPostBase::decode(buf)?.0;
table.get_postbase(index)?.clone()
}
}
// Decode a header bloc received on Request of Push stream. (draft: 4.5)
pub fn decode_header<T: Buf>(&mut self, buf: &mut T) -> Result<Vec<HeaderField>, Error> {
let (_, encoded_largest_ref) = prefix_int::decode(8, buf)?;
let (sign, encoded_base_index) = prefix_int::decode(7, buf)?;
let remote_largest_ref = self.largest_ref(encoded_largest_ref);
if remote_largest_ref > self.vas.total_inserted() {
// TODO here the header block cannot be decoded because it contains references to
// dynamic table entries that have not been recieved yet. It should be saved
// and then be decoded when the missing dynamic entries arrive on encoder
// stream.
return Err(Error::MissingRefs);
}
let base = if sign == 0 {
remote_largest_ref + encoded_base_index
} else {
if encoded_base_index > remote_largest_ref - 1 {
return Err(Error::BadBaseIndex(
remote_largest_ref as isize - encoded_base_index as isize - 1,
));
HeaderBlocField::LiteralWithNameRef => match LiteralWithNameRef::decode(buf)? {
LiteralWithNameRef::Static { index, value } => {
StaticTable::get(index)?.with_value(value)
}
remote_largest_ref - encoded_base_index - 1
};
let mut fields = Vec::new();
while buf.has_remaining() {
fields.push(self.parse_header_field(base, buf)?);
}
Ok(fields)
}
fn parse_header_field<R: Buf>(&self, base: usize, buf: &mut R) -> Result<HeaderField, Error> {
let first = buf.bytes()[0];
let field = match HeaderBlocField::decode(first) {
HeaderBlocField::Indexed => match Indexed::decode(buf)? {
Indexed::Static(index) => StaticTable::get(index)?.clone(),
Indexed::Dynamic(index) => self.table.get(self.vas.relative_base(base, index)?)?.clone(),
},
HeaderBlocField::IndexedWithPostBase => {
let postbase = IndexedWithPostBase::decode(buf)?;
let index = self.vas.post_base(base, postbase.0)?;
self.table.get(index)?.clone()
LiteralWithNameRef::Dynamic { index, value } => {
table.get_relative(index)?.with_value(value)
}
HeaderBlocField::LiteralWithNameRef => match LiteralWithNameRef::decode(buf)? {
LiteralWithNameRef::Static { index, value } => {
StaticTable::get(index)?.with_value(value)
}
LiteralWithNameRef::Dynamic { index, value } => {
self.table.get(self.vas.relative_base(base, index)?)?.with_value(value)
}
},
HeaderBlocField::LiteralWithPostBaseNameRef => {
let literal = LiteralWithPostBaseNameRef::decode(buf)?;
let index = self.vas.post_base(base, literal.index)?;
self.table.get(index)?.with_value(literal.value)
}
HeaderBlocField::Literal => {
let literal = Literal::decode(buf)?;
HeaderField::new(literal.name, literal.value)
}
_ => return Err(Error::UnknownPrefix),
};
Ok(field)
}
fn largest_ref(&self, bloc_largest_ref: usize) -> usize {
if bloc_largest_ref == 0 {
return 0;
},
HeaderBlocField::LiteralWithPostBaseNameRef => {
let literal = LiteralWithPostBaseNameRef::decode(buf)?;
table.get_postbase(literal.index)?.with_value(literal.value)
}
let total_inserted = self.vas.total_inserted();
let mut lref_value = bloc_largest_ref - 1;
let max_entries = self.table.max_mem_size() / 32;
let mut wrapped = total_inserted % (2 * max_entries);
if wrapped >= lref_value + max_entries {
// Largest Reference wrapped around 1 extra time
lref_value += 2 * max_entries;
} else if wrapped + max_entries < lref_value {
// Decoder wrapped around 1 extra time
wrapped += 2 * max_entries;
HeaderBlocField::Literal => {
let literal = Literal::decode(buf)?;
HeaderField::new(literal.name, literal.value)
}
_ => return Err(Error::UnknownPrefix),
};
Ok(field)
}
lref_value + total_inserted - wrapped
}
// The receiving side of encoder stream
pub fn on_encoder_recv<R: Buf, W: BufMut>(
table: &mut DynamicTableInserter,
read: &mut R,
write: &mut W,
) -> Result<(), Error> {
let inserted_on_start = table.total_inserted();
// The receiving side of encoder stream
pub fn on_encoder_recv<R: Buf, W: BufMut>(
&mut self,
read: &mut R,
write: &mut W,
) -> Result<(), Error> {
let inserted_on_start = self.vas.total_inserted();
while let Some(instruction) = self.parse_instruction(read)? {
match instruction {
Instruction::Insert(field) => self.put_field(field),
Instruction::TableSizeUpdate(size) => {
self.table.set_max_mem_size(size)?;
}
while let Some(instruction) = parse_instruction(&table, read)? {
match instruction {
Instruction::Insert(field) => table.put_field(field)?,
Instruction::TableSizeUpdate(size) => {
table.set_max_mem_size(size)?;
}
}
}
if self.vas.total_inserted() != inserted_on_start {
//TODO RENAME
TableSizeSync {
insert_count: self.vas.total_inserted() - inserted_on_start,
if table.total_inserted() != inserted_on_start {
//TODO RENAME
TableSizeSync {
insert_count: table.total_inserted() - inserted_on_start,
}
.encode(write);
}
Ok(())
}
fn parse_instruction<R: Buf>(
table: &DynamicTableInserter,
read: &mut R,
) -> Result<Option<Instruction>, Error> {
if read.remaining() < 1 {
return Ok(None);
}
let mut buf = Cursor::new(read.bytes());
let first = buf.bytes()[0];
let instruction = match InstructionType::decode(first) {
InstructionType::Unknown => return Err(Error::UnknownPrefix),
InstructionType::DynamicTableSizeUpdate => {
DynamicTableSizeUpdate::decode(&mut buf)?.map(|x| Instruction::TableSizeUpdate(x.size))
}
InstructionType::InsertWithoutNameRef => InsertWithoutNameRef::decode(&mut buf)?
.map(|x| Instruction::Insert(HeaderField::new(x.name, x.value))),
InstructionType::Duplicate => match Duplicate::decode(&mut buf)? {
Some(Duplicate { index }) => {
Some(Instruction::Insert(table.get_relative(index)?.clone()))
}
.encode(write);
}
None => None,
},
InstructionType::InsertWithNameRef => match InsertWithNameRef::decode(&mut buf)? {
Some(InsertWithNameRef::Static { index, value }) => Some(Instruction::Insert(
StaticTable::get(index)?.with_value(value),
)),
Some(InsertWithNameRef::Dynamic { index, value }) => Some(Instruction::Insert(
table.get_relative(index)?.with_value(value),
)),
None => None,
},
};
Ok(())
if instruction.is_some() {
read.advance(buf.position() as usize);
}
fn put_field(&mut self, field: HeaderField) {
let (is_added, dropped) = self.table.put_field(field);
if is_added {
self.vas.add();
}
self.vas.drop_many(dropped);
Ok(instruction)
}
fn largest_ref(bloc_largest_ref: usize, total_inserted: usize, max_mem_size: usize) -> usize {
if bloc_largest_ref == 0 {
return 0;
}
fn parse_instruction<R: Buf>(&self, read: &mut R) -> Result<Option<Instruction>, Error> {
if read.remaining() < 1 {
return Ok(None);
}
let total_inserted = total_inserted;
let mut lref_value = bloc_largest_ref - 1;
let max_entries = max_mem_size / 32;
let mut wrapped = total_inserted % (2 * max_entries);
let mut buf = Cursor::new(read.bytes());
let first = buf.bytes()[0];
let instruction = match InstructionType::decode(first) {
InstructionType::Unknown => return Err(Error::UnknownPrefix),
InstructionType::DynamicTableSizeUpdate => DynamicTableSizeUpdate::decode(&mut buf)?
.map(|x| Instruction::TableSizeUpdate(x.size)),
InstructionType::InsertWithoutNameRef => InsertWithoutNameRef::decode(&mut buf)?
.map(|x| Instruction::Insert(HeaderField::new(x.name, x.value))),
InstructionType::Duplicate => match Duplicate::decode(&mut buf)? {
Some(Duplicate { index }) => Some(Instruction::Insert(
self.table.get(self.vas.relative(index)?)?.clone(),
)),
None => None,
},
InstructionType::InsertWithNameRef => match InsertWithNameRef::decode(&mut buf)? {
Some(InsertWithNameRef::Static { index, value }) => Some(Instruction::Insert(
StaticTable::get(index)?.with_value(value),
)),
Some(InsertWithNameRef::Dynamic { index, value }) => Some(Instruction::Insert(
self.table.get(self.vas.relative(index)?)?.with_value(value),
)),
None => None,
},
};
if instruction.is_some() {
read.advance(buf.position() as usize);
}
Ok(instruction)
if wrapped >= lref_value + max_entries {
// Largest Reference wrapped around 1 extra time
lref_value += 2 * max_entries;
} else if wrapped + max_entries < lref_value {
// Decoder wrapped around 1 extra time
wrapped += 2 * max_entries;
}
lref_value + total_inserted - wrapped
}
#[derive(Debug, PartialEq)]
@@ -252,12 +242,9 @@ impl From<static_::Error> for Error {
}
}
impl From<dynamic::ErrorKind> for Error {
fn from(e: dynamic::ErrorKind) -> Self {
match e {
dynamic::ErrorKind::MaximumTableSizeTooLarge => Error::BadMaximumDynamicTableSize,
dynamic::ErrorKind::BadIndex(i) => Error::BadNameIndexOnDynamicTable(i),
}
impl From<DynamicTableError> for Error {
fn from(e: DynamicTableError) -> Self {
Error::DynamicTableError(e)
}
}
@@ -285,13 +272,13 @@ mod tests {
InsertWithNameRef::new_static(1, "serial value")
.encode(&mut buf)
.unwrap();
let mut decoder = Decoder::new();
let mut table = DynamicTable::new();
let mut enc = Cursor::new(&buf);
let mut dec = vec![];
assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok());
assert!(on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec).is_ok());
assert_eq!(
decoder.table.get(decoder.vas.relative_base(1, 0).unwrap()),
table.decoder(1).get_relative(0),
Ok(&StaticTable::get(1).unwrap().with_value("serial value"))
);
@@ -313,7 +300,8 @@ mod tests {
.encode(&mut buf)
.unwrap();
let mut enc = Cursor::new(&buf);
let res = Decoder::new().on_encoder_recv(&mut enc, &mut vec![]);
let mut table = DynamicTable::new();
let res = on_encoder_recv(&mut table.inserter(), &mut enc, &mut vec![]);
assert_eq!(res, Err(Error::InvalidStaticIndex(3000)));
}
@@ -329,10 +317,13 @@ mod tests {
.unwrap();
let mut enc = Cursor::new(&buf);
let mut dec = vec![];
let res = Decoder::new().on_encoder_recv(&mut enc, &mut dec);
let mut table = DynamicTable::new();
let res = on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec);
assert_eq!(
res,
Err(Error::InvalidIndex(vas::Error::BadRelativeIndex(3000)))
Err(Error::DynamicTableError(
DynamicTableError::BadRelativeIndex(3000)
))
);
assert!(dec.is_empty());
@@ -349,13 +340,13 @@ mod tests {
.encode(&mut buf)
.unwrap();
let mut decoder = Decoder::new();
let mut table = DynamicTable::new();
let mut enc = Cursor::new(&buf);
let mut dec = vec![];
assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok());
assert!(on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec).is_ok());
assert_eq!(
decoder.table.get(decoder.vas.relative_base(1, 0).unwrap()),
table.decoder(1).get_relative(0),
Ok(&HeaderField::new("key", "value"))
);
@@ -366,26 +357,35 @@ mod tests {
);
}
fn insert_fields(table: &mut DynamicTable, fields: Vec<HeaderField>) {
let mut inserter = table.inserter();
for field in fields {
inserter.put_field(field).unwrap();
}
}
/**
* https://tools.ietf.org/html/draft-ietf-quic-qpack-00
* 4.3.3. Duplicate
*/
#[test]
fn test_duplicate_field() {
let mut decoder = Decoder::new();
decoder.put_field(HeaderField::new("", ""));
decoder.put_field(HeaderField::new("", ""));
assert_eq!(decoder.table.count(), 2);
let mut table = DynamicTable::new();
insert_fields(
&mut table,
vec![HeaderField::new("", ""), HeaderField::new("", "")],
);
assert_eq!(table.count(), 2);
let mut buf = vec![];
Duplicate { index: 1 }.encode(&mut buf);
let mut enc = Cursor::new(&buf);
let mut dec = vec![];
let res = decoder.on_encoder_recv(&mut enc, &mut dec);
let res = on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec);
assert_eq!(res, Ok(()));
assert_eq!(decoder.table.count(), 3);
assert_eq!(table.count(), 3);
let mut dec_cursor = Cursor::new(&dec);
assert_eq!(
@@ -405,26 +405,28 @@ mod tests {
let mut enc = Cursor::new(&buf);
let mut dec = vec![];
let mut decoder = Decoder::new();
let res = decoder.on_encoder_recv(&mut enc, &mut dec);
let mut table = DynamicTable::new();
let res = on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec);
assert_eq!(res, Ok(()));
let actual_max_size = decoder.table.max_mem_size();
let actual_max_size = table.max_mem_size();
assert_eq!(actual_max_size, 25);
assert!(dec.is_empty());
}
#[test]
fn enc_recv_buf_too_short() {
let mut table = DynamicTable::new();
let inserting = table.inserter();
let mut buf = vec![];
{
let mut enc = Cursor::new(&buf);
assert_eq!(Decoder::new().parse_instruction(&mut enc), Ok(None));
assert_eq!(parse_instruction(&inserting, &mut enc), Ok(None));
}
buf.push(0b1000_0000);
let mut enc = Cursor::new(&buf);
assert_eq!(Decoder::new().parse_instruction(&mut enc), Ok(None));
assert_eq!(parse_instruction(&inserting, &mut enc), Ok(None));
}
#[test]
@@ -434,17 +436,17 @@ mod tests {
.encode(&mut buf)
.unwrap();
let mut decoder = Decoder::new();
let mut table = DynamicTable::new();
// cut in middle of the first int
let mut enc = Cursor::new(&buf[..2]);
let mut dec = vec![];
assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok());
assert!(on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec).is_ok());
assert_eq!(enc.position(), 0);
// cut the last byte of the 2nd string
let mut enc = Cursor::new(&buf[..buf.len() - 1]);
let mut dec = vec![];
assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok());
assert!(on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec).is_ok());
assert_eq!(enc.position(), 0);
InsertWithoutNameRef::new("keyfoobarbaz2", "value")
@@ -454,17 +456,17 @@ mod tests {
// the first valid field is inserted and buf is left at the first byte of incomplete string
let mut enc = Cursor::new(&buf[..buf.len() - 1]);
let mut dec = vec![];
assert!(decoder.on_encoder_recv(&mut enc, &mut dec).is_ok());
assert!(on_encoder_recv(&mut table.inserter(), &mut enc, &mut dec).is_ok());
assert_eq!(enc.position(), 15);
assert_eq!(decoder.table.count(), 1);
assert_eq!(table.count(), 1);
let mut dec_cursor = Cursor::new(&dec);
assert_eq!(prefix_int::decode(6, &mut dec_cursor), Ok((0, 1)));
assert_eq!(prefix_int::decode(6, &mut dec_cursor), Ok((0, 1))); // TODO Implement type
}
#[test]
fn largest_ref_too_big() {
let mut decoder = Decoder::new();
let table = DynamicTable::new();
const MAX_ENTRIES: usize = (4242 * 31) / 32;
let mut buf = vec![];
@@ -472,24 +474,23 @@ mod tests {
prefix_int::encode(8, 0, encoded_largest_ref, &mut buf);
prefix_int::encode(7, 0, 0, &mut buf);
for _ in 0..7 {
decoder.vas.add();
}
let mut read = Cursor::new(&buf);
assert_eq!(decoder.decode_header(&mut read), Err(Error::MissingRefs));
assert_eq!(decode_header(&table, &mut read), Err(Error::MissingRefs));
}
fn build_decoder(n_field: usize, max_table_size: usize) -> (Decoder, usize) {
let mut decoder = Decoder::new();
fn build_table(n_field: usize, max_table_size: usize) -> (DynamicTable, usize) {
let mut table = DynamicTable::new();
let max_entries = max_table_size / 32;
decoder.table.set_max_mem_size(max_table_size).unwrap();
table.set_max_mem_size(max_table_size).unwrap();
let mut inserter = table.inserter();
for i in 0..n_field {
decoder.put_field(HeaderField::new(format!("foo{}", i + 1), "bar"));
inserter
.put_field(HeaderField::new(format!("foo{}", i + 1), "bar"))
.unwrap();
}
(decoder, max_entries)
(table, max_entries)
}
fn field(n: usize) -> HeaderField {
@@ -508,7 +509,7 @@ mod tests {
#[test]
fn decode_indexed_header_field() {
let (mut decoder, max_entries) = build_decoder(2, 4242 * 31);
let (table, max_entries) = build_table(2, 4242 * 31);
let mut buf = vec![];
let encoded_largest_ref = (2 % (2 * max_entries)) + 1;
@@ -519,7 +520,7 @@ mod tests {
Indexed::Static(18).encode(&mut buf);
let mut read = Cursor::new(&buf);
let headers = decoder.decode_header(&mut read).unwrap();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(
headers,
&[field(2), field(1), StaticTable::get(18).unwrap().clone()]
@@ -540,7 +541,7 @@ mod tests {
#[test]
fn decode_post_base_indexed() {
let (mut decoder, max_entries) = build_decoder(4, 4242 * 31);
let (table, max_entries) = build_table(4, 4242 * 31);
let mut buf = vec![];
let encoded_largest_ref = (2 % (2 * max_entries)) + 1;
@@ -551,13 +552,13 @@ mod tests {
IndexedWithPostBase(1).encode(&mut buf);
let mut read = Cursor::new(&buf);
let headers = decoder.decode_header(&mut read).unwrap();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(headers, &[field(2), field(3), field(4)])
}
#[test]
fn decode_name_ref_header_field() {
let (mut decoder, max_entries) = build_decoder(2, 4242 * 31);
let (table, max_entries) = build_table(2, 4242 * 31);
let mut buf = vec![];
let encoded_largest_ref = (2 % (2 * max_entries)) + 1;
@@ -571,7 +572,7 @@ mod tests {
.unwrap();
let mut read = Cursor::new(&buf);
let headers = decoder.decode_header(&mut read).unwrap();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(
headers,
&[
@@ -583,7 +584,7 @@ mod tests {
#[test]
fn decode_post_base_name_ref_header_field() {
let (mut decoder, max_entries) = build_decoder(4, 4242 * 31);
let (table, max_entries) = build_table(4, 4242 * 31);
let mut buf = vec![];
let encoded_largest_ref = (2 % (2 * max_entries)) + 1;
@@ -594,7 +595,7 @@ mod tests {
.unwrap();
let mut read = Cursor::new(&buf);
let headers = decoder.decode_header(&mut read).unwrap();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(headers, &[field(3).with_value("new bar3")]);
}
@@ -606,7 +607,8 @@ mod tests {
Literal::new("foo", "bar").encode(&mut buf).unwrap();
let mut read = Cursor::new(&buf);
let headers = Decoder::new().decode_header(&mut read).unwrap();
let table = DynamicTable::new();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(
headers,
&[HeaderField::new(b"foo".to_vec(), b"bar".to_vec())]
@@ -627,7 +629,7 @@ mod tests {
#[test]
fn decode_single_pass_encoded() {
let (mut decoder, max_entries) = build_decoder(4, 4242 * 31);
let (table, max_entries) = build_table(4, 4242 * 31);
let mut buf = vec![];
let encoded_largest_ref = (4 % (2 * max_entries)) + 1;
@@ -639,13 +641,13 @@ mod tests {
IndexedWithPostBase(3).encode(&mut buf);
let mut read = Cursor::new(&buf);
let headers = decoder.decode_header(&mut read).unwrap();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(headers, &[field(1), field(2), field(3), field(4)]);
}
#[test]
fn base_index_too_small() {
let (mut decoder, max_entries) = build_decoder(2, 4242 * 31);
let (table, max_entries) = build_table(2, 4242 * 31);
let mut buf = vec![];
let encoded_largest_ref = (2 % (2 * max_entries)) + 1;
@@ -654,14 +656,14 @@ mod tests {
let mut read = Cursor::new(&buf);
assert_eq!(
decoder.decode_header(&mut read),
decode_header(&table, &mut read),
Err(Error::BadBaseIndex(-1))
);
}
#[test]
fn largest_ref_greater_than_max_entries() {
let (mut decoder, max_entries) = build_decoder(((4242 * 31) / 32) + 10, 4242 * 31);
let (table, max_entries) = build_table(((4242 * 31) / 32) + 10, 4242 * 31);
let mut buf = vec![];
// Pre-base relative reference
@@ -671,7 +673,7 @@ mod tests {
Indexed::Dynamic(10).encode(&mut buf);
let mut read = Cursor::new(&buf);
let headers = decoder.decode_header(&mut read).unwrap();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(headers, &[field(4104)]);
let mut buf = vec![];
@@ -684,7 +686,7 @@ mod tests {
IndexedWithPostBase(4).encode(&mut buf);
let mut read = Cursor::new(&buf);
let headers = decoder.decode_header(&mut read).unwrap();
let headers = decode_header(&table, &mut read).unwrap();
assert_eq!(headers, &[field(4115), field(4119)]);
}
}
+182 -60
View File
@@ -2,9 +2,11 @@
// TODO remove allow dead code
#![allow(dead_code)]
use std::collections::VecDeque;
use std::borrow::Cow;
use std::collections::{HashMap, VecDeque};
use super::field::HeaderField;
use crate::qpack::vas::{self, VirtualAddressSpace};
/**
* https://tools.ietf.org/html/draft-ietf-quic-qpack-01
@@ -19,15 +21,104 @@ pub const SETTINGS_HEADER_TABLE_SIZE_DEFAULT: usize = 4096;
pub const SETTINGS_HEADER_TABLE_SIZE_MAX: usize = 1073741823; // 2^30 -1
#[derive(Debug, PartialEq)]
pub enum ErrorKind {
MaximumTableSizeTooLarge,
pub enum Error {
BadRelativeIndex(usize),
BadPostbaseIndex(usize),
BadIndex(usize),
MaxTableSizeReached,
MaximumTableSizeTooLarge,
}
pub struct DynamicTable {
fields: VecDeque<HeaderField>,
curr_mem_size: usize,
mem_limit: usize,
vas: VirtualAddressSpace,
}
pub struct DynamicTableDecoder<'a> {
table: &'a DynamicTable,
base: usize,
}
impl<'a> DynamicTableDecoder<'a> {
pub fn get_relative(&self, index: usize) -> Result<&HeaderField, Error> {
let real_index = self.table.vas.relative_base(self.base, index)?;
self.table
.fields
.get(real_index)
.ok_or(Error::BadIndex(real_index))
}
pub fn get_postbase(&self, index: usize) -> Result<&HeaderField, Error> {
let real_index = self.table.vas.post_base(self.base, index)?;
self.table
.fields
.get(real_index)
.ok_or(Error::BadIndex(real_index))
}
}
pub struct DynamicTableInserter<'a> {
table: &'a mut DynamicTable,
}
impl<'a> DynamicTableInserter<'a> {
pub fn put_field(&mut self, field: HeaderField) -> Result<(), Error> {
let at_most = if field.mem_size() <= self.table.mem_limit {
self.table.mem_limit - field.mem_size()
} else {
0
};
let dropped = self.table.shrink_to(at_most);
let available = self.table.mem_limit - self.table.curr_mem_size;
let can_add = field.mem_size() <= available;
if !can_add {
return Err(Error::MaxTableSizeReached);
}
self.table.curr_mem_size += field.mem_size();
self.table.fields.push_back(field);
self.table.vas.add();
self.table.vas.drop_many(dropped);
Ok(())
}
pub fn get_relative(&self, index: usize) -> Result<&HeaderField, Error> {
let real_index = self.table.vas.relative(index)?;
self.table
.fields
.get(real_index)
.ok_or(Error::BadIndex(real_index))
}
/**
* @returns Number of fields removed by resizing
*/
pub fn set_max_mem_size(&mut self, size: usize) -> Result<usize, Error> {
if size > SETTINGS_HEADER_TABLE_SIZE_MAX {
return Err(Error::MaximumTableSizeTooLarge);
}
self.table.mem_limit = size;
Ok(self.table.shrink_to(size))
}
pub fn total_inserted(&self) -> usize {
self.table.vas.total_inserted()
}
}
impl From<vas::Error> for Error {
fn from(e: vas::Error) -> Self {
match e {
vas::Error::BadRelativeIndex(e) => Error::BadRelativeIndex(e),
vas::Error::BadPostbaseIndex(e) => Error::BadPostbaseIndex(e),
vas::Error::BadAbsoluteIndex(e) => Error::BadIndex(e),
}
}
}
impl DynamicTable {
@@ -36,37 +127,28 @@ impl DynamicTable {
fields: VecDeque::new(),
curr_mem_size: 0,
mem_limit: SETTINGS_HEADER_TABLE_SIZE_DEFAULT,
vas: VirtualAddressSpace::new(),
}
}
/**
* @returns Flag to test if field is really in the table, \
* Number of fields removed to have enough space for the field
*/
pub fn put_field(&mut self, field: HeaderField) -> (bool, usize) {
let at_most = if field.mem_size() <= self.mem_limit {
self.mem_limit - field.mem_size()
} else {
0
};
let dropped = self.shrink_to(at_most);
pub fn decoder<'a>(&'a self, base: usize) -> DynamicTableDecoder<'a> {
DynamicTableDecoder { table: self, base }
}
let available = self.mem_limit - self.curr_mem_size;
let can_add = field.mem_size() <= available;
if can_add {
self.curr_mem_size += field.mem_size();
self.fields.push_back(field);
}
pub fn inserter<'a>(&'a mut self) -> DynamicTableInserter<'a> {
DynamicTableInserter { table: self }
}
(can_add, dropped)
pub fn total_inserted(&self) -> usize {
self.vas.total_inserted()
}
/**
* @returns Number of fields removed by resizing
*/
pub fn set_max_mem_size(&mut self, size: usize) -> Result<usize, ErrorKind> {
pub fn set_max_mem_size(&mut self, size: usize) -> Result<usize, Error> {
if size > SETTINGS_HEADER_TABLE_SIZE_MAX {
return Err(ErrorKind::MaximumTableSizeTooLarge);
return Err(Error::MaximumTableSizeTooLarge);
}
self.mem_limit = size;
@@ -97,14 +179,10 @@ impl DynamicTable {
self.mem_limit
}
pub fn mem_size(&self) -> usize {
self.curr_mem_size
}
pub fn get(&self, index: usize) -> Result<&HeaderField, ErrorKind> {
pub fn get(&self, index: usize) -> Result<&HeaderField, Error> {
match self.fields.get(index) {
Some(f) => Ok(f),
None => Err(ErrorKind::BadIndex(index)),
None => Err(Error::BadIndex(index)),
}
}
@@ -113,6 +191,17 @@ impl DynamicTable {
}
}
// pub enum DynamicNameLookup {
// Relative(usize),
// PostBase(usize),
// NotFound,
// }
// pub enum DynamicInsertionResult {
// Inserted { postbase: usize, absolute: usize },
// InsertedWithNameRef { postbase: usize, absolute: usize },
// }
#[cfg(test)]
mod tests {
use super::*;
@@ -135,10 +224,10 @@ mod tests {
for pair in fields.iter() {
let field = HeaderField::new(pair.0, pair.1);
table.put_field(field);
table.inserter().put_field(field).unwrap();
}
assert_eq!(table.mem_size(), table_size);
assert_eq!(table.curr_mem_size, table_size);
}
// Test on maximum table size
@@ -173,7 +262,7 @@ mod tests {
let mut table = DynamicTable::new();
let invalid_size = SETTINGS_HEADER_TABLE_SIZE_MAX + 10;
let res_change = table.set_max_mem_size(invalid_size);
assert_eq!(res_change, Err(ErrorKind::MaximumTableSizeTooLarge));
assert_eq!(res_change, Err(Error::MaximumTableSizeTooLarge));
}
/**
@@ -216,8 +305,14 @@ mod tests {
#[test]
fn test_table_supports_duplicated_entries() {
let mut table = DynamicTable::new();
table.put_field(HeaderField::new("Name", "Value"));
table.put_field(HeaderField::new("Name", "Value"));
table
.inserter()
.put_field(HeaderField::new("Name", "Value"))
.unwrap();
table
.inserter()
.put_field(HeaderField::new("Name", "Value"))
.unwrap();
assert_eq!(table.count(), 2);
}
@@ -228,9 +323,11 @@ mod tests {
fn test_add_field_fitting_free_space() {
let mut table = DynamicTable::new();
let (added, _) = table.put_field(HeaderField::new("Name", "Value"));
assert_eq!(added, true);
assert_eq!(table.count(), 1);
table
.inserter()
.put_field(HeaderField::new("Name", "Value"))
.unwrap();
assert_eq!(table.fields.len(), 1);
}
/** functional test */
@@ -239,8 +336,8 @@ mod tests {
let mut table = DynamicTable::new();
let field = HeaderField::new("Name", "Value");
table.put_field(field.clone());
assert_eq!(table.mem_size(), field.mem_size());
table.inserter().put_field(field.clone()).unwrap();
assert_eq!(table.curr_mem_size, field.mem_size());
}
/**
@@ -254,17 +351,25 @@ mod tests {
fn test_add_field_drop_older_fields_to_have_enough_space() {
let mut table = DynamicTable::new();
table.put_field(HeaderField::new("Name-A", "Value-A"));
table.put_field(HeaderField::new("Name-B", "Value-B"));
let perfect_size = table.mem_size();
table
.inserter()
.put_field(HeaderField::new("Name-A", "Value-A"))
.unwrap();
table
.inserter()
.put_field(HeaderField::new("Name-B", "Value-B"))
.unwrap();
let perfect_size = table.curr_mem_size;
assert!(table.set_max_mem_size(perfect_size).is_ok());
let field = HeaderField::new("Name-Large", "Value-Large");
let (added, dropped) = table.put_field(field);
table.inserter().put_field(field).unwrap();
assert_eq!(added, true);
assert_eq!(dropped, 2);
assert_eq!(table.count(), 1);
assert_eq!(table.fields.len(), 1);
assert_eq!(
table.fields.get(0),
Some(&HeaderField::new("Name-Large", "Value-Large"))
);
}
/**
@@ -277,16 +382,18 @@ mod tests {
fn test_try_add_field_larger_than_maximum_size() {
let mut table = DynamicTable::new();
table.put_field(HeaderField::new("Name-A", "Value-A"));
let perfect_size = table.mem_size();
table
.inserter()
.put_field(HeaderField::new("Name-A", "Value-A"))
.unwrap();
let perfect_size = table.curr_mem_size;
assert!(table.set_max_mem_size(perfect_size).is_ok());
let field = HeaderField::new("Name-Large", "Value-Large");
let (added, dropped) = table.put_field(field);
assert_eq!(added, false);
assert_eq!(dropped, 1);
assert_eq!(table.count(), 0);
assert_eq!(
table.inserter().put_field(field),
Err(Error::MaxTableSizeReached)
);
}
// Test on entry eviction
@@ -301,8 +408,14 @@ mod tests {
fn test_set_maximum_table_size_to_zero_clear_entries() {
let mut table = DynamicTable::new();
table.put_field(HeaderField::new("Name", "Value"));
table.put_field(HeaderField::new("Name", "Value"));
table
.inserter()
.put_field(HeaderField::new("Name", "Value"))
.unwrap();
table
.inserter()
.put_field(HeaderField::new("Name", "Value"))
.unwrap();
assert_eq!(table.count(), 2);
let were_dropped = table.set_max_mem_size(0);
@@ -315,16 +428,25 @@ mod tests {
fn test_eviction_is_fifo() {
let mut table = DynamicTable::new();
table.put_field(HeaderField::new("Name-A", "Value-A"));
table.put_field(HeaderField::new("Name-B", "Value-B"));
let perfect_size = table.mem_size();
table
.inserter()
.put_field(HeaderField::new("Name-A", "Value-A"))
.unwrap();
table
.inserter()
.put_field(HeaderField::new("Name-B", "Value-B"))
.unwrap();
let perfect_size = table.curr_mem_size;
assert!(table.set_max_mem_size(perfect_size).is_ok());
table.put_field(HeaderField::new("Name-C", "Value-C"));
table
.inserter()
.put_field(HeaderField::new("Name-C", "Value-C"))
.unwrap();
assert_eq!(table.get(0), Ok(&HeaderField::new("Name-B", "Value-B")));
assert_eq!(table.get(1), Ok(&HeaderField::new("Name-C", "Value-C")));
assert_eq!(table.get(2), Err(ErrorKind::BadIndex(2)));
assert_eq!(table.get(2), Err(Error::BadIndex(2)));
}
}
+3 -1
View File
@@ -6,7 +6,9 @@ pub mod field;
pub use self::field::HeaderField;
pub mod dynamic;
pub use self::dynamic::{DynamicTable, ErrorKind};
pub use self::dynamic::{
DynamicTable, DynamicTableDecoder, DynamicTableInserter, Error as DynamicTableError,
};
pub mod static_;
pub use self::static_::StaticTable;