use core::fmt;
use super::prefix::QpackFieldSectionPrefix;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QpackFieldSectionContext {
required_insert_count: u64,
base: u64,
}
impl QpackFieldSectionContext {
pub fn decode(
prefix: QpackFieldSectionPrefix<'_>,
total_insert_count: u64,
maximum_table_capacity: u64,
) -> Result<Self, QpackFieldSectionContextError> {
let required_insert_count = Self::reconstruct_required_insert_count(
prefix.encoded_required_insert_count().value(),
total_insert_count,
maximum_table_capacity,
)?;
let base = Self::resolve_base(
required_insert_count,
prefix.is_negative(),
prefix.delta_base().value(),
)?;
Ok(Self {
required_insert_count,
base,
})
}
pub fn reconstruct_required_insert_count(
encoded_required_insert_count: u64,
total_insert_count: u64,
maximum_table_capacity: u64,
) -> Result<u64, QpackFieldSectionContextError> {
if encoded_required_insert_count == 0 {
return Ok(0);
}
let max_entries = maximum_table_capacity / 32;
if max_entries == 0 {
return Err(QpackFieldSectionContextError::ZeroMaximumEntries {
encoded_required_insert_count,
});
}
let full_range =
max_entries
.checked_mul(2)
.ok_or(QpackFieldSectionContextError::FullRangeOverflow {
maximum_table_capacity,
max_entries,
})?;
if encoded_required_insert_count > full_range {
return Err(
QpackFieldSectionContextError::EncodedRequiredInsertCountOutOfRange {
encoded_required_insert_count,
full_range,
},
);
}
let maximum_value = total_insert_count.checked_add(max_entries).ok_or(
QpackFieldSectionContextError::MaximumValueOverflow {
total_insert_count,
max_entries,
},
)?;
let max_wrapped_quotient = maximum_value / full_range;
let max_wrapped = max_wrapped_quotient.checked_mul(full_range).ok_or(
QpackFieldSectionContextError::RequiredInsertCountOverflow {
max_wrapped: max_wrapped_quotient,
encoded_required_insert_count,
},
)?;
let mut reconstructed = max_wrapped
.checked_add(encoded_required_insert_count - 1)
.ok_or(QpackFieldSectionContextError::RequiredInsertCountOverflow {
max_wrapped,
encoded_required_insert_count,
})?;
if reconstructed > maximum_value {
if reconstructed <= full_range {
return Err(QpackFieldSectionContextError::RequiredInsertCountTooOld {
reconstructed,
maximum_value,
full_range,
});
}
reconstructed = reconstructed.checked_sub(full_range).ok_or(
QpackFieldSectionContextError::RequiredInsertCountTooOld {
reconstructed,
maximum_value,
full_range,
},
)?;
}
if reconstructed == 0 {
return Err(QpackFieldSectionContextError::RequiredInsertCountZero);
}
Ok(reconstructed)
}
pub fn encode_required_insert_count(
required_insert_count: u64,
maximum_table_capacity: u64,
) -> Result<u64, QpackFieldSectionContextError> {
if required_insert_count == 0 {
return Ok(0);
}
let max_entries = maximum_table_capacity / 32;
if max_entries == 0 {
return Err(QpackFieldSectionContextError::ZeroMaximumEntries {
encoded_required_insert_count: required_insert_count,
});
}
let full_range =
max_entries
.checked_mul(2)
.ok_or(QpackFieldSectionContextError::FullRangeOverflow {
maximum_table_capacity,
max_entries,
})?;
required_insert_count
.checked_rem(full_range)
.and_then(|count| count.checked_add(1))
.ok_or(QpackFieldSectionContextError::RequiredInsertCountOverflow {
max_wrapped: required_insert_count,
encoded_required_insert_count: required_insert_count,
})
}
pub fn resolve_base(
required_insert_count: u64,
negative: bool,
delta_base: u64,
) -> Result<u64, QpackFieldSectionContextError> {
if negative {
if delta_base >= required_insert_count {
return Err(QpackFieldSectionContextError::BaseUnderflow {
required_insert_count,
delta_base,
});
}
required_insert_count
.checked_sub(delta_base)
.and_then(|base| base.checked_sub(1))
.ok_or(QpackFieldSectionContextError::BaseUnderflow {
required_insert_count,
delta_base,
})
} else {
required_insert_count.checked_add(delta_base).ok_or(
QpackFieldSectionContextError::BaseOverflow {
required_insert_count,
delta_base,
},
)
}
}
pub const fn required_insert_count(self) -> u64 {
self.required_insert_count
}
pub const fn base(self) -> u64 {
self.base
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QpackFieldSectionContextError {
FullRangeOverflow {
maximum_table_capacity: u64,
max_entries: u64,
},
ZeroMaximumEntries {
encoded_required_insert_count: u64,
},
EncodedRequiredInsertCountOutOfRange {
encoded_required_insert_count: u64,
full_range: u64,
},
MaximumValueOverflow {
total_insert_count: u64,
max_entries: u64,
},
RequiredInsertCountOverflow {
max_wrapped: u64,
encoded_required_insert_count: u64,
},
RequiredInsertCountTooOld {
reconstructed: u64,
maximum_value: u64,
full_range: u64,
},
RequiredInsertCountZero,
BaseOverflow {
required_insert_count: u64,
delta_base: u64,
},
BaseUnderflow {
required_insert_count: u64,
delta_base: u64,
},
}
impl fmt::Display for QpackFieldSectionContextError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FullRangeOverflow {
maximum_table_capacity,
max_entries,
} => write!(
formatter,
"QPACK full range overflows u64 for maximum table capacity {maximum_table_capacity} and maximum entries {max_entries}"
),
Self::ZeroMaximumEntries {
encoded_required_insert_count,
} => write!(
formatter,
"QPACK Required Insert Count {encoded_required_insert_count} is nonzero with zero maximum entries"
),
Self::EncodedRequiredInsertCountOutOfRange {
encoded_required_insert_count,
full_range,
} => write!(
formatter,
"QPACK Encoded Required Insert Count {encoded_required_insert_count} exceeds full range {full_range}"
),
Self::MaximumValueOverflow {
total_insert_count,
max_entries,
} => write!(
formatter,
"QPACK maximum Required Insert Count overflows u64 for insert count {total_insert_count} and maximum entries {max_entries}"
),
Self::RequiredInsertCountOverflow {
max_wrapped,
encoded_required_insert_count,
} => write!(
formatter,
"QPACK Required Insert Count overflows u64 for wrapped value {max_wrapped} and count {encoded_required_insert_count}"
),
Self::RequiredInsertCountTooOld {
reconstructed,
maximum_value,
full_range,
} => write!(
formatter,
"QPACK Required Insert Count {reconstructed} exceeds maximum {maximum_value} for full range {full_range}"
),
Self::RequiredInsertCountZero => formatter
.write_str("QPACK nonzero Encoded Required Insert Count reconstructed to zero"),
Self::BaseOverflow {
required_insert_count,
delta_base,
} => write!(
formatter,
"QPACK Base overflows u64 for Required Insert Count {required_insert_count} and Delta Base {delta_base}"
),
Self::BaseUnderflow {
required_insert_count,
delta_base,
} => write!(
formatter,
"QPACK Base underflows u64 for Required Insert Count {required_insert_count} and Delta Base {delta_base}"
),
}
}
}
impl core::error::Error for QpackFieldSectionContextError {}