1use core::fmt;
4
5pub const QPACK_INTEGER_MAX: u64 = (1u64 << 62) - 1;
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub struct QpackInteger<'a> {
11 bytes: &'a [u8],
12 prefix_bits: u8,
13 value: u64,
14 high_bits: u8,
15}
16
17impl<'a> QpackInteger<'a> {
18 pub fn parse(bytes: &'a [u8], prefix_bits: u8) -> Result<Self, QpackIntegerParseError> {
20 let prefix_mask = prefix_mask(prefix_bits)?;
21 let first = *bytes.first().ok_or(QpackIntegerParseError::Incomplete {
22 required: 1,
23 available: 0,
24 })?;
25 let prefix_value = first & prefix_mask;
26 let high_bits = first & !prefix_mask;
27 if prefix_value < prefix_mask {
28 return Ok(Self {
29 bytes: &bytes[..1],
30 prefix_bits,
31 value: u64::from(prefix_value),
32 high_bits,
33 });
34 }
35
36 let mut value = u64::from(prefix_mask);
37 let mut shift = 0u32;
38 let mut index = 1usize;
39 loop {
40 let octet = *bytes.get(index).ok_or(QpackIntegerParseError::Incomplete {
41 required: index
42 .checked_add(1)
43 .ok_or(QpackIntegerParseError::Overflow)?,
44 available: bytes.len(),
45 })?;
46 if shift >= u64::BITS {
47 return Err(QpackIntegerParseError::Overflow);
48 }
49 let contribution = u64::from(octet & 0x7f)
50 .checked_mul(1u64 << shift)
51 .ok_or(QpackIntegerParseError::Overflow)?;
52 value = value
53 .checked_add(contribution)
54 .ok_or(QpackIntegerParseError::Overflow)?;
55 index = index
56 .checked_add(1)
57 .ok_or(QpackIntegerParseError::Overflow)?;
58 if octet & 0x80 == 0 {
59 if value > QPACK_INTEGER_MAX {
60 return Err(QpackIntegerParseError::ValueTooLarge { value });
61 }
62 return Ok(Self {
63 bytes: &bytes[..index],
64 prefix_bits,
65 value,
66 high_bits,
67 });
68 }
69 shift = shift
70 .checked_add(7)
71 .ok_or(QpackIntegerParseError::Overflow)?;
72 }
73 }
74
75 pub const fn value(self) -> u64 {
77 self.value
78 }
79
80 pub const fn as_bytes(self) -> &'a [u8] {
82 self.bytes
83 }
84
85 pub const fn prefix_bits(self) -> u8 {
87 self.prefix_bits
88 }
89
90 pub const fn high_bits(self) -> u8 {
92 self.high_bits
93 }
94}
95
96#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98pub enum QpackIntegerParseError {
99 InvalidPrefixBits {
101 prefix_bits: u8,
103 },
104 Incomplete {
106 required: usize,
108 available: usize,
110 },
111 Overflow,
113 ValueTooLarge {
115 value: u64,
117 },
118}
119
120impl fmt::Display for QpackIntegerParseError {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 match self {
123 Self::InvalidPrefixBits { prefix_bits } => write!(
124 f,
125 "QPACK integer prefix width must be 1 through 8, got {prefix_bits}"
126 ),
127 Self::Incomplete {
128 required,
129 available,
130 } => write!(
131 f,
132 "QPACK integer input is incomplete: need {required} bytes, have {available}"
133 ),
134 Self::Overflow => f.write_str("QPACK integer exceeds u64"),
135 Self::ValueTooLarge { value } => {
136 write!(f, "QPACK integer exceeds the 62-bit limit: {value}")
137 }
138 }
139 }
140}
141
142pub struct QpackIntegerBuilder<'a> {
144 destination: &'a mut [u8],
145 prefix_bits: u8,
146 high_bits: u8,
147 value: u64,
148}
149
150impl<'a> QpackIntegerBuilder<'a> {
151 pub fn new(destination: &'a mut [u8], prefix_bits: u8, high_bits: u8, value: u64) -> Self {
153 Self {
154 destination,
155 prefix_bits,
156 high_bits,
157 value,
158 }
159 }
160
161 pub fn build(self) -> Result<QpackInteger<'a>, QpackIntegerBuildError> {
163 let prefix_mask = prefix_mask(self.prefix_bits).map_err(|_| {
164 QpackIntegerBuildError::InvalidPrefixBits {
165 prefix_bits: self.prefix_bits,
166 }
167 })?;
168 if self.high_bits & prefix_mask != 0 {
169 return Err(QpackIntegerBuildError::HighBitsOverlap {
170 high_bits: self.high_bits,
171 prefix_bits: self.prefix_bits,
172 });
173 }
174 if self.value > QPACK_INTEGER_MAX {
175 return Err(QpackIntegerBuildError::ValueTooLarge { value: self.value });
176 }
177 let required = encoded_len(self.value, prefix_mask);
178 if self.destination.len() < required {
179 return Err(QpackIntegerBuildError::BufferTooShort {
180 required,
181 available: self.destination.len(),
182 });
183 }
184 let bytes = &mut self.destination[..required];
185 write_canonical_prevalidated(bytes, self.prefix_bits, self.high_bits, self.value);
186 Ok(integer_from_prevalidated(
187 bytes,
188 self.prefix_bits,
189 self.high_bits,
190 self.value,
191 ))
192 }
193}
194
195#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum QpackIntegerBuildError {
198 InvalidPrefixBits {
200 prefix_bits: u8,
202 },
203 HighBitsOverlap {
205 high_bits: u8,
207 prefix_bits: u8,
209 },
210 ValueTooLarge {
212 value: u64,
214 },
215 BufferTooShort {
217 required: usize,
219 available: usize,
221 },
222}
223
224impl fmt::Display for QpackIntegerBuildError {
225 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 match self {
227 Self::InvalidPrefixBits { prefix_bits } => write!(
228 f,
229 "QPACK integer prefix width must be 1 through 8, got {prefix_bits}"
230 ),
231 Self::HighBitsOverlap {
232 high_bits,
233 prefix_bits,
234 } => write!(
235 f,
236 "QPACK integer high bits {high_bits:#04x} overlap the {prefix_bits}-bit prefix"
237 ),
238 Self::ValueTooLarge { value } => {
239 write!(f, "QPACK integer exceeds the 62-bit limit: {value}")
240 }
241 Self::BufferTooShort {
242 required,
243 available,
244 } => write!(
245 f,
246 "QPACK integer buffer is too short: need {required} bytes, have {available}"
247 ),
248 }
249 }
250}
251
252pub(super) fn canonical_encoded_len(value: u64, prefix_bits: u8) -> usize {
254 let prefix_mask = match prefix_bits {
255 1..=7 => (1u8 << prefix_bits) - 1,
256 8 => u8::MAX,
257 _ => 0,
258 };
259 encoded_len(value, prefix_mask)
260}
261
262pub(super) fn write_canonical_prevalidated(
267 bytes: &mut [u8],
268 prefix_bits: u8,
269 high_bits: u8,
270 value: u64,
271) {
272 let prefix_mask = match prefix_bits {
273 1..=7 => (1u8 << prefix_bits) - 1,
274 8 => u8::MAX,
275 _ => 0,
276 };
277 if value < u64::from(prefix_mask) {
278 bytes[0] = high_bits | value as u8;
279 return;
280 }
281
282 bytes[0] = high_bits | prefix_mask;
283 let mut remaining = value - u64::from(prefix_mask);
284 let mut index = 1usize;
285 while remaining >= 0x80 {
286 bytes[index] = (remaining as u8 & 0x7f) | 0x80;
287 remaining >>= 7;
288 index += 1;
289 }
290 bytes[index] = remaining as u8;
291}
292
293pub(super) fn integer_from_prevalidated<'a>(
295 bytes: &'a [u8],
296 prefix_bits: u8,
297 high_bits: u8,
298 value: u64,
299) -> QpackInteger<'a> {
300 QpackInteger {
301 bytes,
302 prefix_bits,
303 value,
304 high_bits,
305 }
306}
307
308fn prefix_mask(prefix_bits: u8) -> Result<u8, QpackIntegerParseError> {
309 match prefix_bits {
310 1..=7 => Ok((1u8 << prefix_bits) - 1),
311 8 => Ok(u8::MAX),
312 _ => Err(QpackIntegerParseError::InvalidPrefixBits { prefix_bits }),
313 }
314}
315
316fn encoded_len(value: u64, prefix_mask: u8) -> usize {
317 if value < u64::from(prefix_mask) {
318 return 1;
319 }
320 let mut required = 2usize;
321 let mut remaining = value - u64::from(prefix_mask);
322 while remaining >= 0x80 {
323 required += 1;
324 remaining >>= 7;
325 }
326 required
327}