use core::fmt;
use crate::qpack::integer::{QpackInteger, QpackIntegerParseError};
use crate::qpack::string::{QpackStringLiteral, QpackStringLiteralParseError};
mod apply;
mod builder;
pub use apply::{
QpackEncoderInstructionApplier, QpackEncoderInstructionApplyError,
QpackEncoderInstructionApplyOutcome, QpackEncoderInstructionsApplyError,
};
pub use builder::{
QpackDuplicateBuilder, QpackEncoderInstructionBuildError, QpackInsertWithLiteralNameBuilder,
QpackInsertWithNameReferenceBuilder, QpackSetDynamicTableCapacityBuilder,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackEncoderInstruction<'a> {
SetDynamicTableCapacity(QpackSetDynamicTableCapacity<'a>),
InsertWithNameReference(QpackInsertWithNameReference<'a>),
InsertWithLiteralName(QpackInsertWithLiteralName<'a>),
Duplicate(QpackDuplicate<'a>),
}
impl<'a> QpackEncoderInstruction<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackEncoderInstructionParseError> {
let first = *bytes
.first()
.ok_or(QpackEncoderInstructionParseError::DispatchIncomplete)?;
if first & 0xe0 == 0x20 {
let capacity = QpackInteger::parse(bytes, 5)
.map_err(QpackEncoderInstructionParseError::SetDynamicTableCapacity)?;
return Ok(Self::SetDynamicTableCapacity(
QpackSetDynamicTableCapacity {
bytes: capacity.as_bytes(),
capacity,
},
));
}
if first & 0x80 != 0 {
let name_index = QpackInteger::parse(bytes, 6)
.map_err(QpackEncoderInstructionParseError::InsertWithNameReferenceNameIndex)?;
let value = QpackStringLiteral::parse(&bytes[name_index.as_bytes().len()..], 8)
.map_err(QpackEncoderInstructionParseError::InsertWithNameReferenceValue)?;
let end = name_index.as_bytes().len() + value.as_bytes().len();
return Ok(Self::InsertWithNameReference(
QpackInsertWithNameReference {
bytes: &bytes[..end],
is_static: first & 0x40 != 0,
name_index,
value,
},
));
}
if first & 0xc0 == 0x40 {
let name = QpackStringLiteral::parse(bytes, 6)
.map_err(QpackEncoderInstructionParseError::InsertWithLiteralNameName)?;
let value = QpackStringLiteral::parse(&bytes[name.as_bytes().len()..], 8)
.map_err(QpackEncoderInstructionParseError::InsertWithLiteralNameValue)?;
let end = name.as_bytes().len() + value.as_bytes().len();
return Ok(Self::InsertWithLiteralName(QpackInsertWithLiteralName {
bytes: &bytes[..end],
name,
value,
}));
}
let index = QpackInteger::parse(bytes, 5)
.map_err(QpackEncoderInstructionParseError::DuplicateIndex)?;
Ok(Self::Duplicate(QpackDuplicate {
bytes: index.as_bytes(),
index,
}))
}
pub const fn as_bytes(self) -> &'a [u8] {
match self {
Self::SetDynamicTableCapacity(instruction) => instruction.as_bytes(),
Self::InsertWithNameReference(instruction) => instruction.as_bytes(),
Self::InsertWithLiteralName(instruction) => instruction.as_bytes(),
Self::Duplicate(instruction) => instruction.as_bytes(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackSetDynamicTableCapacity<'a> {
bytes: &'a [u8],
capacity: QpackInteger<'a>,
}
impl<'a> QpackSetDynamicTableCapacity<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn capacity(self) -> QpackInteger<'a> {
self.capacity
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackInsertWithNameReference<'a> {
bytes: &'a [u8],
is_static: bool,
name_index: QpackInteger<'a>,
value: QpackStringLiteral<'a>,
}
impl<'a> QpackInsertWithNameReference<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn is_static(self) -> bool {
self.is_static
}
pub const fn name_index(self) -> QpackInteger<'a> {
self.name_index
}
pub const fn value(self) -> QpackStringLiteral<'a> {
self.value
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackInsertWithLiteralName<'a> {
bytes: &'a [u8],
name: QpackStringLiteral<'a>,
value: QpackStringLiteral<'a>,
}
impl<'a> QpackInsertWithLiteralName<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn name(self) -> QpackStringLiteral<'a> {
self.name
}
pub const fn value(self) -> QpackStringLiteral<'a> {
self.value
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackDuplicate<'a> {
bytes: &'a [u8],
index: QpackInteger<'a>,
}
impl<'a> QpackDuplicate<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn index(self) -> QpackInteger<'a> {
self.index
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackEncoderInstructionParseError {
DispatchIncomplete,
SetDynamicTableCapacity(#[doc = "The nested capacity-integer failure."] QpackIntegerParseError),
InsertWithNameReferenceNameIndex(
#[doc = "The nested name-index failure."] QpackIntegerParseError,
),
InsertWithNameReferenceValue(
#[doc = "The nested value-string failure."] QpackStringLiteralParseError,
),
InsertWithLiteralNameName(
#[doc = "The nested name-string failure."] QpackStringLiteralParseError,
),
InsertWithLiteralNameValue(
#[doc = "The nested value-string failure."] QpackStringLiteralParseError,
),
DuplicateIndex(#[doc = "The nested duplicate-index failure."] QpackIntegerParseError),
}
impl fmt::Display for QpackEncoderInstructionParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DispatchIncomplete => {
f.write_str("QPACK encoder instruction input is incomplete")
}
Self::SetDynamicTableCapacity(error) => {
write!(f, "QPACK Set Dynamic Table Capacity is invalid: {error}")
}
Self::InsertWithNameReferenceNameIndex(error) => {
write!(
f,
"QPACK Insert With Name Reference name index is invalid: {error}"
)
}
Self::InsertWithNameReferenceValue(error) => {
write!(
f,
"QPACK Insert With Name Reference value is invalid: {error}"
)
}
Self::InsertWithLiteralNameName(error) => {
write!(f, "QPACK Insert With Literal Name name is invalid: {error}")
}
Self::InsertWithLiteralNameValue(error) => {
write!(
f,
"QPACK Insert With Literal Name value is invalid: {error}"
)
}
Self::DuplicateIndex(error) => write!(f, "QPACK Duplicate index is invalid: {error}"),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackEncoderInstructions<'a> {
bytes: &'a [u8],
}
impl<'a> QpackEncoderInstructions<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackEncoderInstructionsParseError> {
let mut offset = 0usize;
while offset < bytes.len() {
let instruction = QpackEncoderInstruction::parse(&bytes[offset..])
.map_err(|error| QpackEncoderInstructionsParseError { offset, error })?;
offset += instruction.as_bytes().len();
}
Ok(Self { bytes })
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn iter(self) -> QpackEncoderInstructionIter<'a> {
QpackEncoderInstructionIter {
remaining: self.bytes,
offset: 0,
failed: false,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackEncoderInstructionsParseError {
pub offset: usize,
pub error: QpackEncoderInstructionParseError,
}
impl fmt::Display for QpackEncoderInstructionsParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"QPACK encoder instruction sequence is invalid at byte {}: {}",
self.offset, self.error
)
}
}
#[derive(Clone, Copy, Debug)]
pub struct QpackEncoderInstructionIter<'a> {
remaining: &'a [u8],
offset: usize,
failed: bool,
}
impl<'a> Iterator for QpackEncoderInstructionIter<'a> {
type Item = Result<QpackEncoderInstruction<'a>, QpackEncoderInstructionsParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed || self.remaining.is_empty() {
return None;
}
match QpackEncoderInstruction::parse(self.remaining) {
Ok(instruction) => {
let consumed = instruction.as_bytes().len();
self.remaining = &self.remaining[consumed..];
self.offset += consumed;
Some(Ok(instruction))
}
Err(error) => {
self.failed = true;
Some(Err(QpackEncoderInstructionsParseError {
offset: self.offset,
error,
}))
}
}
}
}
impl core::iter::FusedIterator for QpackEncoderInstructionIter<'_> {}