Skip to main content

net_wire/http2/hpack/
integer.rs

1//! RFC 7541 variable-length integer parsing and construction.
2
3use core::fmt;
4
5/// A validated, exact borrowed HPACK integer representation.
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub struct HpackInteger<'a> {
8    bytes: &'a [u8],
9    prefix_bits: u8,
10    value: u64,
11    high_bits: u8,
12}
13
14impl<'a> HpackInteger<'a> {
15    /// Parses one RFC 7541 section 5.1 integer and excludes any following bytes.
16    pub fn parse(bytes: &'a [u8], prefix_bits: u8) -> Result<Self, HpackIntegerParseError> {
17        let prefix_mask = prefix_mask(prefix_bits)?;
18        let first = *bytes.first().ok_or(HpackIntegerParseError::Incomplete {
19            required: 1,
20            available: 0,
21        })?;
22        let prefix_value = first & prefix_mask;
23        let high_bits = first & !prefix_mask;
24
25        if prefix_value < prefix_mask {
26            return Ok(Self {
27                bytes: &bytes[..1],
28                prefix_bits,
29                value: u64::from(prefix_value),
30                high_bits,
31            });
32        }
33
34        let mut value = u64::from(prefix_mask);
35        let mut shift = 0u32;
36        let mut index = 1usize;
37        loop {
38            let octet = *bytes.get(index).ok_or(HpackIntegerParseError::Incomplete {
39                required: index
40                    .checked_add(1)
41                    .ok_or(HpackIntegerParseError::Overflow)?,
42                available: bytes.len(),
43            })?;
44            if shift >= u64::BITS {
45                return Err(HpackIntegerParseError::Overflow);
46            }
47            let contribution = u64::from(octet & 0x7f)
48                .checked_mul(1u64 << shift)
49                .ok_or(HpackIntegerParseError::Overflow)?;
50            value = value
51                .checked_add(contribution)
52                .ok_or(HpackIntegerParseError::Overflow)?;
53            index = index
54                .checked_add(1)
55                .ok_or(HpackIntegerParseError::Overflow)?;
56            if octet & 0x80 == 0 {
57                return Ok(Self {
58                    bytes: &bytes[..index],
59                    prefix_bits,
60                    value,
61                    high_bits,
62                });
63            }
64            shift = shift
65                .checked_add(7)
66                .ok_or(HpackIntegerParseError::Overflow)?;
67        }
68    }
69
70    /// Returns the decoded integer value.
71    pub const fn value(self) -> u64 {
72        self.value
73    }
74
75    /// Returns the exact encoded representation, excluding any following bytes.
76    pub const fn as_bytes(self) -> &'a [u8] {
77        self.bytes
78    }
79
80    /// Returns the number of controlled low bits in the first octet.
81    pub const fn prefix_bits(self) -> u8 {
82        self.prefix_bits
83    }
84
85    /// Returns the preserved uncontrolled high bits of the first octet.
86    pub const fn high_bits(self) -> u8 {
87        self.high_bits
88    }
89}
90
91pub(crate) fn canonical_encoded_len(value: u64, prefix_mask: u8) -> usize {
92    encoded_len(value, prefix_mask)
93}
94
95pub(crate) fn canonical_integer<'a>(
96    bytes: &'a [u8],
97    prefix_bits: u8,
98    high_bits: u8,
99    value: u64,
100) -> HpackInteger<'a> {
101    HpackInteger {
102        bytes,
103        prefix_bits,
104        value,
105        high_bits,
106    }
107}
108
109/// Failure to parse an HPACK integer.
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
111pub enum HpackIntegerParseError {
112    /// The prefix width is outside the inclusive range 1 through 8.
113    InvalidPrefixBits {
114        /// Supplied prefix width.
115        prefix_bits: u8,
116    },
117    /// The input ended before the first octet or a terminating continuation octet.
118    Incomplete {
119        /// Required bytes to include the missing octet.
120        required: usize,
121        /// Available input bytes.
122        available: usize,
123    },
124    /// The encoded number cannot be represented as a `u64`.
125    Overflow,
126}
127
128impl fmt::Display for HpackIntegerParseError {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        match self {
131            Self::InvalidPrefixBits { prefix_bits } => {
132                write!(
133                    f,
134                    "HPACK integer prefix width must be 1 through 8, got {prefix_bits}"
135                )
136            }
137            Self::Incomplete {
138                required,
139                available,
140            } => write!(
141                f,
142                "HPACK integer input is incomplete: need {required} bytes, have {available}"
143            ),
144            Self::Overflow => f.write_str("HPACK integer exceeds u64"),
145        }
146    }
147}
148
149/// Builds a canonical RFC 7541 section 5.1 integer in caller-owned storage.
150pub struct HpackIntegerBuilder<'a> {
151    destination: &'a mut [u8],
152    prefix_bits: u8,
153    high_bits: u8,
154    value: u64,
155}
156
157impl<'a> HpackIntegerBuilder<'a> {
158    /// Creates a builder with the supplied prefix width, preserved high bits, and value.
159    pub fn new(destination: &'a mut [u8], prefix_bits: u8, high_bits: u8, value: u64) -> Self {
160        Self {
161            destination,
162            prefix_bits,
163            high_bits,
164            value,
165        }
166    }
167
168    /// Validates all inputs and capacity before writing a canonical integer representation.
169    pub fn build(self) -> Result<HpackInteger<'a>, HpackIntegerBuildError> {
170        let prefix_mask = prefix_mask(self.prefix_bits).map_err(|_| {
171            HpackIntegerBuildError::InvalidPrefixBits {
172                prefix_bits: self.prefix_bits,
173            }
174        })?;
175        if self.high_bits & prefix_mask != 0 {
176            return Err(HpackIntegerBuildError::HighBitsOverlap {
177                high_bits: self.high_bits,
178                prefix_bits: self.prefix_bits,
179            });
180        }
181
182        let required = encoded_len(self.value, prefix_mask);
183        if self.destination.len() < required {
184            return Err(HpackIntegerBuildError::BufferTooShort {
185                required,
186                available: self.destination.len(),
187            });
188        }
189
190        Ok(write_canonical(
191            &mut self.destination[..required],
192            self.prefix_bits,
193            prefix_mask,
194            self.high_bits,
195            self.value,
196        ))
197    }
198}
199
200/// Failure to build an HPACK integer.
201#[derive(Clone, Copy, Debug, Eq, PartialEq)]
202pub enum HpackIntegerBuildError {
203    /// The prefix width is outside the inclusive range 1 through 8.
204    InvalidPrefixBits {
205        /// Supplied prefix width.
206        prefix_bits: u8,
207    },
208    /// Supplied high bits overlap the integer-controlled low prefix bits.
209    HighBitsOverlap {
210        /// Supplied high bits.
211        high_bits: u8,
212        /// Number of integer-controlled low bits.
213        prefix_bits: u8,
214    },
215    /// The caller buffer cannot contain the complete integer representation.
216    BufferTooShort {
217        /// Required bytes.
218        required: usize,
219        /// Available bytes.
220        available: usize,
221    },
222}
223
224impl fmt::Display for HpackIntegerBuildError {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        match self {
227            Self::InvalidPrefixBits { prefix_bits } => {
228                write!(
229                    f,
230                    "HPACK integer prefix width must be 1 through 8, got {prefix_bits}"
231                )
232            }
233            Self::HighBitsOverlap {
234                high_bits,
235                prefix_bits,
236            } => write!(
237                f,
238                "HPACK integer high bits {high_bits:#04x} overlap the {prefix_bits}-bit prefix"
239            ),
240            Self::BufferTooShort {
241                required,
242                available,
243            } => write!(
244                f,
245                "HPACK integer buffer is too short: need {required} bytes, have {available}"
246            ),
247        }
248    }
249}
250
251fn write_canonical<'a>(
252    bytes: &'a mut [u8],
253    prefix_bits: u8,
254    prefix_mask: u8,
255    high_bits: u8,
256    value: u64,
257) -> HpackInteger<'a> {
258    if value < u64::from(prefix_mask) {
259        bytes[0] = high_bits | value as u8;
260    } else {
261        bytes[0] = high_bits | prefix_mask;
262        let mut remaining = value - u64::from(prefix_mask);
263        let mut index = 1usize;
264        while remaining >= 0x80 {
265            bytes[index] = (remaining as u8 & 0x7f) | 0x80;
266            remaining >>= 7;
267            index += 1;
268        }
269        bytes[index] = remaining as u8;
270    }
271
272    canonical_integer(bytes, prefix_bits, high_bits, value)
273}
274
275pub(crate) fn write_canonical_for_valid_prefix(
276    bytes: &mut [u8],
277    prefix_bits: u8,
278    prefix_mask: u8,
279    high_bits: u8,
280    value: u64,
281) {
282    let required = canonical_encoded_len(value, prefix_mask);
283    debug_assert_eq!(bytes.len(), required);
284    let _ = write_canonical(bytes, prefix_bits, prefix_mask, high_bits, value);
285}
286
287fn prefix_mask(prefix_bits: u8) -> Result<u8, HpackIntegerParseError> {
288    match prefix_bits {
289        1..=7 => Ok((1u8 << prefix_bits) - 1),
290        8 => Ok(u8::MAX),
291        _ => Err(HpackIntegerParseError::InvalidPrefixBits { prefix_bits }),
292    }
293}
294
295fn encoded_len(value: u64, prefix_mask: u8) -> usize {
296    if value < u64::from(prefix_mask) {
297        return 1;
298    }
299
300    let mut required = 2usize;
301    let mut remaining = value - u64::from(prefix_mask);
302    while remaining >= 0x80 {
303        required += 1;
304        remaining >>= 7;
305    }
306    required
307}