#[derive(Debug)] pubstruct DynamicTableEntry {
base: u64,
name: Vec<u8>,
value: Vec<u8>, /// Number of header blocks that refer this entry. /// This is only used by the encoder.
refs: u64,
}
#[derive(Debug)] pubstruct HeaderTable {
dynamic: VecDeque<DynamicTableEntry>, /// The total capacity (in QPACK bytes) of the table. This is set by /// configuration.
capacity: u64, /// The amount of used capacity.
used: u64, /// The total number of inserts thus far.
base: u64, /// This is number of inserts that are acked. this correspond to index of the first not acked. /// This is only used by the encoder.
acked_inserts_cnt: u64,
}
/// Returns number of inserts. pubconstfn base(&self) -> u64 { self.base
}
/// Returns capacity of the dynamic table pubconstfn capacity(&self) -> u64 { self.capacity
}
/// Change the dynamic table capacity. /// /// # Errors /// /// [`Error::ChangeCapacity`] if table capacity cannot be reduced. /// The table cannot be reduced if there are entries that are referred to at /// the moment, or whose inserts are unacked. pubfn set_capacity(&mutself, cap: u64) -> Res<()> {
qtrace!("[{self}] set capacity to {cap}"); if !self.evict_to(cap) { return Err(Error::ChangeCapacity);
} self.capacity = cap;
Ok(())
}
/// Get a static entry with `index`. /// /// # Errors /// /// `HeaderLookup` if the index does not exist in the static table. pubfn get_static(index: u64) -> Res<&'static StaticTableEntry> { let inx = usize::try_from(index).or(Err(Error::HeaderLookup))?;
HEADER_STATIC_TABLE.get(inx).ok_or(Error::HeaderLookup)
}
fn get_dynamic_with_abs_index(&mutself, index: u64) -> Res<&tyle='color:red'>mut DynamicTableEntry> { ifself.base <= index {
debug_assert!(false, "This is an internal error"); return Err(Error::HeaderLookup);
} let inx = self.base - index - 1; let inx = usize::try_from(inx).or(Err(Error::HeaderLookup))?; self.dynamic.get_mut(inx).ok_or(Error::HeaderLookup)
}
/// Get a entry in the dynamic table. /// /// # Errors /// /// `HeaderLookup` if entry does not exist. pubfn get_dynamic(&self, index: u64, base: u64, post: bool) -> Res<&DynamicTableEntry> { let inx = if post { ifself.base
< base
.checked_add(index)
.and_then(|s| s.checked_add(1))
.ok_or(Error::IntegerOverflow)?
{ return Err(Error::HeaderLookup);
} self.base - (base + index + 1)
} else { ifself.base.checked_add(index).ok_or(Error::IntegerOverflow)? < base { return Err(Error::HeaderLookup);
}
(self.base + index) - base
};
self.get_dynamic_with_relative_index(inx)
}
/// Remove a reference to a dynamic table entry. pubfn remove_ref(&mutself, index: u64) {
qtrace!("[{self}] remove reference to entry {index}"); self.get_dynamic_with_abs_index(index)
.expect("we should have the entry")
.remove_ref();
}
/// Add a reference to a dynamic table entry. pubfn add_ref(&mutself, index: u64) {
qtrace!("[{self}] add reference to entry {index}"); self.get_dynamic_with_abs_index(index)
.expect("we should have the entry")
.add_ref();
}
/// Look for a header pair. /// The function returns `LookupResult`: `index`, `static_table` (if it is a static table entry) /// and `value_matches` (if the header value matches as well not only header name) pubfn lookup(&mutself, name: &[u8], value: &[u8], can_block: bool) -> Option<LookupResult> {
qtrace!("[{self}] lookup name:{name:?} value {value:?} can_block={can_block}"); letmut name_match = None; for iter in HEADER_STATIC_TABLE { if iter.name() == name { if iter.value() == value { return Some(LookupResult {
index: iter.index(),
static_table: true,
value_matches: true,
});
}
for iter in &mutself.dynamic { if !can_block && iter.index() >= self.acked_inserts_cnt { continue;
} if iter.name == name { if iter.value == value { return Some(LookupResult {
index: iter.index(),
static_table: false,
value_matches: true,
});
}
/// Insert a new entry. /// /// # Errors /// /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough /// space and/or other entry cannot be evicted. pubfn insert(&mutself, name: &[u8], value: &[u8]) -> Res<u64> {
qtrace!("[{self}] insert name={name:?} value={value:?}"); let entry = DynamicTableEntry {
name: name.to_vec(),
value: value.to_vec(),
base: self.base,
refs: 0,
}; if u64::try_from(entry.size()).map_err(|_| Error::Internal)? > self.capacity
|| !self
.evict_to(self.capacity - u64::try_from(entry.size()).map_err(|_| Error::Internal)?)
{ return Err(Error::DynamicTableFull);
} self.base += 1; self.used += u64::try_from(entry.size()).map_err(|_| Error::Internal)?; let index = entry.index(); self.dynamic.push_front(entry);
Ok(index)
}
/// Insert a new entry with the name refer to by a index to static or dynamic table. /// /// # Errors /// /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough /// space and/or other entry cannot be evicted. /// `HeaderLookup` if the index dos not exits in the static/dynamic table. pubfn insert_with_name_ref(
&mutself,
name_static_table: bool,
name_index: u64,
value: &[u8],
) -> Res<u64> {
qtrace!( "[{self}] insert with ref to index={name_index} in {} value={value:?}", if name_static_table { "static"
} else { "dynamic"
},
); let name = if name_static_table { Self::get_static(name_index)?.name().to_vec()
} else { self.get_dynamic(name_index, self.base, false)?
.name()
.to_vec()
}; self.insert(&name, value)
}
/// Duplicate an entry. /// /// # Errors /// /// `DynamicTableFull` if an entry cannot be added to the table because there is not enough /// space and/or other entry cannot be evicted. /// `HeaderLookup` if the index dos not exits in the static/dynamic table. pubfn duplicate(&mutself, index: u64) -> Res<u64> {
qtrace!("[{self}] duplicate entry={index}"); // need to remember name and value because insert may delete the entry. let name: Vec<u8>; let value: Vec<u8>;
{ let entry = self.get_dynamic(index, self.base, false)?;
name = entry.name().to_vec();
value = entry.value().to_vec();
qtrace!("[{self}] duplicate name={name:?} value={value:?}");
} self.insert(&name, &value)
}
/// Increment number of acknowledge entries. /// /// # Errors /// /// `IncrementAck` if ack is greater than actual number of inserts. pubfn increment_acked(&mutself, increment: u64) -> Res<()> {
qtrace!("[{self}] increment acked by {increment}"); self.acked_inserts_cnt += increment; ifself.base < self.acked_inserts_cnt { return Err(Error::IncrementAck);
}
Ok(())
}
/// Return number of acknowledge inserts. pubconstfn get_acked_inserts_cnt(&self) -> u64 { self.acked_inserts_cnt
}
}
#[cfg(test)] #[cfg_attr(coverage_nightly, coverage(off))] mod tests { usesuper::*;
/// Due to a bug in [`HeaderTable::can_evict_to`], the function would /// continuously subtract the size of the last entry instead of the size of /// each entry starting from the back. /// /// See <https://github.com/mozilla/neqo/issues/2306> for details. mod issue_2306 { usesuper::*;
const VALUE: &[u8; 2] = b"42";
/// Given two entries where the first is smaller than the second, /// subtracting the size of the second from the overall size twice leads /// to an underflow. #[test] fn can_evict_to_no_underflow() { letmut table = HeaderTable::new(true);
table.set_capacity(10000).unwrap();
/// Given two entries where only the first is acked, continuously /// subtracting the size of the last entry would give a false-positive /// on whether both entries can be evicted. #[test] fn can_evict_to_false() { letmut table = HeaderTable::new(true);
table.set_capacity(10000).unwrap();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.