diff --git a/simdnbt-derive/src/attrs.rs b/simdnbt-derive/src/attrs.rs index a3a8658..c91b495 100644 --- a/simdnbt-derive/src/attrs.rs +++ b/simdnbt-derive/src/attrs.rs @@ -6,6 +6,11 @@ pub struct FieldAttrs { pub flatten: bool, } +#[derive(Default, Debug)] +pub struct UnitAttrs { + pub rename: Option, +} + #[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 { + let mut attrs = Self::default(); + + while !input.is_empty() { + let attr = input.parse::()?; + match attr.to_string().as_str() { + "rename" => { + input.parse::()?; + let rename = input.parse::()?; + + attrs.rename = Some(rename.value()); + } + _ => todo!(), + } + } + + Ok(attrs) + } +} + impl Parse for StructAttrs { fn parse(input: ParseStream) -> syn::Result { 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::() + .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(); diff --git a/simdnbt-derive/src/lib.rs b/simdnbt-derive/src/lib.rs index b12afc9..356b9ea 100644 --- a/simdnbt-derive/src/lib.rs +++ b/simdnbt-derive/src/lib.rs @@ -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 { 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::::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 { + 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::::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() +} diff --git a/simdnbt/examples/hypixel_no_derive.rs b/simdnbt/examples/hypixel_no_derive.rs index e0de3c2..4660fe5 100644 --- a/simdnbt/examples/hypixel_no_derive.rs +++ b/simdnbt/examples/hypixel_no_derive.rs @@ -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>> { Some(items) } -fn nbt_from_items(items: Vec>) -> 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)); } } diff --git a/simdnbt/src/borrow/compound.rs b/simdnbt/src/borrow/compound.rs index f929797..f7c39c1 100644 --- a/simdnbt/src/borrow/compound.rs +++ b/simdnbt/src/borrow/compound.rs @@ -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()); } } } diff --git a/simdnbt/src/common.rs b/simdnbt/src/common.rs index f42db94..5e88780 100644 --- a/simdnbt/src/common.rs +++ b/simdnbt/src/common.rs @@ -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, width: usize, value: &'a [u8]) { +pub fn write_with_u32_length(data: &mut Vec, 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, 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(s: &[T]) -> &[u8] { - unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, s.len() * std::mem::size_of::()) } + 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. This will return the data as big endian (the diff --git a/simdnbt/src/error.rs b/simdnbt/src/error.rs index 4aec5c0..56a18c2 100644 --- a/simdnbt/src/error.rs +++ b/simdnbt/src/error.rs @@ -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), } diff --git a/simdnbt/src/mutf8.rs b/simdnbt/src/mutf8.rs index ad01043..b35993e 100644 --- a/simdnbt/src/mutf8.rs +++ b/simdnbt/src/mutf8.rs @@ -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, } @@ -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; diff --git a/simdnbt/src/owned/compound.rs b/simdnbt/src/owned/compound.rs index a97b98b..36ff795 100644 --- a/simdnbt/src/owned/compound.rs +++ b/simdnbt/src/owned/compound.rs @@ -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::().map_err(|_| Error::UnexpectedEof)?), - )), - INT_ID => values.push(( - tag_name, - NbtTag::Int(data.read_i32::().map_err(|_| Error::UnexpectedEof)?), - )), - LONG_ID => values.push(( - tag_name, - NbtTag::Long(data.read_i64::().map_err(|_| Error::UnexpectedEof)?), - )), - FLOAT_ID => values.push(( - tag_name, - NbtTag::Float(data.read_f32::().map_err(|_| Error::UnexpectedEof)?), - )), - DOUBLE_ID => values.push(( - tag_name, - NbtTag::Double(data.read_f64::().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 { self.values.iter_mut().map(|(_, v)| v) } - pub fn into_iter(self) -> impl Iterator { - 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; + + fn into_iter(self) -> Self::IntoIter { + self.values.into_iter() + } +} diff --git a/simdnbt/src/owned/mod.rs b/simdnbt/src/owned/mod.rs index acad17c..dce03b3 100644 --- a/simdnbt/src/owned/mod.rs +++ b/simdnbt/src/owned/mod.rs @@ -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 { + 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) { match self { Nbt::Some(nbt) => nbt.write(data), @@ -61,6 +78,15 @@ impl Nbt { } } + pub fn write_unnamed(&self, data: &mut Vec) { + 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 { - 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 { - 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; + + 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, tag: NbtCompound) -> Self { let name = name.into(); @@ -132,14 +170,25 @@ impl BaseNbt { self.tag.write(data); } - pub fn into_iter(self) -> impl Iterator { - self.tag.into_iter() + pub fn write_unnamed(&self, data: &mut Vec) { + 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; + + 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::() } } + fn read_with_type(data: &mut Cursor<&[u8]>, tag_type: u8, depth: usize) -> Result { + match tag_type { + BYTE_ID => Ok(NbtTag::Byte( + data.read_i8().map_err(|_| Error::UnexpectedEof)?, + )), + SHORT_ID => Ok(NbtTag::Short( + data.read_i16::().map_err(|_| Error::UnexpectedEof)?, + )), + INT_ID => Ok(NbtTag::Int( + data.read_i32::().map_err(|_| Error::UnexpectedEof)?, + )), + LONG_ID => Ok(NbtTag::Long( + data.read_i64::().map_err(|_| Error::UnexpectedEof)?, + )), + FLOAT_ID => Ok(NbtTag::Float( + data.read_f32::().map_err(|_| Error::UnexpectedEof)?, + )), + DOUBLE_ID => Ok(NbtTag::Double( + data.read_f64::().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 { + 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) { + 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) { + 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 { match self { NbtTag::Byte(byte) => Some(*byte), diff --git a/simdnbt/src/raw_list.rs b/simdnbt/src/raw_list.rs index e91d17e..ea22f3d 100644 --- a/simdnbt/src/raw_list.rs +++ b/simdnbt/src/raw_list.rs @@ -20,6 +20,10 @@ impl<'a, T> RawList<'a, T> { pub fn len(&self) -> usize { self.data.len() / std::mem::size_of::() } + + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } } impl RawList<'_, T> { diff --git a/simdnbt/src/traits.rs b/simdnbt/src/traits.rs index 5ddf90c..bf3a289 100644 --- a/simdnbt/src/traits.rs +++ b/simdnbt/src/traits.rs @@ -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 Deserialize for HashMap ToNbtTag for T { } } +impl FromNbtTag for crate::owned::NbtTag { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + 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 { @@ -167,6 +182,51 @@ impl ToNbtTag for String { } } +// unsigned integers +impl FromNbtTag for u8 { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + 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 { + 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 { + 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 { + 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 { fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { @@ -203,10 +263,7 @@ impl ToNbtTag for Option { panic!("Called to_nbt_tag on Option. Use to_optional_nbt_tag instead.") } fn to_optional_nbt_tag(self) -> Option { - 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 }) + } +}