use thiserror::Error;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum DecodedPageKind {
Leaf,
Internal,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct DecodedPageShape {
pub(crate) kind: DecodedPageKind,
pub(crate) items: usize,
pub(crate) nested_bytes: u64,
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DecodeLimits {
pub maximum_object_bytes: u64,
pub maximum_name_bytes: u32,
pub maximum_page_items: u32,
pub maximum_page_bytes: u32,
pub maximum_page_height: u16,
pub maximum_visited_pages: u32,
}
impl Default for DecodeLimits {
fn default() -> Self {
Self {
maximum_object_bytes: 64 * 1024 * 1024,
maximum_name_bytes: 255,
maximum_page_items: 1_024,
maximum_page_bytes: 256 * 1024,
maximum_page_height: 64,
maximum_visited_pages: 4_096,
}
}
}
impl DecodeLimits {
pub(crate) fn maximum_page_object_bytes(self) -> u64 {
self.maximum_object_bytes
.min(u64::from(self.maximum_page_bytes))
}
pub(crate) fn page_limits_valid(self, minimum_items: u32) -> bool {
self.maximum_page_items >= minimum_items
&& self.maximum_page_bytes != 0
&& self.maximum_page_height != 0
&& self.maximum_visited_pages != 0
}
}
pub(crate) struct Encoder {
bytes: Vec<u8>,
}
impl Encoder {
pub(crate) fn new(domain: &[u8], version: u16) -> Self {
let mut bytes = Vec::with_capacity(domain.len() + 2);
bytes.extend_from_slice(domain);
bytes.extend_from_slice(&version.to_le_bytes());
Self { bytes }
}
pub(crate) fn with_exact_capacity(
domain: &[u8],
version: u16,
capacity: usize,
) -> Result<Self, CanonicalDecodeError> {
let minimum = domain
.len()
.checked_add(2)
.ok_or(CanonicalDecodeError::LengthOverflow)?;
if capacity < minimum {
return Err(CanonicalDecodeError::LengthOverflow);
}
let mut bytes = Vec::new();
bytes
.try_reserve_exact(capacity)
.map_err(|_| CanonicalDecodeError::AllocationFailed)?;
bytes.extend_from_slice(domain);
bytes.extend_from_slice(&version.to_le_bytes());
Ok(Self { bytes })
}
pub(crate) fn u8(&mut self, value: u8) {
self.bytes.push(value);
}
pub(crate) fn u32(&mut self, value: u32) {
self.bytes.extend_from_slice(&value.to_le_bytes());
}
pub(crate) fn u64(&mut self, value: u64) {
self.bytes.extend_from_slice(&value.to_le_bytes());
}
pub(crate) fn i64(&mut self, value: i64) {
self.bytes.extend_from_slice(&value.to_le_bytes());
}
pub(crate) fn fixed(&mut self, value: &[u8]) {
self.bytes.extend_from_slice(value);
}
pub(crate) fn bounded_bytes(&mut self, value: &[u8]) -> Result<(), CanonicalDecodeError> {
let length =
u32::try_from(value.len()).map_err(|_| CanonicalDecodeError::LengthOverflow)?;
self.u32(length);
self.fixed(value);
Ok(())
}
pub(crate) fn finish(self) -> Vec<u8> {
self.bytes
}
}
pub(crate) struct Decoder<'a> {
bytes: &'a [u8],
cursor: usize,
}
impl<'a> Decoder<'a> {
pub(crate) fn new(
bytes: &'a [u8],
domain: &[u8],
expected_version: u16,
maximum_object_bytes: u64,
) -> Result<Self, CanonicalDecodeError> {
let observed = u64::try_from(bytes.len()).unwrap_or(u64::MAX);
if observed > maximum_object_bytes {
return Err(CanonicalDecodeError::ObjectTooLarge {
observed,
maximum: maximum_object_bytes,
});
}
let header_length = domain
.len()
.checked_add(2)
.ok_or(CanonicalDecodeError::LengthOverflow)?;
let domain_prefix = bytes
.get(..domain.len())
.ok_or(CanonicalDecodeError::WrongDomain)?;
if bytes.len() < header_length || domain_prefix != domain {
return Err(CanonicalDecodeError::WrongDomain);
}
let version_bytes: [u8; 2] = bytes
.get(domain.len()..header_length)
.ok_or(CanonicalDecodeError::Truncated)?
.try_into()
.map_err(|_| CanonicalDecodeError::Truncated)?;
let version = u16::from_le_bytes(version_bytes);
if version != expected_version {
return Err(CanonicalDecodeError::UnsupportedVersion(version));
}
Ok(Self {
bytes,
cursor: header_length,
})
}
pub(crate) fn u8(&mut self) -> Result<u8, CanonicalDecodeError> {
let value = *self
.bytes
.get(self.cursor)
.ok_or(CanonicalDecodeError::Truncated)?;
self.cursor = self
.cursor
.checked_add(1)
.ok_or(CanonicalDecodeError::LengthOverflow)?;
Ok(value)
}
pub(crate) fn u32(&mut self) -> Result<u32, CanonicalDecodeError> {
let bytes: [u8; 4] = self
.take(4)?
.try_into()
.map_err(|_| CanonicalDecodeError::Truncated)?;
Ok(u32::from_le_bytes(bytes))
}
pub(crate) fn u64(&mut self) -> Result<u64, CanonicalDecodeError> {
let bytes: [u8; 8] = self
.take(8)?
.try_into()
.map_err(|_| CanonicalDecodeError::Truncated)?;
Ok(u64::from_le_bytes(bytes))
}
pub(crate) fn i64(&mut self) -> Result<i64, CanonicalDecodeError> {
let bytes: [u8; 8] = self
.take(8)?
.try_into()
.map_err(|_| CanonicalDecodeError::Truncated)?;
Ok(i64::from_le_bytes(bytes))
}
pub(crate) fn fixed<const N: usize>(&mut self) -> Result<[u8; N], CanonicalDecodeError> {
self.take(N)?
.try_into()
.map_err(|_| CanonicalDecodeError::Truncated)
}
pub(crate) fn bounded_bytes(&mut self, maximum: u32) -> Result<Vec<u8>, CanonicalDecodeError> {
let length = self.u32()?;
if length > maximum {
return Err(CanonicalDecodeError::FieldTooLarge {
observed: length,
maximum,
});
}
let length = usize::try_from(length).map_err(|_| CanonicalDecodeError::LengthOverflow)?;
let source = self.take(length)?;
let mut value = Vec::new();
value
.try_reserve_exact(length)
.map_err(|_| CanonicalDecodeError::AllocationFailed)?;
value.extend_from_slice(source);
Ok(value)
}
pub(crate) fn skip_bounded_bytes(
&mut self,
maximum: u32,
) -> Result<usize, CanonicalDecodeError> {
let length = self.u32()?;
if length > maximum {
return Err(CanonicalDecodeError::FieldTooLarge {
observed: length,
maximum,
});
}
let length = usize::try_from(length).map_err(|_| CanonicalDecodeError::LengthOverflow)?;
self.take(length)?;
Ok(length)
}
pub(crate) fn take_exact(&mut self, length: usize) -> Result<&'a [u8], CanonicalDecodeError> {
self.take(length)
}
fn take(&mut self, length: usize) -> Result<&'a [u8], CanonicalDecodeError> {
let end = self
.cursor
.checked_add(length)
.ok_or(CanonicalDecodeError::LengthOverflow)?;
let value = self
.bytes
.get(self.cursor..end)
.ok_or(CanonicalDecodeError::Truncated)?;
self.cursor = end;
Ok(value)
}
pub(crate) fn finish(self) -> Result<(), CanonicalDecodeError> {
if self.cursor == self.bytes.len() {
Ok(())
} else {
Err(CanonicalDecodeError::TrailingBytes)
}
}
}
#[derive(Clone, Debug, Eq, Error, PartialEq)]
pub enum CanonicalDecodeError {
#[error("canonical object domain is invalid")]
WrongDomain,
#[error("unsupported canonical object version {0}")]
UnsupportedVersion(u16),
#[error("canonical object is truncated")]
Truncated,
#[error("canonical object has {observed} bytes; maximum is {maximum}")]
ObjectTooLarge {
observed: u64,
maximum: u64,
},
#[error("canonical field has {observed} bytes; maximum is {maximum}")]
FieldTooLarge {
observed: u32,
maximum: u32,
},
#[error("canonical object length overflowed")]
LengthOverflow,
#[error("canonical codec allocation failed")]
AllocationFailed,
#[error("canonical object has trailing bytes")]
TrailingBytes,
#[error("unknown {field} tag {tag}")]
UnknownTag {
field: &'static str,
tag: u8,
},
#[error("canonical object invariant failed: {0}")]
Invariant(String),
}
#[cfg(test)]
#[path = "tests/codec.rs"]
mod tests;