use crate::memory::{Memory, MemoryError, OwnedVec};
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ErrorCode {
UnexpectedEnd,
MalformedTag,
TagTooLarge,
LengthNotMinimal,
LengthTooLarge,
OffsetTooLarge,
IndefiniteLengthNotAllowed,
UnterminatedContents,
MalformedEndOfContents,
WrongConstruction,
UnexpectedTag,
IntegerNotMinimal,
IntegerEmpty,
MalformedOid,
MalformedBitString,
MalformedBoolean,
MalformedNull,
MalformedReal,
LimitExceeded(Limit),
AllocatorExhausted,
CapacityOverflow,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Limit {
Depth,
Elements,
ElementLength,
Work,
Memory,
}
impl fmt::Display for Limit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Depth => "nesting depth",
Self::Elements => "element count",
Self::ElementLength => "element length",
Self::Work => "work",
Self::Memory => "memory",
})
}
}
impl fmt::Display for ErrorCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnexpectedEnd => {
f.write_str("element runs past the end of the one containing it")
}
Self::MalformedTag => f.write_str("malformed tag number"),
Self::TagTooLarge => f.write_str("tag number too large"),
Self::LengthNotMinimal => f.write_str("length not encoded in the fewest bytes"),
Self::LengthTooLarge => f.write_str("length too large"),
Self::OffsetTooLarge => f.write_str("offset too large for this window"),
Self::IndefiniteLengthNotAllowed => f.write_str("indefinite length not allowed here"),
Self::UnterminatedContents => f.write_str("indefinite length never terminated"),
Self::MalformedEndOfContents => f.write_str("end-of-contents with a length"),
Self::WrongConstruction => {
f.write_str("primitive and constructed are the wrong way round")
}
Self::UnexpectedTag => f.write_str("not the element expected here"),
Self::IntegerNotMinimal => f.write_str("integer has a redundant leading byte"),
Self::IntegerEmpty => f.write_str("integer has no content"),
Self::MalformedOid => f.write_str("malformed object identifier"),
Self::MalformedBitString => f.write_str("malformed bit string"),
Self::MalformedBoolean => f.write_str("malformed boolean"),
Self::MalformedNull => f.write_str("null with contents"),
Self::MalformedReal => f.write_str("malformed real"),
Self::LimitExceeded(limit) => write!(f, "{limit} budget spent"),
Self::AllocatorExhausted => f.write_str("allocator could not supply storage"),
Self::CapacityOverflow => f.write_str("storage capacity overflow"),
}
}
}
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Path {
indices: Option<OwnedVec<u32>>,
unavailable: Option<MemoryError>,
}
impl Path {
#[must_use]
pub fn indices(&self) -> &[u32] {
self.indices.as_deref().unwrap_or(&[])
}
#[must_use]
pub fn is_root(&self) -> bool {
self.unavailable.is_none() && self.indices().is_empty()
}
#[must_use]
pub fn unavailable(&self) -> Option<MemoryError> {
self.unavailable
}
pub(crate) fn missing(reason: MemoryError) -> Self {
Self {
indices: None,
unavailable: Some(reason),
}
}
pub(crate) fn try_copy_in(&self, memory: &Memory) -> core::result::Result<Self, MemoryError> {
Ok(Self {
indices: match &self.indices {
Some(indices) => {
let mut copy = memory.vector();
copy.try_extend_from_slice(indices)?;
Some(copy)
}
None => None,
},
unavailable: self.unavailable,
})
}
pub(crate) fn copy_work(&self, additional: usize) -> core::result::Result<u64, MemoryError> {
let length = self.indices().len() as u64;
let additional = additional as u64;
let target = length
.checked_add(additional)
.ok_or(MemoryError::WorkExceeded)?;
let mut work = length
.checked_mul(8)
.and_then(|work| work.checked_add(additional))
.ok_or(MemoryError::WorkExceeded)?;
let mut capacity = length;
while capacity < target {
let next = capacity
.checked_mul(2)
.ok_or(MemoryError::WorkExceeded)?
.max(4);
work = next
.checked_add(capacity)
.and_then(|bytes| bytes.checked_mul(4))
.and_then(|bytes| work.checked_add(bytes))
.ok_or(MemoryError::WorkExceeded)?;
capacity = next;
}
Ok(work)
}
pub(crate) fn push(
&mut self,
memory: &Memory,
index: u32,
) -> core::result::Result<(), MemoryError> {
self.indices
.get_or_insert_with(|| memory.vector())
.try_push(index)
}
}
impl fmt::Display for Path {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(reason) = self.unavailable {
return write!(f, "path unavailable: {reason}");
}
if self.indices().is_empty() {
return f.write_str("/");
}
for index in self.indices() {
write!(f, "/{index}")?;
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Error {
code: ErrorCode,
offset: u64,
path: Path,
}
impl Error {
pub(crate) fn new(code: ErrorCode, offset: u64, path: Path) -> Self {
Self { code, offset, path }
}
#[must_use]
pub fn resource(reason: MemoryError, offset: u64) -> Self {
Self::new(ErrorCode::from(reason), offset, Path::missing(reason))
}
#[must_use]
pub fn code(&self) -> ErrorCode {
self.code
}
#[must_use]
pub fn offset(&self) -> u64 {
self.offset
}
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
#[must_use]
pub fn is_limit(&self) -> bool {
matches!(
self.code,
ErrorCode::LimitExceeded(_) | ErrorCode::TagTooLarge
)
}
#[must_use]
pub fn is_resource(&self) -> bool {
self.is_limit()
|| matches!(
self.code,
ErrorCode::AllocatorExhausted | ErrorCode::CapacityOverflow
)
}
}
impl From<MemoryError> for ErrorCode {
fn from(reason: MemoryError) -> Self {
match reason {
MemoryError::LimitExceeded => Self::LimitExceeded(Limit::Memory),
MemoryError::WorkExceeded => Self::LimitExceeded(Limit::Work),
MemoryError::OutOfMemory => Self::AllocatorExhausted,
MemoryError::CapacityOverflow => Self::CapacityOverflow,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} at {} ({})", self.code, self.offset, self.path)
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
pub type Result<T> = core::result::Result<T, Error>;
#[cfg(test)]
mod tests;