Skip to content

Commit

Permalink
Breaking change: split Error and box it, add "descriptive-deserialize…
Browse files Browse the repository at this point in the history
…-errors"

Boxing the error provides an performance uplift of about 10% and provides a
place to store the ScopeDescription list intorduced by the new feature
"descriptive-deserialize-errors". If it is enabled, it collects various
descriptions of internal state changes (including the names of the currently
deserialized types) and thus allows one locate the origin of deserialization
errors.
  • Loading branch information
kellerkindt committed Sep 12, 2022
1 parent 0056a25 commit 7f3e11a
Show file tree
Hide file tree
Showing 7 changed files with 761 additions and 133 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ protobuf = ["asn1rs-model/protobuf"]
macros = ["asn1rs-macros"]
model = ["asn1rs-model"]
debug-proc-macro = ["asn1rs-macros/debug-proc-macro", "asn1rs-model/debug-proc-macro"]

descriptive-deserialize-errors = []

[package.metadata.docs.rs]
all-features = true
150 changes: 102 additions & 48 deletions src/io/per/err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,62 @@ use crate::model::Charset;
use backtrace::Backtrace;
use std::string::FromUtf8Error;

#[derive(Debug)]
pub enum Error {
#[derive(Debug, Clone, PartialEq)]
pub struct Error(pub(crate) Box<Inner>);

impl Error {
#[inline]
pub fn kind(&self) -> &ErrorKind {
&self.0.kind
}

#[cfg(feature = "descriptive-deserialize-errors")]
pub fn scope_description(&self) -> &[crate::prelude::ScopeDescription] {
&self.0.description[..]
}
}

impl From<ErrorKind> for Error {
#[cold]
#[inline(never)]
fn from(kind: ErrorKind) -> Self {
Self(Box::new(Inner {
kind,
#[cfg(feature = "descriptive-deserialize-errors")]
description: Vec::new(),
}))
}
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.kind)?;
#[cfg(feature = "descriptive-deserialize-errors")]
{
writeln!(f)?;
for desc in &self.0.description {
writeln!(f, " - {desc:?}")?;
}
}
Ok(())
}
}

impl std::error::Error for Error {
fn description(&self) -> &str {
"encoding or decoding UPER failed"
}
}

#[derive(Debug, Clone, PartialEq)]
pub(crate) struct Inner {
pub(crate) kind: ErrorKind,
#[cfg(feature = "descriptive-deserialize-errors")]
pub(crate) description: Vec<crate::syn::io::ScopeDescription>,
}

