1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 23:44:40 +00:00

add rustfmt.toml for comment wrapping and import sorting

This commit is contained in:
mat 2025-01-19 08:17:45 +00:00
commit 47131f4d9e
16 changed files with 116 additions and 93 deletions

2
rustfmt.toml Normal file
View file

@ -0,0 +1,2 @@
wrap_comments = true
group_imports = "StdExternalCrate"

View file

@ -127,7 +127,8 @@ fn simdnbt_items_from_nbt(nbt: simdnbt::borrow::BaseNbt) -> Option<Vec<Option<It
.and_then(|textures| textures.compounds()) .and_then(|textures| textures.compounds())
.and_then(|textures| textures.first()) .and_then(|textures| textures.first())
.and_then(|texture| texture.string("Value")) .and_then(|texture| texture.string("Value"))
// the real program does some base64+json decoding here but that's unnecessary for the benchmark // the real program does some base64+json decoding here but that's unnecessary for
// the benchmark
.map(|value| value.to_string()), .map(|value| value.to_string()),
skyblock_id: item_extra_attributes skyblock_id: item_extra_attributes
.and_then(|e| e.string("id")) .and_then(|e| e.string("id"))
@ -300,7 +301,8 @@ fn graphite_items_from_nbt(nbt: graphite_binary::nbt::NBT) -> Option<Vec<Option<
.and_then(|properties| nbt.find(properties, "textures")) .and_then(|properties| nbt.find(properties, "textures"))
.and_then(|textures| nbt.iter(textures)?.next()) .and_then(|textures| nbt.iter(textures)?.next())
.and_then(|texture| nbt.find(texture, "Value")) .and_then(|texture| nbt.find(texture, "Value"))
// the real program does some base64+json decoding here but that's unnecessary for the benchmark // the real program does some base64+json decoding here but that's unnecessary for
// the benchmark
.and_then(|value| value.as_string().cloned()), .and_then(|value| value.as_string().cloned()),
skyblock_id: item_extra_attributes skyblock_id: item_extra_attributes
.and_then(|e| nbt.find(e, "id")) .and_then(|e| nbt.find(e, "id"))

View file

