Skip to main content

net_wire/qpack/
huffman.rs

1//! RFC 7541 Appendix B Huffman coding for QPACK strings.
2
3use core::fmt;
4
5use crate::rfc7541_huffman;
6
7/// Encodes QPACK Huffman payload bytes into caller-owned storage.
8pub struct QpackHuffmanEncoder<'input, 'output> {
9    decoded: &'input [u8],
10    destination: &'output mut [u8],
11}
12
13impl<'input, 'output> QpackHuffmanEncoder<'input, 'output> {
14    /// Creates an encoder for decoded bytes and caller-owned destination storage.
15    pub fn new(decoded: &'input [u8], destination: &'output mut [u8]) -> Self {
16        Self {
17            decoded,
18            destination,
19        }
20    }
21
22    /// Returns the exact RFC-padded encoded byte length.
23    pub fn required_encoded_len(decoded: &[u8]) -> Result<usize, QpackHuffmanEncodeError> {
24        rfc7541_huffman::encoded_len(decoded).map_err(map_encode)
25    }
26
27    /// Validates all fallible conditions before atomically writing the encoded bytes.
28    pub fn encode(self) -> Result<&'output [u8], QpackHuffmanEncodeError> {
29        let required = Self::required_encoded_len(self.decoded)?;
30        if self.destination.len() < required {
31            return Err(QpackHuffmanEncodeError::DestinationTooShort {
32                required,
33                available: self.destination.len(),
34            });
35        }
36        let output = &mut self.destination[..required];
37        rfc7541_huffman::encode_prevalidated(self.decoded, output);
38        Ok(output)
39    }
40}
41
42/// Failure to encode QPACK Huffman payload bytes.
43#[derive(Clone, Copy, Debug, Eq, PartialEq)]
44pub enum QpackHuffmanEncodeError {
45    /// The concatenated Huffman code length cannot be represented as `usize` bits.
46    EncodedBitLengthOverflow,
47    /// The RFC-padded Huffman byte length cannot be represented as `usize`.
48    EncodedByteLengthOverflow,
49    /// The caller destination cannot contain the complete encoded sequence.
50    DestinationTooShort {
51        /// Bytes required.
52        required: usize,
53        /// Bytes available.
54        available: usize,
55    },
56}
57
58impl fmt::Display for QpackHuffmanEncodeError {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        match self {
61            Self::EncodedBitLengthOverflow => {
62                f.write_str("QPACK Huffman encoded bit length overflows usize")
63            }
64            Self::EncodedByteLengthOverflow => {
65                f.write_str("QPACK Huffman encoded byte length overflows usize")
66            }
67            Self::DestinationTooShort {
68                required,
69                available,
70            } => write!(
71                f,
72                "QPACK Huffman destination is too short: need {required} bytes, have {available}"
73            ),
74        }
75    }
76}
77
78/// Decodes QPACK Huffman payload bytes into caller-owned storage.
79pub struct QpackHuffmanDecoder<'input, 'output> {
80    encoded: &'input [u8],
81    destination: &'output mut [u8],
82}
83
84impl<'input, 'output> QpackHuffmanDecoder<'input, 'output> {
85    /// Creates a decoder for encoded bytes and caller-owned destination storage.
86    pub fn new(encoded: &'input [u8], destination: &'output mut [u8]) -> Self {
87        Self {
88            encoded,
89            destination,
90        }
91    }
92
93    /// Returns the decoded byte length after validating the complete input.
94    pub fn required_decoded_len(encoded: &[u8]) -> Result<usize, QpackHuffmanDecodeError> {
95        rfc7541_huffman::decoded_len(encoded).map_err(map_decode)
96    }
97
98    /// Validates all fallible conditions before atomically writing decoded bytes.
99    pub fn decode(self) -> Result<&'output [u8], QpackHuffmanDecodeError> {
100        let required = Self::required_decoded_len(self.encoded)?;
101        if self.destination.len() < required {
102            return Err(QpackHuffmanDecodeError::OutputTooShort {
103                required,
104                available: self.destination.len(),
105            });
106        }
107        rfc7541_huffman::decode_prevalidated(self.encoded, &mut self.destination[..required]);
108        Ok(&self.destination[..required])
109    }
110}
111
112/// Failure to decode QPACK Huffman payload bytes.
113#[derive(Clone, Copy, Debug, Eq, PartialEq)]
114pub enum QpackHuffmanDecodeError {
115    /// The encoded data contains the reserved EOS symbol.
116    EosSymbol,
117    /// The encoded data reaches a bit sequence that is not a Huffman-code prefix.
118    InvalidHuffmanCode,
119    /// The trailing incomplete code is not valid RFC 7541 EOS-prefix padding.
120    InvalidPadding,
121    /// The decoded byte count cannot be represented as `usize`.
122    DecodedLengthOverflow,
123    /// The caller destination cannot contain the complete decoded sequence.
124    OutputTooShort {
125        /// Bytes required.
126        required: usize,
127        /// Bytes available.
128        available: usize,
129    },
130}
131
132/// Decodes a payload whose validity and exact decoded output length were prevalidated.
133pub(crate) fn decode_prevalidated(encoded: &[u8], destination: &mut [u8]) {
134    rfc7541_huffman::decode_prevalidated(encoded, destination);
135}
136
137impl fmt::Display for QpackHuffmanDecodeError {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        match self {
140            Self::EosSymbol => f.write_str("QPACK Huffman data contains EOS"),
141            Self::InvalidHuffmanCode => f.write_str("QPACK Huffman data contains an invalid code"),
142            Self::InvalidPadding => {
143                f.write_str("QPACK Huffman data has invalid EOS-prefix padding")
144            }
145            Self::DecodedLengthOverflow => {
146                f.write_str("QPACK Huffman decoded length overflows usize")
147            }
148            Self::OutputTooShort {
149                required,
150                available,
151            } => write!(
152                f,
153                "QPACK Huffman output buffer is too short: need {required} bytes, have {available}"
154            ),
155        }
156    }
157}
158
159fn map_encode(error: rfc7541_huffman::EncodeError) -> QpackHuffmanEncodeError {
160    match error {
161        rfc7541_huffman::EncodeError::BitLengthOverflow => {
162            QpackHuffmanEncodeError::EncodedBitLengthOverflow
163        }
164        rfc7541_huffman::EncodeError::ByteLengthOverflow => {
165            QpackHuffmanEncodeError::EncodedByteLengthOverflow
166        }
167    }
168}
169
170fn map_decode(error: rfc7541_huffman::DecodeError) -> QpackHuffmanDecodeError {
171    match error {
172        rfc7541_huffman::DecodeError::EosSymbol => QpackHuffmanDecodeError::EosSymbol,
173        rfc7541_huffman::DecodeError::InvalidHuffmanCode => {
174            QpackHuffmanDecodeError::InvalidHuffmanCode
175        }
176        rfc7541_huffman::DecodeError::InvalidPadding => QpackHuffmanDecodeError::InvalidPadding,
177        rfc7541_huffman::DecodeError::DecodedLengthOverflow => {
178            QpackHuffmanDecodeError::DecodedLengthOverflow
179        }
180    }
181}