Skip to main content

net_wire/qpack/stream/decoder/
builder.rs

1//! Checked construction of QPACK decoder-stream instructions.
2
3use core::fmt;
4
5use super::{QpackInsertCountIncrement, QpackSectionAcknowledgment, QpackStreamCancellation};
6use crate::qpack::integer::{
7    QPACK_INTEGER_MAX, QpackIntegerBuildError, canonical_encoded_len, integer_from_prevalidated,
8    write_canonical_prevalidated,
9};
10
11/// Failure to build a QPACK decoder-stream instruction.
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub enum QpackDecoderInstructionBuildError {
14    /// The Section Acknowledgment stream ID is invalid.
15    SectionAcknowledgment(#[doc = "The nested stream-ID failure."] QpackIntegerBuildError),
16    /// The Stream Cancellation stream ID is invalid.
17    StreamCancellation(#[doc = "The nested stream-ID failure."] QpackIntegerBuildError),
18    /// The Insert Count Increment value is invalid.
19    InsertCountIncrement(#[doc = "The nested increment failure."] QpackIntegerBuildError),
20    /// The caller destination cannot contain the complete instruction.
21    BufferTooShort {
22        /// Bytes required.
23        required: usize,
24        /// Bytes available.
25        available: usize,
26    },
27}
28
29impl fmt::Display for QpackDecoderInstructionBuildError {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::SectionAcknowledgment(error) => {
33                write!(f, "QPACK Section Acknowledgment cannot be built: {error}")
34            }
35            Self::StreamCancellation(error) => {
36                write!(f, "QPACK Stream Cancellation cannot be built: {error}")
37            }
38            Self::InsertCountIncrement(error) => {
39                write!(f, "QPACK Insert Count Increment cannot be built: {error}")
40            }
41            Self::BufferTooShort {
42                required,
43                available,
44            } => write!(
45                f,
46                "QPACK decoder instruction buffer is too short: need {required} bytes, have {available}"
47            ),
48        }
49    }
50}
51
52/// Builds a canonical Section Acknowledgment instruction in caller-owned storage.
53pub struct QpackSectionAcknowledgmentBuilder<'a> {
54    destination: &'a mut [u8],
55    stream_id: u64,
56}
57
58impl<'a> QpackSectionAcknowledgmentBuilder<'a> {
59    /// Creates a builder for the supplied decoded stream ID.
60    pub fn new(destination: &'a mut [u8], stream_id: u64) -> Self {
61        Self {
62            destination,
63            stream_id,
64        }
65    }
66
67    /// Validates all inputs and capacity before atomically writing the instruction.
68    pub fn build(
69        self,
70    ) -> Result<QpackSectionAcknowledgment<'a>, QpackDecoderInstructionBuildError> {
71        let length = integer_length(self.stream_id, 7)
72            .map_err(QpackDecoderInstructionBuildError::SectionAcknowledgment)?;
73        ensure_capacity(self.destination, length)?;
74        write_canonical_prevalidated(&mut self.destination[..length], 7, 0x80, self.stream_id);
75        let bytes = &self.destination[..length];
76        Ok(QpackSectionAcknowledgment {
77            bytes,
78            stream_id: integer_from_prevalidated(bytes, 7, 0x80, self.stream_id),
79        })
80    }
81}
82
83/// Builds a canonical Stream Cancellation instruction in caller-owned storage.
84pub struct QpackStreamCancellationBuilder<'a> {
85    destination: &'a mut [u8],
86    stream_id: u64,
87}
88
89impl<'a> QpackStreamCancellationBuilder<'a> {
90    /// Creates a builder for the supplied decoded stream ID.
91    pub fn new(destination: &'a mut [u8], stream_id: u64) -> Self {
92        Self {
93            destination,
94            stream_id,
95        }
96    }
97
98    /// Validates all inputs and capacity before atomically writing the instruction.
99    pub fn build(self) -> Result<QpackStreamCancellation<'a>, QpackDecoderInstructionBuildError> {
100        let length = integer_length(self.stream_id, 6)
101            .map_err(QpackDecoderInstructionBuildError::StreamCancellation)?;
102        ensure_capacity(self.destination, length)?;
103        write_canonical_prevalidated(&mut self.destination[..length], 6, 0x40, self.stream_id);
104        let bytes = &self.destination[..length];
105        Ok(QpackStreamCancellation {
106            bytes,
107            stream_id: integer_from_prevalidated(bytes, 6, 0x40, self.stream_id),
108        })
109    }
110}
111
112/// Builds a canonical Insert Count Increment instruction in caller-owned storage.
113pub struct QpackInsertCountIncrementBuilder<'a> {
114    destination: &'a mut [u8],
115    increment: u64,
116}
117
118impl<'a> QpackInsertCountIncrementBuilder<'a> {
119    /// Creates a builder for the supplied decoded increment.
120    pub fn new(destination: &'a mut [u8], increment: u64) -> Self {
121        Self {
122            destination,
123            increment,
124        }
125    }
126
127    /// Validates all inputs and capacity before atomically writing the instruction.
128    pub fn build(self) -> Result<QpackInsertCountIncrement<'a>, QpackDecoderInstructionBuildError> {
129        let length = integer_length(self.increment, 6)
130            .map_err(QpackDecoderInstructionBuildError::InsertCountIncrement)?;
131        ensure_capacity(self.destination, length)?;
132        write_canonical_prevalidated(&mut self.destination[..length], 6, 0, self.increment);
133        let bytes = &self.destination[..length];
134        Ok(QpackInsertCountIncrement {
135            bytes,
136            increment: integer_from_prevalidated(bytes, 6, 0, self.increment),
137        })
138    }
139}
140
141fn integer_length(value: u64, prefix_bits: u8) -> Result<usize, QpackIntegerBuildError> {
142    if value > QPACK_INTEGER_MAX {
143        return Err(QpackIntegerBuildError::ValueTooLarge { value });
144    }
145    Ok(canonical_encoded_len(value, prefix_bits))
146}
147
148fn ensure_capacity(
149    destination: &[u8],
150    required: usize,
151) -> Result<(), QpackDecoderInstructionBuildError> {
152    if destination.len() < required {
153        return Err(QpackDecoderInstructionBuildError::BufferTooShort {
154            required,
155            available: destination.len(),
156        });
157    }
158    Ok(())
159}