@ -1,10 +1,11 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
use flate2::read::GzDecoder;
use std::{ use std::{
fs::File, fs::File,
io::{Cursor, Read}, io::{Cursor, Read},
}; };
use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput};
use flate2::read::GzDecoder;
fn bench_file(filename: &str, c: &mut Criterion) { fn bench_file(filename: &str, c: &mut Criterion) {
let mut file = File::open(format!("tests/{filename}")).unwrap(); let mut file = File::open(format!("tests/{filename}")).unwrap();
let mut contents = Vec::new(); let mut contents = Vec::new();

View file

@ -59,7 +59,8 @@ fn items_from_nbt(nbt: BaseNbt) -> Option<Vec<Option<Item>>> {
.and_then(|textures| textures.compounds()) .and_then(|textures| textures.compounds())
.and_then(|textures| textures.first()) .and_then(|textures| textures.first())
.and_then(|texture| texture.string("Value")) .and_then(|texture| texture.string("Value"))
// the real program does some base64+json decoding here but that's unnecessary for the benchmark // the real program does some base64+json decoding here but that's unnecessary for
// the benchmark
.map(|value| value.to_string()), .map(|value| value.to_string()),
skyblock_id: item_extra_attributes skyblock_id: item_extra_attributes
.and_then(|e| e.string("id")) .and_then(|e| e.string("id"))

View file

@ -1,5 +1,11 @@
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use super::{
extra_tapes::ExtraTapes,
list::{self, NbtList},
tape::{TapeElement, TapeTagKind, UnalignedU16},
NbtTag, Tapes,
};
use crate::{ use crate::{
common::{ common::{
extend_unchecked, push_unchecked, read_int_array, read_long_array, read_string, extend_unchecked, push_unchecked, read_int_array, read_long_array, read_string,
@ -12,13 +18,6 @@ use crate::{
Mutf8Str, Mutf8Str,
}; };
use super::{
extra_tapes::ExtraTapes,
list::{self, NbtList},
tape::{TapeElement, TapeTagKind, UnalignedU16},
NbtTag, Tapes,
};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct NbtCompound<'a: 'tape, 'tape> { pub struct NbtCompound<'a: 'tape, 'tape> {
pub(crate) element: *const TapeElement, // includes the initial compound element pub(crate) element: *const TapeElement, // includes the initial compound element
@ -51,8 +50,8 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
for (name, tag) in self.iter() { for (name, tag) in self.iter() {
// reserve 4 bytes extra so we can avoid reallocating for small tags // reserve 4 bytes extra so we can avoid reallocating for small tags
data.reserve(1 + 2 + name.len() + 4); data.reserve(1 + 2 + name.len() + 4);
// SAFETY: We just reserved enough space for the tag ID, the name length, the name, and // SAFETY: We just reserved enough space for the tag ID, the name length, the
// 4 bytes of tag data. // name, and 4 bytes of tag data.
unsafe { unsafe {
push_unchecked(data, tag.id()); push_unchecked(data, tag.id());
write_string_unchecked(data, name); write_string_unchecked(data, name);

View file

@ -14,8 +14,8 @@ impl Debug for ExtraTapes<'_> {
} }
pub union ExtraTapeElement<'a> { pub union ExtraTapeElement<'a> {
/// An indicator for how long the following list is. This is what we point to from /// An indicator for how long the following list is. This is what we point
/// `TapeTagValue`. /// to from `TapeTagValue`.
pub length: u32, pub length: u32,
pub byte_array: &'a [u8], pub byte_array: &'a [u8],
pub string: &'a Mutf8Str, pub string: &'a Mutf8Str,

View file

@ -1,5 +1,11 @@
use std::{marker::PhantomData, mem}; use std::{marker::PhantomData, mem};
use super::{
compound::{ParsingStack, ParsingStackElement},
extra_tapes::{ExtraTapeElement, ExtraTapes},
tape::{TapeElement, TapeTagKind, UnalignedU32},
NbtCompound, Tapes,
};
use crate::{ use crate::{
common::{ common::{
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array, read_i8_array, read_int_array, read_long_array, read_string, read_u8_array,
@ -14,13 +20,6 @@ use crate::{
Mutf8Str, Mutf8Str,
}; };
use super::{
compound::{ParsingStack, ParsingStackElement},
extra_tapes::{ExtraTapeElement, ExtraTapes},
tape::{TapeElement, TapeTagKind, UnalignedU32},
NbtCompound, Tapes,
};
/// A list of NBT tags of a single type. /// A list of NBT tags of a single type.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct NbtList<'a: 'tape, 'tape> { pub struct NbtList<'a: 'tape, 'tape> {
@ -281,8 +280,9 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
} }
} }
/// Returns whether the list is specifically a list with the `empty` tag type. This will return /// Returns whether the list is specifically a list with the `empty` tag
/// false if the list is any other type (even it has a length of zero). /// type. This will return false if the list is any other type (even it
/// has a length of zero).
pub fn empty(&self) -> bool { pub fn empty(&self) -> bool {
self.element().kind() == TapeTagKind::EmptyList self.element().kind() == TapeTagKind::EmptyList
} }
@ -365,10 +365,12 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
Some(NbtListList { Some(NbtListList {
iter: NbtListListIter { iter: NbtListListIter {
current_tape_offset: 0, // it's an iterator, it starts at 0 // it's an iterator, it starts at 0
current_tape_offset: 0,
max_tape_offset: max_tape_offset as usize, max_tape_offset: max_tape_offset as usize,
approx_length, approx_length,
tape: unsafe { self.element.add(1) }, // the first element is the listlist element so we don't include it // the first element is the listlist element so we don't include it
tape: unsafe { self.element.add(1) },
extra_tapes: self.extra_tapes, extra_tapes: self.extra_tapes,
_phantom: PhantomData, _phantom: PhantomData,
}, },
@ -546,8 +548,9 @@ impl<'a, 'tape> NbtListList<'a, 'tape> {
pub fn approx_len(&self) -> u32 { pub fn approx_len(&self) -> u32 {
self.iter.approx_len() self.iter.approx_len()
} }
/// Get the element at the given index. This is O(n) where n is index, so if you'll be calling /// Get the element at the given index. This is O(n) where n is index, so if
/// this more than once you should probably just use the iterator. /// you'll be calling this more than once you should probably just use
/// the iterator.
pub fn get(&self, index: usize) -> Option<NbtList<'a, 'tape>> { pub fn get(&self, index: usize) -> Option<NbtList<'a, 'tape>> {
self.iter.clone().nth(index) self.iter.clone().nth(index)
} }

View file

@ -1,4 +1,5 @@
//! The borrowed variant of NBT. This is useful if you're only reading data and you can keep a reference to the original buffer. //! The borrowed variant of NBT. This is useful if you're only reading data and
//! you can keep a reference to the original buffer.
mod compound; mod compound;
mod extra_tapes; mod extra_tapes;
@ -14,15 +15,6 @@ use byteorder::ReadBytesExt;
use compound::ParsingStackElementKind; use compound::ParsingStackElementKind;
use tape::{UnalignedU16, UnalignedU32, UnalignedU64}; use tape::{UnalignedU16, UnalignedU32, UnalignedU64};
use crate::{
common::{
read_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, SHORT_ID, STRING_ID,
},
reader::{Reader, ReaderFromCursor},
Error, Mutf8Str,
};
use self::{ use self::{
compound::{read_tag_in_compound, ParsingStack, ParsingStackElement}, compound::{read_tag_in_compound, ParsingStack, ParsingStackElement},
extra_tapes::ExtraTapes, extra_tapes::ExtraTapes,
@ -33,8 +25,17 @@ pub use self::{
compound::{NbtCompound, NbtCompoundIter}, compound::{NbtCompound, NbtCompoundIter},
list::{NbtCompoundList, NbtCompoundListIter, NbtList, NbtListList, NbtListListIter}, list::{NbtCompoundList, NbtCompoundListIter, NbtList, NbtListList, NbtListListIter},
}; };
use crate::{
common::{
read_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, SHORT_ID, STRING_ID,
},
reader::{Reader, ReaderFromCursor},
Error, Mutf8Str,
};
/// Read a normal root NBT compound. This is either empty or has a name and compound tag. /// Read a normal root NBT compound. This is either empty or has a name and
/// compound tag.
/// ///
/// Returns `Ok(Nbt::None)` if there is no data. /// Returns `Ok(Nbt::None)` if there is no data.
pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> { pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
@ -63,11 +64,11 @@ pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
Ok(Nbt::Some(BaseNbt { name, tapes })) Ok(Nbt::Some(BaseNbt { name, tapes }))
} }
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading /// Read a root NBT compound, but without reading the name. This is used in
/// NBT over the network. /// Minecraft when reading NBT over the network.
/// ///
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either /// This is similar to [`read_tag`], but returns an [`Nbt`] instead
/// empty or a compound). /// (guaranteeing it'll be either empty or a compound).
pub fn read_unnamed<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> { pub fn read_unnamed<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?; let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID { if root_type == END_ID {
@ -100,8 +101,9 @@ pub fn read_compound<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtCompound<
Ok(BaseNbtCompound { tapes }) Ok(BaseNbtCompound { tapes })
} }
/// 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 /// Read an NBT tag, without reading its name. This may be any type of tag
/// handle end tags, use [`read_optional_tag`]. /// except for an end tag. If you need to be able to handle end tags, use
/// [`read_optional_tag`].
pub fn read_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtTag<'a>, Error> { pub fn read_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtTag<'a>, Error> {
let mut tapes = Tapes::new(); let mut tapes = Tapes::new();
let mut stack = ParsingStack::new(); let mut stack = ParsingStack::new();
@ -117,7 +119,8 @@ pub fn read_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtTag<'a>, Error
Ok(BaseNbtTag { tapes }) Ok(BaseNbtTag { tapes })
} }
/// Read any NBT tag, without reading its name. This may be any type of tag, including an end tag. /// Read any NBT tag, without reading its name. This may be any type of tag,
/// including an end tag.
/// ///
/// Returns `Ok(None)` if there is no data. /// Returns `Ok(None)` if there is no data.
pub fn read_optional_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Option<BaseNbtTag<'a>>, Error> { pub fn read_optional_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Option<BaseNbtTag<'a>>, Error> {
@ -242,8 +245,8 @@ impl Debug for BaseNbt<'_> {
} }
} }
/// A nameless NBT container. This only contains a compound tag. This contains a `TagAllocator`, /// A nameless NBT container. This only contains a compound tag. This contains a
/// so it can exist independently from a [`BaseNbt`]. /// `TagAllocator`, so it can exist independently from a [`BaseNbt`].
pub struct BaseNbtCompound<'a> { pub struct BaseNbtCompound<'a> {
tapes: Tapes<'a>, tapes: Tapes<'a>,
} }
@ -710,14 +713,16 @@ mod tests {
#[test] #[test]
fn list_of_empty_lists() { fn list_of_empty_lists() {
// found from fuzzing // found from fuzzing
// BaseNbt { name: m"", tag: NbtTag::NbtCompound { m"": NbtTag::List(List::List([List::Empty])) } } // 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]; let data = [10, 0, 0, 9, 0, 0, 9, 0, 0, 0, 1, 0, 9, 0, 0, 0, 0];
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap(); let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
nbt.as_compound().to_owned(); nbt.as_compound().to_owned();
} }
#[test] #[test]
fn list_of_byte_arrays() { fn list_of_byte_arrays() {
// BaseNbt { name: m"", tag: NbtCompound { values: [(m"", List(List([List::ByteArray([])])))] } } // 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]; let data = [10, 0, 0, 9, 0, 0, 9, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0];
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap(); let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
nbt.as_compound().to_owned(); nbt.as_compound().to_owned();

View file

@ -166,7 +166,8 @@ impl TapeElement {
pub fn new_with_ptr<T>(kind: TapeTagKind, ptr: *const T) -> Self { pub fn new_with_ptr<T>(kind: TapeTagKind, ptr: *const T) -> Self {
Self(((kind as u64) << 56) | ptr as u64) Self(((kind as u64) << 56) | ptr as u64)
} }
/// Create a new TapeElement with the given kind and everything else set to 0. /// Create a new TapeElement with the given kind and everything else set to
/// 0.
pub fn new_with_0(kind: TapeTagKind) -> Self { pub fn new_with_0(kind: TapeTagKind) -> Self {
Self((kind as u64) << 56) Self((kind as u64) << 56)
} }
@ -176,10 +177,12 @@ impl TapeElement {
} }
impl TapeElement { impl TapeElement {
/// Returns how much we should increment the tape index to get to the next tag. /// Returns how much we should increment the tape index to get to the next
/// tag.
/// ///
/// # Safety /// # Safety
/// The element must be a tag and not something else like a continuation of a long or double. /// The element must be a tag and not something else like a continuation of
/// a long or double.
pub unsafe fn skip_offset(&self) -> usize { pub unsafe fn skip_offset(&self) -> usize {
match self.kind() { match self.kind() {
TapeTagKind::Compound | TapeTagKind::ListList | TapeTagKind::CompoundList => { TapeTagKind::Compound | TapeTagKind::ListList | TapeTagKind::CompoundList => {

View file

@ -95,18 +95,21 @@ pub fn write_string(data: &mut Vec<u8>, value: &Mutf8Str) {
} }
} }
/// Write a string to a Vec<u8> without checking if the Vec has enough capacity. /// Write a string to a Vec<u8> without checking if the Vec has enough capacity.
/// This is unsafe because it can cause a buffer overflow if the Vec doesn't have enough capacity. /// This is unsafe because it can cause a buffer overflow if the Vec doesn't
/// have enough capacity.
/// ///
/// # Safety /// # Safety
/// ///
/// You must reserve enough capacity (2 + value.len()) in the Vec before calling this function. /// You must reserve enough capacity (2 + value.len()) in the Vec before calling
/// this function.
#[inline] #[inline]
pub unsafe fn write_string_unchecked(data: &mut Vec<u8>, value: &Mutf8Str) { pub unsafe fn write_string_unchecked(data: &mut Vec<u8>, value: &Mutf8Str) {
extend_unchecked(data, &(value.len() as u16).to_be_bytes()); extend_unchecked(data, &(value.len() as u16).to_be_bytes());
extend_unchecked(data, value.as_bytes()); extend_unchecked(data, value.as_bytes());
} }
/// Extend a Vec<u8> with a slice of u8 without checking if the Vec has enough capacity. /// Extend a Vec<u8> with a slice of u8 without checking if the Vec has enough
/// capacity.
/// ///
/// This optimization is barely measurable, but it does make it slightly faster! /// This optimization is barely measurable, but it does make it slightly faster!
/// ///
@ -129,15 +132,16 @@ pub unsafe fn push_unchecked(data: &mut Vec<u8>, value: u8) {
data.set_len(len + 1); data.set_len(len + 1);
} }
/// Convert a slice of any type into a slice of u8. This will probably return the data as little /// Convert a slice of any type into a slice of u8. This will probably return
/// endian! Use [`slice_into_u8_big_endian`] to get big endian (the endianness that's used in NBT). /// the data as little endian! Use [`slice_into_u8_big_endian`] to get big
/// endian (the endianness that's used in NBT).
#[inline] #[inline]
pub fn slice_into_u8_native_endian<T>(s: &[T]) -> &[u8] { pub fn slice_into_u8_native_endian<T>(s: &[T]) -> &[u8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, mem::size_of_val(s)) } unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, mem::size_of_val(s)) }
} }
/// Convert a slice of any type into a Vec<u8>. This will return the data as big endian (the /// Convert a slice of any type into a Vec<u8>. This will return the data as big
/// endianness that's used in NBT). /// endian (the endianness that's used in NBT).
#[inline] #[inline]
pub fn slice_into_u8_big_endian<T: SwappableNumber>(s: &[T]) -> Vec<u8> { pub fn slice_into_u8_big_endian<T: SwappableNumber>(s: &[T]) -> Vec<u8> {
swap_endianness_as_u8::<T>(slice_into_u8_native_endian(s)) swap_endianness_as_u8::<T>(slice_into_u8_native_endian(s))

View file

@ -20,9 +20,8 @@ mod traits;
pub use error::{DeserializeError, Error}; pub use error::{DeserializeError, Error};
pub use mutf8::Mutf8Str; pub use mutf8::Mutf8Str;
pub use traits::{Deserialize, FromNbtTag, Serialize, ToNbtTag};
pub use simdnbt_derive::*; pub use simdnbt_derive::*;
pub use traits::{Deserialize, FromNbtTag, Serialize, ToNbtTag};
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -9,7 +9,8 @@ use std::{
use simd_cesu8::mutf8; use simd_cesu8::mutf8;
/// A MUTF-8 string slice. This is how strings are represented internally in NBT. /// A MUTF-8 string slice. This is how strings are represented internally in
/// NBT.
#[derive(Eq, PartialEq)] #[derive(Eq, PartialEq)]
pub struct Mutf8Str { pub struct Mutf8Str {
pub(crate) slice: [u8], pub(crate) slice: [u8],

View file

@ -1,5 +1,6 @@
use std::mem::{self, MaybeUninit}; use std::mem::{self, MaybeUninit};
use super::{list::NbtList, NbtTag};
use crate::{ use crate::{
common::{push_unchecked, read_string, write_string_unchecked, END_ID, MAX_DEPTH}, common::{push_unchecked, read_string, write_string_unchecked, END_ID, MAX_DEPTH},
error::NonRootError, error::NonRootError,
@ -8,8 +9,6 @@ use crate::{
Mutf8Str, ToNbtTag, Mutf8Str, ToNbtTag,
}; };
use super::{list::NbtList, NbtTag};
/// A list of named tags. The order of the tags is preserved. /// A list of named tags. The order of the tags is preserved.
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
pub struct NbtCompound { pub struct NbtCompound {
@ -81,8 +80,8 @@ impl NbtCompound {
for (name, tag) in &self.values { for (name, tag) in &self.values {
// reserve 4 bytes extra so we can avoid reallocating for small tags // reserve 4 bytes extra so we can avoid reallocating for small tags
data.reserve(1 + 2 + name.len() + 4); data.reserve(1 + 2 + name.len() + 4);
// SAFETY: We just reserved enough space for the tag ID, the name length, the name, and // SAFETY: We just reserved enough space for the tag ID, the name length, the
// 4 bytes of tag data. // name, and 4 bytes of tag data.
unsafe { unsafe {
push_unchecked(data, tag.id()); push_unchecked(data, tag.id());
write_string_unchecked(data, name); write_string_unchecked(data, name);

View file

@ -1,10 +1,11 @@
use super::{compound::NbtCompound, MAX_DEPTH};
use crate::{ use crate::{
common::{ common::{
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array, extend_unchecked, push_unchecked, read_i8_array, read_int_array, read_long_array,
read_with_u32_length, slice_i8_into_u8, slice_into_u8_big_endian, extend_unchecked, read_string, read_u8_array, read_with_u32_length, slice_i8_into_u8,
push_unchecked, write_string, write_u32, write_with_u32_length, BYTE_ARRAY_ID, BYTE_ID, slice_into_u8_big_endian, write_string, write_u32, write_with_u32_length, BYTE_ARRAY_ID,
COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID,
LONG_ID, SHORT_ID, STRING_ID, LONG_ARRAY_ID, LONG_ID, SHORT_ID, STRING_ID,
}, },
error::NonRootError, error::NonRootError,
mutf8::Mutf8String, mutf8::Mutf8String,
@ -12,8 +13,6 @@ use crate::{
swap_endianness::swap_endianness, swap_endianness::swap_endianness,
}; };
use super::{compound::NbtCompound, MAX_DEPTH};
/// A list of NBT tags of a single type. /// A list of NBT tags of a single type.
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]

View file

@ -1,11 +1,12 @@
//! The owned variant of NBT. This is useful if you're writing NBT or if you can't keep a reference //! The owned variant of NBT. This is useful if you're writing NBT or if you
//! to the original data. //! can't keep a reference to the original data.
mod compound; mod compound;
mod list; mod list;
use std::{io::Cursor, ops::Deref}; use std::{io::Cursor, ops::Deref};
pub use self::{compound::NbtCompound, list::NbtList};
use crate::{ use crate::{
common::{ common::{
extend_unchecked, push_unchecked, read_int_array, read_long_array, read_string, extend_unchecked, push_unchecked, read_int_array, read_long_array, read_string,
@ -19,20 +20,19 @@ use crate::{
Error, Mutf8Str, Error, Mutf8Str,
}; };
pub use self::{compound::NbtCompound, list::NbtList}; /// Read a normal root NBT compound. This is either empty or has a name and
/// compound tag.
/// Read a normal root NBT compound. This is either empty or has a name and compound tag.
/// ///
/// Returns `Ok(Nbt::None)` if there is no data. /// Returns `Ok(Nbt::None)` if there is no data.
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> { pub fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
let mut reader = ReaderFromCursor::new(data); let mut reader = ReaderFromCursor::new(data);
Nbt::read(&mut reader) Nbt::read(&mut reader)
} }
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading /// Read a root NBT compound, but without reading the name. This is used in
/// NBT over the network. /// Minecraft when reading NBT over the network.
/// ///
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either /// This is similar to [`read_tag`], but returns an [`Nbt`] instead
/// empty or a compound). /// (guaranteeing it'll be either empty or a compound).
pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> { pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
let mut reader = ReaderFromCursor::new(data); let mut reader = ReaderFromCursor::new(data);
Nbt::read_unnamed(&mut reader) Nbt::read_unnamed(&mut reader)
@ -42,13 +42,15 @@ pub fn read_compound(data: &mut Cursor<&[u8]>) -> Result<NbtCompound, NonRootErr
let mut reader = ReaderFromCursor::new(data); let mut reader = ReaderFromCursor::new(data);
NbtCompound::read(&mut reader) NbtCompound::read(&mut reader)
} }
/// Read an NBT tag, without reading its name. This may be any type of tag except for an end tag. If you need to be able to /// Read an NBT tag, without reading its name. This may be any type of tag
/// handle end tags, use [`read_optional_tag`]. /// except for an end tag. If you need to be able to handle end tags, use
/// [`read_optional_tag`].
pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<NbtTag, NonRootError> { pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<NbtTag, NonRootError> {
let mut reader = ReaderFromCursor::new(data); let mut reader = ReaderFromCursor::new(data);
NbtTag::read(&mut reader) NbtTag::read(&mut reader)
} }
/// Read any NBT tag, without reading its name. This may be any type of tag, including an end tag. /// Read any NBT tag, without reading its name. This may be any type of tag,
/// including an end tag.
/// ///
/// Returns `Ok(None)` if there is no data. /// Returns `Ok(None)` if there is no data.
pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<Option<NbtTag>, NonRootError> { pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<Option<NbtTag>, NonRootError> {
@ -75,7 +77,8 @@ impl Nbt {
Self::Some(BaseNbt { name, tag }) Self::Some(BaseNbt { name, tag })
} }
/// Reads NBT from the given data. Returns `Ok(Nbt::None)` if there is no data. /// Reads NBT from the given data. Returns `Ok(Nbt::None)` if there is no
/// data.
fn read(data: &mut Reader<'_>) -> Result<Nbt, Error> { fn read(data: &mut Reader<'_>) -> Result<Nbt, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?; let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID { if root_type == END_ID {
@ -322,8 +325,9 @@ impl NbtTag {
/// ///
/// # Safety /// # Safety
/// ///
/// This function is unsafe because it doesn't check that there's enough space in the data. /// This function is unsafe because it doesn't check that there's enough
/// 4 bytes MUST be reserved before calling this function. /// space in the data. 4 bytes MUST be reserved before calling this
/// function.
#[inline] #[inline]
unsafe fn write_without_tag_type_unchecked(&self, data: &mut Vec<u8>) { unsafe fn write_without_tag_type_unchecked(&self, data: &mut Vec<u8>) {
match self { match self {

View file

@ -260,8 +260,8 @@ fn swap_endianness_64bit(bytes: &mut [u8], num: usize) {
} }
} }
/// Swap the endianness of the given array (unless we're on a big-endian system) in-place depending /// Swap the endianness of the given array (unless we're on a big-endian system)
/// on the width of the given type. /// in-place depending on the width of the given type.
fn swap_endianness_from_type<T: SwappableNumber>(items: &mut [u8]) { fn swap_endianness_from_type<T: SwappableNumber>(items: &mut [u8]) {
let item_width = mem::size_of::<T>(); let item_width = mem::size_of::<T>();
let length = items.len() / item_width; let length = items.len() / item_width;
@ -298,7 +298,8 @@ pub fn swap_endianness<T: SwappableNumber>(data: &[u8]) -> Vec<T> {
let mut vec_u8: Vec<u8> = { let mut vec_u8: Vec<u8> = {
let ptr = vec_t.as_mut_ptr() as *mut u8; let ptr = vec_t.as_mut_ptr() as *mut u8;
mem::forget(vec_t); mem::forget(vec_t);
// SAFETY: the new capacity is correct since we checked that data.len() is a multiple of width_of_t // SAFETY: the new capacity is correct since we checked that data.len() is a
// multiple of width_of_t
unsafe { Vec::from_raw_parts(ptr, 0, data.len()) } unsafe { Vec::from_raw_parts(ptr, 0, data.len()) }
}; };
vec_u8.extend_from_slice(data); vec_u8.extend_from_slice(data);