1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 07:26:04 +00:00

Optimize errors by using slimmer error types

This commit is contained in:
mat 2024-07-15 07:39:12 +00:00
parent 4fa1f11f76
commit b71d67e5b1
8 changed files with 134 additions and 62 deletions

View file

@ -7,8 +7,9 @@ use crate::{
DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID,
MAX_DEPTH, SHORT_ID, STRING_ID,
},
error::NonRootError,
reader::Reader,
Error, Mutf8Str,
Mutf8Str,
};
use super::{
@ -30,7 +31,7 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
_data: &mut Reader<'a>,
tapes: &'tape mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
) -> Result<(), NonRootError> {
let index_of_compound_element = tapes.main.len();
stack.push(ParsingStackElement::Compound {
@ -255,12 +256,12 @@ impl ParsingStack {
}
#[inline]
pub fn push(&mut self, state: ParsingStackElement) -> Result<(), Error> {
pub fn push(&mut self, state: ParsingStackElement) -> Result<(), NonRootError> {
unsafe { self.stack.get_unchecked_mut(self.depth).write(state) };
self.depth += 1;
if self.depth >= MAX_DEPTH {
Err(Error::MaxDepthExceeded)
Err(NonRootError::max_depth_exceeded())
} else {
Ok(())
}
@ -327,7 +328,7 @@ pub(crate) fn read_tag<'a>(
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
tag_type: u8,
) -> Result<(), Error> {
) -> Result<(), NonRootError> {
match tag_type {
COMPOUND_ID => return NbtCompound::read(data, tapes, stack),
LIST_ID => return NbtList::read(data, tapes, stack),
@ -426,7 +427,7 @@ pub(crate) fn read_tag<'a>(
),
});
}
_ => return Err(Error::UnknownTagId(tag_type)),
_ => return Err(NonRootError::unknown_tag_id(tag_type)),
};
Ok(())
}
@ -436,7 +437,7 @@ pub(crate) fn read_tag_in_compound<'a>(
data: &mut Reader<'a>,
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
) -> Result<(), NonRootError> {
let tag_type = data.read_u8()?;
if tag_type == END_ID {
handle_compound_end(tapes, stack);

View file

@ -7,10 +7,11 @@ use crate::{
BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID,
LIST_ID, LONG_ARRAY_ID, LONG_ID, SHORT_ID, STRING_ID,
},
error::NonRootError,
raw_list::RawList,
reader::Reader,
swap_endianness::SwappableNumber,
Error, Mutf8Str,
Mutf8Str,
};
use super::{
@ -31,7 +32,7 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
data: &mut Reader<'a>,
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
) -> Result<(), NonRootError> {
let tag_type = data.read_u8()?;
match tag_type {
END_ID => {
@ -221,7 +222,7 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
tapes.extra.elements.push(ExtraTapeElement { long_array });
}
}
_ => return Err(Error::UnknownTagId(tag_type)),
_ => return Err(NonRootError::unknown_tag_id(tag_type)),
};
Ok(())
}
@ -867,7 +868,7 @@ pub fn read_list_in_list<'a>(
data: &mut Reader<'a>,
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
) -> Result<(), NonRootError> {
let ParsingStackElement::ListOfLists {
index_of_list_element,
} = stack.peek()
@ -903,7 +904,7 @@ pub(crate) fn read_compound_in_list<'a>(
data: &mut Reader<'a>,
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
) -> Result<(), NonRootError> {
let ParsingStackElement::ListOfCompounds {
index_of_list_element,
} = stack.peek()

View file

@ -1,10 +1,11 @@
use std::{mem, slice};
use crate::{
error::UnexpectedEofError,
raw_list::RawList,
reader::Reader,
swap_endianness::{swap_endianness_as_u8, SwappableNumber},
Error, Mutf8Str,
Mutf8Str,
};
pub const END_ID: u8 = 0;
@ -24,37 +25,43 @@ pub const LONG_ARRAY_ID: u8 = 12;
pub const MAX_DEPTH: usize = 512;
#[inline(always)]
pub fn read_with_u16_length<'a>(data: &mut Reader<'a>, width: usize) -> Result<&'a [u8], Error> {
pub fn read_with_u16_length<'a>(
data: &mut Reader<'a>,
width: usize,
) -> Result<&'a [u8], UnexpectedEofError> {
let length = data.read_u16()?;
let length_in_bytes = length as usize * width;
data.read_slice(length_in_bytes)
}
#[inline(never)]
pub fn read_with_u32_length<'a>(data: &mut Reader<'a>, width: usize) -> Result<&'a [u8], Error> {
pub fn read_with_u32_length<'a>(
data: &mut Reader<'a>,
width: usize,
) -> Result<&'a [u8], UnexpectedEofError> {
let length = data.read_u32()?;
let length_in_bytes = length as usize * width;
data.read_slice(length_in_bytes)
}
pub fn read_string<'a>(data: &mut Reader<'a>) -> Result<&'a Mutf8Str, Error> {
pub fn read_string<'a>(data: &mut Reader<'a>) -> Result<&'a Mutf8Str, UnexpectedEofError> {
let data = read_with_u16_length(data, 1)?;
Ok(Mutf8Str::from_slice(data))
}
pub fn read_u8_array<'a>(data: &mut Reader<'a>) -> Result<&'a [u8], Error> {
pub fn read_u8_array<'a>(data: &mut Reader<'a>) -> Result<&'a [u8], UnexpectedEofError> {
read_with_u32_length(data, 1)
}
pub fn read_i8_array<'a>(data: &mut Reader<'a>) -> Result<&'a [i8], Error> {
pub fn read_i8_array<'a>(data: &mut Reader<'a>) -> Result<&'a [i8], UnexpectedEofError> {
Ok(slice_u8_into_i8(read_u8_array(data)?))
}
pub fn read_int_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i32>, Error> {
pub fn read_int_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i32>, UnexpectedEofError> {
let array_bytes = read_with_u32_length(data, 4)?;
Ok(RawList::new(array_bytes))
}
pub fn read_long_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i64>, Error> {
pub fn read_long_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i64>, UnexpectedEofError> {
let array_bytes = read_with_u32_length(data, 8)?;
Ok(RawList::new(array_bytes))
}

View file

@ -14,6 +14,55 @@ pub enum Error {
MaxDepthExceeded,
}
// these two structs exist to optimize errors, since Error is an entire 2 bytes
// which are often unnecessary
pub(crate) struct UnexpectedEofError;
impl From<UnexpectedEofError> for Error {
fn from(_: UnexpectedEofError) -> Self {
Error::UnexpectedEof
}
}
pub struct NonRootError {
// 0 = unexpected eof
// 1 = max depth exceeded
// anything else = unknown tag id, the id is value-1
value: u8,
}
impl From<NonRootError> for Error {
#[inline]
fn from(e: NonRootError) -> Self {
match e.value {
0 => Error::UnexpectedEof,
1 => Error::MaxDepthExceeded,
_ => Error::UnknownTagId(e.value.wrapping_add(1)),
}
}
}
impl NonRootError {
#[inline]
pub fn unexpected_eof() -> Self {
NonRootError { value: 0 }
}
#[inline]
pub fn max_depth_exceeded() -> Self {
NonRootError { value: 1 }
}
#[inline]
pub fn unknown_tag_id(id: u8) -> Self {
// the value can't be 1 or 2 (because those are always valid tag ids),
// so we take advantage of that in our encoding
NonRootError {
value: id.wrapping_sub(1),
}
}
}
impl From<UnexpectedEofError> for NonRootError {
#[inline]
fn from(_: UnexpectedEofError) -> Self {
NonRootError::unexpected_eof()
}
}
#[derive(Error, Debug)]
pub enum DeserializeError {
#[error("Missing field")]

View file

@ -2,9 +2,10 @@ use std::mem::{self, MaybeUninit};
use crate::{
common::{read_string, unchecked_push, unchecked_write_string, END_ID, MAX_DEPTH},
error::NonRootError,
mutf8::Mutf8String,
reader::Reader,
Error, Mutf8Str, ToNbtTag,
Mutf8Str, ToNbtTag,
};
use super::{list::NbtList, NbtTag};
@ -24,7 +25,7 @@ impl NbtCompound {
Self { values }
}
pub(crate) fn read(data: &mut Reader<'_>) -> Result<Self, Error> {
pub(crate) fn read(data: &mut Reader<'_>) -> Result<Self, NonRootError> {
Self::read_with_depth(data, 0)
}
@ -32,9 +33,9 @@ impl NbtCompound {
data: &mut Reader<'_>,
depth: usize,
capacity: usize,
) -> Result<Self, Error> {
) -> Result<Self, NonRootError> {
if depth > MAX_DEPTH {
return Err(Error::MaxDepthExceeded);
return Err(NonRootError::max_depth_exceeded());
}
let mut tags_buffer = unsafe {
@ -44,7 +45,7 @@ impl NbtCompound {
let mut values = Vec::with_capacity(capacity);
loop {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
let tag_type = data.read_u8().map_err(|_| NonRootError::unexpected_eof())?;
if tag_type == END_ID {
break;
}
@ -69,7 +70,10 @@ impl NbtCompound {
Ok(Self { values })
}
pub(crate) fn read_with_depth(data: &mut Reader<'_>, depth: usize) -> Result<Self, Error> {
pub(crate) fn read_with_depth(
data: &mut Reader<'_>,
depth: usize,
) -> Result<Self, NonRootError> {
Self::read_with_depth_and_capacity(data, depth, 8)
}

View file

@ -6,10 +6,10 @@ use crate::{
COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID,
LONG_ID, SHORT_ID, STRING_ID,
},
error::NonRootError,
mutf8::Mutf8String,
reader::Reader,
swap_endianness::swap_endianness,
Error,
};
use super::{compound::NbtCompound, MAX_DEPTH};
@ -34,11 +34,11 @@ pub enum NbtList {
LongArray(Vec<Vec<i64>>) = LONG_ARRAY_ID,
}
impl NbtList {
pub(crate) fn read(data: &mut Reader<'_>, depth: usize) -> Result<Self, Error> {
pub(crate) fn read(data: &mut Reader<'_>, depth: usize) -> Result<Self, NonRootError> {
if depth > MAX_DEPTH {
return Err(Error::MaxDepthExceeded);
return Err(NonRootError::max_depth_exceeded());
}
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
let tag_type = data.read_u8().map_err(|_| NonRootError::unexpected_eof())?;
Ok(match tag_type {
END_ID => {
data.skip(4)?;
@ -107,7 +107,7 @@ impl NbtList {
}
arrays
}),
_ => return Err(Error::UnknownTagId(tag_type)),
_ => return Err(NonRootError::unknown_tag_id(tag_type)),
})
}

View file

@ -13,6 +13,7 @@ use crate::{
BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID,
LONG_ARRAY_ID, LONG_ID, MAX_DEPTH, SHORT_ID, STRING_ID,
},
error::NonRootError,
mutf8::Mutf8String,
reader::{Reader, ReaderFromCursor},
Error, Mutf8Str,
@ -37,20 +38,20 @@ pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
Nbt::read_unnamed(&mut reader)
}
/// Read a compound tag. This may have any number of items.
pub fn read_compound(data: &mut Cursor<&[u8]>) -> Result<NbtCompound, Error> {
pub fn read_compound(data: &mut Cursor<&[u8]>) -> Result<NbtCompound, NonRootError> {
let mut reader = ReaderFromCursor::new(data);
NbtCompound::read(&mut reader)
}
/// Read an NBT tag, without reading its name. This may be any type of tag except for an end tag. If you need to be able to
/// handle end tags, use [`read_optional_tag`].
pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<NbtTag, Error> {
pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<NbtTag, NonRootError> {
let mut reader = ReaderFromCursor::new(data);
NbtTag::read(&mut reader)
}
/// Read any NBT tag, without reading its name. This may be any type of tag, including an end tag.
///
/// Returns `Ok(None)` if there is no data.
pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<Option<NbtTag>, Error> {
pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<Option<NbtTag>, NonRootError> {
let mut reader = ReaderFromCursor::new(data);
NbtTag::read_optional(&mut reader)
}
@ -262,25 +263,34 @@ impl NbtTag {
}
#[inline(always)]
fn read_with_type(data: &mut Reader<'_>, tag_type: u8, depth: usize) -> Result<Self, Error> {
fn read_with_type(
data: &mut Reader<'_>,
tag_type: u8,
depth: usize,
) -> Result<Self, NonRootError> {
match tag_type {
BYTE_ID => Ok(NbtTag::Byte(
data.read_i8().map_err(|_| Error::UnexpectedEof)?,
data.read_i8().map_err(|_| NonRootError::unexpected_eof())?,
)),
SHORT_ID => Ok(NbtTag::Short(
data.read_i16().map_err(|_| Error::UnexpectedEof)?,
data.read_i16()
.map_err(|_| NonRootError::unexpected_eof())?,
)),
INT_ID => Ok(NbtTag::Int(
data.read_i32().map_err(|_| Error::UnexpectedEof)?,
data.read_i32()
.map_err(|_| NonRootError::unexpected_eof())?,
)),
LONG_ID => Ok(NbtTag::Long(
data.read_i64().map_err(|_| Error::UnexpectedEof)?,
data.read_i64()
.map_err(|_| NonRootError::unexpected_eof())?,
)),
FLOAT_ID => Ok(NbtTag::Float(
data.read_f32().map_err(|_| Error::UnexpectedEof)?,
data.read_f32()
.map_err(|_| NonRootError::unexpected_eof())?,
)),
DOUBLE_ID => Ok(NbtTag::Double(
data.read_f64().map_err(|_| Error::UnexpectedEof)?,
data.read_f64()
.map_err(|_| NonRootError::unexpected_eof())?,
)),
BYTE_ARRAY_ID => Ok(NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned())),
STRING_ID => Ok(NbtTag::String(read_string(data)?.to_owned())),
@ -291,17 +301,17 @@ impl NbtTag {
)?)),
INT_ARRAY_ID => Ok(NbtTag::IntArray(read_int_array(data)?.to_vec())),
LONG_ARRAY_ID => Ok(NbtTag::LongArray(read_long_array(data)?.to_vec())),
_ => Err(Error::UnknownTagId(tag_type)),
_ => Err(NonRootError::unknown_tag_id(tag_type)),
}
}
fn read(data: &mut Reader<'_>) -> Result<Self, Error> {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
fn read(data: &mut Reader<'_>) -> Result<Self, NonRootError> {
let tag_type = data.read_u8().map_err(|_| NonRootError::unexpected_eof())?;
Self::read_with_type(data, tag_type, 0)
}
fn read_optional(data: &mut Reader<'_>) -> Result<Option<Self>, Error> {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
fn read_optional(data: &mut Reader<'_>) -> Result<Option<Self>, NonRootError> {
let tag_type = data.read_u8().map_err(|_| NonRootError::unexpected_eof())?;
if tag_type == END_ID {
return Ok(None);
}

View file

@ -4,7 +4,7 @@ use std::{
ops::{Deref, DerefMut},
};
use crate::Error;
use crate::error::UnexpectedEofError;
pub struct Reader<'a> {
pub cur: *const u8,
@ -23,11 +23,11 @@ impl<'a> Reader<'a> {
}
}
pub fn ensure_can_read(&self, size: usize) -> Result<(), Error> {
pub fn ensure_can_read(&self, size: usize) -> Result<(), UnexpectedEofError> {
let data_addr = self.cur as usize;
let end_addr = self.end as usize;
if data_addr + size > end_addr {
Err(Error::UnexpectedEof)
Err(UnexpectedEofError)
} else {
Ok(())
}
@ -39,75 +39,75 @@ impl<'a> Reader<'a> {
value
}
pub fn read_type<T: Copy>(&mut self) -> Result<T, Error> {
pub fn read_type<T: Copy>(&mut self) -> Result<T, UnexpectedEofError> {
self.ensure_can_read(std::mem::size_of::<T>())?;
Ok(unsafe { self.unchecked_read_type() })
}
#[inline]
pub fn read_u8(&mut self) -> Result<u8, Error> {
pub fn read_u8(&mut self) -> Result<u8, UnexpectedEofError> {
self.read_type()
}
#[inline]
pub fn read_i8(&mut self) -> Result<i8, Error> {
pub fn read_i8(&mut self) -> Result<i8, UnexpectedEofError> {
self.read_u8().map(|x| x as i8)
}
#[inline]
pub fn read_u16(&mut self) -> Result<u16, Error> {
pub fn read_u16(&mut self) -> Result<u16, UnexpectedEofError> {
let value = self.read_type::<u16>();
#[cfg(target_endian = "little")]
let value = value.map(u16::swap_bytes);
value
}
#[inline]
pub fn read_i16(&mut self) -> Result<i16, Error> {
pub fn read_i16(&mut self) -> Result<i16, UnexpectedEofError> {
self.read_u16().map(|x| x as i16)
}
#[inline]
pub fn read_u32(&mut self) -> Result<u32, Error> {
pub fn read_u32(&mut self) -> Result<u32, UnexpectedEofError> {
let value = self.read_type::<u32>();
#[cfg(target_endian = "little")]
let value = value.map(u32::swap_bytes);
value
}
#[inline]
pub fn read_i32(&mut self) -> Result<i32, Error> {
pub fn read_i32(&mut self) -> Result<i32, UnexpectedEofError> {
self.read_u32().map(|x| x as i32)
}
#[inline]
pub fn read_u64(&mut self) -> Result<u64, Error> {
pub fn read_u64(&mut self) -> Result<u64, UnexpectedEofError> {
let value = self.read_type::<u64>();
#[cfg(target_endian = "little")]
let value = value.map(u64::swap_bytes);
value
}
#[inline]
pub fn read_i64(&mut self) -> Result<i64, Error> {
pub fn read_i64(&mut self) -> Result<i64, UnexpectedEofError> {
self.read_u64().map(|x| x as i64)
}
#[inline]
pub fn read_f32(&mut self) -> Result<f32, Error> {
pub fn read_f32(&mut self) -> Result<f32, UnexpectedEofError> {
self.read_u32().map(f32::from_bits)
}
#[inline]
pub fn read_f64(&mut self) -> Result<f64, Error> {
pub fn read_f64(&mut self) -> Result<f64, UnexpectedEofError> {
self.read_u64().map(f64::from_bits)
}
#[inline]
pub fn skip(&mut self, size: usize) -> Result<(), Error> {
pub fn skip(&mut self, size: usize) -> Result<(), UnexpectedEofError> {
self.ensure_can_read(size)?;
self.cur = unsafe { self.cur.add(size) };
Ok(())
}
#[inline]
pub fn read_slice(&mut self, size: usize) -> Result<&'a [u8], Error> {
pub fn read_slice(&mut self, size: usize) -> Result<&'a [u8], UnexpectedEofError> {
self.ensure_can_read(size)?;
let slice = unsafe { std::slice::from_raw_parts(self.cur, size) };
self.cur = unsafe { self.cur.add(size) };