1
0
Fork 0
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:
mat 2023-11-19 16:13:36 -06:00
parent 57dfc2c1a9
commit 820cb1f334
11 changed files with 441 additions and 179 deletions

View file

@ -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();

View file

@ -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()
}

View file

@ -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));
}
}

View file

@ -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());
}
}
}

View file

@ -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

View file

@ -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>),
}

View file

@ -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;

View file

@ -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()
}
}

View file

@ -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),

View file

@ -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> {

View file

@ -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 })
}
}