Skip to main content

net_wire/qpack/stream/encoder/
builder.rs

1//! Checked construction of QPACK encoder-stream instructions.
2
3use core::fmt;
4
5use super::{
6    QpackDuplicate, QpackInsertWithLiteralName, QpackInsertWithNameReference,
7    QpackSetDynamicTableCapacity,
8};
9use crate::qpack::integer::{
10    QPACK_INTEGER_MAX, QpackIntegerBuildError, canonical_encoded_len, integer_from_prevalidated,
11    write_canonical_prevalidated,
12};
13use crate::qpack::string::{
14    QpackStringLiteralBuildError, string_from_prevalidated, string_plan, write_string_prevalidated,
15};
16
17/// Failure to build a QPACK encoder-stream instruction.
18#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum QpackEncoderInstructionBuildError {
20    /// The Set Dynamic Table Capacity value is invalid.
21    SetDynamicTableCapacity(#[doc = "The nested capacity-integer failure."] QpackIntegerBuildError),
22    /// The Insert With Name Reference name index is invalid.
23    InsertWithNameReferenceNameIndex(
24        #[doc = "The nested name-index failure."] QpackIntegerBuildError,
25    ),
26    /// The Insert With Name Reference value is invalid.
27    InsertWithNameReferenceValue(
28        #[doc = "The nested value-string failure."] QpackStringLiteralBuildError,
29    ),
30    /// The Insert With Literal Name name is invalid.
31    InsertWithLiteralNameName(
32        #[doc = "The nested name-string failure."] QpackStringLiteralBuildError,
33    ),
34    /// The Insert With Literal Name value is invalid.
35    InsertWithLiteralNameValue(
36        #[doc = "The nested value-string failure."] QpackStringLiteralBuildError,
37    ),
38    /// The Duplicate index is invalid.
39    DuplicateIndex(#[doc = "The nested duplicate-index failure."] QpackIntegerBuildError),
40    /// Computing the total instruction length overflowed `usize`.
41    RequiredLengthOverflow,
42    /// The caller destination cannot contain the complete instruction.
43    BufferTooShort {
44        /// Bytes required.
45        required: usize,
46        /// Bytes available.
47        available: usize,
48    },
49}
50
51impl fmt::Display for QpackEncoderInstructionBuildError {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        match self {
54            Self::SetDynamicTableCapacity(error) => {
55                write!(
56                    f,
57                    "QPACK Set Dynamic Table Capacity cannot be built: {error}"
58                )
59            }
60            Self::InsertWithNameReferenceNameIndex(error) => {
61                write!(
62                    f,
63                    "QPACK Insert With Name Reference name index cannot be built: {error}"
64                )
65            }
66            Self::InsertWithNameReferenceValue(error) => {
67                write!(
68                    f,
69                    "QPACK Insert With Name Reference value cannot be built: {error}"
70                )
71            }
72            Self::InsertWithLiteralNameName(error) => {
73                write!(
74                    f,
75                    "QPACK Insert With Literal Name name cannot be built: {error}"
76                )
77            }
78            Self::InsertWithLiteralNameValue(error) => {
79                write!(
80                    f,
81                    "QPACK Insert With Literal Name value cannot be built: {error}"
82                )
83            }
84            Self::DuplicateIndex(error) => {
85                write!(f, "QPACK Duplicate index cannot be built: {error}")
86            }
87            Self::RequiredLengthOverflow => {
88                f.write_str("QPACK encoder instruction required length overflows usize")
89            }
90            Self::BufferTooShort {
91                required,
92                available,
93            } => write!(
94                f,
95                "QPACK encoder instruction buffer is too short: need {required} bytes, have {available}"
96            ),
97        }
98    }
99}
100
101/// Builds a canonical Set Dynamic Table Capacity instruction in caller-owned storage.
102pub struct QpackSetDynamicTableCapacityBuilder<'a> {
103    destination: &'a mut [u8],
104    capacity: u64,
105}
106
107impl<'a> QpackSetDynamicTableCapacityBuilder<'a> {
108    /// Creates a builder for the supplied decoded capacity.
109    pub fn new(destination: &'a mut [u8], capacity: u64) -> Self {
110        Self {
111            destination,
112            capacity,
113        }
114    }
115
116    /// Validates all inputs and capacity before atomically writing the instruction.
117    pub fn build(
118        self,
119    ) -> Result<QpackSetDynamicTableCapacity<'a>, QpackEncoderInstructionBuildError> {
120        let length = integer_length(self.capacity, 5)
121            .map_err(QpackEncoderInstructionBuildError::SetDynamicTableCapacity)?;
122        ensure_capacity(self.destination, length)?;
123        write_canonical_prevalidated(&mut self.destination[..length], 5, 0x20, self.capacity);
124        let bytes = &self.destination[..length];
125        Ok(QpackSetDynamicTableCapacity {
126            bytes,
127            capacity: integer_from_prevalidated(bytes, 5, 0x20, self.capacity),
128        })
129    }
130}
131
132/// Builds a canonical Insert With Name Reference instruction in caller-owned storage.
133pub struct QpackInsertWithNameReferenceBuilder<'a, 'value> {
134    destination: &'a mut [u8],
135    is_static: bool,
136    name_index: u64,
137    value_huffman: bool,
138    encoded_value: &'value [u8],
139}
140
141impl<'a, 'value> QpackInsertWithNameReferenceBuilder<'a, 'value> {
142    /// Creates a builder around an opaque, already-encoded value payload.
143    pub fn new(
144        destination: &'a mut [u8],
145        is_static: bool,
146        name_index: u64,
147        value_huffman: bool,
148        encoded_value: &'value [u8],
149    ) -> Self {
150        Self {
151            destination,
152            is_static,
153            name_index,
154            value_huffman,
155            encoded_value,
156        }
157    }
158
159    /// Validates all inputs and capacity before atomically writing the instruction.
160    pub fn build(
161        self,
162    ) -> Result<QpackInsertWithNameReference<'a>, QpackEncoderInstructionBuildError> {
163        let name_length = integer_length(self.name_index, 6)
164            .map_err(QpackEncoderInstructionBuildError::InsertWithNameReferenceNameIndex)?;
165        let value_plan = string_plan(8, 0, self.value_huffman, self.encoded_value)
166            .map_err(QpackEncoderInstructionBuildError::InsertWithNameReferenceValue)?;
167        let required = checked_sum(name_length, value_plan.required())?;
168        ensure_capacity(self.destination, required)?;
169        let name_high_bits = 0x80 | if self.is_static { 0x40 } else { 0 };
170        let (name_destination, value_destination) =
171            self.destination[..required].split_at_mut(name_length);
172        write_canonical_prevalidated(name_destination, 6, name_high_bits, self.name_index);
173        write_string_prevalidated(value_destination, value_plan);
174        let bytes = &self.destination[..required];
175        let value = string_from_prevalidated(&bytes[name_length..], value_plan);
176        Ok(QpackInsertWithNameReference {
177            bytes,
178            is_static: self.is_static,
179            name_index: integer_from_prevalidated(
180                &bytes[..name_length],
181                6,
182                name_high_bits,
183                self.name_index,
184            ),
185            value,
186        })
187    }
188}
189
190/// Builds a canonical Insert With Literal Name instruction in caller-owned storage.
191pub struct QpackInsertWithLiteralNameBuilder<'a, 'name, 'value> {
192    destination: &'a mut [u8],
193    name_huffman: bool,
194    encoded_name: &'name [u8],
195    value_huffman: bool,
196    encoded_value: &'value [u8],
197}
198
199impl<'a, 'name, 'value> QpackInsertWithLiteralNameBuilder<'a, 'name, 'value> {
200    /// Creates a builder around opaque, already-encoded name and value payloads.
201    pub fn new(
202        destination: &'a mut [u8],
203        name_huffman: bool,
204        encoded_name: &'name [u8],
205        value_huffman: bool,
206        encoded_value: &'value [u8],
207    ) -> Self {
208        Self {
209            destination,
210            name_huffman,
211            encoded_name,
212            value_huffman,
213            encoded_value,
214        }
215    }
216
217    /// Validates all inputs and capacity before atomically writing the instruction.
218    pub fn build(
219        self,
220    ) -> Result<QpackInsertWithLiteralName<'a>, QpackEncoderInstructionBuildError> {
221        let name_plan = string_plan(6, 0x40, self.name_huffman, self.encoded_name)
222            .map_err(QpackEncoderInstructionBuildError::InsertWithLiteralNameName)?;
223        let value_plan = string_plan(8, 0, self.value_huffman, self.encoded_value)
224            .map_err(QpackEncoderInstructionBuildError::InsertWithLiteralNameValue)?;
225        let required = checked_sum(name_plan.required(), value_plan.required())?;
226        ensure_capacity(self.destination, required)?;
227        let (name_destination, value_destination) =
228            self.destination[..required].split_at_mut(name_plan.required());
229        write_string_prevalidated(name_destination, name_plan);
230        write_string_prevalidated(value_destination, value_plan);
231        let bytes = &self.destination[..required];
232        let name = string_from_prevalidated(&bytes[..name_plan.required()], name_plan);
233        let value = string_from_prevalidated(&bytes[name_plan.required()..], value_plan);
234        Ok(QpackInsertWithLiteralName { bytes, name, value })
235    }
236}
237
238/// Builds a canonical Duplicate instruction in caller-owned storage.
239pub struct QpackDuplicateBuilder<'a> {
240    destination: &'a mut [u8],
241    index: u64,
242}
243
244impl<'a> QpackDuplicateBuilder<'a> {
245    /// Creates a builder for the supplied decoded dynamic-relative index.
246    pub fn new(destination: &'a mut [u8], index: u64) -> Self {
247        Self { destination, index }
248    }
249
250    /// Validates all inputs and capacity before atomically writing the instruction.
251    pub fn build(self) -> Result<QpackDuplicate<'a>, QpackEncoderInstructionBuildError> {
252        let length = integer_length(self.index, 5)
253            .map_err(QpackEncoderInstructionBuildError::DuplicateIndex)?;
254        ensure_capacity(self.destination, length)?;
255        write_canonical_prevalidated(&mut self.destination[..length], 5, 0, self.index);
256        let bytes = &self.destination[..length];
257        Ok(QpackDuplicate {
258            bytes,
259            index: integer_from_prevalidated(bytes, 5, 0, self.index),
260        })
261    }
262}
263
264fn integer_length(value: u64, prefix_bits: u8) -> Result<usize, QpackIntegerBuildError> {
265    if value > QPACK_INTEGER_MAX {
266        return Err(QpackIntegerBuildError::ValueTooLarge { value });
267    }
268    Ok(canonical_encoded_len(value, prefix_bits))
269}
270
271fn checked_sum(left: usize, right: usize) -> Result<usize, QpackEncoderInstructionBuildError> {
272    left.checked_add(right)
273        .ok_or(QpackEncoderInstructionBuildError::RequiredLengthOverflow)
274}
275
276fn ensure_capacity(
277    destination: &[u8],
278    required: usize,
279) -> Result<(), QpackEncoderInstructionBuildError> {
280    if destination.len() < required {
281        return Err(QpackEncoderInstructionBuildError::BufferTooShort {
282            required,
283            available: destination.len(),
284        });
285    }
286    Ok(())
287}