use core::{
fmt::{
self,
Display,
Formatter,
},
num::NonZeroUsize,
};
use crate::Leb128DecodeErrorKind;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Leb128DecodeError {
kind: Leb128DecodeErrorKind,
start_index: usize,
error_index: usize,
consumed: Option<NonZeroUsize>,
required: Option<NonZeroUsize>,
available: Option<usize>,
}
impl Leb128DecodeError {
pub const fn incomplete(
start_index: usize,
required: NonZeroUsize,
available: usize,
) -> Self {
assert!(
required.get() > available,
"incomplete LEB128 required bytes must exceed available bytes",
);
Self {
kind: Leb128DecodeErrorKind::Incomplete,
start_index,
error_index: add_offset(start_index, available),
consumed: None,
required: Some(required),
available: Some(available),
}
}
pub const fn malformed(
start_index: usize,
error_index: usize,
consumed: NonZeroUsize,
) -> Self {
assert_error_index_in_consumed_span(start_index, error_index, consumed);
Self {
kind: Leb128DecodeErrorKind::Malformed,
start_index,
error_index,
consumed: Some(consumed),
required: None,
available: None,
}
}
pub const fn noncanonical(
start_index: usize,
consumed: NonZeroUsize,
) -> Self {
Self {
kind: Leb128DecodeErrorKind::NonCanonical,
start_index,
error_index: last_consumed_index(start_index, consumed),
consumed: Some(consumed),
required: None,
available: None,
}
}
#[must_use]
pub const fn kind(self) -> Leb128DecodeErrorKind {
self.kind
}
#[must_use]
pub const fn start_index(self) -> usize {
self.start_index
}
#[must_use]
pub const fn error_index(self) -> usize {
self.error_index
}
#[must_use]
pub const fn consumed(self) -> Option<NonZeroUsize> {
self.consumed
}
#[must_use]
pub const fn is_incomplete(self) -> bool {
matches!(self.kind, Leb128DecodeErrorKind::Incomplete)
}
#[must_use]
pub const fn is_malformed(self) -> bool {
matches!(self.kind, Leb128DecodeErrorKind::Malformed)
}
#[must_use]
pub const fn is_noncanonical(self) -> bool {
matches!(self.kind, Leb128DecodeErrorKind::NonCanonical)
}
#[must_use]
pub const fn required(self) -> Option<NonZeroUsize> {
self.required
}
#[must_use]
pub const fn additional(self) -> Option<NonZeroUsize> {
match (self.required, self.available) {
(Some(required), Some(available)) => {
let additional = required.get() - available;
Some(unsafe { NonZeroUsize::new_unchecked(additional) })
}
_ => None,
}
}
#[must_use]
pub const fn available(self) -> Option<usize> {
self.available
}
}
impl Display for Leb128DecodeError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
match self.kind {
Leb128DecodeErrorKind::Incomplete => {
let required = self
.required
.expect("incomplete LEB128 errors always store a required byte bound");
let available = self
.available
.expect("incomplete LEB128 errors always store an available byte count");
write!(
formatter,
"{} at byte {}: need at least {} bytes, only {} available (next byte boundary {})",
self.kind,
self.start_index,
required,
available,
self.error_index,
)
}
Leb128DecodeErrorKind::Malformed
| Leb128DecodeErrorKind::NonCanonical => {
let consumed = self.consumed.expect(
"invalid LEB128 errors always store a consumed byte count",
);
write!(
formatter,
"{} at byte {}: detected at byte {} after consuming {} bytes",
self.kind, self.start_index, self.error_index, consumed,
)
}
}
}
}
impl std::error::Error for Leb128DecodeError {}
const fn add_offset(index: usize, offset: usize) -> usize {
match index.checked_add(offset) {
Some(value) => value,
None => panic!("LEB128 byte index overflow"),
}
}
const fn last_consumed_index(
start_index: usize,
consumed: NonZeroUsize,
) -> usize {
add_offset(start_index, consumed.get() - 1)
}
const fn assert_error_index_in_consumed_span(
start_index: usize,
error_index: usize,
consumed: NonZeroUsize,
) {
let last_index = last_consumed_index(start_index, consumed);
assert!(
error_index >= start_index,
"LEB128 error index must not precede value start",
);
assert!(
error_index <= last_index,
"LEB128 error index must lie inside consumed input",
);
}