Skip to main content

net_wire/qpack/stream/
encoder.rs

1//! RFC 9204 sections 4.2 and 4.3 encoder-stream instructions.
2
3use core::fmt;
4
5use crate::qpack::integer::{QpackInteger, QpackIntegerParseError};
6use crate::qpack::string::{QpackStringLiteral, QpackStringLiteralParseError};
7
8mod apply;
9mod builder;
10
11pub use apply::{
12    QpackEncoderInstructionApplier, QpackEncoderInstructionApplyError,
13    QpackEncoderInstructionApplyOutcome, QpackEncoderInstructionsApplyError,
14};
15pub use builder::{
16    QpackDuplicateBuilder, QpackEncoderInstructionBuildError, QpackInsertWithLiteralNameBuilder,
17    QpackInsertWithNameReferenceBuilder, QpackSetDynamicTableCapacityBuilder,
18};
19
20/// A validated, exact borrowed QPACK encoder-stream instruction.
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
22pub enum QpackEncoderInstruction<'a> {
23    /// A Set Dynamic Table Capacity instruction.
24    SetDynamicTableCapacity(QpackSetDynamicTableCapacity<'a>),
25    /// An Insert With Name Reference instruction.
26    InsertWithNameReference(QpackInsertWithNameReference<'a>),
27    /// An Insert With Literal Name instruction.
28    InsertWithLiteralName(QpackInsertWithLiteralName<'a>),
29    /// A Duplicate instruction.
30    Duplicate(QpackDuplicate<'a>),
31}
32
33impl<'a> QpackEncoderInstruction<'a> {
34    /// Parses exactly one encoder-stream instruction and excludes following bytes.
35    pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackEncoderInstructionParseError> {
36        let first = *bytes
37            .first()
38            .ok_or(QpackEncoderInstructionParseError::DispatchIncomplete)?;
39        if first & 0xe0 == 0x20 {
40            let capacity = QpackInteger::parse(bytes, 5)
41                .map_err(QpackEncoderInstructionParseError::SetDynamicTableCapacity)?;
42            return Ok(Self::SetDynamicTableCapacity(
43                QpackSetDynamicTableCapacity {
44                    bytes: capacity.as_bytes(),
45                    capacity,
46                },
47            ));
48        }
49        if first & 0x80 != 0 {
50            let name_index = QpackInteger::parse(bytes, 6)
51                .map_err(QpackEncoderInstructionParseError::InsertWithNameReferenceNameIndex)?;
52            let value = QpackStringLiteral::parse(&bytes[name_index.as_bytes().len()..], 8)
53                .map_err(QpackEncoderInstructionParseError::InsertWithNameReferenceValue)?;
54            let end = name_index.as_bytes().len() + value.as_bytes().len();
55            return Ok(Self::InsertWithNameReference(
56                QpackInsertWithNameReference {
57                    bytes: &bytes[..end],
58                    is_static: first & 0x40 != 0,
59                    name_index,
60                    value,
61                },
62            ));
63        }
64        if first & 0xc0 == 0x40 {
65            let name = QpackStringLiteral::parse(bytes, 6)
66                .map_err(QpackEncoderInstructionParseError::InsertWithLiteralNameName)?;
67            let value = QpackStringLiteral::parse(&bytes[name.as_bytes().len()..], 8)
68                .map_err(QpackEncoderInstructionParseError::InsertWithLiteralNameValue)?;
69            let end = name.as_bytes().len() + value.as_bytes().len();
70            return Ok(Self::InsertWithLiteralName(QpackInsertWithLiteralName {
71                bytes: &bytes[..end],
72                name,
73                value,
74            }));
75        }
76        let index = QpackInteger::parse(bytes, 5)
77            .map_err(QpackEncoderInstructionParseError::DuplicateIndex)?;
78        Ok(Self::Duplicate(QpackDuplicate {
79            bytes: index.as_bytes(),
80            index,
81        }))
82    }
83
84    /// Returns the complete exact instruction bytes.
85    pub const fn as_bytes(self) -> &'a [u8] {
86        match self {
87            Self::SetDynamicTableCapacity(instruction) => instruction.as_bytes(),
88            Self::InsertWithNameReference(instruction) => instruction.as_bytes(),
89            Self::InsertWithLiteralName(instruction) => instruction.as_bytes(),
90            Self::Duplicate(instruction) => instruction.as_bytes(),
91        }
92    }
93}
94
95/// A Set Dynamic Table Capacity instruction view.
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
97pub struct QpackSetDynamicTableCapacity<'a> {
98    bytes: &'a [u8],
99    capacity: QpackInteger<'a>,
100}
101
102impl<'a> QpackSetDynamicTableCapacity<'a> {
103    /// Returns the complete exact instruction bytes.
104    pub const fn as_bytes(self) -> &'a [u8] {
105        self.bytes
106    }
107
108    /// Returns the exact capacity integer.
109    pub const fn capacity(self) -> QpackInteger<'a> {
110        self.capacity
111    }
112}
113
114/// An Insert With Name Reference instruction view.
115#[derive(Clone, Copy, Debug, Eq, PartialEq)]
116pub struct QpackInsertWithNameReference<'a> {
117    bytes: &'a [u8],
118    is_static: bool,
119    name_index: QpackInteger<'a>,
120    value: QpackStringLiteral<'a>,
121}
122
123impl<'a> QpackInsertWithNameReference<'a> {
124    /// Returns the complete exact instruction bytes.
125    pub const fn as_bytes(self) -> &'a [u8] {
126        self.bytes
127    }
128
129    /// Returns whether the name index addresses the static table.
130    pub const fn is_static(self) -> bool {
131        self.is_static
132    }
133
134    /// Returns the exact name-index integer.
135    pub const fn name_index(self) -> QpackInteger<'a> {
136        self.name_index
137    }
138
139    /// Returns the exact opaque value string literal.
140    pub const fn value(self) -> QpackStringLiteral<'a> {
141        self.value
142    }
143}
144
145/// An Insert With Literal Name instruction view.
146#[derive(Clone, Copy, Debug, Eq, PartialEq)]
147pub struct QpackInsertWithLiteralName<'a> {
148    bytes: &'a [u8],
149    name: QpackStringLiteral<'a>,
150    value: QpackStringLiteral<'a>,
151}
152
153impl<'a> QpackInsertWithLiteralName<'a> {
154    /// Returns the complete exact instruction bytes.
155    pub const fn as_bytes(self) -> &'a [u8] {
156        self.bytes
157    }
158
159    /// Returns the exact opaque name string literal.
160    pub const fn name(self) -> QpackStringLiteral<'a> {
161        self.name
162    }
163
164    /// Returns the exact opaque value string literal.
165    pub const fn value(self) -> QpackStringLiteral<'a> {
166        self.value
167    }
168}
169
170/// A Duplicate instruction view.
171#[derive(Clone, Copy, Debug, Eq, PartialEq)]
172pub struct QpackDuplicate<'a> {
173    bytes: &'a [u8],
174    index: QpackInteger<'a>,
175}
176
177impl<'a> QpackDuplicate<'a> {
178    /// Returns the complete exact instruction bytes.
179    pub const fn as_bytes(self) -> &'a [u8] {
180        self.bytes
181    }
182
183    /// Returns the exact dynamic-relative index integer.
184    pub const fn index(self) -> QpackInteger<'a> {
185        self.index
186    }
187}
188
189/// Failure to parse one QPACK encoder-stream instruction.
190#[derive(Clone, Copy, Debug, Eq, PartialEq)]
191pub enum QpackEncoderInstructionParseError {
192    /// The input contains no first octet to dispatch.
193    DispatchIncomplete,
194    /// The Set Dynamic Table Capacity integer is invalid.
195    SetDynamicTableCapacity(#[doc = "The nested capacity-integer failure."] QpackIntegerParseError),
196    /// The Insert With Name Reference name index is invalid.
197    InsertWithNameReferenceNameIndex(
198        #[doc = "The nested name-index failure."] QpackIntegerParseError,
199    ),
200    /// The Insert With Name Reference value is invalid.
201    InsertWithNameReferenceValue(
202        #[doc = "The nested value-string failure."] QpackStringLiteralParseError,
203    ),
204    /// The Insert With Literal Name name is invalid.
205    InsertWithLiteralNameName(
206        #[doc = "The nested name-string failure."] QpackStringLiteralParseError,
207    ),
208    /// The Insert With Literal Name value is invalid.
209    InsertWithLiteralNameValue(
210        #[doc = "The nested value-string failure."] QpackStringLiteralParseError,
211    ),
212    /// The Duplicate index is invalid.
213    DuplicateIndex(#[doc = "The nested duplicate-index failure."] QpackIntegerParseError),
214}
215
216impl fmt::Display for QpackEncoderInstructionParseError {
217    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218        match self {
219            Self::DispatchIncomplete => {
220                f.write_str("QPACK encoder instruction input is incomplete")
221            }
222            Self::SetDynamicTableCapacity(error) => {
223                write!(f, "QPACK Set Dynamic Table Capacity is invalid: {error}")
224            }
225            Self::InsertWithNameReferenceNameIndex(error) => {
226                write!(
227                    f,
228                    "QPACK Insert With Name Reference name index is invalid: {error}"
229                )
230            }
231            Self::InsertWithNameReferenceValue(error) => {
232                write!(
233                    f,
234                    "QPACK Insert With Name Reference value is invalid: {error}"
235                )
236            }
237            Self::InsertWithLiteralNameName(error) => {
238                write!(f, "QPACK Insert With Literal Name name is invalid: {error}")
239            }
240            Self::InsertWithLiteralNameValue(error) => {
241                write!(
242                    f,
243                    "QPACK Insert With Literal Name value is invalid: {error}"
244                )
245            }
246            Self::DuplicateIndex(error) => write!(f, "QPACK Duplicate index is invalid: {error}"),
247        }
248    }
249}
250
251/// A validated complete, unframed QPACK encoder-stream instruction sequence.
252#[derive(Clone, Copy, Debug, Eq, PartialEq)]
253pub struct QpackEncoderInstructions<'a> {
254    bytes: &'a [u8],
255}
256
257impl<'a> QpackEncoderInstructions<'a> {
258    /// Validates a complete unframed encoder-stream instruction sequence.
259    pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackEncoderInstructionsParseError> {
260        let mut offset = 0usize;
261        while offset < bytes.len() {
262            let instruction = QpackEncoderInstruction::parse(&bytes[offset..])
263                .map_err(|error| QpackEncoderInstructionsParseError { offset, error })?;
264            offset += instruction.as_bytes().len();
265        }
266        Ok(Self { bytes })
267    }
268
269    /// Returns the complete exact sequence bytes.
270    pub const fn as_bytes(self) -> &'a [u8] {
271        self.bytes
272    }
273
274    /// Returns a fallible fused iterator over exact instructions.
275    pub const fn iter(self) -> QpackEncoderInstructionIter<'a> {
276        QpackEncoderInstructionIter {
277            remaining: self.bytes,
278            offset: 0,
279            failed: false,
280        }
281    }
282}
283
284/// Failure to validate a complete QPACK encoder-stream instruction sequence.
285#[derive(Clone, Copy, Debug, Eq, PartialEq)]
286pub struct QpackEncoderInstructionsParseError {
287    /// Absolute byte offset of the invalid instruction.
288    pub offset: usize,
289    /// Local instruction parse failure.
290    pub error: QpackEncoderInstructionParseError,
291}
292
293impl fmt::Display for QpackEncoderInstructionsParseError {
294    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
295        write!(
296            f,
297            "QPACK encoder instruction sequence is invalid at byte {}: {}",
298            self.offset, self.error
299        )
300    }
301}
302
303/// A fallible fused iterator over QPACK encoder-stream instructions.
304#[derive(Clone, Copy, Debug)]
305pub struct QpackEncoderInstructionIter<'a> {
306    remaining: &'a [u8],
307    offset: usize,
308    failed: bool,
309}
310
311impl<'a> Iterator for QpackEncoderInstructionIter<'a> {
312    type Item = Result<QpackEncoderInstruction<'a>, QpackEncoderInstructionsParseError>;
313
314    fn next(&mut self) -> Option<Self::Item> {
315        if self.failed || self.remaining.is_empty() {
316            return None;
317        }
318        match QpackEncoderInstruction::parse(self.remaining) {
319            Ok(instruction) => {
320                let consumed = instruction.as_bytes().len();
321                self.remaining = &self.remaining[consumed..];
322                self.offset += consumed;
323                Some(Ok(instruction))
324            }
325            Err(error) => {
326                self.failed = true;
327                Some(Err(QpackEncoderInstructionsParseError {
328                    offset: self.offset,
329                    error,
330                }))
331            }
332        }
333    }
334}
335
336impl core::iter::FusedIterator for QpackEncoderInstructionIter<'_> {}