Skip to main content

net_wire/http2/hpack/
huffman.rs

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