1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 07:26:04 +00:00

use a u64 instead of a union for TapeElement

This commit is contained in:
mat 2024-07-27 03:34:59 +00:00
parent 16c67d2743
commit 6d23e4e481
5 changed files with 339 additions and 427 deletions

View file

@ -15,7 +15,7 @@ use crate::{
use super::{
extra_tapes::ExtraTapes,
list::{self, NbtList},
tape::{TapeElement, TapeTagKind, TapeTagValue, UnalignedU16},
tape::{TapeElement, TapeTagKind, UnalignedU16},
NbtTag, Tapes,
};
@ -37,15 +37,12 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
stack.push(ParsingStackElement::Compound {
index_of_compound_element: index_of_compound_element as u32,
})?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::Compound,
TapeTagValue {
// this gets overwritten later
compound: (0.into(), 0.into()),
},
),
});
tapes.main.push(TapeElement::new_with_approx_len_and_offset(
TapeTagKind::Compound,
// these get overwritten later
0,
0,
));
Ok(())
}
@ -128,15 +125,15 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
}
/// Get the tape element kind and value for this compound.
fn element(&self) -> (TapeTagKind, TapeTagValue) {
unsafe { (*self.element).kind }
fn element(&self) -> TapeElement {
unsafe { *self.element }
}
pub fn iter(&self) -> CompoundIter<'a, 'tape> {
let (kind, value) = self.element();
debug_assert_eq!(kind, TapeTagKind::Compound);
let el = self.element();
debug_assert_eq!(el.kind(), TapeTagKind::Compound);
let max_tape_offset = u32::from(unsafe { value.list_list.1 }) as usize;
let max_tape_offset = el.approx_len_and_offset().1 as usize;
let tape_slice =
unsafe { std::slice::from_raw_parts(self.element.add(1), max_tape_offset) };
@ -155,18 +152,18 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
/// want to avoid that.
pub fn len(&self) -> usize {
let len = self.approx_len();
if len < 2usize.pow(24) {
len
if len < 2u32.pow(24) {
len as usize
} else {
self.iter().count()
}
}
/// A version of [`Self::len`] that saturates at 2^24.
pub fn approx_len(self) -> usize {
let (kind, value) = self.element();
debug_assert_eq!(kind, TapeTagKind::Compound);
unsafe { u32::from(value.list_list.0) as usize }
pub fn approx_len(self) -> u32 {
let el = self.element();
debug_assert_eq!(el.kind(), TapeTagKind::Compound);
el.approx_len_and_offset().0
}
pub fn is_empty(&self) -> bool {
@ -212,11 +209,10 @@ impl<'a: 'tape, 'tape> Iterator for CompoundIter<'a, 'tape> {
return None;
}
let name_length_ptr = unsafe { self.tape[self.current_tape_offset].name };
let name_length_ptr = name_length_ptr as *const UnalignedU16;
let name_length_ptr = self.tape[self.current_tape_offset].u64() as *const UnalignedU16;
let name_length = u16::from(unsafe { *name_length_ptr }).swap_bytes();
let name_pointer = unsafe { name_length_ptr.add(1) as *const u8 };
let name_slice = unsafe { std::slice::from_raw_parts(name_pointer, name_length as usize) };
let name_ptr = unsafe { name_length_ptr.add(1) as *const u8 };
let name_slice = unsafe { std::slice::from_raw_parts(name_ptr, name_length as usize) };
let name = Mutf8Str::from_slice(name_slice);
self.current_tape_offset += 1;
@ -338,94 +334,75 @@ pub(crate) fn read_tag<'a>(
match tag_type {
BYTE_ID => {
let byte = data.read_i8()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Byte, TapeTagValue { byte }),
});
tapes
.main
.push(TapeElement::new_with_u8(TapeTagKind::Byte, byte as u8));
}
SHORT_ID => {
let short = data.read_i16()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Short, TapeTagValue { short }),
});
tapes
.main
.push(TapeElement::new_with_u16(TapeTagKind::Short, short as u16));
}
INT_ID => {
let int = data.read_i32()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Int, TapeTagValue { int }),
});
tapes
.main
.push(TapeElement::new_with_u32(TapeTagKind::Int, int as u32));
}
LONG_ID => {
let long = data.read_i64()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Long, TapeTagValue { long: () }),
});
tapes.main.push(TapeElement { long });
tapes.main.push(TapeElement::new_with_0(TapeTagKind::Long));
tapes.main.push(TapeElement::new(long as u64));
}
FLOAT_ID => {
let float = data.read_f32()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Float, TapeTagValue { float }),
});
tapes
.main
.push(TapeElement::new_with_u32(TapeTagKind::Float, float as u32));
}
DOUBLE_ID => {
let double = data.read_f64()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Double, TapeTagValue { double: () }),
});
tapes.main.push(TapeElement { double });
tapes
.main
.push(TapeElement::new_with_0(TapeTagKind::Double));
tapes.main.push(TapeElement::new(double as u64));
}
BYTE_ARRAY_ID => {
let byte_array_pointer = data.cur as u64;
let byte_array_ptr = data.cur;
read_with_u32_length(data, 1)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::ByteArray,
TapeTagValue {
byte_array: byte_array_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::ByteArray,
byte_array_ptr,
));
}
STRING_ID => {
let string_pointer = data.cur as u64;
let string_ptr = data.cur;
// assert that the top 8 bits of the pointer are 0 (because we rely on this)
debug_assert_eq!(string_pointer >> 56, 0);
debug_assert_eq!(string_ptr as u64 >> 56, 0);
read_string(data)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::String,
TapeTagValue {
string: string_pointer.into(),
},
),
});
tapes
.main
.push(TapeElement::new_with_ptr(TapeTagKind::String, string_ptr));
}
INT_ARRAY_ID => {
let int_array_pointer = data.cur as u64;
let int_array_ptr = data.cur;
read_int_array(data)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::IntArray,
TapeTagValue {
int_array: int_array_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::IntArray,
int_array_ptr,
));
}
LONG_ARRAY_ID => {
let long_array_pointer = data.cur as u64;
let long_array_ptr = data.cur;
read_long_array(data)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::LongArray,
TapeTagValue {
long_array: long_array_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::LongArray,
long_array_ptr,
));
}
_ => return Err(NonRootError::unknown_tag_id(tag_type)),
};
@ -444,12 +421,10 @@ pub(crate) fn read_tag_in_compound<'a>(
return Ok(());
}
let tag_name_pointer = data.cur as u64;
debug_assert_eq!(tag_name_pointer >> 56, 0);
let tag_name_ptr = data.cur;
debug_assert_eq!(tag_name_ptr as u64 >> 56, 0);
read_string(data)?;
tapes.main.push(TapeElement {
name: tag_name_pointer,
});
tapes.main.push(TapeElement::new(tag_name_ptr as u64));
read_tag(data, tapes, stack, tag_type)
}
@ -468,16 +443,13 @@ fn handle_compound_end(tapes: &mut Tapes, stack: &mut ParsingStack) {
tapes
.main
.get_unchecked_mut(index_of_compound_element as usize)
.kind
.1
.compound
.1 = (index_after_end_element as u32 - index_of_compound_element).into();
.set_offset(index_after_end_element as u32 - index_of_compound_element);
};
}
pub(crate) fn write_tag(tag: NbtTag, data: &mut Vec<u8>) {
let (kind, value) = tag.element();
match kind {
let el = tag.element();
match el.kind() {
TapeTagKind::Byte => unsafe {
unchecked_push(data, tag.byte().unwrap() as u8);
},
@ -507,7 +479,7 @@ pub(crate) fn write_tag(tag: NbtTag, data: &mut Vec<u8>) {
let string = tag.string().unwrap();
write_string(data, string);
}
_ if kind.is_list() => {
kind if kind.is_list() => {
tag.list().unwrap().write(data);
}
TapeTagKind::Compound => {
@ -515,7 +487,7 @@ pub(crate) fn write_tag(tag: NbtTag, data: &mut Vec<u8>) {
}
TapeTagKind::IntArray => {
let int_array =
unsafe { list::u32_prefixed_list_to_rawlist_unchecked::<i32>(value).unwrap() };
unsafe { list::u32_prefixed_list_to_rawlist_unchecked::<i32>(el.ptr()).unwrap() };
unsafe {
unchecked_extend(data, &int_array.len().to_be_bytes());
}
@ -523,12 +495,12 @@ pub(crate) fn write_tag(tag: NbtTag, data: &mut Vec<u8>) {
}
TapeTagKind::LongArray => {
let long_array =
unsafe { list::u32_prefixed_list_to_rawlist_unchecked::<i64>(value).unwrap() };
unsafe { list::u32_prefixed_list_to_rawlist_unchecked::<i64>(el.ptr()).unwrap() };
unsafe {
unchecked_extend(data, &long_array.len().to_be_bytes());
}
data.extend_from_slice(long_array.as_big_endian());
}
_ => unreachable!("Invalid tag kind {kind:?}"),
kind => unreachable!("Invalid tag kind {kind:?}"),
}
}

View file

@ -17,7 +17,7 @@ use crate::{
use super::{
compound::{ParsingStack, ParsingStackElement},
extra_tapes::{ExtraTapeElement, ExtraTapes},
tape::{TapeElement, TapeTagKind, TapeTagValue, UnalignedU32},
tape::{TapeElement, TapeTagKind, UnalignedU32},
NbtCompound, Tapes,
};
@ -38,92 +38,64 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
END_ID => {
// the length is unused for this type of lists
data.skip(4)?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::EmptyList, TapeTagValue { empty_list: () }),
});
tapes
.main
.push(TapeElement::new_with_0(TapeTagKind::EmptyList));
}
BYTE_ID => {
let byte_list_pointer = data.cur as u64;
let byte_list_ptr = data.cur;
let _ = read_i8_array(data)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::ByteList,
TapeTagValue {
byte_list: byte_list_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::ByteList,
byte_list_ptr,
));
}
SHORT_ID => {
let short_list_pointer = data.cur as u64;
let short_list_ptr = data.cur;
read_with_u32_length(data, 2)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::ShortList,
TapeTagValue {
short_list: short_list_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::ShortList,
short_list_ptr,
));
}
INT_ID => {
let int_list_pointer = data.cur as u64;
let int_list_ptr = data.cur;
read_with_u32_length(data, 4)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::IntList,
TapeTagValue {
int_list: int_list_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::IntList,
int_list_ptr,
));
}
LONG_ID => {
let long_list_pointer = data.cur as u64;
let long_list_ptr = data.cur;
read_with_u32_length(data, 8)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::LongList,
TapeTagValue {
long_list: long_list_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::LongList,
long_list_ptr,
));
}
FLOAT_ID => {
let float_list_pointer = data.cur as u64;
let float_list_ptr = data.cur;
read_with_u32_length(data, 4)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::FloatList,
TapeTagValue {
float_list: float_list_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::FloatList,
float_list_ptr,
));
}
DOUBLE_ID => {
let double_list_pointer = data.cur as u64;
let double_list_ptr = data.cur;
read_with_u32_length(data, 8)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::DoubleList,
TapeTagValue {
double_list: double_list_pointer.into(),
},
),
});
tapes.main.push(TapeElement::new_with_ptr(
TapeTagKind::DoubleList,
double_list_ptr,
));
}
BYTE_ARRAY_ID => {
let index_of_element = tapes.extra.elements.len() as u32;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::ByteArrayList,
TapeTagValue {
byte_array_list: (0.into(), index_of_element.into()),
},
),
});
tapes.main.push(TapeElement::new_with_u32(
TapeTagKind::ByteArrayList,
index_of_element,
));
let length = data.read_u32()?;
tapes.extra.elements.push(ExtraTapeElement { length });
@ -134,14 +106,10 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
}
STRING_ID => {
let index_of_element = tapes.extra.elements.len() as u32;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::StringList,
TapeTagValue {
string_list: (0.into(), index_of_element.into()),
},
),
});
tapes.main.push(TapeElement::new_with_u32(
TapeTagKind::StringList,
index_of_element,
));
let length = data.read_u32()?;
tapes.extra.elements.push(ExtraTapeElement { length });
@ -159,15 +127,12 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
index_of_list_element: index_of_list_element as u32,
})?;
stack.set_list_length(length);
tapes.main.push(TapeElement {
kind: (
TapeTagKind::ListList,
TapeTagValue {
// can't know the offset until after
list_list: (length.into(), 0.into()),
},
),
});
tapes.main.push(TapeElement::new_with_approx_len_and_offset(
TapeTagKind::ListList,
length,
// can't know the offset until after
0,
));
}
COMPOUND_ID => {
let length = data.read_u32()?;
@ -178,26 +143,19 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
index_of_list_element: index_of_list_element as u32,
})?;
stack.set_list_length(length);
tapes.main.push(TapeElement {
kind: (
TapeTagKind::CompoundList,
TapeTagValue {
// this gets overwritten after the list is fully read
compound_list: (length.into(), 0.into()),
},
),
});
tapes.main.push(TapeElement::new_with_approx_len_and_offset(
TapeTagKind::CompoundList,
length,
// this gets overwritten after the list is fully read
0,
));
}
INT_ARRAY_ID => {
let index_of_element = tapes.extra.elements.len() as u32;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::IntArrayList,
TapeTagValue {
int_array_list: (0.into(), index_of_element.into()),
},
),
});
tapes.main.push(TapeElement::new_with_u32(
TapeTagKind::IntArrayList,
index_of_element,
));
let length = data.read_u32()?;
tapes.extra.elements.push(ExtraTapeElement { length });
for _ in 0..length {
@ -207,14 +165,10 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
}
LONG_ARRAY_ID => {
let index_of_element = tapes.extra.elements.len() as u32;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::LongArrayList,
TapeTagValue {
long_array_list: (0.into(), index_of_element.into()),
},
),
});
tapes.main.push(TapeElement::new_with_u32(
TapeTagKind::LongArrayList,
index_of_element.into(),
));
let length = data.read_u32()?;
tapes.extra.elements.push(ExtraTapeElement { length });
for _ in 0..length {
@ -228,11 +182,11 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
}
pub fn write(&self, data: &mut Vec<u8>) {
let (kind, _) = self.element();
let el = self.element();
data.push(self.id());
match kind {
match el.kind() {
TapeTagKind::EmptyList => {
data.extend(&0u32.to_be_bytes());
}
@ -326,14 +280,14 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
}
/// Get the tape element kind and value for this list.
fn element(&self) -> (TapeTagKind, TapeTagValue) {
unsafe { (*self.element).kind }
fn element(&self) -> TapeElement {
unsafe { *self.element }
}
/// Get the numerical ID of the tag type.
#[inline]
pub fn id(&self) -> u8 {
match self.element().0 {
match self.element().kind() {
TapeTagKind::EmptyList => END_ID,
TapeTagKind::ByteList => BYTE_ID,
TapeTagKind::ShortList => SHORT_ID,
@ -354,15 +308,15 @@ 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).
pub fn empty(&self) -> bool {
self.element().0 == TapeTagKind::EmptyList
self.element().kind() == TapeTagKind::EmptyList
}
pub fn bytes(&self) -> Option<&[i8]> {
let (kind, value) = self.element();
if kind != TapeTagKind::ByteList {
let el = self.element();
if el.kind() != TapeTagKind::ByteList {
return None;
}
let length_ptr = u64::from(unsafe { value.byte_list }) as usize as *const UnalignedU32;
let length_ptr = el.ptr::<UnalignedU32>();
let length = unsafe { u32::from(*length_ptr).swap_bytes() as usize };
let byte_array =
unsafe { std::slice::from_raw_parts(length_ptr.add(1) as *const i8, length) };
@ -384,11 +338,11 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
u32_prefixed_list_to_vec(TapeTagKind::DoubleList, self.element)
}
pub fn byte_arrays(&self) -> Option<&'a [&'a [u8]]> {
let (kind, value) = self.element();
if kind != TapeTagKind::ByteArrayList {
let el = self.element();
if el.kind() != TapeTagKind::ByteArrayList {
return None;
}
let index_to_extra_tapes = u32::from(unsafe { value.byte_array_list.1 }) as usize;
let index_to_extra_tapes = el.u32() as usize;
let length_ref = &self.extra_tapes.elements[index_to_extra_tapes];
let length = unsafe { length_ref.length as usize };
let slice = unsafe {
@ -404,11 +358,11 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
Some(slice)
}
pub fn strings(&self) -> Option<&'a [&'a Mutf8Str]> {
let (kind, value) = self.element();
if kind != TapeTagKind::StringList {
let el = self.element();
if el.kind() != TapeTagKind::StringList {
return None;
}
let index_to_extra_tapes = u32::from(unsafe { value.string_list.1 }) as usize;
let index_to_extra_tapes = el.u32() as usize;
let length_ref = &self.extra_tapes.elements[index_to_extra_tapes];
let length = unsafe { length_ref.length as usize };
let slice = unsafe {
@ -424,19 +378,18 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
Some(slice)
}
pub fn lists(&self) -> Option<ListList<'a, 'tape>> {
let (kind, value) = self.element();
if kind != TapeTagKind::ListList {
let el = self.element();
if el.kind() != TapeTagKind::ListList {
return None;
}
let length = u32::from(unsafe { value.list_list.0 }) as usize;
let max_tape_offset = u32::from(unsafe { value.list_list.1 }) as usize;
let (approx_length, max_tape_offset) = el.approx_len_and_offset();
Some(ListList {
iter: ListListIter {
current_tape_offset: 0, // it's an iterator, it starts at 0
max_tape_offset,
approx_length: length,
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
extra_tapes: self.extra_tapes,
_phantom: PhantomData,
@ -445,14 +398,14 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
}
pub fn compounds(&self) -> Option<CompoundList<'a, 'tape>> {
let (kind, value) = self.element();
if kind != TapeTagKind::CompoundList {
let el = self.element();
if el.kind() != TapeTagKind::CompoundList {
return None;
}
let length = u32::from(unsafe { value.compound_list.0 }) as usize;
let (approx_length, max_tape_offset) = el.approx_len_and_offset();
let max_tape_offset = max_tape_offset as usize;
let max_tape_offset = u32::from(unsafe { value.compound_list.1 }) as usize;
let tape_slice =
unsafe { std::slice::from_raw_parts(self.element.add(1), max_tape_offset) };
@ -460,18 +413,18 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
iter: CompoundListIter {
current_tape_offset: 0,
max_tape_offset,
approx_length: length,
approx_length,
tape: tape_slice,
extra_tapes: self.extra_tapes,
},
})
}
pub fn int_arrays(&self) -> Option<&[RawList<i32>]> {
let (kind, value) = self.element();
if kind != TapeTagKind::IntArrayList {
let el = self.element();
if el.kind() != TapeTagKind::IntArrayList {
return None;
}
let index_to_extra_tapes = u32::from(unsafe { value.int_array_list.1 }) as usize;
let index_to_extra_tapes = el.u32() as usize;
let length_ref = &self.extra_tapes.elements[index_to_extra_tapes];
let length = unsafe { length_ref.length as usize };
let slice = unsafe {
@ -487,11 +440,11 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
Some(slice)
}
pub fn long_arrays(&self) -> Option<&[RawList<i64>]> {
let (kind, value) = self.element();
if kind != TapeTagKind::LongArrayList {
let el = self.element();
if el.kind() != TapeTagKind::LongArrayList {
return None;
}
let index_to_extra_tapes = u32::from(unsafe { value.long_array_list.1 }) as usize;
let index_to_extra_tapes = el.u32() as usize;
let length_ref = &self.extra_tapes.elements[index_to_extra_tapes];
let length = unsafe { length_ref.length as usize };
let slice = unsafe {
@ -508,9 +461,9 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
}
pub fn to_owned(&self) -> crate::owned::NbtList {
let (kind, _value) = self.element();
let el = self.element();
match kind {
match el.kind() {
TapeTagKind::EmptyList => crate::owned::NbtList::Empty,
TapeTagKind::ByteList => crate::owned::NbtList::Byte(self.bytes().unwrap().to_vec()),
TapeTagKind::ShortList => crate::owned::NbtList::Short(self.shorts().unwrap().to_vec()),
@ -569,12 +522,12 @@ impl<'a, 'tape> NbtList<'a, 'tape> {
impl PartialEq for NbtList<'_, '_> {
fn eq(&self, other: &Self) -> bool {
let (self_kind, _) = self.element();
let (other_kind, _) = other.element();
if self_kind != other_kind {
let self_el = self.element();
let other_el = other.element();
if self_el.kind() != other_el.kind() {
return false;
}
match self_kind {
match self_el.kind() {
TapeTagKind::EmptyList => true,
TapeTagKind::ByteList => self.bytes().unwrap() == other.bytes().unwrap(),
TapeTagKind::ShortList => self.shorts().unwrap() == other.shorts().unwrap(),
@ -612,7 +565,7 @@ impl<'a, 'tape> ListList<'a, 'tape> {
self.iter.len()
}
/// A version of [`Self::len`] that saturates at 2^24.
pub fn approx_len(&self) -> usize {
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
@ -654,7 +607,7 @@ impl PartialEq for ListList<'_, '_> {
pub struct ListListIter<'a, 'tape> {
current_tape_offset: usize,
max_tape_offset: usize,
approx_length: usize,
approx_length: u32,
tape: *const TapeElement,
extra_tapes: *const ExtraTapes<'a>,
_phantom: PhantomData<&'tape ()>,
@ -667,15 +620,15 @@ impl<'a: 'tape, 'tape> ListListIter<'a, 'tape> {
/// want to avoid that.
pub fn len(self) -> usize {
let len = self.approx_len();
if len < 2usize.pow(24) {
len
if len < 2u32.pow(24) {
len as usize
} else {
self.count()
}
}
/// A version of [`Self::len`] that saturates at 2^24.
pub fn approx_len(&self) -> usize {
pub fn approx_len(&self) -> u32 {
self.approx_length
}
}
@ -687,19 +640,18 @@ impl<'a: 'tape, 'tape> Iterator for ListListIter<'a, 'tape> {
return None;
}
let element = unsafe { self.tape.add(self.current_tape_offset) };
// println!("{:?}", unsafe { *element });
let (kind, value) = unsafe { (*element).kind };
debug_assert!(kind.is_list());
let el_ptr = unsafe { self.tape.add(self.current_tape_offset) };
let el = unsafe { *el_ptr };
debug_assert!(el.kind().is_list());
let offset = if matches!(kind, TapeTagKind::CompoundList | TapeTagKind::ListList) {
u32::from(unsafe { value.list_list.1 }) as usize
let offset = if matches!(el.kind(), TapeTagKind::CompoundList | TapeTagKind::ListList) {
el.u32() as usize
} else {
1
};
let nbt_list = NbtList {
element,
element: el_ptr,
extra_tapes: unsafe { &*self.extra_tapes },
};
@ -736,7 +688,7 @@ impl<'a, 'tape> CompoundList<'a, 'tape> {
self.iter.len()
}
/// A version of [`Self::len`] that saturates at 2^24.
pub fn approx_len(&self) -> usize {
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
@ -779,7 +731,7 @@ impl PartialEq for CompoundList<'_, '_> {
pub struct CompoundListIter<'a, 'tape> {
current_tape_offset: usize,
max_tape_offset: usize,
approx_length: usize,
approx_length: u32,
tape: &'tape [TapeElement],
extra_tapes: *const ExtraTapes<'a>,
}
@ -791,15 +743,15 @@ impl<'a: 'tape, 'tape> CompoundListIter<'a, 'tape> {
/// want to avoid that.
pub fn len(self) -> usize {
let len = self.approx_len();
if len < 2usize.pow(24) {
len
if len < 2u32.pow(24) {
len as usize
} else {
self.count()
}
}
/// A version of [`Self::len`] that saturates at 2^24.
pub fn approx_len(&self) -> usize {
pub fn approx_len(&self) -> u32 {
self.approx_length
}
}
@ -811,14 +763,14 @@ impl<'a: 'tape, 'tape> Iterator for CompoundListIter<'a, 'tape> {
return None;
}
let element = unsafe { self.tape.as_ptr().add(self.current_tape_offset) };
let (kind, value) = unsafe { (*element).kind };
debug_assert_eq!(kind, TapeTagKind::Compound);
let el_ptr = unsafe { self.tape.as_ptr().add(self.current_tape_offset) };
let el = unsafe { *el_ptr };
debug_assert_eq!(el.kind(), TapeTagKind::Compound);
let offset = u32::from(unsafe { value.list_list.1 }) as usize;
let offset = el.u32() as usize;
let compound = NbtCompound {
element,
element: el_ptr,
extra_tapes: unsafe { &*self.extra_tapes },
};
@ -841,32 +793,30 @@ impl Default for CompoundListIter<'_, '_> {
pub(crate) fn u32_prefixed_list_to_rawlist<'a, T>(
expected_kind: TapeTagKind,
element: *const TapeElement,
el_ptr: *const TapeElement,
) -> Option<RawList<'a, T>>
where
T: Copy + SwappableNumber,
{
let (kind, value) = unsafe { (*element).kind };
if kind != expected_kind {
let el = unsafe { *el_ptr };
if el.kind() != expected_kind {
return None;
}
unsafe { u32_prefixed_list_to_rawlist_unchecked(value) }
unsafe { u32_prefixed_list_to_rawlist_unchecked(el.ptr()) }
}
#[inline]
pub(crate) unsafe fn u32_prefixed_list_to_rawlist_unchecked<'a, T>(
value: TapeTagValue,
ptr: *const UnalignedU32,
) -> Option<RawList<'a, T>>
where
T: Copy + SwappableNumber,
{
// length is always a u32
let length_ptr = u64::from(unsafe { value.int_list }) as usize as *const UnalignedU32;
let length = unsafe { u32::from(*length_ptr).swap_bytes() as usize };
let length = unsafe { u32::from(*ptr).swap_bytes() as usize };
let length_in_bytes = length * std::mem::size_of::<T>();
let array_be =
unsafe { std::slice::from_raw_parts(length_ptr.add(1) as *const u8, length_in_bytes) };
let array_be = unsafe { std::slice::from_raw_parts(ptr.add(1) as *const u8, length_in_bytes) };
Some(RawList::new(array_be))
}
@ -903,10 +853,7 @@ pub fn read_list_in_list<'a>(
tapes
.main
.get_unchecked_mut(index_of_list_element as usize)
.kind
.1
.list_list
.1 = (index_after_end_element as u32 - index_of_list_element).into();
.set_offset(index_after_end_element as u32 - index_of_list_element);
};
return Ok(());
}
@ -939,10 +886,7 @@ pub(crate) fn read_compound_in_list<'a>(
tapes
.main
.get_unchecked_mut(index_of_list_element as usize)
.kind
.1
.compound_list
.1 = (index_after_end_element as u32 - index_of_list_element).into();
.set_offset(index_after_end_element as u32 - index_of_list_element);
};
return Ok(());
}

View file

@ -11,7 +11,7 @@ use std::{
};
use byteorder::ReadBytesExt;
use tape::UnalignedU32;
use tape::{UnalignedU16, UnalignedU32};
use crate::{
common::{
@ -27,7 +27,7 @@ use self::{
compound::{read_tag_in_compound, ParsingStack, ParsingStackElement},
extra_tapes::ExtraTapes,
list::{read_compound_in_list, read_list_in_list},
tape::{MainTape, TapeElement, TapeTagKind, TapeTagValue, UnalignedU16},
tape::{MainTape, TapeElement, TapeTagKind},
};
/// Read a normal root NBT compound. This is either empty or has a name and compound tag.
@ -50,15 +50,12 @@ pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
stack.push(ParsingStackElement::Compound {
index_of_compound_element: 0,
})?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::Compound,
TapeTagValue {
// this gets overwritten later
compound: (0.into(), 0.into()),
},
),
});
tapes.main.push(TapeElement::new_with_approx_len_and_offset(
TapeTagKind::Compound,
// these get overwritten later
0,
0,
));
read_with_stack(&mut data, &mut tapes, &mut stack)?;
@ -91,15 +88,12 @@ pub fn read_compound<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtCompound<
stack.push(ParsingStackElement::Compound {
index_of_compound_element: 0,
})?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::Compound,
TapeTagValue {
// this gets overwritten later
compound: (0.into(), 0.into()),
},
),
});
tapes.main.push(TapeElement::new_with_approx_len_and_offset(
TapeTagKind::Compound,
// these get overwritten later
0,
0,
));
read_with_stack(&mut data, &mut tapes, &mut stack)?;
@ -350,7 +344,7 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
/// Get the numerical ID of the tag type.
#[inline]
pub fn id(&self) -> u8 {
match self.element().0 {
match self.element().kind() {
TapeTagKind::Byte => BYTE_ID,
TapeTagKind::Short => SHORT_ID,
TapeTagKind::Int => INT_ID,
@ -368,74 +362,62 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
}
pub fn byte(&self) -> Option<i8> {
let (kind, value) = self.element();
if kind != TapeTagKind::Byte {
return None;
}
Some(unsafe { value.byte })
let el = self.element();
ensure_kind(el, TapeTagKind::Byte)?;
Some(el.u8() as i8)
}
pub fn short(&self) -> Option<i16> {
let (kind, value) = self.element();
if kind != TapeTagKind::Short {
return None;
}
Some(unsafe { value.short })
let el = self.element();
ensure_kind(el, TapeTagKind::Short)?;
Some(el.u16() as i16)
}
pub fn int(&self) -> Option<i32> {
let (kind, value) = unsafe { (*self.element).kind };
if kind != TapeTagKind::Int {
return None;
}
Some(unsafe { value.int })
let el = self.element();
ensure_kind(el, TapeTagKind::Int)?;
Some(el.u32() as i32)
}
pub fn long(&self) -> Option<i64> {
let (kind, _) = self.element();
if kind != TapeTagKind::Long {
return None;
}
let el = self.element();
ensure_kind(el, TapeTagKind::Long)?;
// the value is in the next element because longs are too big to fit in a single element
let value = unsafe { self.element.add(1) };
Some(unsafe { (*value).long })
let value_el = unsafe { *self.element.add(1) };
Some(value_el.u64() as i64)
}
pub fn float(&self) -> Option<f32> {
let (kind, value) = self.element();
if kind != TapeTagKind::Float {
return None;
}
Some(unsafe { value.float })
let el = self.element();
ensure_kind(el, TapeTagKind::Float)?;
Some(el.u32() as f32)
}
pub fn double(&self) -> Option<f64> {
let (kind, _) = self.element();
if kind != TapeTagKind::Double {
return None;
}
let el = self.element();
ensure_kind(el, TapeTagKind::Double)?;
// the value is in the next element because doubles are too big to fit in a single element
let value = unsafe { self.element.add(1) };
Some(unsafe { (*value).double })
let value_el = unsafe { *self.element.add(1) };
Some(value_el.u64() as f64)
}
pub fn byte_array(&self) -> Option<&'a [u8]> {
let (kind, value) = self.element();
if kind != TapeTagKind::ByteArray {
return None;
}
let length_ptr = unsafe { u64::from(value.byte_array) as *const UnalignedU32 };
let length = unsafe { u32::from(*length_ptr).swap_bytes() as usize };
let el = self.element();
ensure_kind(el, TapeTagKind::ByteArray)?;
let length_ptr = el.ptr::<UnalignedU32>();
let length = u32::from(unsafe { *length_ptr });
#[cfg(target_endian = "little")]
let length = length.swap_bytes();
let data_ptr = unsafe { length_ptr.add(1) as *const u8 };
Some(unsafe { std::slice::from_raw_parts(data_ptr, length) })
Some(unsafe { std::slice::from_raw_parts(data_ptr, length as usize) })
}
pub fn string(&self) -> Option<&'a Mutf8Str> {
let (kind, value) = self.element();
if kind != TapeTagKind::String {
return None;
}
let length_ptr = unsafe { u64::from(value.string) as usize as *const UnalignedU16 };
let length = unsafe { u16::from(*length_ptr).swap_bytes() as usize };
let el = self.element();
ensure_kind(el, TapeTagKind::String)?;
let length_ptr = el.ptr::<UnalignedU16>();
let length = u16::from(unsafe { *length_ptr });
#[cfg(target_endian = "little")]
let length = length.swap_bytes();
let data_ptr = unsafe { length_ptr.add(1) as *const u8 };
Some(unsafe { Mutf8Str::from_slice(std::slice::from_raw_parts(data_ptr, length)) })
Some(unsafe { Mutf8Str::from_slice(std::slice::from_raw_parts(data_ptr, length as usize)) })
}
pub fn list(&self) -> Option<NbtList<'a, 'tape>> {
let (kind, _) = self.element();
if !kind.is_list() {
let el = self.element();
if !el.kind().is_list() {
return None;
}
@ -445,10 +427,8 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
})
}
pub fn compound(&self) -> Option<NbtCompound<'a, 'tape>> {
let (kind, _) = self.element();
if kind != TapeTagKind::Compound {
return None;
}
let el = self.element();
ensure_kind(el, TapeTagKind::Compound)?;
Some(NbtCompound {
element: self.element,
@ -462,15 +442,15 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
list::u32_prefixed_list_to_vec(TapeTagKind::LongArray, self.element)
}
/// Get the tape element kind and value for this tag.
fn element(&self) -> (TapeTagKind, TapeTagValue) {
unsafe { (*self.element).kind }
/// Get the tape element for this tag.
fn element(&self) -> TapeElement {
unsafe { *self.element }
}
pub fn to_owned(&self) -> crate::owned::NbtTag {
let (kind, _value) = self.element();
let el = self.element();
match kind {
match el.kind() {
TapeTagKind::Byte => crate::owned::NbtTag::Byte(self.byte().unwrap()),
TapeTagKind::Short => crate::owned::NbtTag::Short(self.short().unwrap()),
TapeTagKind::Int => crate::owned::NbtTag::Int(self.int().unwrap()),
@ -484,7 +464,7 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
TapeTagKind::Compound => {
crate::owned::NbtTag::Compound(self.compound().unwrap().to_owned())
}
_ if kind.is_list() => crate::owned::NbtTag::List(self.list().unwrap().to_owned()),
kind if kind.is_list() => crate::owned::NbtTag::List(self.list().unwrap().to_owned()),
TapeTagKind::IntArray => crate::owned::NbtTag::IntArray(self.int_array().unwrap()),
TapeTagKind::LongArray => crate::owned::NbtTag::LongArray(self.long_array().unwrap()),
_ => unreachable!(),
@ -492,14 +472,22 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
}
}
fn ensure_kind(el: TapeElement, other: TapeTagKind) -> Option<()> {
if el.kind() != other {
return None;
} else {
Some(())
}
}
impl PartialEq for NbtTag<'_, '_> {
fn eq(&self, other: &Self) -> bool {
let (self_kind, _) = self.element();
let (other_kind, _) = other.element();
if self_kind != other_kind {
let self_el = self.element();
let other_el = other.element();
if self_el.kind() != other_el.kind() {
return false;
}
match self_kind {
match self_el.kind() {
TapeTagKind::Byte => self.byte().unwrap() == other.byte().unwrap(),
TapeTagKind::Short => self.short().unwrap() == other.short().unwrap(),
TapeTagKind::Int => self.int().unwrap() == other.int().unwrap(),

View file

@ -1,4 +1,7 @@
use std::fmt::{self, Debug};
use std::{
fmt::{self, Debug},
mem,
};
use crate::common::{
BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LONG_ARRAY_ID,
@ -39,41 +42,77 @@ impl Default for MainTape {
}
#[derive(Clone, Copy)]
#[repr(C)]
pub union TapeElement {
pub kind: (TapeTagKind, TapeTagValue),
pub struct TapeElement(u64);
pub long: i64,
pub double: f64,
impl TapeElement {
pub fn kind(self) -> TapeTagKind {
let kind_id = (self.0 >> 56) as u8;
unsafe { mem::transmute::<u8, TapeTagKind>(kind_id) }
}
pub fn u8(self) -> u8 {
self.0 as u8
}
pub fn u16(self) -> u16 {
self.0 as u16
}
pub fn u32(self) -> u32 {
self.0 as u32
}
pub fn u64(self) -> u64 {
self.0
}
pub name: u64, // pointer to the original data
pub fn approx_len_and_offset(self) -> (u32, u32) {
((self.0 >> 32) as u32 & 0xff_ffff, self.0 as u32)
}
pub fn ptr<T>(self) -> *const T {
(self.0 & 0xff_ffff_ffff_ffff) as *const T
}
pub fn new_with_approx_len_and_offset(kind: TapeTagKind, approx_len: u32, offset: u32) -> Self {
debug_assert!(approx_len < 2u32.pow(24));
Self((kind as u64) << 56 | (approx_len as u64) << 32 | (offset as u64))
}
pub fn set_offset(&mut self, offset: u32) {
*self = Self::new_with_approx_len_and_offset(
self.kind(),
self.approx_len_and_offset().0,
offset,
)
}
pub fn new_with_u8(kind: TapeTagKind, u8: u8) -> Self {
Self((kind as u64) << 56 | u8 as u64)
}
pub fn new_with_u16(kind: TapeTagKind, u16: u16) -> Self {
Self((kind as u64) << 56 | u16 as u64)
}
pub fn new_with_u32(kind: TapeTagKind, u32: u32) -> Self {
Self((kind as u64) << 56 | u32 as u64)
}
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.
pub fn new_with_0(kind: TapeTagKind) -> Self {
Self((kind as u64) << 56)
}
pub fn new(u64: u64) -> Self {
Self(u64)
}
}
impl TapeElement {
/// 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.
pub unsafe fn skip_offset(&self) -> usize {
match self.kind {
(TapeTagKind::Long | TapeTagKind::Double, _) => 2,
(
TapeTagKind::Compound,
TapeTagValue {
compound: (_, offset),
},
) => u32::from(offset) as usize,
(
TapeTagKind::ListList,
TapeTagValue {
list_list: (_, offset),
},
) => u32::from(offset) as usize,
(
TapeTagKind::CompoundList,
TapeTagValue {
compound_list: (_, offset),
},
) => u32::from(offset) as usize,
match self.kind() {
TapeTagKind::Long | TapeTagKind::Double => 2,
TapeTagKind::Compound | TapeTagKind::ListList | TapeTagKind::CompoundList => {
self.approx_len_and_offset().1 as usize
}
_ => 1,
}
}
@ -81,42 +120,11 @@ impl TapeElement {
impl Debug for TapeElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// just writes the u64
write!(f, "TapeElement({:#016x})", unsafe { self.name })?;
write!(f, "TapeElement({:#016x})", self.0)?;
Ok(())
}
}
#[repr(C, packed)]
#[derive(Copy, Clone)]
pub union TapeTagValue {
pub byte: i8,
pub short: i16,
pub int: i32,
pub long: (), // value is in next tape element
pub float: f32,
pub double: (), // value is in next tape element
pub byte_array: u56, // pointer to the original data
pub string: u56, // pointer to the original data
pub compound: (u24, UnalignedU32), // length estimate + tape index offset to the end of the compound
pub int_array: u56, // pointer to the original data
pub long_array: u56, // pointer to the original data
// lists
pub empty_list: (),
pub byte_list: u56, // pointer to the original data
pub short_list: u56, // pointer to the original data
pub int_list: u56, // pointer to the original data
pub long_list: u56, // pointer to the original data
pub float_list: u56, // pointer to the original data
pub double_list: u56, // pointer to the original data
pub byte_array_list: (u24, UnalignedU32), // padding + index to ExtraTapes which has a fat pointer that points to the original data
pub string_list: (u24, UnalignedU32), // padding + index to ExtraTapes
pub list_list: (u24, UnalignedU32), // length estimate + tape index offset to the end of the list
pub compound_list: (u24, UnalignedU32), // length estimate + tape index offset to the end of the list
pub int_array_list: (u24, UnalignedU32), // padding + index to ExtraTapes
pub long_array_list: (u24, UnalignedU32), // padding + index to ExtraTapes
}
#[derive(Debug, Copy, Clone)]
#[repr(packed)]
#[allow(non_camel_case_types)]

View file

@ -39,7 +39,7 @@ pub trait ToNbtTag: Sized {
impl<K: Display + FromStr + Eq + Hash, V: FromNbtTag> Deserialize for HashMap<K, V> {
fn from_compound(compound: crate::borrow::NbtCompound) -> Result<Self, DeserializeError> {
let mut hashmap = HashMap::with_capacity(compound.approx_len());
let mut hashmap = HashMap::with_capacity(compound.approx_len() as usize);
for (k, v) in compound.iter() {
let k_str = k.to_str();
@ -277,7 +277,7 @@ impl<T: Deserialize> FromNbtTag for Vec<Option<T>> {
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
let list = tag.list()?;
let list = list.compounds()?;
let mut vec = Vec::with_capacity(list.approx_len());
let mut vec = Vec::with_capacity(list.approx_len() as usize);
for tag in list {
if tag.is_empty() {
vec.push(None);
@ -306,7 +306,7 @@ impl<T: Deserialize> FromNbtTag for Vec<T> {
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
let list = tag.list()?;
let list = list.compounds()?;
let mut vec = Vec::with_capacity(list.approx_len());
let mut vec = Vec::with_capacity(list.approx_len() as usize);
for tag in list {
vec.push(T::from_compound(tag).ok()?);
}