mirror of
https://github.com/azalea-rs/simdnbt.git
synced 2025-08-02 23:44:40 +00:00
additional optimizations
This commit is contained in:
parent
e3bf977ba4
commit
27a1b63ace
6 changed files with 361 additions and 173 deletions
|
@ -70,7 +70,6 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
let where_clause = &generics.where_clause;
|
||||
|
||||
let struct_attrs = attrs::parse_struct_attrs(&input.attrs);
|
||||
let deny_unknown_fields = struct_attrs.deny_unknown_fields;
|
||||
|
||||
// let extra_checks = if struct_attrs.deny_unknown_fields {
|
||||
// quote! {
|
||||
|
@ -162,25 +161,39 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
let nbt_name = &field_data.nbt_name;
|
||||
update_partial_inner.extend(if field_data.is_flatten {
|
||||
quote! {
|
||||
if <#field_type>::update_partial(&mut partial.#index, tag_name, tag_type, data)? {
|
||||
if <#field_type>::update_partial(&mut partial.#index, validator, tag_name, tag_type, data)? {
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
if <#field_type>::type_matches(tag_type) && tag_name == #nbt_name {
|
||||
partial.#index = Some(simdnbt::Deserialize::read_value_direct_with_explicit_type(data, tag_type)?);
|
||||
partial.#index = Some(simdnbt::Deserialize::read_value_direct_with_explicit_type(data, validator, tag_type)?);
|
||||
} else
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let on_unknown_field = if struct_attrs.deny_unknown_fields {
|
||||
quote! {
|
||||
return Err(simdnbt::DeserializeError::UnknownField(
|
||||
tag_name.to_str().into(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
// skip the field
|
||||
validator.internal_read_tag(data, tag_type)?;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let output = quote! {
|
||||
impl #generics simdnbt::Deserialize<#data_lifetime> for #ident #generics #where_clause {
|
||||
const NBT_TYPE_ID: u8 = simdnbt::common::COMPOUND_ID;
|
||||
type Partial<'PARTIAL> = (#partial_type_inner);
|
||||
|
||||
#[inline]
|
||||
fn read_value_direct(data: &mut simdnbt::reader::Reader<#data_lifetime>) -> Result<Self, simdnbt::DeserializeError> {
|
||||
fn read_value_direct(data: &mut simdnbt::reader::Reader<#data_lifetime>, validator: &mut simdnbt::validate::NbtValidator) -> Result<Self, simdnbt::DeserializeError> {
|
||||
let mut partial = Self::Partial::default();
|
||||
|
||||
loop {
|
||||
|
@ -190,20 +203,11 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
}
|
||||
let tag_name = simdnbt::common::read_string(data)?;
|
||||
|
||||
let matched = Self::update_partial(&mut partial, tag_name, tag_type, data)?;
|
||||
let matched = Self::update_partial(&mut partial, validator, tag_name, tag_type, data)?;
|
||||
|
||||
if !matched {
|
||||
let field_name = #name_to_field_if_statements {
|
||||
if #deny_unknown_fields {
|
||||
return Err(simdnbt::DeserializeError::UnknownField(
|
||||
tag_name.to_str().into(),
|
||||
));
|
||||
}
|
||||
|
||||
// skip the field
|
||||
simdnbt::validate::internal_read_tag(data, tag_type)?;
|
||||
|
||||
continue;
|
||||
#on_unknown_field
|
||||
};
|
||||
|
||||
return Err(simdnbt::DeserializeError::MismatchedFieldType(field_name));
|
||||
|
@ -216,6 +220,7 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
#[inline]
|
||||
fn update_partial(
|
||||
partial: &mut Self::Partial<'_>,
|
||||
validator: &mut simdnbt::validate::NbtValidator,
|
||||
tag_name: &simdnbt::Mutf8Str,
|
||||
tag_type: u8,
|
||||
data: &mut simdnbt::reader::Reader<#data_lifetime>,
|
||||
|
|
|
@ -40,7 +40,9 @@ fn bench_read_file(filename: &str, c: &mut Criterion) {
|
|||
});
|
||||
group.bench_function("simdnbt_validate_parse", |b| {
|
||||
b.iter(|| {
|
||||
simdnbt::validate::read(&mut input_stream).unwrap();
|
||||
simdnbt::validate::NbtValidator::new()
|
||||
.read(&mut input_stream)
|
||||
.unwrap();
|
||||
input_stream.set_position(0);
|
||||
})
|
||||
});
|
||||
|
@ -76,10 +78,10 @@ pub struct Item<'a> {
|
|||
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
||||
pub struct ItemTag<'a> {
|
||||
#[simdnbt(rename = "SkullOwner")]
|
||||
pub skull_owner: Option<SkullOwner<'a>>,
|
||||
pub skull_owner: Option<Box<SkullOwner<'a>>>,
|
||||
#[simdnbt(rename = "ExtraAttributes")]
|
||||
pub extra_attributes: Option<ExtraAttributes<'a>>,
|
||||
pub display: Option<ItemDisplay<'a>>,
|
||||
pub extra_attributes: Option<Box<ExtraAttributes<'a>>>,
|
||||
pub display: Option<Box<ItemDisplay<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -28,7 +28,7 @@ impl<T, A: Allocator> FastVec<T, A> {
|
|||
// this is faster than Layout::array
|
||||
alloc::alloc(Layout::from_size_align_unchecked(
|
||||
capacity * element_size,
|
||||
element_size,
|
||||
Self::alignment(),
|
||||
))
|
||||
};
|
||||
let ptr = NonNull::new(ptr as *mut T).expect("allocation failed");
|
||||
|
@ -41,6 +41,11 @@ impl<T, A: Allocator> FastVec<T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn alignment() -> usize {
|
||||
mem::align_of::<T>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn push(&mut self, element: T) {
|
||||
if self.cur == self.end {
|
||||
|
@ -72,8 +77,8 @@ impl<T, A: Allocator> FastVec<T, A> {
|
|||
let new_ptr = unsafe {
|
||||
self.alloc.grow(
|
||||
self.ptr,
|
||||
Layout::from_size_align_unchecked(old_cap_bytes, element_size),
|
||||
Layout::from_size_align_unchecked(new_cap_bytes, element_size),
|
||||
Layout::from_size_align_unchecked(old_cap_bytes, Self::alignment()),
|
||||
Layout::from_size_align_unchecked(new_cap_bytes, Self::alignment()),
|
||||
)
|
||||
};
|
||||
let new_ptr = new_ptr.expect("allocation failed");
|
||||
|
@ -99,8 +104,8 @@ impl<T, A: Allocator> FastVec<T, A> {
|
|||
let new_ptr = unsafe {
|
||||
self.alloc.grow(
|
||||
self.ptr,
|
||||
Layout::from_size_align_unchecked(old_cap_bytes, element_size),
|
||||
Layout::from_size_align_unchecked(new_cap_bytes, element_size),
|
||||
Layout::from_size_align_unchecked(old_cap_bytes, Self::alignment()),
|
||||
Layout::from_size_align_unchecked(new_cap_bytes, Self::alignment()),
|
||||
)
|
||||
};
|
||||
let new_ptr = new_ptr.expect("allocation failed");
|
||||
|
@ -202,11 +207,22 @@ impl<T> FastVec<T, alloc::Global> {
|
|||
Self::with_capacity_in(capacity, alloc::Global)
|
||||
}
|
||||
|
||||
/// Create a new FastVec with the default capacity of 1.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub unsafe fn from_raw_parts(ptr: *mut T, len: usize, capacity: usize) -> Self {
|
||||
Self::from_raw_parts_in(ptr, len, capacity, alloc::Global)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`FastVec`] that allows having a capacity of 0.
|
||||
pub enum OptionalFastVec<T> {
|
||||
Empty,
|
||||
FastVec(FastVec<T>),
|
||||
}
|
||||
|
||||
const DEFAULT_CAPACITY: usize = 1;
|
||||
|
||||
impl<T> Default for FastVec<T> {
|
||||
|
@ -216,7 +232,7 @@ impl<T> Default for FastVec<T> {
|
|||
// this is faster than Layout::array
|
||||
alloc::alloc(Layout::from_size_align_unchecked(
|
||||
DEFAULT_CAPACITY * element_size,
|
||||
element_size,
|
||||
Self::alignment(),
|
||||
))
|
||||
};
|
||||
let ptr = NonNull::new(ptr as *mut T).expect("allocation failed");
|
||||
|
|
|
@ -4,9 +4,11 @@ use byteorder::ReadBytesExt;
|
|||
|
||||
use crate::{
|
||||
common::{self, read_string},
|
||||
fastvec::{FastVec, OptionalFastVec},
|
||||
mutf8::Mutf8String,
|
||||
owned,
|
||||
reader::{Reader, ReaderFromCursor},
|
||||
validate::NbtValidator,
|
||||
DeserializeError, Error, Mutf8Str,
|
||||
};
|
||||
|
||||
|
@ -25,22 +27,27 @@ pub trait Deserialize<'a>: Sized {
|
|||
let mut data = ReaderFromCursor::new(data);
|
||||
let name = read_string(&mut data)?;
|
||||
|
||||
let mut validator = NbtValidator::new();
|
||||
|
||||
Ok((
|
||||
name,
|
||||
Self::read_value_direct_with_explicit_type(&mut data, root_type)?,
|
||||
Self::read_value_direct_with_explicit_type(&mut data, &mut validator, root_type)?,
|
||||
))
|
||||
}
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'a>) -> Result<Self, DeserializeError>;
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError>;
|
||||
|
||||
#[inline]
|
||||
fn read_value_direct_with_explicit_type(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
type_id: u8,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
debug_assert_eq!(type_id, Self::NBT_TYPE_ID);
|
||||
|
||||
Self::read_value_direct(data)
|
||||
Self::read_value_direct(data, validator)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -51,6 +58,7 @@ pub trait Deserialize<'a>: Sized {
|
|||
#[inline]
|
||||
fn update_partial(
|
||||
_partial: &mut Self::Partial<'_>,
|
||||
_validator: &mut NbtValidator,
|
||||
_name: &Mutf8Str,
|
||||
_tag_type: u8,
|
||||
_data: &mut Reader<'a>,
|
||||
|
@ -89,7 +97,10 @@ impl<'a, K: From<&'a Mutf8Str> + Eq + Hash, V: Deserialize<'a>> Deserialize<'a>
|
|||
const NBT_TYPE_ID: u8 = common::COMPOUND_ID;
|
||||
type Partial<'b> = HashMap<K, V>;
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'a>) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
loop {
|
||||
|
@ -102,7 +113,7 @@ impl<'a, K: From<&'a Mutf8Str> + Eq + Hash, V: Deserialize<'a>> Deserialize<'a>
|
|||
if !V::type_matches(tag_type) {
|
||||
return Err(DeserializeError::MismatchedFieldType("HashMap"));
|
||||
}
|
||||
let value = V::read_value_direct_with_explicit_type(data, tag_type)?;
|
||||
let value = V::read_value_direct_with_explicit_type(data, validator, tag_type)?;
|
||||
|
||||
map.insert(name.into(), value);
|
||||
}
|
||||
|
@ -114,8 +125,11 @@ impl<'a, K: From<&'a Mutf8Str> + Eq + Hash, V: Deserialize<'a>> Deserialize<'a>
|
|||
const NBT_TYPE_ID: u8 = common::COMPOUND_ID;
|
||||
type Partial<'b> = HashMap<K, V>;
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'a>) -> Result<Self, DeserializeError> {
|
||||
let mut map = Vec::new();
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let mut map = FastVec::new();
|
||||
|
||||
loop {
|
||||
let tag_type = data.read_u8()?;
|
||||
|
@ -127,12 +141,12 @@ impl<'a, K: From<&'a Mutf8Str> + Eq + Hash, V: Deserialize<'a>> Deserialize<'a>
|
|||
if !V::type_matches(tag_type) {
|
||||
return Err(DeserializeError::MismatchedFieldType("HashMap"));
|
||||
}
|
||||
let value = V::read_value_direct_with_explicit_type(data, tag_type)?;
|
||||
let value = V::read_value_direct_with_explicit_type(data, validator, tag_type)?;
|
||||
|
||||
map.push((name.into(), value));
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
Ok(map.into())
|
||||
}
|
||||
}
|
||||
impl<K: Into<Mutf8String> + Eq + Hash, V: ToNbtTag> Serialize for HashMap<K, V> {
|
||||
|
@ -152,12 +166,16 @@ impl Deserialize<'_> for owned::NbtCompound {
|
|||
|
||||
type Partial<'b> = owned::NbtCompound;
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'_>) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'_>,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
owned::NbtCompound::read(data).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn update_partial(
|
||||
partial: &mut Self::Partial<'_>,
|
||||
_validator: &mut NbtValidator,
|
||||
name: &Mutf8Str,
|
||||
tag_type: u8,
|
||||
data: &mut Reader<'_>,
|
||||
|
@ -188,12 +206,16 @@ impl Deserialize<'_> for owned::NbtTag {
|
|||
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(_data: &mut Reader<'_>) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
_data: &mut Reader<'_>,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
unimplemented!("can't deserialize an NbtTag without the type being known")
|
||||
}
|
||||
|
||||
fn read_value_direct_with_explicit_type(
|
||||
data: &mut Reader<'_>,
|
||||
_validator: &mut NbtValidator,
|
||||
type_id: u8,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let tag = owned::NbtTag::read_with_type(data, type_id, 0).map_err(Error::from)?;
|
||||
|
@ -211,7 +233,10 @@ impl Deserialize<'_> for i8 {
|
|||
const NBT_TYPE_ID: u8 = common::BYTE_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_i8().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -225,7 +250,10 @@ impl Deserialize<'_> for i16 {
|
|||
const NBT_TYPE_ID: u8 = common::SHORT_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_i16().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +267,10 @@ impl Deserialize<'_> for i32 {
|
|||
const NBT_TYPE_ID: u8 = common::INT_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_i32().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +284,10 @@ impl Deserialize<'_> for i64 {
|
|||
const NBT_TYPE_ID: u8 = common::LONG_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_i64().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -267,7 +301,10 @@ impl Deserialize<'_> for f32 {
|
|||
const NBT_TYPE_ID: u8 = common::FLOAT_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_f32().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +318,10 @@ impl Deserialize<'_> for f64 {
|
|||
const NBT_TYPE_ID: u8 = common::DOUBLE_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_f64().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -295,7 +335,10 @@ impl Deserialize<'_> for String {
|
|||
const NBT_TYPE_ID: u8 = common::STRING_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let str = common::read_string(data)?;
|
||||
Ok(str.to_string())
|
||||
}
|
||||
|
@ -304,7 +347,10 @@ impl<'a> Deserialize<'a> for &'a Mutf8Str {
|
|||
const NBT_TYPE_ID: u8 = common::STRING_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'a>) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
common::read_string(data).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -312,7 +358,10 @@ impl<'a> Deserialize<'a> for Cow<'a, str> {
|
|||
const NBT_TYPE_ID: u8 = common::STRING_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'a>) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
common::read_string(data)
|
||||
.map_err(Into::into)
|
||||
.map(|s| s.to_str())
|
||||
|
@ -351,7 +400,10 @@ impl Deserialize<'_> for u8 {
|
|||
const NBT_TYPE_ID: u8 = common::BYTE_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_u8().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -365,7 +417,10 @@ impl Deserialize<'_> for u16 {
|
|||
const NBT_TYPE_ID: u8 = common::SHORT_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_u16().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +434,10 @@ impl Deserialize<'_> for u32 {
|
|||
const NBT_TYPE_ID: u8 = common::INT_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_u32().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -393,7 +451,10 @@ impl Deserialize<'_> for u64 {
|
|||
const NBT_TYPE_ID: u8 = common::LONG_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader,
|
||||
_validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
data.read_u64().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
@ -417,7 +478,10 @@ impl<'a, T: Deserialize<'a>> Deserialize<'a> for Option<T> {
|
|||
const NBT_TYPE_ID: u8 = T::NBT_TYPE_ID;
|
||||
type Partial<'b> = Option<T>;
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'a>) -> Result<Self, DeserializeError> {
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
// empty compounds also count as None
|
||||
if Self::NBT_TYPE_ID == common::COMPOUND_ID {
|
||||
let next_tag_type = data.peek_u8()?;
|
||||
|
@ -427,9 +491,10 @@ impl<'a, T: Deserialize<'a>> Deserialize<'a> for Option<T> {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(Some(T::read_value_direct(data)?))
|
||||
Ok(Some(T::read_value_direct(data, validator)?))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_flatten_with_option(other: Option<Self>) -> Option<Self> {
|
||||
Some(other.flatten())
|
||||
}
|
||||
|
@ -456,28 +521,85 @@ impl<T: Serialize> ToNbtTag for Vec<Option<T>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Deserialize<'a>> Deserialize<'a> for Vec<T> {
|
||||
impl<'a, T: Deserialize<'a>> Deserialize<'a> for Box<T> {
|
||||
const NBT_TYPE_ID: u8 = T::NBT_TYPE_ID;
|
||||
type Partial<'b> = T::Partial<'b>;
|
||||
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
T::read_value_direct(data, validator).map(Box::new)
|
||||
}
|
||||
|
||||
fn update_partial(
|
||||
partial: &mut Self::Partial<'_>,
|
||||
validator: &mut NbtValidator,
|
||||
name: &Mutf8Str,
|
||||
tag_type: u8,
|
||||
data: &mut Reader<'a>,
|
||||
) -> Result<bool, DeserializeError> {
|
||||
T::update_partial(partial, validator, name, tag_type, data)
|
||||
}
|
||||
|
||||
fn from_partial(partial: Self::Partial<'_>) -> Result<Self, DeserializeError> {
|
||||
T::from_partial(partial).map(Box::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Deserialize<'a>> Deserialize<'a> for OptionalFastVec<T> {
|
||||
const NBT_TYPE_ID: u8 = common::LIST_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(data: &mut Reader<'a>) -> Result<Self, DeserializeError> {
|
||||
#[inline(always)]
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
let tag_type = data.read_u8()?;
|
||||
let list_length = data.read_i32()?;
|
||||
if tag_type == common::END_ID || list_length <= 0 {
|
||||
return Ok(Vec::new());
|
||||
return Ok(OptionalFastVec::Empty);
|
||||
}
|
||||
if !T::type_matches(tag_type) {
|
||||
return Err(DeserializeError::MismatchedListType(tag_type));
|
||||
}
|
||||
|
||||
let mut vec = Vec::with_capacity(list_length.min(128) as usize);
|
||||
let mut vec = FastVec::with_capacity((list_length as usize).next_power_of_two().min(128));
|
||||
for _ in 0..list_length {
|
||||
vec.push(T::read_value_direct_with_explicit_type(data, tag_type)?);
|
||||
let value = T::read_value_direct_with_explicit_type(data, validator, tag_type)?;
|
||||
vec.push(value);
|
||||
}
|
||||
|
||||
Ok(vec)
|
||||
Ok(OptionalFastVec::FastVec(vec))
|
||||
}
|
||||
}
|
||||
impl<'a, T: Deserialize<'a>> Deserialize<'a> for Vec<T> {
|
||||
const NBT_TYPE_ID: u8 = common::LIST_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
OptionalFastVec::<T>::read_value_direct(data, validator).map(|v| match v {
|
||||
OptionalFastVec::Empty => Vec::new(),
|
||||
OptionalFastVec::FastVec(v) => v.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'a, T: Deserialize<'a>> Deserialize<'a> for Box<[T]> {
|
||||
const NBT_TYPE_ID: u8 = common::LIST_ID;
|
||||
type Partial<'b> = ();
|
||||
|
||||
fn read_value_direct(
|
||||
data: &mut Reader<'a>,
|
||||
validator: &mut NbtValidator,
|
||||
) -> Result<Self, DeserializeError> {
|
||||
Vec::<T>::read_value_direct(data, validator).map(|v| v.into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize> ToNbtTag for Vec<T> {
|
||||
fn to_nbt_tag(self) -> owned::NbtTag {
|
||||
owned::NbtTag::List(owned::NbtList::Compound(
|
||||
|
|
|
@ -48,6 +48,11 @@ impl ParsingStack {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.depth = 0;
|
||||
// the other two fields are reset as they're used
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, state: ParsingStackElementKind) -> Result<(), NonRootError> {
|
||||
unsafe { self.stack.get_unchecked_mut(self.depth).write(state) };
|
||||
|
|
|
@ -21,9 +21,19 @@ use crate::{
|
|||
Error,
|
||||
};
|
||||
|
||||
/// Read a normal root NBT compound. This is either empty or has a name and
|
||||
/// compound tag.
|
||||
pub fn read(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
pub struct NbtValidator {
|
||||
stack: ParsingStack,
|
||||
}
|
||||
impl NbtValidator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stack: ParsingStack::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a normal root NBT compound. This is either empty or has a name and
|
||||
/// compound tag.
|
||||
pub fn read(&mut self, data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(());
|
||||
|
@ -35,19 +45,28 @@ pub fn read(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
|||
let mut data = ReaderFromCursor::new(data);
|
||||
read_string(&mut data)?;
|
||||
|
||||
let mut stack = ParsingStack::new();
|
||||
stack.push(ParsingStackElementKind::Compound)?;
|
||||
self.stack.clear();
|
||||
self.stack.push(ParsingStackElementKind::Compound)?;
|
||||
|
||||
read_with_stack(&mut data, &mut stack)?;
|
||||
self.read_with_stack(&mut data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
/// Read a root NBT compound, but without reading the name. This is used in
|
||||
/// Minecraft when reading NBT over the network.
|
||||
///
|
||||
/// This is similar to [`read_tag`], but only allows the data to be empty or a
|
||||
/// compound.
|
||||
pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn internal_read_tag(&mut self, data: &mut Reader, tag_type: u8) -> Result<(), Error> {
|
||||
self.stack.clear();
|
||||
compound::read_tag(data, &mut self.stack, tag_type)?;
|
||||
self.read_with_stack(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read a root NBT compound, but without reading the name. This is used in
|
||||
/// Minecraft when reading NBT over the network.
|
||||
///
|
||||
/// This is similar to [`read_tag`], but only allows the data to be empty or
|
||||
/// a compound.
|
||||
pub fn read_unnamed(&mut self, data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(());
|
||||
|
@ -55,22 +74,22 @@ pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
|||
if root_type != COMPOUND_ID {
|
||||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
read_compound(data)?;
|
||||
self.read_compound(data)?;
|
||||
Ok(())
|
||||
}
|
||||
/// Read a compound tag. This may have any number of items.
|
||||
pub fn read_compound(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
let mut stack = ParsingStack::new();
|
||||
}
|
||||
/// Read a compound tag. This may have any number of items.
|
||||
pub fn read_compound(&mut self, data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
self.stack.clear();
|
||||
let mut data = ReaderFromCursor::new(data);
|
||||
stack.push(ParsingStackElementKind::Compound)?;
|
||||
read_with_stack(&mut data, &mut stack)?;
|
||||
self.stack.push(ParsingStackElementKind::Compound)?;
|
||||
self.read_with_stack(&mut data)?;
|
||||
Ok(())
|
||||
}
|
||||
/// 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<(), Error> {
|
||||
let mut stack = ParsingStack::new();
|
||||
}
|
||||
/// 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(&mut self, data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
self.stack.clear();
|
||||
|
||||
let mut data = ReaderFromCursor::new(data);
|
||||
|
||||
|
@ -78,23 +97,15 @@ pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
|||
if tag_type == END_ID {
|
||||
return Err(Error::InvalidRootType(0));
|
||||
}
|
||||
compound::read_tag(&mut data, &mut stack, tag_type)?;
|
||||
read_with_stack(&mut data, &mut stack)?;
|
||||
compound::read_tag(&mut data, &mut self.stack, tag_type)?;
|
||||
self.read_with_stack(&mut data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn internal_read_tag(data: &mut Reader, tag_type: u8) -> Result<(), Error> {
|
||||
let mut stack = ParsingStack::new();
|
||||
compound::read_tag(data, &mut stack, tag_type)?;
|
||||
read_with_stack(data, &mut stack)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read any NBT tag, without reading its name. This may be any type of tag,
|
||||
/// including an end tag.
|
||||
pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
/// Read any NBT tag, without reading its name. This may be any type of tag,
|
||||
/// including an end tag.
|
||||
pub fn read_optional_tag(&mut self, data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
||||
let mut stack = ParsingStack::new();
|
||||
|
||||
let mut data = ReaderFromCursor::new(data);
|
||||
|
@ -104,22 +115,25 @@ pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<(), Error> {
|
|||
return Ok(());
|
||||
}
|
||||
compound::read_tag(&mut data, &mut stack, tag_type)?;
|
||||
read_with_stack(&mut data, &mut stack)?;
|
||||
self.read_with_stack(&mut data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_with_stack<'a>(data: &mut Reader<'a>, stack: &mut ParsingStack) -> Result<(), Error> {
|
||||
while !stack.is_empty() {
|
||||
let top = stack.peek();
|
||||
fn read_with_stack<'a>(&mut self, data: &mut Reader<'a>) -> Result<(), Error> {
|
||||
while !self.stack.is_empty() {
|
||||
let top = self.stack.peek();
|
||||
match top {
|
||||
ParsingStackElementKind::Compound => read_tag_in_compound(data, stack)?,
|
||||
ParsingStackElementKind::ListOfCompounds => read_compound_in_list(data, stack)?,
|
||||
ParsingStackElementKind::ListOfLists => read_list_in_list(data, stack)?,
|
||||
ParsingStackElementKind::Compound => read_tag_in_compound(data, &mut self.stack)?,
|
||||
ParsingStackElementKind::ListOfCompounds => {
|
||||
read_compound_in_list(data, &mut self.stack)?
|
||||
}
|
||||
ParsingStackElementKind::ListOfLists => read_list_in_list(data, &mut self.stack)?,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -134,7 +148,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
super::read(&mut Cursor::new(include_bytes!(
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(include_bytes!(
|
||||
"../../tests/hello_world.nbt"
|
||||
)))
|
||||
.unwrap();
|
||||
|
@ -147,7 +162,9 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
super::read(&mut Cursor::new(&decoded_src)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -157,18 +174,23 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
super::read(&mut Cursor::new(&decoded_src)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_hypixel() {
|
||||
let src = include_bytes!("../../tests/hypixel.nbt").to_vec();
|
||||
super::read(&mut Cursor::new(&src[..])).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&src[..]))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inttest_1023() {
|
||||
super::read(&mut Cursor::new(include_bytes!(
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(include_bytes!(
|
||||
"../../tests/inttest1023.nbt"
|
||||
)))
|
||||
.unwrap();
|
||||
|
@ -188,7 +210,9 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
super::read(&mut Cursor::new(&data)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&data))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -205,7 +229,9 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
super::read(&mut Cursor::new(&data)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&data))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -222,7 +248,9 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
super::read(&mut Cursor::new(&data)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&data))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -234,7 +262,7 @@ mod tests {
|
|||
data.write_u16::<BE>(0).unwrap(); // first element name length
|
||||
// eof
|
||||
|
||||
let res = super::read(&mut Cursor::new(&data));
|
||||
let res = super::NbtValidator::new().read(&mut Cursor::new(&data));
|
||||
assert_eq!(res, Err(Error::UnexpectedEof));
|
||||
}
|
||||
|
||||
|
@ -251,14 +279,18 @@ mod tests {
|
|||
decoded_src_as_tag.extend_from_slice(&decoded_src);
|
||||
decoded_src_as_tag.push(END_ID);
|
||||
|
||||
super::read_tag(&mut Cursor::new(&decoded_src_as_tag)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read_tag(&mut Cursor::new(&decoded_src_as_tag))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_array() {
|
||||
// found from fuzzing
|
||||
let data = [10, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0];
|
||||
super::read(&mut Cursor::new(&data)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&data))
|
||||
.unwrap();
|
||||
}
|
||||
#[test]
|
||||
fn list_of_empty_lists() {
|
||||
|
@ -266,14 +298,18 @@ mod tests {
|
|||
// BaseNbt { name: m"", tag: NbtTag::NbtCompound { m"":
|
||||
// NbtTag::List(List::List([List::Empty])) } }
|
||||
let data = [10, 0, 0, 9, 0, 0, 9, 0, 0, 0, 1, 0, 9, 0, 0, 0, 0];
|
||||
super::read(&mut Cursor::new(&data)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&data))
|
||||
.unwrap();
|
||||
}
|
||||
#[test]
|
||||
fn list_of_byte_arrays() {
|
||||
// BaseNbt { name: m"", tag: NbtCompound { values: [(m"",
|
||||
// List(List([List::ByteArray([])])))] } }
|
||||
let data = [10, 0, 0, 9, 0, 0, 9, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0];
|
||||
super::read(&mut Cursor::new(&data)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&data))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -283,6 +319,8 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
super::read(&mut Cursor::new(&decoded_src)).unwrap();
|
||||
super::NbtValidator::new()
|
||||
.read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue