use core::fmt;
use super::integer::{canonical_encoded_len, write_canonical_for_valid_prefix};
use super::representation::HpackLiteralMode;
use super::string::HpackStringLiteralBuildError;
pub struct HpackIndexedFieldBuilder<'a> {
destination: &'a mut [u8],
index: u64,
}
impl<'a> HpackIndexedFieldBuilder<'a> {
pub fn new(destination: &'a mut [u8], index: u64) -> Self {
Self { destination, index }
}
pub fn build(self) -> Result<&'a [u8], HpackRepresentationBuildError> {
if self.index == 0 {
return Err(HpackRepresentationBuildError::IndexedFieldZero);
}
let required = canonical_encoded_len(self.index, 0x7f);
check_capacity(self.destination.len(), required)?;
let representation = &mut self.destination[..required];
write_integer(representation, 7, 0x7f, 0x80, self.index);
Ok(&*representation)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HpackLiteralName<'a> {
Indexed(u64),
Literal {
encoded_bytes: &'a [u8],
huffman: bool,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct HpackLiteralValue<'a> {
encoded_bytes: &'a [u8],
huffman: bool,
}
impl<'a> HpackLiteralValue<'a> {
pub const fn new_encoded(encoded_bytes: &'a [u8], huffman: bool) -> Self {
Self {
encoded_bytes,
huffman,
}
}
pub const fn encoded_bytes(self) -> &'a [u8] {
self.encoded_bytes
}
pub const fn is_huffman(self) -> bool {
self.huffman
}
}
pub struct HpackLiteralFieldBuilder<'a, 'b> {
destination: &'a mut [u8],
mode: HpackLiteralMode,
name: HpackLiteralName<'b>,
value: HpackLiteralValue<'b>,
}
impl<'a, 'b> HpackLiteralFieldBuilder<'a, 'b> {
pub fn new(
destination: &'a mut [u8],
mode: HpackLiteralMode,
name: HpackLiteralName<'b>,
value: HpackLiteralValue<'b>,
) -> Self {
Self {
destination,
mode,
name,
value,
}
}
pub fn build(self) -> Result<&'a [u8], HpackRepresentationBuildError> {
let (prefix_bits, high_bits) = match self.mode {
HpackLiteralMode::IncrementalIndexing => (6, 0x40),
HpackLiteralMode::WithoutIndexing => (4, 0),
HpackLiteralMode::NeverIndexed => (4, 0x10),
};
let name_index = self.name_index();
if matches!(self.name, HpackLiteralName::Indexed(0)) {
return Err(HpackRepresentationBuildError::LiteralNameIndexZero);
}
let literal_name = self
.literal_name()
.map(|name| {
string_info(name)
.map(|info| (name, info))
.map_err(HpackRepresentationBuildError::LiteralName)
})
.transpose()?;
let name_index_len = canonical_encoded_len(name_index, prefix_mask(prefix_bits));
let literal_name_len = literal_name.map_or(0, |(_, (length, _))| length);
let (value_len, value_encoded_length) =
string_info(self.value).map_err(HpackRepresentationBuildError::LiteralValue)?;
let required = name_index_len
.checked_add(literal_name_len)
.and_then(|length| length.checked_add(value_len))
.ok_or(HpackRepresentationBuildError::TotalLengthOverflow)?;
check_capacity(self.destination.len(), required)?;
let representation = &mut self.destination[..required];
let (name_index_destination, after_name_index) =
representation.split_at_mut(name_index_len);
write_integer(
name_index_destination,
prefix_bits,
prefix_mask(prefix_bits),
high_bits,
name_index,
);
let after_name = match literal_name {
Some((name, (_, encoded_length))) => {
let (name_destination, remainder) = after_name_index.split_at_mut(literal_name_len);
write_string(name_destination, name, encoded_length);
remainder
}
None => after_name_index,
};
write_string(after_name, self.value, value_encoded_length);
Ok(&*representation)
}
fn name_index(&self) -> u64 {
match self.name {
HpackLiteralName::Indexed(index) => index,
HpackLiteralName::Literal { .. } => 0,
}
}
fn literal_name(&self) -> Option<HpackLiteralValue<'b>> {
match self.name {
HpackLiteralName::Indexed(_) => None,
HpackLiteralName::Literal {
encoded_bytes,
huffman,
} => Some(HpackLiteralValue::new_encoded(encoded_bytes, huffman)),
}
}
}
pub struct HpackDynamicTableSizeUpdateBuilder<'a> {
destination: &'a mut [u8],
size: u64,
}
impl<'a> HpackDynamicTableSizeUpdateBuilder<'a> {
pub fn new(destination: &'a mut [u8], size: u64) -> Self {
Self { destination, size }
}
pub fn build(self) -> Result<&'a [u8], HpackRepresentationBuildError> {
let required = canonical_encoded_len(self.size, 0x1f);
check_capacity(self.destination.len(), required)?;
let representation = &mut self.destination[..required];
write_integer(representation, 5, 0x1f, 0x20, self.size);
Ok(&*representation)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum HpackRepresentationBuildError {
IndexedFieldZero,
LiteralNameIndexZero,
LiteralName(HpackStringLiteralBuildError),
LiteralValue(HpackStringLiteralBuildError),
TotalLengthOverflow,
BufferTooShort {
required: usize,
available: usize,
},
}
impl fmt::Display for HpackRepresentationBuildError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IndexedFieldZero => f.write_str("HPACK indexed field index must not be zero"),
Self::LiteralNameIndexZero => {
f.write_str("HPACK literal indexed name must not use index zero")
}
Self::LiteralName(error) => write!(f, "could not build HPACK literal name: {error}"),
Self::LiteralValue(error) => write!(f, "could not build HPACK literal value: {error}"),
Self::TotalLengthOverflow => f.write_str("HPACK representation length overflows usize"),
Self::BufferTooShort {
required,
available,
} => write!(
f,
"HPACK representation buffer is too short: need {required} bytes, have {available}"
),
}
}
}
fn check_capacity(available: usize, required: usize) -> Result<(), HpackRepresentationBuildError> {
if available < required {
return Err(HpackRepresentationBuildError::BufferTooShort {
required,
available,
});
}
Ok(())
}
fn string_info(value: HpackLiteralValue<'_>) -> Result<(usize, u64), HpackStringLiteralBuildError> {
let encoded_length = u64::try_from(value.encoded_bytes.len()).map_err(|_| {
HpackStringLiteralBuildError::EncodedLengthNotRepresentable {
encoded_length: value.encoded_bytes.len(),
}
})?;
let length_length = canonical_encoded_len(encoded_length, 0x7f);
let total = length_length.checked_add(value.encoded_bytes.len()).ok_or(
HpackStringLiteralBuildError::TotalLengthOverflow {
length_length,
encoded_length: value.encoded_bytes.len(),
},
)?;
Ok((total, encoded_length))
}
fn write_string(destination: &mut [u8], value: HpackLiteralValue<'_>, encoded_length: u64) {
let length_length = canonical_encoded_len(encoded_length, 0x7f);
let (length_destination, payload_destination) = destination.split_at_mut(length_length);
write_integer(
length_destination,
7,
0x7f,
if value.huffman { 0x80 } else { 0 },
encoded_length,
);
payload_destination.copy_from_slice(value.encoded_bytes);
}
fn write_integer(
destination: &mut [u8],
prefix_bits: u8,
prefix_mask: u8,
high_bits: u8,
value: u64,
) {
write_canonical_for_valid_prefix(destination, prefix_bits, prefix_mask, high_bits, value);
}
const fn prefix_mask(prefix_bits: u8) -> u8 {
(1 << prefix_bits) - 1
}