1use core::{fmt, iter::FusedIterator};
4
5use super::context::{QpackFieldSectionContext, QpackFieldSectionContextError};
6use super::prefix::{QpackFieldSectionPrefix, QpackFieldSectionPrefixParseError};
7use crate::qpack::field::line::{QpackFieldLine, QpackFieldLines, QpackFieldLinesParseError};
8use crate::qpack::huffman::{QpackHuffmanDecodeError, QpackHuffmanDecoder, decode_prevalidated};
9use crate::qpack::string::QpackStringLiteral;
10use crate::qpack::table::{
11 QPACK_STATIC_TABLE_LEN, QpackDynamicTable, QpackDynamicTableError, QpackHeaderFieldRef,
12 QpackStaticTable,
13};
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub struct QpackDecodedFieldEntry {
18 offset: usize,
19 name_len: usize,
20 value_len: usize,
21 never_indexed: bool,
22}
23
24impl QpackDecodedFieldEntry {
25 pub const EMPTY: Self = Self {
27 offset: 0,
28 name_len: 0,
29 value_len: 0,
30 never_indexed: false,
31 };
32
33 pub const fn never_indexed(self) -> bool {
35 self.never_indexed
36 }
37}
38
39pub struct QpackFieldSectionOutput<'bytes, 'fields> {
41 bytes: &'bytes mut [u8],
42 fields: &'fields mut [QpackDecodedFieldEntry],
43 len: usize,
44}
45
46impl<'bytes, 'fields> QpackFieldSectionOutput<'bytes, 'fields> {
47 pub fn new(bytes: &'bytes mut [u8], fields: &'fields mut [QpackDecodedFieldEntry]) -> Self {
49 Self {
50 bytes,
51 fields,
52 len: 0,
53 }
54 }
55
56 pub const fn byte_capacity(&self) -> usize {
58 self.bytes.len()
59 }
60
61 pub const fn field_capacity(&self) -> usize {
63 self.fields.len()
64 }
65
66 pub const fn len(&self) -> usize {
68 self.len
69 }
70
71 pub const fn is_empty(&self) -> bool {
73 self.len == 0
74 }
75}
76
77#[derive(Clone, Copy, Debug, Eq, PartialEq)]
79pub struct QpackDecodedFieldRef<'a> {
80 name: &'a [u8],
81 value: &'a [u8],
82 never_indexed: bool,
83}
84
85impl<'a> QpackDecodedFieldRef<'a> {
86 pub const fn name(self) -> &'a [u8] {
88 self.name
89 }
90
91 pub const fn value(self) -> &'a [u8] {
93 self.value
94 }
95
96 pub const fn never_indexed(self) -> bool {
98 self.never_indexed
99 }
100}
101
102#[derive(Clone, Copy, Debug, Eq, PartialEq)]
104pub struct QpackDecodedFieldSection<'a> {
105 bytes: &'a [u8],
106 fields: &'a [QpackDecodedFieldEntry],
107 required_insert_count: u64,
108 base: u64,
109 contains_dynamic_references: bool,
110}
111
112impl<'a> QpackDecodedFieldSection<'a> {
113 pub const fn required_insert_count(self) -> u64 {
115 self.required_insert_count
116 }
117
118 pub const fn base(self) -> u64 {
120 self.base
121 }
122
123 pub const fn contains_dynamic_references(self) -> bool {
125 self.contains_dynamic_references
126 }
127
128 pub const fn len(self) -> usize {
130 self.fields.len()
131 }
132
133 pub const fn is_empty(self) -> bool {
135 self.fields.is_empty()
136 }
137
138 pub fn get(self, index: usize) -> Option<QpackDecodedFieldRef<'a>> {
140 self.fields
141 .get(index)
142 .and_then(|entry| field_from_entry(self.bytes, *entry))
143 }
144
145 pub const fn iter(self) -> QpackDecodedFieldIter<'a> {
147 QpackDecodedFieldIter {
148 bytes: self.bytes,
149 fields: self.fields,
150 index: 0,
151 }
152 }
153}
154
155#[derive(Clone, Copy, Debug)]
157pub struct QpackDecodedFieldIter<'a> {
158 bytes: &'a [u8],
159 fields: &'a [QpackDecodedFieldEntry],
160 index: usize,
161}
162
163impl<'a> Iterator for QpackDecodedFieldIter<'a> {
164 type Item = QpackDecodedFieldRef<'a>;
165
166 fn next(&mut self) -> Option<Self::Item> {
167 let entry = *self.fields.get(self.index)?;
168 self.index = self.index.saturating_add(1);
169 field_from_entry(self.bytes, entry)
170 }
171
172 fn size_hint(&self) -> (usize, Option<usize>) {
173 let remaining = self.fields.len().saturating_sub(self.index);
174 (remaining, Some(remaining))
175 }
176}
177
178impl ExactSizeIterator for QpackDecodedFieldIter<'_> {
179 fn len(&self) -> usize {
180 self.fields.len().saturating_sub(self.index)
181 }
182}
183
184impl FusedIterator for QpackDecodedFieldIter<'_> {}
185
186#[derive(Clone, Copy, Debug, Eq, PartialEq)]
188pub struct QpackFieldSectionBlocked {
189 required_insert_count: u64,
190 base: u64,
191}
192
193impl QpackFieldSectionBlocked {
194 pub const fn required_insert_count(self) -> u64 {
196 self.required_insert_count
197 }
198
199 pub const fn base(self) -> u64 {
201 self.base
202 }
203}
204
205#[derive(Clone, Copy, Debug, Eq, PartialEq)]
207pub enum QpackFieldSectionDecodeOutcome<'a> {
208 Decoded(QpackDecodedFieldSection<'a>),
210 Blocked(QpackFieldSectionBlocked),
212}
213
214#[derive(Clone, Copy, Debug, Eq, PartialEq)]
216pub enum QpackFieldSectionDecodeError {
217 Prefix(QpackFieldSectionPrefixParseError),
219 Context(QpackFieldSectionContextError),
221 FieldLines(QpackFieldLinesParseError),
223 StaticIndexOutOfRange {
225 index: u64,
227 },
228 DynamicReference(QpackDynamicTableError),
230 LiteralNameHuffman(QpackHuffmanDecodeError),
232 ValueHuffman(QpackHuffmanDecodeError),
234 OutputBytesTooShort {
236 required: usize,
238 available: usize,
240 },
241 OutputFieldsTooShort {
243 required: usize,
245 available: usize,
247 },
248 OutputLengthOverflow,
250}
251
252impl QpackFieldSectionDecodeError {
253 pub const fn is_decompression_failed(self) -> bool {
255 matches!(
256 self,
257 Self::Prefix(_)
258 | Self::Context(_)
259 | Self::FieldLines(_)
260 | Self::StaticIndexOutOfRange { .. }
261 | Self::DynamicReference(_)
262 | Self::LiteralNameHuffman(_)
263 | Self::ValueHuffman(_)
264 )
265 }
266
267 pub const fn is_output_provisioning_error(self) -> bool {
269 !self.is_decompression_failed()
270 }
271}
272
273impl fmt::Display for QpackFieldSectionDecodeError {
274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275 match self {
276 Self::Prefix(error) => write!(f, "invalid QPACK field-section prefix: {error}"),
277 Self::Context(error) => write!(f, "invalid QPACK field-section context: {error}"),
278 Self::FieldLines(error) => write!(f, "invalid QPACK field lines: {error}"),
279 Self::StaticIndexOutOfRange { index } => {
280 write!(f, "QPACK static index {index} is out of range")
281 }
282 Self::DynamicReference(error) => write!(f, "invalid QPACK dynamic reference: {error}"),
283 Self::LiteralNameHuffman(error) => {
284 write!(f, "invalid QPACK literal-name Huffman data: {error}")
285 }
286 Self::ValueHuffman(error) => write!(f, "invalid QPACK value Huffman data: {error}"),
287 Self::OutputBytesTooShort {
288 required,
289 available,
290 } => write!(
291 f,
292 "QPACK field-section output bytes are too short: need {required}, have {available}"
293 ),
294 Self::OutputFieldsTooShort {
295 required,
296 available,
297 } => write!(
298 f,
299 "QPACK field-section output fields are too short: need {required}, have {available}"
300 ),
301 Self::OutputLengthOverflow => {
302 f.write_str("QPACK field-section output length overflows usize")
303 }
304 }
305 }
306}
307
308impl core::error::Error for QpackFieldSectionDecodeError {}
309
310#[derive(Clone, Copy, Debug, Eq, PartialEq)]
312pub struct QpackFieldSectionDecoder {
313 maximum_dynamic_table_capacity: u64,
314}
315
316impl QpackFieldSectionDecoder {
317 pub const fn new(maximum_dynamic_table_capacity: u64) -> Self {
319 Self {
320 maximum_dynamic_table_capacity,
321 }
322 }
323
324 pub const fn maximum_dynamic_table_capacity(self) -> u64 {
326 self.maximum_dynamic_table_capacity
327 }
328
329 pub fn decode<'output>(
331 &self,
332 encoded: &[u8],
333 table: &QpackDynamicTable<'_, '_>,
334 output: &'output mut QpackFieldSectionOutput<'_, '_>,
335 ) -> Result<QpackFieldSectionDecodeOutcome<'output>, QpackFieldSectionDecodeError> {
336 let prefix = QpackFieldSectionPrefix::parse(encoded)
337 .map_err(QpackFieldSectionDecodeError::Prefix)?;
338 let context = QpackFieldSectionContext::decode(
339 prefix,
340 table.insert_count(),
341 self.maximum_dynamic_table_capacity,
342 )
343 .map_err(QpackFieldSectionDecodeError::Context)?;
344 if context.required_insert_count() > table.insert_count() {
345 return Ok(QpackFieldSectionDecodeOutcome::Blocked(
346 QpackFieldSectionBlocked {
347 required_insert_count: context.required_insert_count(),
348 base: context.base(),
349 },
350 ));
351 }
352
353 let lines = QpackFieldLines::parse(&encoded[prefix.as_bytes().len()..])
354 .map_err(QpackFieldSectionDecodeError::FieldLines)?;
355 let required = preflight(lines, table, context)?;
356 if output.fields.len() < required.fields {
357 return Err(QpackFieldSectionDecodeError::OutputFieldsTooShort {
358 required: required.fields,
359 available: output.fields.len(),
360 });
361 }
362 if output.bytes.len() < required.bytes {
363 return Err(QpackFieldSectionDecodeError::OutputBytesTooShort {
364 required: required.bytes,
365 available: output.bytes.len(),
366 });
367 }
368
369 let verified = preflight(lines, table, context)?;
372 if verified != required {
373 return Err(QpackFieldSectionDecodeError::OutputLengthOverflow);
374 }
375 commit(
376 lines,
377 table,
378 context,
379 output,
380 required.bytes,
381 required.fields,
382 )?;
383
384 let bytes = &output.bytes[..required.bytes];
385 let fields = &output.fields[..required.fields];
386 Ok(QpackFieldSectionDecodeOutcome::Decoded(
387 QpackDecodedFieldSection {
388 bytes,
389 fields,
390 required_insert_count: context.required_insert_count(),
391 base: context.base(),
392 contains_dynamic_references: verified.contains_dynamic_references,
393 },
394 ))
395 }
396}
397
398#[derive(Clone, Copy, Debug, Eq, PartialEq)]
399struct Preflight {
400 bytes: usize,
401 fields: usize,
402 contains_dynamic_references: bool,
403}
404
405fn preflight(
406 lines: QpackFieldLines<'_>,
407 table: &QpackDynamicTable<'_, '_>,
408 context: QpackFieldSectionContext,
409) -> Result<Preflight, QpackFieldSectionDecodeError> {
410 let mut bytes = 0usize;
411 let mut fields = 0usize;
412 let mut contains_dynamic_references = false;
413 for line in lines.iter() {
414 let line = match line {
415 Ok(line) => line,
416 Err(_) => return Err(QpackFieldSectionDecodeError::OutputLengthOverflow),
418 };
419 let (name, value, references_dynamic_table) = line_lengths(line, table, context)?;
420 bytes = bytes
421 .checked_add(name)
422 .and_then(|length| length.checked_add(value))
423 .ok_or(QpackFieldSectionDecodeError::OutputLengthOverflow)?;
424 fields = fields
425 .checked_add(1)
426 .ok_or(QpackFieldSectionDecodeError::OutputLengthOverflow)?;
427 contains_dynamic_references |= references_dynamic_table;
428 }
429 Ok(Preflight {
430 bytes,
431 fields,
432 contains_dynamic_references,
433 })
434}
435
436fn line_lengths(
437 line: QpackFieldLine<'_>,
438 table: &QpackDynamicTable<'_, '_>,
439 context: QpackFieldSectionContext,
440) -> Result<(usize, usize, bool), QpackFieldSectionDecodeError> {
441 match line {
442 QpackFieldLine::Indexed(line) => {
443 let field = resolve_indexed(line.is_static(), line.index().value(), table, context)?;
444 Ok((field.name().len(), field.value().len(), !line.is_static()))
445 }
446 QpackFieldLine::IndexedPostBase(line) => {
447 let field = table
448 .get_post_base(
449 context.required_insert_count(),
450 context.base(),
451 line.index().value(),
452 )
453 .map_err(QpackFieldSectionDecodeError::DynamicReference)?;
454 Ok((field.name().len(), field.value().len(), true))
455 }
456 QpackFieldLine::LiteralNameReference(line) => {
457 let name =
458 resolve_indexed(line.is_static(), line.name_index().value(), table, context)?
459 .name();
460 Ok((
461 name.len(),
462 literal_len(line.value(), false)?,
463 !line.is_static(),
464 ))
465 }
466 QpackFieldLine::LiteralPostBaseNameReference(line) => {
467 let name = table
468 .get_post_base(
469 context.required_insert_count(),
470 context.base(),
471 line.name_index().value(),
472 )
473 .map_err(QpackFieldSectionDecodeError::DynamicReference)?
474 .name();
475 Ok((name.len(), literal_len(line.value(), false)?, true))
476 }
477 QpackFieldLine::LiteralName(line) => Ok((
478 literal_len(line.name(), true)?,
479 literal_len(line.value(), false)?,
480 false,
481 )),
482 }
483}
484
485fn resolve_indexed<'a>(
486 is_static: bool,
487 index: u64,
488 table: &'a QpackDynamicTable<'_, '_>,
489 context: QpackFieldSectionContext,
490) -> Result<QpackHeaderFieldRef<'a>, QpackFieldSectionDecodeError> {
491 if is_static {
492 static_field(index)
493 } else {
494 table
495 .get_field_relative(context.required_insert_count(), context.base(), index)
496 .map_err(QpackFieldSectionDecodeError::DynamicReference)
497 }
498}
499
500fn literal_len(
501 literal: QpackStringLiteral<'_>,
502 name: bool,
503) -> Result<usize, QpackFieldSectionDecodeError> {
504 if !literal.is_huffman() {
505 return Ok(literal.encoded_payload().len());
506 }
507 QpackHuffmanDecoder::required_decoded_len(literal.encoded_payload()).map_err(|error| {
508 if name {
509 QpackFieldSectionDecodeError::LiteralNameHuffman(error)
510 } else {
511 QpackFieldSectionDecodeError::ValueHuffman(error)
512 }
513 })
514}
515
516fn static_field(index: u64) -> Result<QpackHeaderFieldRef<'static>, QpackFieldSectionDecodeError> {
517 if index >= QPACK_STATIC_TABLE_LEN as u64 {
518 return Err(QpackFieldSectionDecodeError::StaticIndexOutOfRange { index });
519 }
520 QpackStaticTable::get(index as usize)
521 .ok_or(QpackFieldSectionDecodeError::StaticIndexOutOfRange { index })
522}
523
524fn commit(
525 lines: QpackFieldLines<'_>,
526 table: &QpackDynamicTable<'_, '_>,
527 context: QpackFieldSectionContext,
528 output: &mut QpackFieldSectionOutput<'_, '_>,
529 required_bytes: usize,
530 required_fields: usize,
531) -> Result<(), QpackFieldSectionDecodeError> {
532 let mut offset = 0usize;
533 let mut field_index = 0usize;
534 for line in lines.iter() {
535 let line = match line {
536 Ok(line) => line,
537 Err(_) => return Err(QpackFieldSectionDecodeError::OutputLengthOverflow),
538 };
539 let entry = write_prevalidated_line(line, table, context, &mut output.bytes[offset..])?;
540 output.fields[field_index] = QpackDecodedFieldEntry {
541 offset,
542 name_len: entry.name_len,
543 value_len: entry.value_len,
544 never_indexed: entry.never_indexed,
545 };
546 offset += entry.name_len + entry.value_len;
547 field_index += 1;
548 }
549 if offset != required_bytes || field_index != required_fields {
550 return Err(QpackFieldSectionDecodeError::OutputLengthOverflow);
551 }
552 output.len = required_fields;
553 Ok(())
554}
555
556fn write_prevalidated_line(
557 line: QpackFieldLine<'_>,
558 table: &QpackDynamicTable<'_, '_>,
559 context: QpackFieldSectionContext,
560 destination: &mut [u8],
561) -> Result<QpackDecodedFieldEntry, QpackFieldSectionDecodeError> {
562 match line {
563 QpackFieldLine::Indexed(line) => {
564 let field = resolve_indexed(line.is_static(), line.index().value(), table, context)?;
565 Ok(write_field(field, destination, false))
566 }
567 QpackFieldLine::IndexedPostBase(line) => {
568 let field = table
569 .get_post_base(
570 context.required_insert_count(),
571 context.base(),
572 line.index().value(),
573 )
574 .map_err(QpackFieldSectionDecodeError::DynamicReference)?;
575 Ok(write_field(field, destination, false))
576 }
577 QpackFieldLine::LiteralNameReference(line) => {
578 let name =
579 resolve_indexed(line.is_static(), line.name_index().value(), table, context)?
580 .name();
581 let value_len = literal_len(line.value(), false)?;
582 Ok(write_name_and_literal(
583 name,
584 line.value(),
585 value_len,
586 line.is_never_indexed(),
587 destination,
588 ))
589 }
590 QpackFieldLine::LiteralPostBaseNameReference(line) => {
591 let name = table
592 .get_post_base(
593 context.required_insert_count(),
594 context.base(),
595 line.name_index().value(),
596 )
597 .map_err(QpackFieldSectionDecodeError::DynamicReference)?
598 .name();
599 let value_len = literal_len(line.value(), false)?;
600 Ok(write_name_and_literal(
601 name,
602 line.value(),
603 value_len,
604 line.is_never_indexed(),
605 destination,
606 ))
607 }
608 QpackFieldLine::LiteralName(line) => {
609 let name_len = literal_len(line.name(), true)?;
610 let value_len = literal_len(line.value(), false)?;
611 write_prevalidated_literal(line.name(), name_len, destination);
612 write_prevalidated_literal(line.value(), value_len, &mut destination[name_len..]);
613 Ok(QpackDecodedFieldEntry {
614 offset: 0,
615 name_len,
616 value_len,
617 never_indexed: line.is_never_indexed(),
618 })
619 }
620 }
621}
622
623fn write_field(
624 field: QpackHeaderFieldRef<'_>,
625 destination: &mut [u8],
626 never_indexed: bool,
627) -> QpackDecodedFieldEntry {
628 let name_len = field.name().len();
629 let value_len = field.value().len();
630 destination[..name_len].copy_from_slice(field.name());
631 destination[name_len..name_len + value_len].copy_from_slice(field.value());
632 QpackDecodedFieldEntry {
633 offset: 0,
634 name_len,
635 value_len,
636 never_indexed,
637 }
638}
639
640fn write_name_and_literal(
641 name: &[u8],
642 value: QpackStringLiteral<'_>,
643 value_len: usize,
644 never_indexed: bool,
645 destination: &mut [u8],
646) -> QpackDecodedFieldEntry {
647 destination[..name.len()].copy_from_slice(name);
648 write_prevalidated_literal(value, value_len, &mut destination[name.len()..]);
649 QpackDecodedFieldEntry {
650 offset: 0,
651 name_len: name.len(),
652 value_len,
653 never_indexed,
654 }
655}
656
657fn write_prevalidated_literal(
658 literal: QpackStringLiteral<'_>,
659 decoded_len: usize,
660 destination: &mut [u8],
661) {
662 if literal.is_huffman() {
663 decode_prevalidated(literal.encoded_payload(), &mut destination[..decoded_len]);
664 } else {
665 destination[..decoded_len].copy_from_slice(literal.encoded_payload());
666 }
667}
668
669fn field_from_entry<'a>(
670 bytes: &'a [u8],
671 entry: QpackDecodedFieldEntry,
672) -> Option<QpackDecodedFieldRef<'a>> {
673 let name_end = entry.offset.checked_add(entry.name_len)?;
674 let value_end = name_end.checked_add(entry.value_len)?;
675 Some(QpackDecodedFieldRef {
676 name: bytes.get(entry.offset..name_end)?,
677 value: bytes.get(name_end..value_end)?,
678 never_indexed: entry.never_indexed,
679 })
680}