Skip to main content

net_wire/qpack/field/section/
context.rs

1//! RFC 9204 field-section Required Insert Count and Base arithmetic.
2
3use core::fmt;
4
5use super::prefix::QpackFieldSectionPrefix;
6
7/// A resolved QPACK field-section Required Insert Count and Base.
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct QpackFieldSectionContext {
10    required_insert_count: u64,
11    base: u64,
12}
13
14impl QpackFieldSectionContext {
15    /// Decodes a field-section prefix using the decoder's insert count and peer maximum capacity.
16    pub fn decode(
17        prefix: QpackFieldSectionPrefix<'_>,
18        total_insert_count: u64,
19        maximum_table_capacity: u64,
20    ) -> Result<Self, QpackFieldSectionContextError> {
21        let required_insert_count = Self::reconstruct_required_insert_count(
22            prefix.encoded_required_insert_count().value(),
23            total_insert_count,
24            maximum_table_capacity,
25        )?;
26        let base = Self::resolve_base(
27            required_insert_count,
28            prefix.is_negative(),
29            prefix.delta_base().value(),
30        )?;
31        Ok(Self {
32            required_insert_count,
33            base,
34        })
35    }
36
37    /// Reconstructs a Required Insert Count according to RFC 9204 section 4.5.1.1.
38    pub fn reconstruct_required_insert_count(
39        encoded_required_insert_count: u64,
40        total_insert_count: u64,
41        maximum_table_capacity: u64,
42    ) -> Result<u64, QpackFieldSectionContextError> {
43        if encoded_required_insert_count == 0 {
44            return Ok(0);
45        }
46
47        let max_entries = maximum_table_capacity / 32;
48        if max_entries == 0 {
49            return Err(QpackFieldSectionContextError::ZeroMaximumEntries {
50                encoded_required_insert_count,
51            });
52        }
53        let full_range =
54            max_entries
55                .checked_mul(2)
56                .ok_or(QpackFieldSectionContextError::FullRangeOverflow {
57                    maximum_table_capacity,
58                    max_entries,
59                })?;
60        if encoded_required_insert_count > full_range {
61            return Err(
62                QpackFieldSectionContextError::EncodedRequiredInsertCountOutOfRange {
63                    encoded_required_insert_count,
64                    full_range,
65                },
66            );
67        }
68        let maximum_value = total_insert_count.checked_add(max_entries).ok_or(
69            QpackFieldSectionContextError::MaximumValueOverflow {
70                total_insert_count,
71                max_entries,
72            },
73        )?;
74        let max_wrapped_quotient = maximum_value / full_range;
75        let max_wrapped = max_wrapped_quotient.checked_mul(full_range).ok_or(
76            QpackFieldSectionContextError::RequiredInsertCountOverflow {
77                max_wrapped: max_wrapped_quotient,
78                encoded_required_insert_count,
79            },
80        )?;
81        let mut reconstructed = max_wrapped
82            .checked_add(encoded_required_insert_count - 1)
83            .ok_or(QpackFieldSectionContextError::RequiredInsertCountOverflow {
84                max_wrapped,
85                encoded_required_insert_count,
86            })?;
87        if reconstructed > maximum_value {
88            if reconstructed <= full_range {
89                return Err(QpackFieldSectionContextError::RequiredInsertCountTooOld {
90                    reconstructed,
91                    maximum_value,
92                    full_range,
93                });
94            }
95            reconstructed = reconstructed.checked_sub(full_range).ok_or(
96                QpackFieldSectionContextError::RequiredInsertCountTooOld {
97                    reconstructed,
98                    maximum_value,
99                    full_range,
100                },
101            )?;
102        }
103        if reconstructed == 0 {
104            return Err(QpackFieldSectionContextError::RequiredInsertCountZero);
105        }
106        Ok(reconstructed)
107    }
108
109    /// Encodes a nonzero Required Insert Count modulo the RFC 9204 full range.
110    pub fn encode_required_insert_count(
111        required_insert_count: u64,
112        maximum_table_capacity: u64,
113    ) -> Result<u64, QpackFieldSectionContextError> {
114        if required_insert_count == 0 {
115            return Ok(0);
116        }
117
118        let max_entries = maximum_table_capacity / 32;
119        if max_entries == 0 {
120            return Err(QpackFieldSectionContextError::ZeroMaximumEntries {
121                encoded_required_insert_count: required_insert_count,
122            });
123        }
124        let full_range =
125            max_entries
126                .checked_mul(2)
127                .ok_or(QpackFieldSectionContextError::FullRangeOverflow {
128                    maximum_table_capacity,
129                    max_entries,
130                })?;
131        required_insert_count
132            .checked_rem(full_range)
133            .and_then(|count| count.checked_add(1))
134            .ok_or(QpackFieldSectionContextError::RequiredInsertCountOverflow {
135                max_wrapped: required_insert_count,
136                encoded_required_insert_count: required_insert_count,
137            })
138    }
139
140    /// Resolves a Base from a Required Insert Count, sign bit, and Delta Base.
141    pub fn resolve_base(
142        required_insert_count: u64,
143        negative: bool,
144        delta_base: u64,
145    ) -> Result<u64, QpackFieldSectionContextError> {
146        if negative {
147            if delta_base >= required_insert_count {
148                return Err(QpackFieldSectionContextError::BaseUnderflow {
149                    required_insert_count,
150                    delta_base,
151                });
152            }
153            required_insert_count
154                .checked_sub(delta_base)
155                .and_then(|base| base.checked_sub(1))
156                .ok_or(QpackFieldSectionContextError::BaseUnderflow {
157                    required_insert_count,
158                    delta_base,
159                })
160        } else {
161            required_insert_count.checked_add(delta_base).ok_or(
162                QpackFieldSectionContextError::BaseOverflow {
163                    required_insert_count,
164                    delta_base,
165                },
166            )
167        }
168    }
169
170    /// Returns the resolved Required Insert Count.
171    pub const fn required_insert_count(self) -> u64 {
172        self.required_insert_count
173    }
174
175    /// Returns the resolved Base.
176    pub const fn base(self) -> u64 {
177        self.base
178    }
179}
180
181/// A failure while resolving QPACK field-section prefix semantics.
182#[derive(Clone, Copy, Debug, Eq, PartialEq)]
183pub enum QpackFieldSectionContextError {
184    /// Doubling the maximum entry count overflowed `u64`.
185    FullRangeOverflow {
186        /// The peer's maximum dynamic-table capacity.
187        maximum_table_capacity: u64,
188        /// The maximum entry count derived from that capacity.
189        max_entries: u64,
190    },
191    /// A nonzero count cannot be represented when the maximum entry count is zero.
192    ZeroMaximumEntries {
193        /// The triggering encoded or semantic Required Insert Count.
194        encoded_required_insert_count: u64,
195    },
196    /// The encoded Required Insert Count exceeds the full range.
197    EncodedRequiredInsertCountOutOfRange {
198        /// The wire Encoded Required Insert Count.
199        encoded_required_insert_count: u64,
200        /// Twice the maximum entry count.
201        full_range: u64,
202    },
203    /// Adding the maximum entry count to the decoder insert count overflowed `u64`.
204    MaximumValueOverflow {
205        /// The decoder's total insert count.
206        total_insert_count: u64,
207        /// The maximum entry count derived from peer capacity.
208        max_entries: u64,
209    },
210    /// Reconstructing or encoding the Required Insert Count overflowed `u64`.
211    RequiredInsertCountOverflow {
212        /// The wrapped count or quotient involved in the failed calculation.
213        max_wrapped: u64,
214        /// The encoded or semantic Required Insert Count involved in the calculation.
215        encoded_required_insert_count: u64,
216    },
217    /// The reconstructed count cannot be valid for the decoder state.
218    RequiredInsertCountTooOld {
219        /// The reconstructed Required Insert Count.
220        reconstructed: u64,
221        /// The largest Required Insert Count valid for the decoder state.
222        maximum_value: u64,
223        /// Twice the maximum entry count.
224        full_range: u64,
225    },
226    /// A nonzero encoded count reconstructed to zero.
227    RequiredInsertCountZero,
228    /// Adding Delta Base to Required Insert Count overflowed `u64`.
229    BaseOverflow {
230        /// The Required Insert Count.
231        required_insert_count: u64,
232        /// The Delta Base.
233        delta_base: u64,
234    },
235    /// Subtracting Delta Base and one from Required Insert Count underflowed `u64`.
236    BaseUnderflow {
237        /// The Required Insert Count.
238        required_insert_count: u64,
239        /// The Delta Base.
240        delta_base: u64,
241    },
242}
243
244impl fmt::Display for QpackFieldSectionContextError {
245    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
246        match self {
247            Self::FullRangeOverflow {
248                maximum_table_capacity,
249                max_entries,
250            } => write!(
251                formatter,
252                "QPACK full range overflows u64 for maximum table capacity {maximum_table_capacity} and maximum entries {max_entries}"
253            ),
254            Self::ZeroMaximumEntries {
255                encoded_required_insert_count,
256            } => write!(
257                formatter,
258                "QPACK Required Insert Count {encoded_required_insert_count} is nonzero with zero maximum entries"
259            ),
260            Self::EncodedRequiredInsertCountOutOfRange {
261                encoded_required_insert_count,
262                full_range,
263            } => write!(
264                formatter,
265                "QPACK Encoded Required Insert Count {encoded_required_insert_count} exceeds full range {full_range}"
266            ),
267            Self::MaximumValueOverflow {
268                total_insert_count,
269                max_entries,
270            } => write!(
271                formatter,
272                "QPACK maximum Required Insert Count overflows u64 for insert count {total_insert_count} and maximum entries {max_entries}"
273            ),
274            Self::RequiredInsertCountOverflow {
275                max_wrapped,
276                encoded_required_insert_count,
277            } => write!(
278                formatter,
279                "QPACK Required Insert Count overflows u64 for wrapped value {max_wrapped} and count {encoded_required_insert_count}"
280            ),
281            Self::RequiredInsertCountTooOld {
282                reconstructed,
283                maximum_value,
284                full_range,
285            } => write!(
286                formatter,
287                "QPACK Required Insert Count {reconstructed} exceeds maximum {maximum_value} for full range {full_range}"
288            ),
289            Self::RequiredInsertCountZero => formatter
290                .write_str("QPACK nonzero Encoded Required Insert Count reconstructed to zero"),
291            Self::BaseOverflow {
292                required_insert_count,
293                delta_base,
294            } => write!(
295                formatter,
296                "QPACK Base overflows u64 for Required Insert Count {required_insert_count} and Delta Base {delta_base}"
297            ),
298            Self::BaseUnderflow {
299                required_insert_count,
300                delta_base,
301            } => write!(
302                formatter,
303                "QPACK Base underflows u64 for Required Insert Count {required_insert_count} and Delta Base {delta_base}"
304            ),
305        }
306    }
307}
308
309impl core::error::Error for QpackFieldSectionContextError {}