Skip to main content

net_wire/qpack/stream/
decoder.rs

1//! RFC 9204 section 4.4 decoder-stream instructions.
2
3use core::fmt;
4
5use crate::qpack::integer::{QpackInteger, QpackIntegerParseError};
6
7mod builder;
8
9pub use builder::{
10    QpackDecoderInstructionBuildError, QpackInsertCountIncrementBuilder,
11    QpackSectionAcknowledgmentBuilder, QpackStreamCancellationBuilder,
12};
13
14/// A validated, exact borrowed QPACK decoder-stream instruction.
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum QpackDecoderInstruction<'a> {
17    /// A Section Acknowledgment instruction.
18    SectionAcknowledgment(QpackSectionAcknowledgment<'a>),
19    /// A Stream Cancellation instruction.
20    StreamCancellation(QpackStreamCancellation<'a>),
21    /// An Insert Count Increment instruction.
22    InsertCountIncrement(QpackInsertCountIncrement<'a>),
23}
24
25impl<'a> QpackDecoderInstruction<'a> {
26    /// Parses exactly one decoder-stream instruction and excludes following bytes.
27    pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackDecoderInstructionParseError> {
28        let first = *bytes
29            .first()
30            .ok_or(QpackDecoderInstructionParseError::DispatchIncomplete)?;
31        if first & 0x80 != 0 {
32            let stream_id = QpackInteger::parse(bytes, 7)
33                .map_err(QpackDecoderInstructionParseError::SectionAcknowledgment)?;
34            return Ok(Self::SectionAcknowledgment(QpackSectionAcknowledgment {
35                bytes: stream_id.as_bytes(),
36                stream_id,
37            }));
38        }
39        if first & 0xc0 == 0x40 {
40            let stream_id = QpackInteger::parse(bytes, 6)
41                .map_err(QpackDecoderInstructionParseError::StreamCancellation)?;
42            return Ok(Self::StreamCancellation(QpackStreamCancellation {
43                bytes: stream_id.as_bytes(),
44                stream_id,
45            }));
46        }
47        let increment = QpackInteger::parse(bytes, 6)
48            .map_err(QpackDecoderInstructionParseError::InsertCountIncrement)?;
49        Ok(Self::InsertCountIncrement(QpackInsertCountIncrement {
50            bytes: increment.as_bytes(),
51            increment,
52        }))
53    }
54
55    /// Returns the complete exact instruction bytes.
56    pub const fn as_bytes(self) -> &'a [u8] {
57        match self {
58            Self::SectionAcknowledgment(instruction) => instruction.as_bytes(),
59            Self::StreamCancellation(instruction) => instruction.as_bytes(),
60            Self::InsertCountIncrement(instruction) => instruction.as_bytes(),
61        }
62    }
63}
64
65/// A Section Acknowledgment instruction view.
66#[derive(Clone, Copy, Debug, Eq, PartialEq)]
67pub struct QpackSectionAcknowledgment<'a> {
68    bytes: &'a [u8],
69    stream_id: QpackInteger<'a>,
70}
71
72impl<'a> QpackSectionAcknowledgment<'a> {
73    /// Returns the complete exact instruction bytes.
74    pub const fn as_bytes(self) -> &'a [u8] {
75        self.bytes
76    }
77
78    /// Returns the exact stream-ID integer.
79    pub const fn stream_id(self) -> QpackInteger<'a> {
80        self.stream_id
81    }
82}
83
84/// A Stream Cancellation instruction view.
85#[derive(Clone, Copy, Debug, Eq, PartialEq)]
86pub struct QpackStreamCancellation<'a> {
87    bytes: &'a [u8],
88    stream_id: QpackInteger<'a>,
89}
90
91impl<'a> QpackStreamCancellation<'a> {
92    /// Returns the complete exact instruction bytes.
93    pub const fn as_bytes(self) -> &'a [u8] {
94        self.bytes
95    }
96
97    /// Returns the exact stream-ID integer.
98    pub const fn stream_id(self) -> QpackInteger<'a> {
99        self.stream_id
100    }
101}
102
103/// An Insert Count Increment instruction view.
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105pub struct QpackInsertCountIncrement<'a> {
106    bytes: &'a [u8],
107    increment: QpackInteger<'a>,
108}
109
110impl<'a> QpackInsertCountIncrement<'a> {
111    /// Returns the complete exact instruction bytes.
112    pub const fn as_bytes(self) -> &'a [u8] {
113        self.bytes
114    }
115
116    /// Returns the exact increment integer.
117    pub const fn increment(self) -> QpackInteger<'a> {
118        self.increment
119    }
120}
121
122/// Failure to parse one QPACK decoder-stream instruction.
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124pub enum QpackDecoderInstructionParseError {
125    /// The input contains no first octet to dispatch.
126    DispatchIncomplete,
127    /// The Section Acknowledgment stream ID is invalid.
128    SectionAcknowledgment(#[doc = "The nested stream-ID failure."] QpackIntegerParseError),
129    /// The Stream Cancellation stream ID is invalid.
130    StreamCancellation(#[doc = "The nested stream-ID failure."] QpackIntegerParseError),
131    /// The Insert Count Increment integer is invalid.
132    InsertCountIncrement(#[doc = "The nested increment failure."] QpackIntegerParseError),
133}
134
135impl fmt::Display for QpackDecoderInstructionParseError {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        match self {
138            Self::DispatchIncomplete => {
139                f.write_str("QPACK decoder instruction input is incomplete")
140            }
141            Self::SectionAcknowledgment(error) => {
142                write!(f, "QPACK Section Acknowledgment is invalid: {error}")
143            }
144            Self::StreamCancellation(error) => {
145                write!(f, "QPACK Stream Cancellation is invalid: {error}")
146            }
147            Self::InsertCountIncrement(error) => {
148                write!(f, "QPACK Insert Count Increment is invalid: {error}")
149            }
150        }
151    }
152}
153
154/// A validated complete, unframed QPACK decoder-stream instruction sequence.
155#[derive(Clone, Copy, Debug, Eq, PartialEq)]
156pub struct QpackDecoderInstructions<'a> {
157    bytes: &'a [u8],
158}
159
160impl<'a> QpackDecoderInstructions<'a> {
161    /// Validates a complete unframed decoder-stream instruction sequence.
162    pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackDecoderInstructionsParseError> {
163        let mut offset = 0usize;
164        while offset < bytes.len() {
165            let instruction = QpackDecoderInstruction::parse(&bytes[offset..])
166                .map_err(|error| QpackDecoderInstructionsParseError { offset, error })?;
167            offset += instruction.as_bytes().len();
168        }
169        Ok(Self { bytes })
170    }
171
172    /// Returns the complete exact sequence bytes.
173    pub const fn as_bytes(self) -> &'a [u8] {
174        self.bytes
175    }
176
177    /// Returns a fallible fused iterator over exact instructions.
178    pub const fn iter(self) -> QpackDecoderInstructionIter<'a> {
179        QpackDecoderInstructionIter {
180            remaining: self.bytes,
181            offset: 0,
182            failed: false,
183        }
184    }
185}
186
187/// Failure to validate a complete QPACK decoder-stream instruction sequence.
188#[derive(Clone, Copy, Debug, Eq, PartialEq)]
189pub struct QpackDecoderInstructionsParseError {
190    /// Absolute byte offset of the invalid instruction.
191    pub offset: usize,
192    /// Local instruction parse failure.
193    pub error: QpackDecoderInstructionParseError,
194}
195
196impl fmt::Display for QpackDecoderInstructionsParseError {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        write!(
199            f,
200            "QPACK decoder instruction sequence is invalid at byte {}: {}",
201            self.offset, self.error
202        )
203    }
204}
205
206/// A fallible fused iterator over QPACK decoder-stream instructions.
207#[derive(Clone, Copy, Debug)]
208pub struct QpackDecoderInstructionIter<'a> {
209    remaining: &'a [u8],
210    offset: usize,
211    failed: bool,
212}
213
214impl<'a> Iterator for QpackDecoderInstructionIter<'a> {
215    type Item = Result<QpackDecoderInstruction<'a>, QpackDecoderInstructionsParseError>;
216
217    fn next(&mut self) -> Option<Self::Item> {
218        if self.failed || self.remaining.is_empty() {
219            return None;
220        }
221        match QpackDecoderInstruction::parse(self.remaining) {
222            Ok(instruction) => {
223                let consumed = instruction.as_bytes().len();
224                self.remaining = &self.remaining[consumed..];
225                self.offset += consumed;
226                Some(Ok(instruction))
227            }
228            Err(error) => {
229                self.failed = true;
230                Some(Err(QpackDecoderInstructionsParseError {
231                    offset: self.offset,
232                    error,
233                }))
234            }
235        }
236    }
237}
238
239impl core::iter::FusedIterator for QpackDecoderInstructionIter<'_> {}