Skip to main content

net_wire/qpack/field/section/
prefix.rs

1//! RFC 9204 section 4.5.1 encoded field-section prefixes.
2
3use core::fmt;
4
5use crate::qpack::integer::{
6    QPACK_INTEGER_MAX, QpackInteger, QpackIntegerBuildError, QpackIntegerParseError,
7    canonical_encoded_len, integer_from_prevalidated, write_canonical_prevalidated,
8};
9
10/// A validated, exact borrowed QPACK encoded field-section prefix.
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub struct QpackFieldSectionPrefix<'a> {
13    bytes: &'a [u8],
14    encoded_required_insert_count: QpackInteger<'a>,
15    negative: bool,
16    delta_base: QpackInteger<'a>,
17}
18
19impl<'a> QpackFieldSectionPrefix<'a> {
20    /// Parses one RFC 9204 section 4.5.1 prefix and excludes following field-line bytes.
21    pub fn parse(bytes: &'a [u8]) -> Result<Self, QpackFieldSectionPrefixParseError> {
22        let encoded_required_insert_count = QpackInteger::parse(bytes, 8)
23            .map_err(QpackFieldSectionPrefixParseError::RequiredInsertCount)?;
24        let required_insert_count_len = encoded_required_insert_count.as_bytes().len();
25        let delta_bytes = &bytes[required_insert_count_len..];
26        let delta_base = QpackInteger::parse(delta_bytes, 7)
27            .map_err(QpackFieldSectionPrefixParseError::DeltaBase)?;
28        let length = required_insert_count_len
29            .checked_add(delta_base.as_bytes().len())
30            .ok_or(QpackFieldSectionPrefixParseError::LengthOverflow)?;
31        Ok(Self {
32            bytes: &bytes[..length],
33            encoded_required_insert_count,
34            negative: delta_base.high_bits() & 0x80 != 0,
35            delta_base,
36        })
37    }
38
39    /// Returns the complete exact prefix bytes, excluding following field-line bytes.
40    pub const fn as_bytes(self) -> &'a [u8] {
41        self.bytes
42    }
43    /// Returns the exact encoded Required Insert Count integer.
44    pub const fn encoded_required_insert_count(self) -> QpackInteger<'a> {
45        self.encoded_required_insert_count
46    }
47    /// Returns whether the Delta Base sign bit is set.
48    pub const fn is_negative(self) -> bool {
49        self.negative
50    }
51    /// Returns the exact Delta Base integer.
52    pub const fn delta_base(self) -> QpackInteger<'a> {
53        self.delta_base
54    }
55}
56
57/// Failure to parse a QPACK encoded field-section prefix.
58#[derive(Clone, Copy, Debug, Eq, PartialEq)]
59pub enum QpackFieldSectionPrefixParseError {
60    /// The Encoded Required Insert Count integer is invalid.
61    RequiredInsertCount(
62        #[doc = "The nested Required Insert Count failure."] QpackIntegerParseError,
63    ),
64    /// The signed Delta Base integer is invalid.
65    DeltaBase(#[doc = "The nested Delta Base failure."] QpackIntegerParseError),
66    /// Computing the complete prefix length overflowed `usize`.
67    LengthOverflow,
68}
69impl fmt::Display for QpackFieldSectionPrefixParseError {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        match self {
72            Self::RequiredInsertCount(error) => {
73                write!(f, "QPACK Encoded Required Insert Count is invalid: {error}")
74            }
75            Self::DeltaBase(error) => write!(f, "QPACK Delta Base is invalid: {error}"),
76            Self::LengthOverflow => {
77                f.write_str("QPACK field-section prefix length overflows usize")
78            }
79        }
80    }
81}
82impl core::error::Error for QpackFieldSectionPrefixParseError {}
83
84/// A fully validated, allocation-free field-section prefix write plan.
85#[derive(Clone, Copy)]
86pub(crate) struct QpackFieldSectionPrefixPlan {
87    encoded_required_insert_count: u64,
88    negative: bool,
89    delta_base: u64,
90    required_insert_count_len: usize,
91    encoded_len: usize,
92}
93impl QpackFieldSectionPrefixPlan {
94    /// Returns the exact number of bytes this plan writes.
95    pub(crate) const fn encoded_len(self) -> usize {
96        self.encoded_len
97    }
98    /// Writes this plan into its exact preallocated output region.
99    pub(crate) fn write(self, destination: &mut [u8]) {
100        if destination.len() != self.encoded_len {
101            return;
102        }
103        let (ric, delta) = destination.split_at_mut(self.required_insert_count_len);
104        write_canonical_prevalidated(ric, 8, 0, self.encoded_required_insert_count);
105        write_canonical_prevalidated(
106            delta,
107            7,
108            if self.negative { 0x80 } else { 0 },
109            self.delta_base,
110        );
111    }
112}
113
114/// Validates one prefix emission without mutating output storage.
115pub(crate) fn plan_field_section_prefix(
116    encoded_required_insert_count: u64,
117    negative: bool,
118    delta_base: u64,
119) -> Result<QpackFieldSectionPrefixPlan, QpackFieldSectionPrefixBuildError> {
120    if encoded_required_insert_count > QPACK_INTEGER_MAX {
121        return Err(QpackFieldSectionPrefixBuildError::RequiredInsertCount(
122            QpackIntegerBuildError::ValueTooLarge {
123                value: encoded_required_insert_count,
124            },
125        ));
126    }
127    if delta_base > QPACK_INTEGER_MAX {
128        return Err(QpackFieldSectionPrefixBuildError::DeltaBase(
129            QpackIntegerBuildError::ValueTooLarge { value: delta_base },
130        ));
131    }
132    let required_insert_count_len = canonical_encoded_len(encoded_required_insert_count, 8);
133    let delta_base_len = canonical_encoded_len(delta_base, 7);
134    let encoded_len = required_insert_count_len
135        .checked_add(delta_base_len)
136        .ok_or(QpackFieldSectionPrefixBuildError::RequiredLengthOverflow)?;
137    Ok(QpackFieldSectionPrefixPlan {
138        encoded_required_insert_count,
139        negative,
140        delta_base,
141        required_insert_count_len,
142        encoded_len,
143    })
144}
145
146/// Builds a canonical QPACK encoded field-section prefix in caller-owned storage.
147pub struct QpackFieldSectionPrefixBuilder<'a> {
148    destination: &'a mut [u8],
149    encoded_required_insert_count: u64,
150    negative: bool,
151    delta_base: u64,
152}
153impl<'a> QpackFieldSectionPrefixBuilder<'a> {
154    /// Creates a builder from raw encoded Required Insert Count and Delta Base values.
155    pub fn new(
156        destination: &'a mut [u8],
157        encoded_required_insert_count: u64,
158        negative: bool,
159        delta_base: u64,
160    ) -> Self {
161        Self {
162            destination,
163            encoded_required_insert_count,
164            negative,
165            delta_base,
166        }
167    }
168    /// Validates all inputs and capacity before atomically writing a canonical prefix.
169    pub fn build(self) -> Result<QpackFieldSectionPrefix<'a>, QpackFieldSectionPrefixBuildError> {
170        let plan = plan_field_section_prefix(
171            self.encoded_required_insert_count,
172            self.negative,
173            self.delta_base,
174        )?;
175        let required = plan.encoded_len();
176        if self.destination.len() < required {
177            return Err(QpackFieldSectionPrefixBuildError::BufferTooShort {
178                required,
179                available: self.destination.len(),
180            });
181        }
182        plan.write(&mut self.destination[..required]);
183        let bytes = &self.destination[..required];
184        let ric_len = plan.required_insert_count_len;
185        Ok(QpackFieldSectionPrefix {
186            bytes,
187            encoded_required_insert_count: integer_from_prevalidated(
188                &bytes[..ric_len],
189                8,
190                0,
191                self.encoded_required_insert_count,
192            ),
193            negative: self.negative,
194            delta_base: integer_from_prevalidated(
195                &bytes[ric_len..],
196                7,
197                if self.negative { 0x80 } else { 0 },
198                self.delta_base,
199            ),
200        })
201    }
202}
203
204/// Failure to build a QPACK encoded field-section prefix.
205#[derive(Clone, Copy, Debug, Eq, PartialEq)]
206pub enum QpackFieldSectionPrefixBuildError {
207    /// The Encoded Required Insert Count value is invalid.
208    RequiredInsertCount(
209        #[doc = "The nested Required Insert Count failure."] QpackIntegerBuildError,
210    ),
211    /// The Delta Base value is invalid.
212    DeltaBase(#[doc = "The nested Delta Base failure."] QpackIntegerBuildError),
213    /// Computing the complete prefix length overflowed `usize`.
214    RequiredLengthOverflow,
215    /// The caller destination cannot contain the complete prefix.
216    BufferTooShort {
217        /// Bytes required.
218        required: usize,
219        /// Bytes available.
220        available: usize,
221    },
222}
223impl fmt::Display for QpackFieldSectionPrefixBuildError {
224    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225        match self {
226            Self::RequiredInsertCount(error) => write!(
227                f,
228                "QPACK Encoded Required Insert Count cannot be built: {error}"
229            ),
230            Self::DeltaBase(error) => write!(f, "QPACK Delta Base cannot be built: {error}"),
231            Self::RequiredLengthOverflow => {
232                f.write_str("QPACK field-section prefix length overflows usize")
233            }
234            Self::BufferTooShort {
235                required,
236                available,
237            } => write!(
238                f,
239                "QPACK field-section prefix buffer is too short: need {required} bytes, have {available}"
240            ),
241        }
242    }
243}
244impl core::error::Error for QpackFieldSectionPrefixBuildError {}