mirror of
https://github.com/azalea-rs/simdnbt.git
synced 2025-08-02 07:26:04 +00:00
fix some bugs
This commit is contained in:
parent
57dfc2c1a9
commit
820cb1f334
11 changed files with 441 additions and 179 deletions
|
@ -6,6 +6,11 @@ pub struct FieldAttrs {
|
|||
pub flatten: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct UnitAttrs {
|
||||
pub rename: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct StructAttrs {
|
||||
pub deny_unknown_fields: bool,
|
||||
|
@ -35,6 +40,27 @@ impl Parse for FieldAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
impl Parse for UnitAttrs {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mut attrs = Self::default();
|
||||
|
||||
while !input.is_empty() {
|
||||
let attr = input.parse::<proc_macro2::Ident>()?;
|
||||
match attr.to_string().as_str() {
|
||||
"rename" => {
|
||||
input.parse::<syn::Token![=]>()?;
|
||||
let rename = input.parse::<syn::LitStr>()?;
|
||||
|
||||
attrs.rename = Some(rename.value());
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(attrs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for StructAttrs {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let mut attrs = Self::default();
|
||||
|
@ -63,11 +89,29 @@ pub fn parse_field_attrs(attrs: &[syn::Attribute]) -> FieldAttrs {
|
|||
if let Some(rename) = new_attr.rename {
|
||||
field_attrs.rename = Some(rename);
|
||||
}
|
||||
if new_attr.flatten {
|
||||
field_attrs.flatten = true;
|
||||
}
|
||||
}
|
||||
|
||||
field_attrs
|
||||
}
|
||||
|
||||
pub fn parse_unit_attrs(attrs: &[syn::Attribute]) -> UnitAttrs {
|
||||
let mut unit_attrs = UnitAttrs::default();
|
||||
|
||||
for attr in attrs.iter().filter(|attr| attr.path().is_ident("simdnbt")) {
|
||||
let new_attr = attr
|
||||
.parse_args::<UnitAttrs>()
|
||||
.expect("invalid simdnbt attr");
|
||||
if let Some(rename) = new_attr.rename {
|
||||
unit_attrs.rename = Some(rename);
|
||||
}
|
||||
}
|
||||
|
||||
unit_attrs
|
||||
}
|
||||
|
||||
pub fn parse_struct_attrs(attrs: &[syn::Attribute]) -> StructAttrs {
|
||||
let mut struct_attrs = StructAttrs::default();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
mod attrs;
|
||||
|
||||
use attrs::parse_field_attrs;
|
||||
use attrs::{parse_field_attrs, parse_unit_attrs};
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
|
@ -30,10 +30,12 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
#struct_field_name: simdnbt::Deserialize::from_compound(nbt)?,
|
||||
})
|
||||
} else {
|
||||
let debug_ident = format!("{ident}::{struct_field_name}");
|
||||
|
||||
field_deserializers.push(quote! {
|
||||
#struct_field_name: simdnbt::FromNbtTag::from_optional_nbt_tag(
|
||||
nbt.take(#field_name)
|
||||
)?.ok_or(simdnbt::DeserializeError::MismatchedFieldType)?
|
||||
)?.ok_or(simdnbt::DeserializeError::MismatchedFieldType(#debug_ident.to_owned()))?
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -46,6 +48,8 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
}
|
||||
|
||||
let generics = input.generics;
|
||||
let where_clause = &generics.where_clause;
|
||||
|
||||
let struct_attrs = attrs::parse_struct_attrs(&input.attrs);
|
||||
|
||||
let extra_checks = if struct_attrs.deny_unknown_fields {
|
||||
|
@ -59,7 +63,7 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
};
|
||||
|
||||
let output = quote! {
|
||||
impl #generics simdnbt::Deserialize for #ident #generics {
|
||||
impl #generics simdnbt::Deserialize for #ident #generics #where_clause {
|
||||
fn from_compound(mut nbt: simdnbt::owned::NbtCompound) -> Result<Self, simdnbt::DeserializeError> {
|
||||
let value = Self {
|
||||
#(#field_deserializers),*
|
||||
|
@ -109,10 +113,10 @@ pub fn serialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
|
|||
}
|
||||
|
||||
let generics = input.generics;
|
||||
let struct_attrs = attrs::parse_struct_attrs(&input.attrs);
|
||||
let where_clause = &generics.where_clause;
|
||||
|
||||
let output = quote! {
|
||||
impl #generics simdnbt::Serialize for #ident #generics {
|
||||
impl #generics simdnbt::Serialize for #ident #generics #where_clause {
|
||||
fn to_compound(self) -> simdnbt::owned::NbtCompound {
|
||||
let mut nbt = simdnbt::owned::NbtCompound::new();
|
||||
#(#field_serializers)*
|
||||
|
@ -123,3 +127,106 @@ pub fn serialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
|
|||
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromNbtTag, attributes(simdnbt))]
|
||||
pub fn from_nbt_tag_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident = input.ident;
|
||||
|
||||
let mut matchers = Vec::<proc_macro2::TokenStream>::new();
|
||||
|
||||
match input.data {
|
||||
syn::Data::Struct(_) => panic!("Use #[derive(Deserialize)] instead"),
|
||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||
for variant in variants {
|
||||
match variant.fields {
|
||||
syn::Fields::Named(_) => todo!(),
|
||||
syn::Fields::Unnamed(_) => todo!(),
|
||||
syn::Fields::Unit => {
|
||||
let enum_variant_name = variant.ident;
|
||||
|
||||
let mut unit_attrs = parse_unit_attrs(&variant.attrs);
|
||||
|
||||
let variant_name = unit_attrs
|
||||
.rename
|
||||
.take()
|
||||
.unwrap_or_else(|| enum_variant_name.to_string());
|
||||
|
||||
matchers.push(quote! {
|
||||
#variant_name => Some(Self::#enum_variant_name),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Data::Union(_) => todo!(),
|
||||
}
|
||||
|
||||
let generics = input.generics;
|
||||
let where_clause = &generics.where_clause;
|
||||
|
||||
let output = quote! {
|
||||
impl #generics simdnbt::FromNbtTag for #ident #generics #where_clause {
|
||||
fn from_nbt_tag(tag: simdnbt::owned::NbtTag) -> Option<Self> {
|
||||
match tag.string()?.to_str().as_ref() {
|
||||
#(#matchers)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ToNbtTag, attributes(simdnbt))]
|
||||
pub fn to_nbt_tag_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let ident = input.ident;
|
||||
|
||||
let mut field_matchers = Vec::<proc_macro2::TokenStream>::new();
|
||||
|
||||
match input.data {
|
||||
syn::Data::Struct(_) => panic!("Use #[derive(Serialize)] instead"),
|
||||
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||
for variant in variants {
|
||||
match variant.fields {
|
||||
syn::Fields::Named(_) => todo!(),
|
||||
syn::Fields::Unnamed(_) => todo!(),
|
||||
syn::Fields::Unit => {
|
||||
let enum_variant_name = variant.ident;
|
||||
|
||||
let mut unit_attrs = parse_unit_attrs(&variant.attrs);
|
||||
|
||||
let variant_name = unit_attrs
|
||||
.rename
|
||||
.take()
|
||||
.unwrap_or_else(|| enum_variant_name.to_string());
|
||||
|
||||
field_matchers.push(quote! {
|
||||
Self::#enum_variant_name => simdnbt::owned::NbtTag::String(#variant_name.into()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Data::Union(_) => todo!(),
|
||||
}
|
||||
|
||||
let generics = input.generics;
|
||||
let where_clause = &generics.where_clause;
|
||||
|
||||
let output = quote! {
|
||||
impl #generics simdnbt::ToNbtTag for #ident #generics #where_clause {
|
||||
fn to_nbt_tag(self) -> simdnbt::owned::NbtTag {
|
||||
match self {
|
||||
#(#field_matchers)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
output.into()
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
use simdnbt::ToNbtTag;
|
||||
use std::{collections::HashMap, hint::black_box, io::Cursor};
|
||||
|
||||
use simdnbt::{
|
||||
borrow::{BaseNbt, Nbt},
|
||||
owned,
|
||||
};
|
||||
use simdnbt::borrow::{BaseNbt, Nbt};
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub struct Item {
|
||||
|
@ -103,44 +99,12 @@ fn items_from_nbt(nbt: BaseNbt) -> Option<Vec<Option<Item>>> {
|
|||
Some(items)
|
||||
}
|
||||
|
||||
fn nbt_from_items(items: Vec<Option<Item>>) -> owned::BaseNbt {
|
||||
owned::BaseNbt::new("", {
|
||||
let i = {
|
||||
let mut i = Vec::new();
|
||||
|
||||
for item in items {
|
||||
let Some(item) = item else {
|
||||
i.push(owned::NbtCompound::new());
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut nbt = {
|
||||
let mut nbt = owned::NbtCompound::new();
|
||||
nbt.insert("id", item.id.to_nbt_tag());
|
||||
nbt.insert("Damage", item.damage.to_nbt_tag());
|
||||
nbt.insert("Count", item.count.to_nbt_tag());
|
||||
nbt
|
||||
};
|
||||
|
||||
i.push(nbt);
|
||||
}
|
||||
|
||||
owned::NbtList::Compound(i)
|
||||
};
|
||||
|
||||
let mut nbt = owned::NbtCompound::new();
|
||||
nbt.insert("i", owned::NbtTag::List(i));
|
||||
nbt
|
||||
})
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input = black_box(include_bytes!("../tests/realworld.nbt"));
|
||||
|
||||
for _ in 0..1 {
|
||||
let nbt = Nbt::read(&mut Cursor::new(input));
|
||||
let nbt = black_box(nbt.unwrap().unwrap());
|
||||
println!("{:?}", items_from_nbt(nbt));
|
||||
// black_box(simdnbt_items_from_nbt(nbt));
|
||||
black_box(items_from_nbt(nbt));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,13 +127,13 @@ impl<'a> NbtCompound<'a> {
|
|||
unsafe {
|
||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&int_array.as_big_endian());
|
||||
data.extend_from_slice(int_array.as_big_endian());
|
||||
}
|
||||
NbtTag::LongArray(long_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&long_array.as_big_endian());
|
||||
data.extend_from_slice(long_array.as_big_endian());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ pub fn slice_i8_into_u8(s: &[i8]) -> &[u8] {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn write_with_u32_length<'a>(data: &mut Vec<u8>, width: usize, value: &'a [u8]) {
|
||||
pub fn write_with_u32_length(data: &mut Vec<u8>, width: usize, value: &[u8]) {
|
||||
let length = value.len() / width;
|
||||
data.reserve(4 + value.len());
|
||||
unsafe {
|
||||
|
@ -171,7 +171,7 @@ pub unsafe fn unchecked_push(data: &mut Vec<u8>, value: u8) {
|
|||
/// endian! Use [`slice_into_u8_big_endian`] to get big endian (the endianness that's used in NBT).
|
||||
#[inline]
|
||||
pub fn slice_into_u8_native_endian<T>(s: &[T]) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, s.len() * std::mem::size_of::<T>()) }
|
||||
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, std::mem::size_of_val(s)) }
|
||||
}
|
||||
|
||||
/// Convert a slice of any type into a Vec<u8>. This will return the data as big endian (the
|
||||
|
|
|
@ -18,8 +18,8 @@ pub enum Error {
|
|||
pub enum DeserializeError {
|
||||
#[error("Missing field")]
|
||||
MissingField,
|
||||
#[error("Mismatched type")]
|
||||
MismatchedFieldType,
|
||||
#[error("Mismatched type for {0}")]
|
||||
MismatchedFieldType(String),
|
||||
#[error("Unknown fields {0:?}")]
|
||||
UnknownField(Vec<String>),
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@ use std::{
|
|||
};
|
||||
|
||||
/// A M-UTF8 string slice. This is how strings are represented internally in NBT.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub struct Mutf8Str {
|
||||
pub(crate) slice: [u8],
|
||||
}
|
||||
/// An owned M-UTF8 string.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Default)]
|
||||
#[derive(Eq, PartialEq, Clone, Default)]
|
||||
pub struct Mutf8String {
|
||||
pub(crate) vec: Vec<u8>,
|
||||
}
|
||||
|
@ -116,6 +116,10 @@ impl Mutf8Str {
|
|||
self.slice.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.slice.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.slice
|
||||
|
@ -128,6 +132,18 @@ impl fmt::Display for Mutf8Str {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Mutf8Str {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Mutf8Str").field(&self.to_str()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Mutf8String {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Mutf8String").field(&self.to_str()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Mutf8Str {
|
||||
type Owned = Mutf8String;
|
||||
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
use std::{io::Cursor, mem};
|
||||
|
||||
use byteorder::{ReadBytesExt, BE};
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
read_int_array, read_long_array, read_string, read_with_u32_length,
|
||||
slice_into_u8_big_endian, unchecked_extend, unchecked_push, unchecked_write_string,
|
||||
write_string, 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, MAX_DEPTH, SHORT_ID, STRING_ID,
|
||||
},
|
||||
common::{read_string, unchecked_push, unchecked_write_string, END_ID, MAX_DEPTH},
|
||||
mutf8::Mutf8String,
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
@ -46,49 +41,7 @@ impl NbtCompound {
|
|||
}
|
||||
let tag_name = read_string(data)?.to_owned();
|
||||
|
||||
match tag_type {
|
||||
BYTE_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
SHORT_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
INT_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
LONG_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
FLOAT_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
DOUBLE_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
BYTE_ARRAY_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned()),
|
||||
)),
|
||||
STRING_ID => values.push((tag_name, NbtTag::String(read_string(data)?.to_owned()))),
|
||||
LIST_ID => values.push((tag_name, NbtTag::List(NbtList::read(data, depth + 1)?))),
|
||||
COMPOUND_ID => values.push((
|
||||
tag_name,
|
||||
NbtTag::Compound(NbtCompound::read_with_depth(data, depth + 1)?),
|
||||
)),
|
||||
INT_ARRAY_ID => {
|
||||
values.push((tag_name, NbtTag::IntArray(read_int_array(data)?.to_vec())))
|
||||
}
|
||||
LONG_ARRAY_ID => {
|
||||
values.push((tag_name, NbtTag::LongArray(read_long_array(data)?.to_vec())))
|
||||
}
|
||||
_ => return Err(Error::UnknownTagId(tag_type)),
|
||||
}
|
||||
values.push((tag_name, NbtTag::read_with_type(data, tag_type, depth)?));
|
||||
}
|
||||
Ok(Self { values })
|
||||
}
|
||||
|
@ -102,53 +55,7 @@ impl NbtCompound {
|
|||
unsafe {
|
||||
unchecked_push(data, tag.id());
|
||||
unchecked_write_string(data, name);
|
||||
}
|
||||
match tag {
|
||||
NbtTag::Byte(byte) => unsafe {
|
||||
unchecked_push(data, *byte as u8);
|
||||
},
|
||||
NbtTag::Short(short) => unsafe {
|
||||
unchecked_extend(data, &short.to_be_bytes());
|
||||
},
|
||||
NbtTag::Int(int) => unsafe {
|
||||
unchecked_extend(data, &int.to_be_bytes());
|
||||
},
|
||||
NbtTag::Long(long) => {
|
||||
data.extend_from_slice(&long.to_be_bytes());
|
||||
}
|
||||
NbtTag::Float(float) => unsafe {
|
||||
unchecked_extend(data, &float.to_be_bytes());
|
||||
},
|
||||
NbtTag::Double(double) => {
|
||||
data.extend_from_slice(&double.to_be_bytes());
|
||||
}
|
||||
NbtTag::ByteArray(byte_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(byte_array);
|
||||
}
|
||||
NbtTag::String(string) => {
|
||||
write_string(data, string);
|
||||
}
|
||||
NbtTag::List(list) => {
|
||||
list.write(data);
|
||||
}
|
||||
NbtTag::Compound(compound) => {
|
||||
compound.write(data);
|
||||
}
|
||||
NbtTag::IntArray(int_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&slice_into_u8_big_endian(int_array));
|
||||
}
|
||||
NbtTag::LongArray(long_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&slice_into_u8_big_endian(long_array));
|
||||
}
|
||||
tag.unchecked_write_without_tag_type(data);
|
||||
}
|
||||
}
|
||||
data.push(END_ID);
|
||||
|
@ -301,9 +208,6 @@ impl NbtCompound {
|
|||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut NbtTag> {
|
||||
self.values.iter_mut().map(|(_, v)| v)
|
||||
}
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
||||
self.values.into_iter()
|
||||
}
|
||||
pub fn clear(&mut self) {
|
||||
self.values.clear();
|
||||
}
|
||||
|
@ -322,3 +226,12 @@ impl NbtCompound {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for NbtCompound {
|
||||
type Item = (Mutf8String, NbtTag);
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.values.into_iter()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,14 @@ mod list;
|
|||
|
||||
use std::{io::Cursor, ops::Deref};
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
use byteorder::{ReadBytesExt, BE};
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
read_string, read_u32, write_string, 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, MAX_DEPTH,
|
||||
SHORT_ID, STRING_ID,
|
||||
read_int_array, read_long_array, read_string, read_u32, read_with_u32_length,
|
||||
slice_into_u8_big_endian, unchecked_extend, unchecked_push, write_string, 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, MAX_DEPTH, SHORT_ID, STRING_ID,
|
||||
},
|
||||
mutf8::Mutf8String,
|
||||
Error, Mutf8Str,
|
||||
|
@ -52,6 +53,22 @@ impl Nbt {
|
|||
Ok(Nbt::Some(BaseNbt { name, tag }))
|
||||
}
|
||||
|
||||
pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(Nbt::None);
|
||||
}
|
||||
if root_type != COMPOUND_ID {
|
||||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let tag = NbtCompound::read_with_depth(data, 0)?;
|
||||
|
||||
Ok(Nbt::Some(BaseNbt {
|
||||
name: Mutf8String::from(""),
|
||||
tag,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn write(&self, data: &mut Vec<u8>) {
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt.write(data),
|
||||
|
@ -61,6 +78,15 @@ impl Nbt {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write_unnamed(&self, data: &mut Vec<u8>) {
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt.write_unnamed(data),
|
||||
Nbt::None => {
|
||||
data.push(END_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(self) -> BaseNbt {
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt,
|
||||
|
@ -68,6 +94,13 @@ impl Nbt {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_or<'a>(&'a self, default: &'a BaseNbt) -> &'a BaseNbt {
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt,
|
||||
Nbt::None => default,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_some(&self) -> bool {
|
||||
match self {
|
||||
Nbt::Some(_) => true,
|
||||
|
@ -80,7 +113,7 @@ impl Nbt {
|
|||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag)> {
|
||||
const EMPTY: &'static NbtCompound = &NbtCompound { values: Vec::new() };
|
||||
const EMPTY: &NbtCompound = &NbtCompound { values: Vec::new() };
|
||||
|
||||
if let Nbt::Some(nbt) = self {
|
||||
nbt.iter()
|
||||
|
@ -88,21 +121,12 @@ impl Nbt {
|
|||
EMPTY.iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
||||
const EMPTY: NbtCompound = NbtCompound { values: Vec::new() };
|
||||
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt.tag.into_iter(),
|
||||
Nbt::None => EMPTY.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Deref for Nbt {
|
||||
type Target = BaseNbt;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
const EMPTY: &'static BaseNbt = &BaseNbt {
|
||||
const EMPTY: &BaseNbt = &BaseNbt {
|
||||
name: Mutf8String { vec: Vec::new() },
|
||||
tag: NbtCompound { values: Vec::new() },
|
||||
};
|
||||
|
@ -114,6 +138,20 @@ impl Deref for Nbt {
|
|||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Nbt {
|
||||
type Item = (Mutf8String, NbtTag);
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
const EMPTY: NbtCompound = NbtCompound { values: Vec::new() };
|
||||
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt.tag.into_iter(),
|
||||
Nbt::None => EMPTY.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BaseNbt {
|
||||
pub fn new(name: impl Into<Mutf8String>, tag: NbtCompound) -> Self {
|
||||
let name = name.into();
|
||||
|
@ -132,14 +170,25 @@ impl BaseNbt {
|
|||
self.tag.write(data);
|
||||
}
|
||||
|
||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
||||
self.tag.into_iter()
|
||||
pub fn write_unnamed(&self, data: &mut Vec<u8>) {
|
||||
data.push(COMPOUND_ID);
|
||||
self.tag.write(data);
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> NbtCompound {
|
||||
self.tag
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for BaseNbt {
|
||||
type Item = (Mutf8String, NbtTag);
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.tag.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BaseNbt {
|
||||
type Target = NbtCompound;
|
||||
|
||||
|
@ -176,6 +225,109 @@ impl NbtTag {
|
|||
unsafe { *<*const _>::from(self).cast::<u8>() }
|
||||
}
|
||||
|
||||
fn read_with_type(data: &mut Cursor<&[u8]>, tag_type: u8, depth: usize) -> Result<Self, Error> {
|
||||
match tag_type {
|
||||
BYTE_ID => Ok(NbtTag::Byte(
|
||||
data.read_i8().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
SHORT_ID => Ok(NbtTag::Short(
|
||||
data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
INT_ID => Ok(NbtTag::Int(
|
||||
data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
LONG_ID => Ok(NbtTag::Long(
|
||||
data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
FLOAT_ID => Ok(NbtTag::Float(
|
||||
data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
DOUBLE_ID => Ok(NbtTag::Double(
|
||||
data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
BYTE_ARRAY_ID => Ok(NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned())),
|
||||
STRING_ID => Ok(NbtTag::String(read_string(data)?.to_owned())),
|
||||
LIST_ID => Ok(NbtTag::List(NbtList::read(data, depth + 1)?)),
|
||||
COMPOUND_ID => Ok(NbtTag::Compound(NbtCompound::read_with_depth(
|
||||
data,
|
||||
depth + 1,
|
||||
)?)),
|
||||
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)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
Self::read_with_type(data, tag_type, 0)
|
||||
}
|
||||
|
||||
/// Write to the data without checking that there's enough space in it.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it doesn't check that there's enough space in the data.
|
||||
/// 4 bytes MUST be reserved before calling this function.
|
||||
unsafe fn unchecked_write_without_tag_type(&self, data: &mut Vec<u8>) {
|
||||
match self {
|
||||
NbtTag::Byte(byte) => unsafe {
|
||||
unchecked_push(data, *byte as u8);
|
||||
},
|
||||
NbtTag::Short(short) => unsafe {
|
||||
unchecked_extend(data, &short.to_be_bytes());
|
||||
},
|
||||
NbtTag::Int(int) => unsafe {
|
||||
unchecked_extend(data, &int.to_be_bytes());
|
||||
},
|
||||
NbtTag::Long(long) => {
|
||||
data.extend_from_slice(&long.to_be_bytes());
|
||||
}
|
||||
NbtTag::Float(float) => unsafe {
|
||||
unchecked_extend(data, &float.to_be_bytes());
|
||||
},
|
||||
NbtTag::Double(double) => {
|
||||
data.extend_from_slice(&double.to_be_bytes());
|
||||
}
|
||||
NbtTag::ByteArray(byte_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(byte_array);
|
||||
}
|
||||
NbtTag::String(string) => {
|
||||
write_string(data, string);
|
||||
}
|
||||
NbtTag::List(list) => {
|
||||
list.write(data);
|
||||
}
|
||||
NbtTag::Compound(compound) => {
|
||||
compound.write(data);
|
||||
}
|
||||
NbtTag::IntArray(int_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&slice_into_u8_big_endian(int_array));
|
||||
}
|
||||
NbtTag::LongArray(long_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(&slice_into_u8_big_endian(long_array));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self, data: &mut Vec<u8>) {
|
||||
data.reserve(1 + 4);
|
||||
// SAFETY: We just reserved enough space for the tag ID and 4 bytes of tag data.
|
||||
unsafe {
|
||||
unchecked_push(data, self.id());
|
||||
self.unchecked_write_without_tag_type(data);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn byte(&self) -> Option<i8> {
|
||||
match self {
|
||||
NbtTag::Byte(byte) => Some(*byte),
|
||||
|
|
|
@ -20,6 +20,10 @@ impl<'a, T> RawList<'a, T> {
|
|||
pub fn len(&self) -> usize {
|
||||
self.data.len() / std::mem::size_of::<T>()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SwappableNumber> RawList<'_, T> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{collections::HashMap, fmt::Display, hash::Hash, hash::Hasher, str::FromStr};
|
||||
use std::{collections::HashMap, fmt::Display, hash::Hash, str::FromStr};
|
||||
|
||||
use crate::DeserializeError;
|
||||
|
||||
|
@ -42,12 +42,16 @@ impl<K: Display + FromStr + Eq + Hash, V: FromNbtTag> Deserialize for HashMap<K,
|
|||
let mut hashmap = HashMap::with_capacity(compound.values.len());
|
||||
|
||||
for (k, v) in compound.values {
|
||||
hashmap.insert(
|
||||
k.to_str()
|
||||
.parse()
|
||||
.map_err(|_| DeserializeError::MismatchedFieldType)?,
|
||||
V::from_nbt_tag(v).ok_or(DeserializeError::MismatchedFieldType)?,
|
||||
);
|
||||
let k_str = k.to_str();
|
||||
let k_parsed = k_str
|
||||
.parse()
|
||||
.map_err(|_| DeserializeError::MismatchedFieldType("key".to_owned()))?;
|
||||
|
||||
let v_parsed = V::from_nbt_tag(v).ok_or_else(|| {
|
||||
DeserializeError::MismatchedFieldType(format!("value for key {k_str}"))
|
||||
})?;
|
||||
|
||||
hashmap.insert(k_parsed, v_parsed);
|
||||
}
|
||||
|
||||
Ok(hashmap)
|
||||
|
@ -89,6 +93,17 @@ impl<T: Serialize> ToNbtTag for T {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromNbtTag for crate::owned::NbtTag {
|
||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||
Some(tag)
|
||||
}
|
||||
}
|
||||
impl ToNbtTag for crate::owned::NbtTag {
|
||||
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// standard nbt types
|
||||
impl FromNbtTag for i8 {
|
||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||
|
@ -167,6 +182,51 @@ impl ToNbtTag for String {
|
|||
}
|
||||
}
|
||||
|
||||
// unsigned integers
|
||||
impl FromNbtTag for u8 {
|
||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||
tag.byte().map(|b| b as u8)
|
||||
}
|
||||
}
|
||||
impl ToNbtTag for u8 {
|
||||
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||
crate::owned::NbtTag::Byte(self as i8)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromNbtTag for u16 {
|
||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||
tag.short().map(|s| s as u16)
|
||||
}
|
||||
}
|
||||
impl ToNbtTag for u16 {
|
||||
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||
crate::owned::NbtTag::Short(self as i16)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromNbtTag for u32 {
|
||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||
tag.int().map(|i| i as u32)
|
||||
}
|
||||
}
|
||||
impl ToNbtTag for u32 {
|
||||
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||
crate::owned::NbtTag::Int(self as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromNbtTag for u64 {
|
||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||
tag.long().map(|l| l as u64)
|
||||
}
|
||||
}
|
||||
impl ToNbtTag for u64 {
|
||||
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||
crate::owned::NbtTag::Long(self as i64)
|
||||
}
|
||||
}
|
||||
|
||||
// lists
|
||||
impl FromNbtTag for Vec<String> {
|
||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||
|
@ -203,10 +263,7 @@ impl<T: ToNbtTag> ToNbtTag for Option<T> {
|
|||
panic!("Called to_nbt_tag on Option<T>. Use to_optional_nbt_tag instead.")
|
||||
}
|
||||
fn to_optional_nbt_tag(self) -> Option<crate::owned::NbtTag> {
|
||||
match self {
|
||||
Some(t) => Some(t.to_nbt_tag()),
|
||||
None => None,
|
||||
}
|
||||
self.map(|t| t.to_nbt_tag())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,3 +320,8 @@ impl FromNbtTag for bool {
|
|||
tag.byte().map(|b| b != 0)
|
||||
}
|
||||
}
|
||||
impl ToNbtTag for bool {
|
||||
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||
crate::owned::NbtTag::Byte(if self { 1 } else { 0 })
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue