1use core::fmt;
4
5use super::integer::{
6 QPACK_INTEGER_MAX, QpackInteger, QpackIntegerBuildError, QpackIntegerParseError,
7 canonical_encoded_len, integer_from_prevalidated, write_canonical_prevalidated,
8};
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub struct QpackStringLiteral<'a> {
13 bytes: &'a [u8],
14 prefix_bits: u8,
15 previous_high_bits: u8,
16 huffman: bool,
17 length: QpackInteger<'a>,
18 encoded_payload: &'a [u8],
19}
20
21impl<'a> QpackStringLiteral<'a> {
22 pub fn parse(bytes: &'a [u8], prefix_bits: u8) -> Result<Self, QpackStringLiteralParseError> {
24 let (integer_bits, huffman_mask) = string_bits(prefix_bits)?;
25 let length = QpackInteger::parse(bytes, integer_bits)
26 .map_err(QpackStringLiteralParseError::Length)?;
27 let payload_len = usize::try_from(length.value()).map_err(|_| {
28 QpackStringLiteralParseError::LengthTooLarge {
29 value: length.value(),
30 }
31 })?;
32 let end = length.as_bytes().len().checked_add(payload_len).ok_or(
33 QpackStringLiteralParseError::LengthTooLarge {
34 value: length.value(),
35 },
36 )?;
37 if bytes.len() < end {
38 return Err(QpackStringLiteralParseError::Incomplete {
39 required: end,
40 available: bytes.len(),
41 });
42 }
43 let first = bytes[0];
44 Ok(Self {
45 bytes: &bytes[..end],
46 prefix_bits,
47 previous_high_bits: first & !controlled_mask(prefix_bits),
48 huffman: first & huffman_mask != 0,
49 length,
50 encoded_payload: &bytes[length.as_bytes().len()..end],
51 })
52 }
53
54 pub const fn prefix_bits(self) -> u8 {
56 self.prefix_bits
57 }
58
59 pub const fn previous_high_bits(self) -> u8 {
61 self.previous_high_bits
62 }
63
64 pub const fn is_huffman(self) -> bool {
66 self.huffman
67 }
68
69 pub const fn length(self) -> QpackInteger<'a> {
71 self.length
72 }
73
74 pub const fn encoded_payload(self) -> &'a [u8] {
76 self.encoded_payload
77 }
78
79 pub const fn as_bytes(self) -> &'a [u8] {
81 self.bytes
82 }
83}
84
85#[derive(Clone, Copy, Debug, Eq, PartialEq)]
87pub enum QpackStringLiteralParseError {
88 InvalidPrefixBits {
90 prefix_bits: u8,
92 },
93 Length(#[doc = "The nested length-integer failure."] QpackIntegerParseError),
95 LengthTooLarge {
97 value: u64,
99 },
100 Incomplete {
102 required: usize,
104 available: usize,
106 },
107}
108
109impl fmt::Display for QpackStringLiteralParseError {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self {
112 Self::InvalidPrefixBits { prefix_bits } => {
113 write!(
114 f,
115 "QPACK string prefix width must be 2 through 8, got {prefix_bits}"
116 )
117 }
118 Self::Length(error) => write!(f, "QPACK string length is invalid: {error}"),
119 Self::LengthTooLarge { value } => {
120 write!(f, "QPACK string length cannot be indexed: {value}")
121 }
122 Self::Incomplete {
123 required,
124 available,
125 } => write!(
126 f,
127 "QPACK string input is incomplete: need {required} bytes, have {available}"
128 ),
129 }
130 }
131}
132
133pub struct QpackStringLiteralBuilder<'a, 'payload> {
135 destination: &'a mut [u8],
136 prefix_bits: u8,
137 previous_high_bits: u8,
138 huffman: bool,
139 encoded_payload: &'payload [u8],
140}
141
142impl<'a, 'payload> QpackStringLiteralBuilder<'a, 'payload> {
143 pub fn new(
145 destination: &'a mut [u8],
146 prefix_bits: u8,
147 previous_high_bits: u8,
148 huffman: bool,
149 encoded_payload: &'payload [u8],
150 ) -> Self {
151 Self {
152 destination,
153 prefix_bits,
154 previous_high_bits,
155 huffman,
156 encoded_payload,
157 }
158 }
159
160 pub fn build(self) -> Result<QpackStringLiteral<'a>, QpackStringLiteralBuildError> {
162 let plan = string_plan(
163 self.prefix_bits,
164 self.previous_high_bits,
165 self.huffman,
166 self.encoded_payload,
167 )?;
168 if self.destination.len() < plan.required {
169 return Err(QpackStringLiteralBuildError::BufferTooShort {
170 required: plan.required,
171 available: self.destination.len(),
172 });
173 }
174 write_string_prevalidated(&mut self.destination[..plan.required], plan);
175 Ok(string_from_prevalidated(
176 &self.destination[..plan.required],
177 plan,
178 ))
179 }
180}
181
182#[derive(Clone, Copy)]
184pub(super) struct QpackStringLiteralPlan<'a> {
185 prefix_bits: u8,
186 previous_high_bits: u8,
187 huffman: bool,
188 encoded_payload: &'a [u8],
189 integer_bits: u8,
190 high_bits: u8,
191 payload_len: u64,
192 length_len: usize,
193 required: usize,
194}
195
196impl QpackStringLiteralPlan<'_> {
197 pub(super) const fn required(self) -> usize {
199 self.required
200 }
201}
202
203pub(super) fn string_plan<'a>(
205 prefix_bits: u8,
206 previous_high_bits: u8,
207 huffman: bool,
208 encoded_payload: &'a [u8],
209) -> Result<QpackStringLiteralPlan<'a>, QpackStringLiteralBuildError> {
210 let (integer_bits, huffman_mask) = string_bits(prefix_bits)
211 .map_err(|_| QpackStringLiteralBuildError::InvalidPrefixBits { prefix_bits })?;
212 if previous_high_bits & controlled_mask(prefix_bits) != 0 {
213 return Err(QpackStringLiteralBuildError::HighBitsOverlap {
214 high_bits: previous_high_bits,
215 prefix_bits,
216 });
217 }
218 let payload_len = u64::try_from(encoded_payload.len()).map_err(|_| {
219 QpackStringLiteralBuildError::PayloadLengthTooLarge {
220 length: encoded_payload.len(),
221 }
222 })?;
223 if payload_len > QPACK_INTEGER_MAX {
224 return Err(QpackStringLiteralBuildError::Length(
225 QpackIntegerBuildError::ValueTooLarge { value: payload_len },
226 ));
227 }
228 let length_len = canonical_encoded_len(payload_len, integer_bits);
229 let required = length_len
230 .checked_add(encoded_payload.len())
231 .ok_or(QpackStringLiteralBuildError::RequiredLengthOverflow)?;
232 let high_bits = previous_high_bits | if huffman { huffman_mask } else { 0 };
233 Ok(QpackStringLiteralPlan {
234 prefix_bits,
235 previous_high_bits,
236 huffman,
237 encoded_payload,
238 integer_bits,
239 high_bits,
240 payload_len,
241 length_len,
242 required,
243 })
244}
245
246pub(super) fn write_string_prevalidated(destination: &mut [u8], plan: QpackStringLiteralPlan<'_>) {
248 let (length_destination, payload_destination) = destination.split_at_mut(plan.length_len);
249 write_canonical_prevalidated(
250 length_destination,
251 plan.integer_bits,
252 plan.high_bits,
253 plan.payload_len,
254 );
255 payload_destination.copy_from_slice(plan.encoded_payload);
256}
257
258pub(super) fn string_from_prevalidated<'a>(
260 bytes: &'a [u8],
261 plan: QpackStringLiteralPlan<'_>,
262) -> QpackStringLiteral<'a> {
263 let length = integer_from_prevalidated(
264 &bytes[..plan.length_len],
265 plan.integer_bits,
266 plan.high_bits,
267 plan.payload_len,
268 );
269 QpackStringLiteral {
270 bytes,
271 prefix_bits: plan.prefix_bits,
272 previous_high_bits: plan.previous_high_bits,
273 huffman: plan.huffman,
274 length,
275 encoded_payload: &bytes[plan.length_len..],
276 }
277}
278
279#[derive(Clone, Copy, Debug, Eq, PartialEq)]
281pub enum QpackStringLiteralBuildError {
282 InvalidPrefixBits {
284 prefix_bits: u8,
286 },
287 HighBitsOverlap {
289 high_bits: u8,
291 prefix_bits: u8,
293 },
294 PayloadLengthTooLarge {
296 length: usize,
298 },
299 RequiredLengthOverflow,
301 BufferTooShort {
303 required: usize,
305 available: usize,
307 },
308 Length(#[doc = "The nested length-builder failure."] QpackIntegerBuildError),
310}
311
312impl fmt::Display for QpackStringLiteralBuildError {
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 match self {
315 Self::InvalidPrefixBits { prefix_bits } => {
316 write!(
317 f,
318 "QPACK string prefix width must be 2 through 8, got {prefix_bits}"
319 )
320 }
321 Self::HighBitsOverlap {
322 high_bits,
323 prefix_bits,
324 } => write!(
325 f,
326 "QPACK string high bits {high_bits:#04x} overlap the {prefix_bits}-bit string prefix"
327 ),
328 Self::PayloadLengthTooLarge { length } => {
329 write!(
330 f,
331 "QPACK string payload length cannot be represented: {length}"
332 )
333 }
334 Self::RequiredLengthOverflow => {
335 f.write_str("QPACK string required length overflows usize")
336 }
337 Self::BufferTooShort {
338 required,
339 available,
340 } => write!(
341 f,
342 "QPACK string buffer is too short: need {required} bytes, have {available}"
343 ),
344 Self::Length(error) => write!(f, "QPACK string length cannot be built: {error}"),
345 }
346 }
347}
348
349fn string_bits(prefix_bits: u8) -> Result<(u8, u8), QpackStringLiteralParseError> {
350 match prefix_bits {
351 2..=8 => Ok((prefix_bits - 1, 1u8 << (prefix_bits - 1))),
352 _ => Err(QpackStringLiteralParseError::InvalidPrefixBits { prefix_bits }),
353 }
354}
355
356const fn controlled_mask(prefix_bits: u8) -> u8 {
357 match prefix_bits {
358 2..=7 => (1u8 << prefix_bits) - 1,
359 8 => u8::MAX,
360 _ => 0,
361 }
362}