use core::fmt;
use crate::qpack::stream::decoder::{
QpackDecoderInstruction, QpackDecoderInstructions, QpackDecoderInstructionsParseError,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackEncoderOutstandingSection {
stream_id: u64,
required_insert_count: u64,
reference_start: usize,
reference_len: usize,
}
impl QpackEncoderOutstandingSection {
pub const EMPTY: Self = Self {
stream_id: 0,
required_insert_count: 0,
reference_start: 0,
reference_len: 0,
};
pub const fn stream_id(self) -> u64 {
self.stream_id
}
pub const fn required_insert_count(self) -> u64 {
self.required_insert_count
}
pub const fn reference_len(self) -> usize {
self.reference_len
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackEncoderStateError {
RequiredInsertCountMismatch {
required_insert_count: u64,
expected_required_insert_count: u64,
},
RequiredInsertCountOverflow {
absolute_index: u64,
},
RequiredInsertCountExceedsInsertCount {
required_insert_count: u64,
encoder_insert_count: u64,
},
SectionStorageExhausted {
capacity: usize,
},
ReferenceStorageTooShort {
required: usize,
available: usize,
},
BlockedStreamsLimitExceeded {
maximum: u64,
},
}
impl QpackEncoderStateError {
pub const fn is_provisioning_error(self) -> bool {
matches!(
self,
Self::SectionStorageExhausted { .. } | Self::ReferenceStorageTooShort { .. }
)
}
pub const fn is_blocked_streams_limit_error(self) -> bool {
matches!(self, Self::BlockedStreamsLimitExceeded { .. })
}
}
impl fmt::Display for QpackEncoderStateError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RequiredInsertCountMismatch {
required_insert_count,
expected_required_insert_count,
} => write!(
formatter,
"Required Insert Count {required_insert_count} does not match expected count {expected_required_insert_count}"
),
Self::RequiredInsertCountOverflow { absolute_index } => write!(
formatter,
"Required Insert Count overflows u64 for absolute index {absolute_index}"
),
Self::RequiredInsertCountExceedsInsertCount {
required_insert_count,
encoder_insert_count,
} => write!(
formatter,
"Required Insert Count {required_insert_count} exceeds encoder insert count {encoder_insert_count}"
),
Self::SectionStorageExhausted { capacity } => {
write!(
formatter,
"section storage capacity {capacity} is exhausted"
)
}
Self::ReferenceStorageTooShort {
required,
available,
} => write!(
formatter,
"reference storage requires {required} slots but only {available} remain"
),
Self::BlockedStreamsLimitExceeded { maximum } => {
write!(formatter, "blocked streams exceed maximum {maximum}")
}
}
}
}
impl core::error::Error for QpackEncoderStateError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackDecoderInstructionApplyError {
SectionAcknowledgmentWithoutOutstandingReferences {
stream_id: u64,
},
InsertCountIncrementZero,
InsertCountIncrementExceedsInsertCount {
known_received_count: u64,
increment: u64,
encoder_insert_count: u64,
},
}
impl QpackDecoderInstructionApplyError {
pub const fn is_decoder_stream_error(self) -> bool {
true
}
}
impl fmt::Display for QpackDecoderInstructionApplyError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SectionAcknowledgmentWithoutOutstandingReferences { stream_id } => write!(
formatter,
"Section Acknowledgment stream {stream_id} has no outstanding dynamic references"
),
Self::InsertCountIncrementZero => {
formatter.write_str("Insert Count Increment must be greater than zero")
}
Self::InsertCountIncrementExceedsInsertCount {
known_received_count,
increment,
encoder_insert_count,
} => write!(
formatter,
"Insert Count Increment {increment} from Known Received Count {known_received_count} exceeds encoder insert count {encoder_insert_count}"
),
}
}
}
impl core::error::Error for QpackDecoderInstructionApplyError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackDecoderInstructionsApplyError {
Parse(QpackDecoderInstructionsParseError),
Apply {
offset: usize,
error: QpackDecoderInstructionApplyError,
},
}
impl QpackDecoderInstructionsApplyError {
pub const fn is_decoder_stream_error(self) -> bool {
match self {
Self::Parse(_) => true,
Self::Apply { error, .. } => error.is_decoder_stream_error(),
}
}
}
impl fmt::Display for QpackDecoderInstructionsApplyError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Parse(error) => write!(
formatter,
"QPACK decoder instruction sequence is invalid: {error}"
),
Self::Apply { offset, error } => write!(
formatter,
"QPACK decoder instruction application failed at byte {offset}: {error}"
),
}
}
}
impl core::error::Error for QpackDecoderInstructionsApplyError {}
pub struct QpackEncoderState<'sections, 'references> {
sections: &'sections mut [QpackEncoderOutstandingSection],
references: &'references mut [u64],
section_len: usize,
reference_len: usize,
known_received_count: u64,
maximum_blocked_streams: u64,
}
pub(in crate::qpack) struct QpackEncoderStateRegistration<'state, 'sections, 'references, 'input> {
state: &'state mut QpackEncoderState<'sections, 'references>,
stream_id: u64,
required_insert_count: u64,
absolute_references: &'input [u64],
reference_start: usize,
}
impl<'state, 'sections, 'references, 'input>
QpackEncoderStateRegistration<'state, 'sections, 'references, 'input>
{
pub(in crate::qpack) fn commit(self) {
if self.absolute_references.is_empty() {
return;
}
let reference_end = self.reference_start + self.absolute_references.len();
self.state.references[self.reference_start..reference_end]
.copy_from_slice(self.absolute_references);
self.state.sections[self.state.section_len] = QpackEncoderOutstandingSection {
stream_id: self.stream_id,
required_insert_count: self.required_insert_count,
reference_start: self.reference_start,
reference_len: self.absolute_references.len(),
};
self.state.reference_len += self.absolute_references.len();
self.state.section_len += 1;
}
}
impl<'sections, 'references> QpackEncoderState<'sections, 'references> {
pub fn new(
sections: &'sections mut [QpackEncoderOutstandingSection],
references: &'references mut [u64],
maximum_blocked_streams: u64,
) -> Self {
for section in sections.iter_mut() {
*section = QpackEncoderOutstandingSection::EMPTY;
}
for reference in references.iter_mut() {
*reference = 0;
}
Self {
sections,
references,
section_len: 0,
reference_len: 0,
known_received_count: 0,
maximum_blocked_streams,
}
}
pub const fn known_received_count(&self) -> u64 {
self.known_received_count
}
pub const fn maximum_blocked_streams(&self) -> u64 {
self.maximum_blocked_streams
}
pub const fn len(&self) -> usize {
self.section_len
}
pub const fn is_empty(&self) -> bool {
self.section_len == 0
}
pub const fn reference_len(&self) -> usize {
self.reference_len
}
pub const fn section_storage_capacity(&self) -> usize {
self.sections.len()
}
pub const fn reference_storage_capacity(&self) -> usize {
self.references.len()
}
pub fn blocked_stream_count(&self) -> usize {
let mut count = 0;
for (index, section) in self.sections[..self.section_len].iter().enumerate() {
if section.required_insert_count > self.known_received_count
&& !self.sections[..index].iter().any(|previous| {
previous.required_insert_count > self.known_received_count
&& previous.stream_id == section.stream_id
})
{
count += 1;
}
}
count
}
pub fn register(
&mut self,
stream_id: u64,
required_insert_count: u64,
absolute_references: &[u64],
encoder_insert_count: u64,
) -> Result<(), QpackEncoderStateError> {
self.reserve_registration(
stream_id,
required_insert_count,
absolute_references,
encoder_insert_count,
)?
.commit();
Ok(())
}
pub(in crate::qpack) fn reserve_registration<'input>(
&mut self,
stream_id: u64,
required_insert_count: u64,
absolute_references: &'input [u64],
encoder_insert_count: u64,
) -> Result<
QpackEncoderStateRegistration<'_, 'sections, 'references, 'input>,
QpackEncoderStateError,
> {
if absolute_references.is_empty() {
if required_insert_count == 0 {
let reference_start = self.reference_len;
return Ok(QpackEncoderStateRegistration {
state: self,
stream_id,
required_insert_count,
absolute_references,
reference_start,
});
}
return Err(QpackEncoderStateError::RequiredInsertCountMismatch {
required_insert_count,
expected_required_insert_count: 0,
});
}
let mut largest = absolute_references[0];
for &absolute_index in &absolute_references[1..] {
if absolute_index > largest {
largest = absolute_index;
}
}
let expected_required_insert_count =
largest
.checked_add(1)
.ok_or(QpackEncoderStateError::RequiredInsertCountOverflow {
absolute_index: largest,
})?;
if required_insert_count != expected_required_insert_count {
return Err(QpackEncoderStateError::RequiredInsertCountMismatch {
required_insert_count,
expected_required_insert_count,
});
}
if required_insert_count > encoder_insert_count {
return Err(
QpackEncoderStateError::RequiredInsertCountExceedsInsertCount {
required_insert_count,
encoder_insert_count,
},
);
}
if self.section_len == self.sections.len() {
return Err(QpackEncoderStateError::SectionStorageExhausted {
capacity: self.sections.len(),
});
}
let available_references = self.references.len() - self.reference_len;
if absolute_references.len() > available_references {
return Err(QpackEncoderStateError::ReferenceStorageTooShort {
required: absolute_references.len(),
available: available_references,
});
}
let could_block = required_insert_count > self.known_received_count;
let stream_already_risks_blocking = could_block
&& self.sections[..self.section_len].iter().any(|section| {
section.stream_id == stream_id
&& section.required_insert_count > self.known_received_count
});
if could_block && !stream_already_risks_blocking {
let blocked_stream_count = self.blocked_stream_count();
if u64::try_from(blocked_stream_count)
.map_or(true, |count| count >= self.maximum_blocked_streams)
{
return Err(QpackEncoderStateError::BlockedStreamsLimitExceeded {
maximum: self.maximum_blocked_streams,
});
}
}
let reference_start = self.reference_len;
Ok(QpackEncoderStateRegistration {
state: self,
stream_id,
required_insert_count,
absolute_references,
reference_start,
})
}
pub fn apply(
&mut self,
instruction: QpackDecoderInstruction<'_>,
encoder_insert_count: u64,
) -> Result<(), QpackDecoderInstructionApplyError> {
match instruction {
QpackDecoderInstruction::SectionAcknowledgment(acknowledgment) => {
let stream_id = acknowledgment.stream_id().value();
let section_index = self.sections[..self.section_len]
.iter()
.position(|section| section.stream_id == stream_id)
.ok_or(
QpackDecoderInstructionApplyError::SectionAcknowledgmentWithoutOutstandingReferences {
stream_id,
},
)?;
self.known_received_count = self
.known_received_count
.max(self.sections[section_index].required_insert_count);
self.remove_section(section_index);
Ok(())
}
QpackDecoderInstruction::StreamCancellation(cancellation) => {
let stream_id = cancellation.stream_id().value();
let mut section_index = 0;
while section_index < self.section_len {
if self.sections[section_index].stream_id == stream_id {
self.remove_section(section_index);
} else {
section_index += 1;
}
}
Ok(())
}
QpackDecoderInstruction::InsertCountIncrement(increment) => {
let increment = increment.increment().value();
if increment == 0 {
return Err(QpackDecoderInstructionApplyError::InsertCountIncrementZero);
}
let known_received_count = self.known_received_count;
let Some(next_known_received_count) = known_received_count.checked_add(increment)
else {
return Err(
QpackDecoderInstructionApplyError::InsertCountIncrementExceedsInsertCount {
known_received_count,
increment,
encoder_insert_count,
},
);
};
if next_known_received_count > encoder_insert_count {
return Err(
QpackDecoderInstructionApplyError::InsertCountIncrementExceedsInsertCount {
known_received_count,
increment,
encoder_insert_count,
},
);
}
self.known_received_count = next_known_received_count;
Ok(())
}
}
}
pub fn apply_sequence(
&mut self,
bytes: &[u8],
encoder_insert_count: u64,
) -> Result<(), QpackDecoderInstructionsApplyError> {
let instructions = QpackDecoderInstructions::parse(bytes)
.map_err(QpackDecoderInstructionsApplyError::Parse)?;
let mut offset = 0usize;
for instruction in instructions.iter() {
let instruction = instruction.map_err(QpackDecoderInstructionsApplyError::Parse)?;
self.apply(instruction, encoder_insert_count)
.map_err(|error| QpackDecoderInstructionsApplyError::Apply { offset, error })?;
offset += instruction.as_bytes().len();
}
Ok(())
}
fn remove_section(&mut self, section_index: usize) {
let removed = self.sections[section_index];
let reference_end = removed.reference_start + removed.reference_len;
self.references
.copy_within(reference_end..self.reference_len, removed.reference_start);
for section in &mut self.sections[section_index + 1..self.section_len] {
section.reference_start -= removed.reference_len;
}
self.sections
.copy_within(section_index + 1..self.section_len, section_index);
self.section_len -= 1;
self.reference_len -= removed.reference_len;
self.sections[self.section_len] = QpackEncoderOutstandingSection::EMPTY;
self.references[self.reference_len..self.reference_len + removed.reference_len].fill(0);
}
pub fn is_evictable(&self, absolute_index: u64) -> bool {
self.known_received_count > absolute_index
&& !self.references[..self.reference_len].contains(&absolute_index)
}
}