#[derive(Debug, Clone)]
pub enum ErrorKind {
FromUtf8Error(FromUtf8Error),
InvalidString(Charset, char, usize),
UnsupportedOperation(String),
Expand All @@ -20,38 +74,44 @@ pub enum Error {
}

impl Error {
#[cold]
#[inline(never)]
pub fn ensure_string_valid(charset: Charset, str: &str) -> Result<(), Self> {
match charset.find_invalid(str) {
None => Ok(()),
Some((index, char)) => Err(Self::InvalidString(charset, char, index)),
Some((index, char)) => Err(ErrorKind::InvalidString(charset, char, index).into()),
}
}

#[cold]
#[inline(never)]
pub fn insufficient_space_in_destination_buffer() -> Self {
Error::InsufficientSpaceInDestinationBuffer(Backtrace::new_unresolved())
ErrorKind::InsufficientSpaceInDestinationBuffer(Backtrace::new_unresolved()).into()
}

#[cold]
#[inline(never)]
pub fn insufficient_data_in_source_buffer() -> Self {
Error::InsufficientDataInSourceBuffer(Backtrace::new_unresolved())
ErrorKind::InsufficientDataInSourceBuffer(Backtrace::new_unresolved()).into()
}
}

impl std::fmt::Display for Error {
impl std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::FromUtf8Error(err) => {
Self::FromUtf8Error(err) => {
write!(f, "Failed to call String::from_utf8: ")?;
err.fmt(f)
}
Error::InvalidString(charset, char, index) => {
Self::InvalidString(charset, char, index) => {
write!(
f,
"Invalid character for a string with the charset {:?} at index {}: {}",
charset, index, char
)
}
Error::UnsupportedOperation(o) => write!(f, "The operation is not supported: {}", o),
Error::InsufficientSpaceInDestinationBuffer(backtrace) => write!(
Self::UnsupportedOperation(o) => write!(f, "The operation is not supported: {}", o),
Self::InsufficientSpaceInDestinationBuffer(backtrace) => write!(
f,
"There is insufficient space in the destination buffer for this operation:\n{:?}",
{
Expand All @@ -60,7 +120,7 @@ impl std::fmt::Display for Error {
b
}
),
Error::InsufficientDataInSourceBuffer(backtrace) => write!(
Self::InsufficientDataInSourceBuffer(backtrace) => write!(
f,
"There is insufficient data in the source buffer for this operation:\n{:?}",
{
Expand All @@ -69,85 +129,79 @@ impl std::fmt::Display for Error {
b
}
),
Error::InvalidChoiceIndex(index, variant_count) => write!(
Self::InvalidChoiceIndex(index, variant_count) => write!(
f,
"Unexpected choice-index {} with variant count {}",
index, variant_count
),
Error::ExtensionFieldsInconsistent(name) => {
Self::ExtensionFieldsInconsistent(name) => {
write!(
f,
"The extension fields of {} are inconsistent, either all or none must be present",
name
)
}
Error::ValueNotInRange(value, min, max) => write!(
Self::ValueNotInRange(value, min, max) => write!(
f,
"The value {} is not within the inclusive range of {} and {}",
value, min, max
),
Error::ValueExceedsMaxInt => {
Self::ValueExceedsMaxInt => {
write!(f, "The value exceeds the maximum supported integer size",)
}
Error::ValueIsNegativeButExpectedUnsigned(value) => write!(
Self::ValueIsNegativeButExpectedUnsigned(value) => write!(
f,
"The value {} is negative, but expected an unsigned/positive value",
value
),
Error::SizeNotInRange(size, min, max) => write!(
Self::SizeNotInRange(size, min, max) => write!(
f,
"The size {} is not within the inclusive range of {} and {}",
size, min, max
),
Error::OptFlagsExhausted => write!(f, "All optional flags have already been exhausted"),
Error::EndOfStream => write!(
Self::OptFlagsExhausted => write!(f, "All optional flags have already been exhausted"),
Self::EndOfStream => write!(
f,
"Can no longer read or write any bytes from the underlying dataset"
),
}
}
}

impl std::error::Error for Error {
fn description(&self) -> &str {
"encoding or decoding UPER failed"
}
}

impl PartialEq for Error {
impl PartialEq for ErrorKind {
fn eq(&self, other: &Self) -> bool {
match self {
Error::FromUtf8Error(a) => matches!(other, Error::FromUtf8Error(oa) if a == oa),
Error::InvalidString(a, b, c) => {
matches!(other, Error::InvalidString(oa, ob, oc) if (a, b, c) == (oa, ob, oc))
Self::FromUtf8Error(a) => matches!(other, Self::FromUtf8Error(oa) if a == oa),
Self::InvalidString(a, b, c) => {
matches!(other, Self::InvalidString(oa, ob, oc) if (a, b, c) == (oa, ob, oc))
}
Error::UnsupportedOperation(a) => {
matches!(other, Error::UnsupportedOperation(oa) if a == oa)
Self::UnsupportedOperation(a) => {
matches!(other, Self::UnsupportedOperation(oa) if a == oa)
}
Error::InsufficientSpaceInDestinationBuffer(_) => {
matches!(other, Error::InsufficientSpaceInDestinationBuffer(_))
Self::InsufficientSpaceInDestinationBuffer(_) => {
matches!(other, Self::InsufficientSpaceInDestinationBuffer(_))
}
Error::InsufficientDataInSourceBuffer(_) => {
matches!(other, Error::InsufficientDataInSourceBuffer(_))
Self::InsufficientDataInSourceBuffer(_) => {
matches!(other, Self::InsufficientDataInSourceBuffer(_))
}
Error::InvalidChoiceIndex(a, b) => {
matches!(other, Error::InvalidChoiceIndex(oa, ob) if (a, b) == (oa, ob))
Self::InvalidChoiceIndex(a, b) => {
matches!(other, Self::InvalidChoiceIndex(oa, ob) if (a, b) == (oa, ob))
}
Error::ExtensionFieldsInconsistent(a) => {
matches!(other, Error::ExtensionFieldsInconsistent(oa) if a == oa)
Self::ExtensionFieldsInconsistent(a) => {
matches!(other, Self::ExtensionFieldsInconsistent(oa) if a == oa)
}
Error::ValueNotInRange(a, b, c) => {
matches!(other, Error::ValueNotInRange(oa, ob, oc) if (a, b, c) == (oa, ob, oc))
Self::ValueNotInRange(a, b, c) => {
matches!(other, Self::ValueNotInRange(oa, ob, oc) if (a, b, c) == (oa, ob, oc))
}
Error::ValueExceedsMaxInt => matches!(other, Error::ValueExceedsMaxInt),
Error::ValueIsNegativeButExpectedUnsigned(a) => {
matches!(other, Error::ValueIsNegativeButExpectedUnsigned(oa) if a == oa)
Self::ValueExceedsMaxInt => matches!(other, Self::ValueExceedsMaxInt),
Self::ValueIsNegativeButExpectedUnsigned(a) => {
matches!(other, Self::ValueIsNegativeButExpectedUnsigned(oa) if a == oa)
}
Error::SizeNotInRange(a, b, c) => {
matches!(other, Error::SizeNotInRange(oa, ob, oc) if (a,b ,c) == (oa, ob,oc))
Self::SizeNotInRange(a, b, c) => {
matches!(other, Self::SizeNotInRange(oa, ob, oc) if (a,b ,c) == (oa, ob,oc))
}
Error::OptFlagsExhausted => matches!(other, Error::OptFlagsExhausted),
Error::EndOfStream => matches!(other, Error::EndOfStream),
Self::OptFlagsExhausted => matches!(other, Self::OptFlagsExhausted),
Self::EndOfStream => matches!(other, Self::EndOfStream),
}
}
}
1 change: 1 addition & 0 deletions src/io/per/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod err;
pub mod unaligned;

pub use err::Error;
pub use err::ErrorKind;

/// According to ITU-T X.691 | ISO/IEC 8825-2:2015
pub trait PackedRead {
Expand Down
5 changes: 3 additions & 2 deletions src/io/per/unaligned/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::*;
use crate::io::per::Error;
use crate::io::per::ErrorKind;

#[derive(Debug, Default)]
pub struct BitBuffer {
Expand Down Expand Up @@ -132,7 +133,7 @@ impl BitRead for BitBuffer {
if self.read_position < self.write_position {
BitRead::read_bit(&mut (&self.buffer[..], &mut self.read_position))
} else {
Err(Error::EndOfStream)
Err(ErrorKind::EndOfStream.into())
}
}

Expand Down Expand Up @@ -268,7 +269,7 @@ impl BitRead for Bits<'_> {
if self.pos < self.len {
BitRead::read_bit(&mut (self.slice, &mut self.pos))
} else {
Err(Error::EndOfStream)
Err(ErrorKind::EndOfStream.into())
}
}

Expand Down
17 changes: 9 additions & 8 deletions src/io/per/unaligned/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::io::per::Error;
use crate::io::per::{Error, ErrorKind};
use crate::io::per::{PackedRead, PackedWrite};

pub mod buffer;
Expand Down Expand Up @@ -449,7 +449,7 @@ impl<T: BitWrite> PackedWrite for T {
let range = upper_bound - lower_bound;
if range > 0 {
if value < lower_bound || value > upper_bound {
Err(Error::ValueNotInRange(value, lower_bound, upper_bound))
Err(ErrorKind::ValueNotInRange(value, lower_bound, upper_bound).into())
} else {
self.write_non_negative_binary_integer(
None,
Expand Down Expand Up @@ -485,7 +485,7 @@ impl<T: BitWrite> PackedWrite for T {
value: i64,
) -> Result<(), Error> {
if value < lower_bound {
Err(Error::ValueNotInRange(value, lower_bound, i64::MAX))
Err(ErrorKind::ValueNotInRange(value, lower_bound, i64::MAX).into())
} else {
self.write_non_negative_binary_integer(None, None, (value - lower_bound) as u64)
}
Expand Down Expand Up @@ -530,11 +530,12 @@ impl<T: BitWrite> PackedWrite for T {
if lower_bound == upper_bound {
Ok(None)
} else if value < lower_bound_unwrapped {
Err(Error::ValueNotInRange(
Err(ErrorKind::ValueNotInRange(
value as i64,
lower_bound_unwrapped as i64,
upper_bound_unwrapped as i64,
))
)
.into())
} else {
self.write_non_negative_binary_integer(
lower_bound,
Expand Down Expand Up @@ -601,7 +602,7 @@ impl<T: BitWrite> PackedWrite for T {
// self.read_non_negative_binary_integer(0, MAX) + lb | lb=0=>MIN for unsigned
self.write_length_determinant(None, None, length)?;
} else {
return Err(Error::SizeNotInRange(length, lower_bound, upper_bound));
return Err(ErrorKind::SizeNotInRange(length, lower_bound, upper_bound).into());
}
}
/*else if const_is_some!(lower_bound_size)
Expand Down Expand Up @@ -675,7 +676,7 @@ impl<T: BitWrite> PackedWrite for T {
// self.read_non_negative_binary_integer(0, MAX) + lb | lb=0=>MIN for unsigned
self.write_length_determinant(None, None, length)?
} else {
return Err(Error::SizeNotInRange(length, lower_bound, upper_bound));
return Err(ErrorKind::SizeNotInRange(length, lower_bound, upper_bound).into());
}
} else if upper_bound == 0 {
// 17.5
Expand Down Expand Up @@ -748,7 +749,7 @@ impl<T: BitWrite> PackedWrite for T {
if extensible {
self.write_normally_small_length(index - std_variants)
} else {
Err(Error::InvalidChoiceIndex(index, std_variants))
Err(ErrorKind::InvalidChoiceIndex(index, std_variants).into())
}
} else {
self.write_non_negative_binary_integer(None, Some(std_variants - 1), index)
Expand Down
6 changes: 3 additions & 3 deletions src/io/per/unaligned/slice.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use super::BitRead;
use crate::io::per::unaligned::BitWrite;
use crate::io::per::unaligned::BYTE_LEN;
use crate::io::per::Error;
use crate::io::per::{Error, ErrorKind};

impl BitRead for (&[u8], &mut usize) {
#[inline]
fn read_bit(&mut self) -> Result<bool, Error> {
if *self.1 > self.0.len() * BYTE_LEN {
return Err(Error::EndOfStream);
return Err(ErrorKind::EndOfStream.into());
}
let bit = self.0[*self.1 / BYTE_LEN] & (0x80 >> (*self.1 % BYTE_LEN)) != 0;
*self.1 += 1;
Expand Down Expand Up @@ -50,7 +50,7 @@ impl<'a> BitWrite for (&'a mut [u8], &mut usize) {
#[inline]
fn write_bit(&mut self, bit: bool) -> Result<(), Error> {
if *self.1 + 1 > self.0.len() * BYTE_LEN {
return Err(Error::EndOfStream);
return Err(ErrorKind::EndOfStream.into());
}
if bit {
self.0[*self.1 / BYTE_LEN] |= 0x80 >> (*self.1 % BYTE_LEN);
Expand Down
Loading

0 comments on commit 7f3e11a

Please sign in to comment.