1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 07:26:04 +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
parent acb56cd97a
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.first())
.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()),
skyblock_id: item_extra_attributes
.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(|textures| nbt.iter(textures)?.next())
.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()),
skyblock_id: item_extra_attributes
.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::{
fs::File,
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) {
let mut file = File::open(format!("tests/{filename}")).unwrap();
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.first())
.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()),
skyblock_id: item_extra_attributes
.and_then(|e| e.string("id"))

View file

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

View file

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

View file

@ -1,5 +1,11 @@
use std::{marker::PhantomData, mem};
use super::{
compound::{ParsingStack, ParsingStackElement},
extra_tapes::{ExtraTapeElement, ExtraTapes},
tape::{TapeElement, TapeTagKind, UnalignedU32},
NbtCompound, Tapes,
};
use crate::{
common::{
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array,
@ -14,13 +20,6 @@ use crate::{
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.
#[derive(Clone, Copy, Debug)]
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
/// false if the list is any other type (even it has a length of zero).
/// Returns whether the list is specifically a list with the `empty` tag
/// type. This will return false if the list is any other type (even it
/// has a length of zero).
pub fn empty(&self) -> bool {
self.element().kind() == TapeTagKind::EmptyList
}
@ -365,10 +365,12 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
Some(NbtListList {
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,
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,
_phantom: PhantomData,
},
@ -546,8 +548,9 @@ impl<'a, 'tape> NbtListList<'a, 'tape> {
pub fn approx_len(&self) -> u32 {
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
/// this more than once you should probably just use the iterator.
/// Get the element at the given index. This is O(n) where n is index, so if
/// 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>> {
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 extra_tapes;
@ -14,15 +15,6 @@ use byteorder::ReadBytesExt;
use compound::ParsingStackElementKind;
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::{
compound::{read_tag_in_compound, ParsingStack, ParsingStackElement},
extra_tapes::ExtraTapes,
@ -33,8 +25,17 @@ pub use self::{
compound::{NbtCompound, NbtCompoundIter},
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.
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 }))
}
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading
/// NBT over the network.
/// Read a root NBT compound, but without reading the name. This is used in
/// Minecraft when reading NBT over the network.
///
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either
/// empty or a compound).
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead
/// (guaranteeing it'll be either empty or a compound).
pub fn read_unnamed<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
@ -100,8 +101,9 @@ pub fn read_compound<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtCompound<
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
/// handle end tags, use [`read_optional_tag`].
/// Read an NBT tag, without reading its name. This may be any type of tag
/// except for an end tag. If you need to be able to handle end tags, use
/// [`read_optional_tag`].
pub fn read_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtTag<'a>, Error> {
let mut tapes = Tapes::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 })
}
/// 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.
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`,
/// so it can exist independently from a [`BaseNbt`].
/// A nameless NBT container. This only contains a compound tag. This contains a
/// `TagAllocator`, so it can exist independently from a [`BaseNbt`].
pub struct BaseNbtCompound<'a> {
tapes: Tapes<'a>,
}
@ -710,14 +713,16 @@ mod tests {
#[test]
fn list_of_empty_lists() {
// 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 nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
nbt.as_compound().to_owned();
}
#[test]
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 nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
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 {
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 {
Self((kind as u64) << 56)
}
@ -176,10 +177,12 @@ 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
/// 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 {
match self.kind() {
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.
/// 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
///
/// 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]
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.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!
///
@ -129,15 +132,16 @@ pub unsafe fn push_unchecked(data: &mut Vec<u8>, value: u8) {
data.set_len(len + 1);
}
/// Convert a slice of any type into a slice of u8. This will probably return the data as little
/// endian! Use [`slice_into_u8_big_endian`] to get big endian (the endianness that's used in NBT).
/// Convert a slice of any type into a slice of u8. This will probably return
/// the data as little 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, mem::size_of_val(s)) }
}
/// Convert a slice of any type into a Vec<u8>. This will return the data as big endian (the
/// endianness that's used in NBT).
/// Convert a slice of any type into a Vec<u8>. This will return the data as big
/// endian (the endianness that's used in NBT).
#[inline]
pub fn slice_into_u8_big_endian<T: SwappableNumber>(s: &[T]) -> Vec<u8> {
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 mutf8::Mutf8Str;
pub use traits::{Deserialize, FromNbtTag, Serialize, ToNbtTag};
pub use simdnbt_derive::*;
pub use traits::{Deserialize, FromNbtTag, Serialize, ToNbtTag};
#[cfg(test)]
mod tests {

View file

@ -9,7 +9,8 @@ use std::{
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)]
pub struct Mutf8Str {
pub(crate) slice: [u8],

View file

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

View file

@ -1,10 +1,11 @@
use super::{compound::NbtCompound, MAX_DEPTH};
use crate::{
common::{
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array,
read_with_u32_length, slice_i8_into_u8, slice_into_u8_big_endian, extend_unchecked,
push_unchecked, write_string, write_u32, write_with_u32_length, 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,
extend_unchecked, push_unchecked, read_i8_array, read_int_array, read_long_array,
read_string, read_u8_array, read_with_u32_length, slice_i8_into_u8,
slice_into_u8_big_endian, write_string, write_u32, write_with_u32_length, 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,
},
error::NonRootError,
mutf8::Mutf8String,
@ -12,8 +13,6 @@ use crate::{
swap_endianness::swap_endianness,
};
use super::{compound::NbtCompound, MAX_DEPTH};
/// A list of NBT tags of a single type.
#[repr(u8)]
#[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
//! to the original data.
//! The owned variant of NBT. This is useful if you're writing NBT or if you
//! can't keep a reference to the original data.
mod compound;
mod list;
use std::{io::Cursor, ops::Deref};
pub use self::{compound::NbtCompound, list::NbtList};
use crate::{
common::{
extend_unchecked, push_unchecked, read_int_array, read_long_array, read_string,
@ -19,20 +20,19 @@ use crate::{
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.
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
let mut reader = ReaderFromCursor::new(data);
Nbt::read(&mut reader)
}
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading
/// NBT over the network.
/// Read a root NBT compound, but without reading the name. This is used in
/// Minecraft when reading NBT over the network.
///
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either
/// empty or a compound).
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead
/// (guaranteeing it'll be either empty or a compound).
pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
let mut reader = ReaderFromCursor::new(data);
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);
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
/// handle end tags, use [`read_optional_tag`].
/// Read an NBT tag, without reading its name. This may be any type of tag
/// except for an end tag. If you need to be able to handle end tags, use
/// [`read_optional_tag`].
pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<NbtTag, NonRootError> {
let mut reader = ReaderFromCursor::new(data);
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.
pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<Option<NbtTag>, NonRootError> {
@ -75,7 +77,8 @@ impl Nbt {
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> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
@ -322,8 +325,9 @@ impl NbtTag {
///
/// # 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.
/// 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.
#[inline]
unsafe fn write_without_tag_type_unchecked(&self, data: &mut Vec<u8>) {
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
/// on the width of the given type.
/// Swap the endianness of the given array (unless we're on a big-endian system)
/// in-place depending on the width of the given type.
fn swap_endianness_from_type<T: SwappableNumber>(items: &mut [u8]) {
let item_width = mem::size_of::<T>();
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 ptr = vec_t.as_mut_ptr() as *mut u8;
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()) }
};
vec_u8.extend_from_slice(data);