use core::fmt;
use crate::qpack::field::section::decode::QpackDecodedFieldSection;
use crate::qpack::integer::QPACK_INTEGER_MAX;
use crate::qpack::stream::decoder::{
QpackDecoderInstructionBuildError, QpackInsertCountIncrement, QpackInsertCountIncrementBuilder,
QpackSectionAcknowledgment, QpackSectionAcknowledgmentBuilder, QpackStreamCancellation,
QpackStreamCancellationBuilder,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackDecoderState {
known_received_count: u64,
}
impl QpackDecoderState {
pub const fn new() -> Self {
Self {
known_received_count: 0,
}
}
pub const fn known_received_count(&self) -> u64 {
self.known_received_count
}
pub fn acknowledge_field_section<'a>(
&mut self,
destination: &'a mut [u8],
stream_id: u64,
section: QpackDecodedFieldSection<'_>,
) -> Result<Option<QpackSectionAcknowledgment<'a>>, QpackDecoderFeedbackError> {
if !section.contains_dynamic_references() {
return Ok(None);
}
let required_insert_count = section.required_insert_count();
let acknowledgment = QpackSectionAcknowledgmentBuilder::new(destination, stream_id)
.build()
.map_err(QpackDecoderFeedbackError::InstructionBuild)?;
self.known_received_count = self.known_received_count.max(required_insert_count);
Ok(Some(acknowledgment))
}
pub fn cancel_stream<'a>(
&mut self,
destination: &'a mut [u8],
stream_id: u64,
) -> Result<QpackStreamCancellation<'a>, QpackDecoderFeedbackError> {
QpackStreamCancellationBuilder::new(destination, stream_id)
.build()
.map_err(QpackDecoderFeedbackError::InstructionBuild)
}
pub fn emit_insert_count_increment<'a>(
&mut self,
destination: &'a mut [u8],
decoder_insert_count: u64,
) -> Result<Option<QpackInsertCountIncrement<'a>>, QpackDecoderFeedbackError> {
if decoder_insert_count == self.known_received_count {
return Ok(None);
}
if decoder_insert_count < self.known_received_count {
return Err(QpackDecoderFeedbackError::InsertCountRegression {
known_received_count: self.known_received_count,
decoder_insert_count,
});
}
let increment = core::cmp::min(
decoder_insert_count - self.known_received_count,
QPACK_INTEGER_MAX,
);
let advanced_count = self.known_received_count.checked_add(increment).ok_or(
QpackDecoderFeedbackError::InsertCountAdvanceOverflow {
known_received_count: self.known_received_count,
increment,
},
)?;
let instruction = QpackInsertCountIncrementBuilder::new(destination, increment)
.build()
.map_err(QpackDecoderFeedbackError::InstructionBuild)?;
self.known_received_count = advanced_count;
Ok(Some(instruction))
}
}
impl Default for QpackDecoderState {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackDecoderFeedbackError {
InsertCountRegression {
known_received_count: u64,
decoder_insert_count: u64,
},
InsertCountAdvanceOverflow {
known_received_count: u64,
increment: u64,
},
InstructionBuild(
#[doc = "The nested local instruction build failure."] QpackDecoderInstructionBuildError,
),
}
impl QpackDecoderFeedbackError {
pub const fn is_provisioning_error(self) -> bool {
matches!(
self,
Self::InstructionBuild(QpackDecoderInstructionBuildError::BufferTooShort { .. })
)
}
}
impl fmt::Display for QpackDecoderFeedbackError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InsertCountRegression {
known_received_count,
decoder_insert_count,
} => write!(
formatter,
"decoder insert count {decoder_insert_count} regresses below Known Received Count {known_received_count}"
),
Self::InsertCountAdvanceOverflow {
known_received_count,
increment,
} => write!(
formatter,
"Known Received Count {known_received_count} cannot advance by {increment}"
),
Self::InstructionBuild(error) => write!(
formatter,
"QPACK decoder feedback instruction cannot be built: {error}"
),
}
}
}
impl core::error::Error for QpackDecoderFeedbackError {}