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

implement thin slices

This commit is contained in:
mat 2024-05-13 23:09:25 +00:00
parent 39b68ea725
commit c69dc2300e
14 changed files with 409 additions and 93 deletions

View file

@ -7,6 +7,7 @@ use crate::{
read_string, unchecked_extend, unchecked_push, unchecked_write_string, write_string,
END_ID, MAX_DEPTH,
},
thin_slices::{Slice16Bit, Slice32Bit},
Error, Mutf8Str,
};
@ -15,7 +16,7 @@ use super::{list::NbtList, tag_alloc::TagAllocator, NbtTag};
/// A list of named tags. The order of the tags is preserved.
#[derive(Debug, Default, PartialEq, Clone)]
pub struct NbtCompound<'a> {
values: &'a [(&'a Mutf8Str, NbtTag<'a>)],
values: Slice32Bit<'a, [(Slice16Bit<'a, Mutf8Str>, NbtTag<'a>)]>,
}
impl<'a> NbtCompound<'a> {
@ -35,7 +36,8 @@ impl<'a> NbtCompound<'a> {
let mut tags = alloc.get().named.start(depth);
let mut tags_buffer = unsafe {
MaybeUninit::<[MaybeUninit<(&Mutf8Str, NbtTag<'a>)>; 4]>::uninit().assume_init()
MaybeUninit::<[MaybeUninit<(Slice16Bit<Mutf8Str>, NbtTag<'a>)>; 4]>::uninit()
.assume_init()
};
let mut tags_buffer_len: usize = 0;
@ -90,9 +92,9 @@ impl<'a> NbtCompound<'a> {
}
pub fn write(&self, data: &mut Vec<u8>) {
for (name, tag) in self.values {
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);
data.reserve(1 + 2 + name.len() as usize + 4);
// SAFETY: We just reserved enough space for the tag ID, the name length, the name, and
// 4 bytes of tag data.
unsafe {
@ -154,8 +156,8 @@ impl<'a> NbtCompound<'a> {
pub fn get(&self, name: &str) -> Option<&NbtTag<'a>> {
let name = Mutf8Str::from_str(name);
let name = name.as_ref();
for (key, value) in self.values {
if key == &name {
for (key, value) in &*self.values {
if &**key == name {
return Some(value);
}
}
@ -166,8 +168,8 @@ impl<'a> NbtCompound<'a> {
pub fn contains(&self, name: &str) -> bool {
let name = Mutf8Str::from_str(name);
let name = name.as_ref();
for (key, _) in self.values {
if key == &name {
for (key, _) in &*self.values {
if &**key == name {
return true;
}
}
@ -212,9 +214,9 @@ impl<'a> NbtCompound<'a> {
}
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag<'a>)> {
self.values.iter().map(|(k, v)| (*k, v))
self.values.iter().map(|(k, v)| (&**k, v))
}
pub fn len(&self) -> usize {
pub fn len(&self) -> u32 {
self.values.len()
}
pub fn is_empty(&self) -> bool {
@ -226,7 +228,7 @@ impl<'a> NbtCompound<'a> {
values: self
.values
.iter()
.map(|(k, v)| ((*k).to_owned(), v.to_owned()))
.map(|(k, v)| ((**k).to_owned(), v.to_owned()))
.collect(),
}
}

View file

@ -10,6 +10,7 @@ use crate::{
FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID, SHORT_ID, STRING_ID,
},
raw_list::RawList,
thin_slices::{Slice16Bit, Slice32Bit},
Error, Mutf8Str,
};
@ -21,18 +22,18 @@ use super::{read_u32, tag_alloc::TagAllocator, NbtCompound, MAX_DEPTH};
pub enum NbtList<'a> {
#[default]
Empty = END_ID,
Byte(&'a [i8]) = BYTE_ID,
Byte(Slice32Bit<'a, [i8]>) = BYTE_ID,
Short(RawList<'a, i16>) = SHORT_ID,
Int(RawList<'a, i32>) = INT_ID,
Long(RawList<'a, i64>) = LONG_ID,
Float(RawList<'a, f32>) = FLOAT_ID,
Double(RawList<'a, f64>) = DOUBLE_ID,
ByteArray(&'a [&'a [u8]]) = BYTE_ARRAY_ID,
String(&'a [&'a Mutf8Str]) = STRING_ID,
List(&'a [NbtList<'a>]) = LIST_ID,
Compound(&'a [NbtCompound<'a>]) = COMPOUND_ID,
IntArray(&'a [RawList<'a, i32>]) = INT_ARRAY_ID,
LongArray(&'a [RawList<'a, i64>]) = LONG_ARRAY_ID,
ByteArray(Slice32Bit<'a, [Slice32Bit<'a, [u8]>]>) = BYTE_ARRAY_ID,
String(Slice32Bit<'a, [Slice16Bit<'a, Mutf8Str>]>) = STRING_ID,
List(Slice32Bit<'a, [NbtList<'a>]>) = LIST_ID,
Compound(Slice32Bit<'a, [NbtCompound<'a>]>) = COMPOUND_ID,
IntArray(Slice32Bit<'a, [RawList<'a, i32>]>) = INT_ARRAY_ID,
LongArray(Slice32Bit<'a, [RawList<'a, i64>]>) = LONG_ARRAY_ID,
}
impl<'a> NbtList<'a> {
pub fn read(
@ -158,7 +159,7 @@ impl<'a> NbtList<'a> {
unchecked_push(data, COMPOUND_ID);
unchecked_extend(data, &(compounds.len() as u32).to_be_bytes());
}
for compound in *compounds {
for compound in &**compounds {
compound.write(data);
}
return;
@ -195,13 +196,13 @@ impl<'a> NbtList<'a> {
}
NbtList::String(strings) => {
write_u32(data, strings.len() as u32);
for string in *strings {
for string in &**strings {
write_string(data, string);
}
}
NbtList::List(lists) => {
write_u32(data, lists.len() as u32);
for list in *lists {
for list in &**lists {
list.write(data);
}
}
@ -210,13 +211,13 @@ impl<'a> NbtList<'a> {
}
NbtList::IntArray(int_arrays) => {
write_u32(data, int_arrays.len() as u32);
for array in *int_arrays {
for array in &**int_arrays {
write_with_u32_length(data, 4, array.as_big_endian());
}
}
NbtList::LongArray(long_arrays) => {
write_u32(data, long_arrays.len() as u32);
for array in *long_arrays {
for array in &**long_arrays {
write_with_u32_length(data, 8, array.as_big_endian());
}
}
@ -269,13 +270,13 @@ impl<'a> NbtList<'a> {
_ => None,
}
}
pub fn byte_arrays(&self) -> Option<&[&[u8]]> {
pub fn byte_arrays(&self) -> Option<&[Slice32Bit<'a, [u8]>]> {
match self {
NbtList::ByteArray(byte_arrays) => Some(byte_arrays),
_ => None,
}
}
pub fn strings(&self) -> Option<&[&Mutf8Str]> {
pub fn strings(&self) -> Option<&[Slice16Bit<Mutf8Str>]> {
match self {
NbtList::String(strings) => Some(strings),
_ => None,
@ -319,7 +320,7 @@ impl<'a> NbtList<'a> {
byte_arrays.iter().map(|array| array.to_vec()).collect(),
),
NbtList::String(strings) => crate::owned::NbtList::String(
strings.iter().map(|&string| string.to_owned()).collect(),
strings.iter().map(|&string| (*string).to_owned()).collect(),
),
NbtList::List(lists) => {
crate::owned::NbtList::List(lists.iter().map(|list| list.to_owned()).collect())

View file

@ -15,6 +15,7 @@ use crate::{
LIST_ID, LONG_ARRAY_ID, LONG_ID, MAX_DEPTH, SHORT_ID, STRING_ID,
},
raw_list::RawList,
thin_slices::{Slice16Bit, Slice32Bit},
Error, Mutf8Str,
};
@ -24,7 +25,7 @@ pub use self::{compound::NbtCompound, list::NbtList};
/// A complete NBT container. This contains a name and a compound tag.
#[derive(Debug)]
pub struct BaseNbt<'a> {
name: &'a Mutf8Str,
name: Slice16Bit<'a, Mutf8Str>,
tag: NbtCompound<'a>,
// we need to keep this around so it's not deallocated
_tag_alloc: TagAllocator<'a>,
@ -89,7 +90,7 @@ impl<'a> Nbt<'a> {
impl<'a> BaseNbt<'a> {
/// Get the name of the NBT compound. This is often an empty string.
pub fn name(&self) -> &'a Mutf8Str {
pub fn name(&self) -> Slice16Bit<'a, Mutf8Str> {
self.name
}
}
@ -113,7 +114,7 @@ impl<'a> Deref for BaseNbt<'a> {
impl<'a> BaseNbt<'a> {
pub fn write(&self, data: &mut Vec<u8>) {
data.push(COMPOUND_ID);
write_string(data, self.name);
write_string(data, &self.name);
self.tag.write(data);
data.push(END_ID);
}
@ -128,8 +129,8 @@ pub enum NbtTag<'a> {
Long(i64),
Float(f32),
Double(f64),
ByteArray(&'a [u8]),
String(&'a Mutf8Str),
ByteArray(Slice32Bit<'a, [u8]>),
String(Slice16Bit<'a, Mutf8Str>),
List(NbtList<'a>),
Compound(NbtCompound<'a>),
IntArray(RawList<'a, i32>),
@ -293,7 +294,7 @@ impl<'a> NbtTag<'a> {
NbtTag::Float(float) => crate::owned::NbtTag::Float(*float),
NbtTag::Double(double) => crate::owned::NbtTag::Double(*double),
NbtTag::ByteArray(byte_array) => crate::owned::NbtTag::ByteArray(byte_array.to_vec()),
NbtTag::String(string) => crate::owned::NbtTag::String((*string).to_owned()),
NbtTag::String(string) => crate::owned::NbtTag::String((&**string).to_owned()),
NbtTag::List(list) => crate::owned::NbtTag::List(list.to_owned()),
NbtTag::Compound(compound) => crate::owned::NbtTag::Compound(compound.to_owned()),
NbtTag::IntArray(int_array) => crate::owned::NbtTag::IntArray(int_array.to_vec()),

View file

@ -22,7 +22,11 @@ use std::{
ptr::NonNull,
};
use crate::{raw_list::RawList, Mutf8Str};
use crate::{
raw_list::RawList,
thin_slices::{Slice16Bit, Slice32Bit},
Mutf8Str,
};
use super::{NbtCompound, NbtList, NbtTag};
@ -44,7 +48,7 @@ impl<'a> TagAllocator<'a> {
#[derive(Default)]
pub struct TagAllocatorImpl<'a> {
pub named: IndividualTagAllocator<(&'a Mutf8Str, NbtTag<'a>)>,
pub named: IndividualTagAllocator<(Slice16Bit<'a, Mutf8Str>, NbtTag<'a>)>,
// so remember earlier when i said the depth thing is only necessary because compounds aren't
// length prefixed? ... well soooo i decided to make arrays store per-depth separately too to
@ -52,8 +56,8 @@ pub struct TagAllocatorImpl<'a> {
// a lot
pub unnamed_list: IndividualTagAllocator<NbtList<'a>>,
pub unnamed_compound: IndividualTagAllocator<NbtCompound<'a>>,
pub unnamed_bytearray: IndividualTagAllocator<&'a [u8]>,
pub unnamed_string: IndividualTagAllocator<&'a Mutf8Str>,
pub unnamed_bytearray: IndividualTagAllocator<Slice32Bit<'a, [u8]>>,
pub unnamed_string: IndividualTagAllocator<Slice16Bit<'a, Mutf8Str>>,
pub unnamed_intarray: IndividualTagAllocator<RawList<'a, i32>>,
pub unnamed_longarray: IndividualTagAllocator<RawList<'a, i64>>,
}
@ -87,7 +91,11 @@ where
start_allocating_tags(alloc)
}
pub fn finish<'a>(&mut self, alloc: ContiguousTagsAllocator<T>, depth: usize) -> &'a [T] {
pub fn finish<'a>(
&mut self,
alloc: ContiguousTagsAllocator<T>,
depth: usize,
) -> Slice32Bit<'a, [T]> {
finish_allocating_tags(alloc, &mut self.current[depth], &mut self.previous[depth])
}
}
@ -121,16 +129,17 @@ fn finish_allocating_tags<'a, T>(
alloc: ContiguousTagsAllocator<T>,
current_alloc: &mut TagsAllocation<T>,
previous_allocs: &mut Vec<TagsAllocation<T>>,
) -> &'a [T] {
) -> Slice32Bit<'a, [T]> {
let slice = unsafe {
std::slice::from_raw_parts(
Slice32Bit::new(
alloc
.alloc
.ptr
.as_ptr()
.add(alloc.alloc.len)
.sub(alloc.size),
alloc.size,
.sub(alloc.size)
.cast(),
alloc.size as u32,
)
};

View file

@ -3,6 +3,7 @@ use std::{io::Cursor, mem, slice};
use crate::{
raw_list::RawList,
swap_endianness::{swap_endianness_as_u8, SwappableNumber},
thin_slices::{Slice16Bit, Slice32Bit},
Error, Mutf8Str,
};
@ -54,7 +55,7 @@ pub fn read_u16(data: &mut Cursor<&[u8]>) -> Result<u16, Error> {
pub fn read_with_u16_length<'a>(
data: &mut Cursor<&'a [u8]>,
width: usize,
) -> Result<&'a [u8], Error> {
) -> Result<Slice16Bit<'a, [u8]>, Error> {
let length = read_u16(data)?;
let length_in_bytes = length as usize * width;
// make sure we don't read more than the length
@ -63,14 +64,17 @@ pub fn read_with_u16_length<'a>(
}
let start_position = data.position() as usize;
data.set_position(data.position() + length_in_bytes as u64);
Ok(&data.get_ref()[start_position..start_position + length_in_bytes])
Ok(Slice16Bit::new(
data.get_ref().as_ptr().wrapping_add(start_position),
length_in_bytes as u16,
))
}
#[inline(never)]
pub fn read_with_u32_length<'a>(
data: &mut Cursor<&'a [u8]>,
width: usize,
) -> Result<&'a [u8], Error> {
) -> Result<Slice32Bit<'a, [u8]>, Error> {
let length = read_u32(data)?;
let length_in_bytes = length as usize * width;
// make sure we don't read more than the length
@ -79,19 +83,23 @@ pub fn read_with_u32_length<'a>(
}
let start_position = data.position() as usize;
data.set_position(data.position() + length_in_bytes as u64);
Ok(&data.get_ref()[start_position..start_position + length_in_bytes])
// Ok(&data.get_ref()[start_position..start_position + length_in_bytes])
Ok(Slice32Bit::new(
data.get_ref().as_ptr().wrapping_add(start_position),
length_in_bytes as u32,
))
}
pub fn read_string<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a Mutf8Str, Error> {
pub fn read_string<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Slice16Bit<'a, Mutf8Str>, Error> {
let data = read_with_u16_length(data, 1)?;
Ok(Mutf8Str::from_slice(data))
Ok(Mutf8Str::from_slice_16bit(data))
}
pub fn read_u8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [u8], Error> {
pub fn read_u8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Slice32Bit<'a, [u8]>, Error> {
read_with_u32_length(data, 1)
}
pub fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [i8], Error> {
Ok(slice_u8_into_i8(read_u8_array(data)?))
pub fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Slice32Bit<'a, [i8]>, Error> {
Ok(slice_32bit_u8_into_i8(read_u8_array(data)?))
}
pub fn read_int_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i32>, Error> {
@ -107,11 +115,17 @@ pub fn read_long_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i6
fn slice_u8_into_i8(s: &[u8]) -> &[i8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
}
pub fn slice_i8_into_u8(s: &[i8]) -> &[u8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, s.len()) }
}
fn slice_32bit_u8_into_i8(s: Slice32Bit<[u8]>) -> Slice32Bit<[i8]> {
Slice32Bit::new(s.as_ptr(), s.len())
}
fn slice_32bit_i8_into_u8(s: Slice32Bit<[i8]>) -> Slice32Bit<[u8]> {
Slice32Bit::new(s.as_ptr(), s.len())
}
#[inline(always)]
pub fn write_with_u32_length(data: &mut Vec<u8>, width: usize, value: &[u8]) {
let length = value.len() / width;
@ -170,14 +184,17 @@ pub unsafe fn unchecked_push(data: &mut Vec<u8>, value: u8) {
/// 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)) }
pub fn slice_into_u8_native_endian<T>(s: Slice32Bit<'_, [T]>) -> Slice32Bit<'_, [u8]> {
Slice32Bit::new(
s.as_ptr() as *const u8,
s.len() * mem::size_of::<T>() as u32,
)
}
/// 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> {
pub fn slice_into_u8_big_endian<T: SwappableNumber>(s: Slice32Bit<'_, [T]>) -> Vec<u8> {
swap_endianness_as_u8::<T>(slice_into_u8_native_endian(s))
}
@ -189,18 +206,24 @@ mod tests {
#[cfg(target_endian = "little")]
#[test]
fn test_slice_into_u8_native_endian() {
assert_eq!(slice_into_u8_native_endian(&[1u16, 2u16]), [1, 0, 2, 0]);
assert_eq!(
slice_into_u8_native_endian(&[1u32, 2u32]),
[1, 0, 0, 0, 2, 0, 0, 0]
slice_into_u8_native_endian([1u16, 2u16].as_slice().into()),
[1, 0, 2, 0].as_slice().into()
);
assert_eq!(
slice_into_u8_native_endian([1u32, 2u32].as_slice().into()),
[1, 0, 0, 0, 2, 0, 0, 0].as_slice().into()
);
}
#[test]
fn test_slice_into_u8_big_endian() {
assert_eq!(slice_into_u8_big_endian(&[1u16, 2u16]), [0, 1, 0, 2]);
assert_eq!(
slice_into_u8_big_endian(&[1u32, 2u32]),
slice_into_u8_big_endian([1u16, 2u16].as_slice().into()),
[0, 1, 0, 2]
);
assert_eq!(
slice_into_u8_big_endian([1u32, 2u32].as_slice().into()),
[0, 0, 0, 1, 0, 0, 0, 2]
);
}

View file

@ -1,6 +1,7 @@
#![doc = include_str!("../README.md")]
#![feature(portable_simd)]
#![feature(array_chunks)]
#![feature(ptr_metadata)]
pub mod borrow;
mod common;
@ -9,6 +10,7 @@ mod mutf8;
pub mod owned;
pub mod raw_list;
pub mod swap_endianness;
mod thin_slices;
mod traits;
pub use error::{DeserializeError, Error};

View file

@ -7,6 +7,8 @@ use std::{
simd::prelude::*,
};
use crate::thin_slices::Slice16Bit;
/// A M-UTF8 string slice. This is how strings are represented internally in NBT.
#[derive(Eq, PartialEq)]
pub struct Mutf8Str {
@ -87,6 +89,11 @@ impl Mutf8Str {
unsafe { mem::transmute::<&[u8], &Mutf8Str>(slice) }
}
#[inline]
pub fn from_slice_16bit(slice: Slice16Bit<[u8]>) -> Slice16Bit<Mutf8Str> {
Slice16Bit::new(slice.as_ptr(), slice.len())
}
// we can't implement FromStr on Cow<Mutf8Str>
#[allow(clippy::should_implement_trait)]
#[inline]

View file

@ -48,7 +48,7 @@ impl NbtCompound {
if tag_type == END_ID {
break;
}
let tag_name = read_string(data)?.to_owned();
let tag_name: Mutf8String = (*read_string(data)?).to_owned();
let tag = NbtTag::read_with_type(data, tag_type, depth)?;
tags_buffer[tags_buffer_len] = MaybeUninit::new((tag_name, tag));

View file

@ -47,7 +47,7 @@ impl NbtList {
data.set_position(data.position() + 4);
NbtList::Empty
}
BYTE_ID => NbtList::Byte(read_i8_array(data)?.to_owned()),
BYTE_ID => NbtList::Byte(read_i8_array(data)?.to_vec()),
SHORT_ID => NbtList::Short(swap_endianness(read_with_u32_length(data, 2)?)),
INT_ID => NbtList::Int(swap_endianness(read_with_u32_length(data, 4)?)),
LONG_ID => NbtList::Long(swap_endianness(read_with_u32_length(data, 8)?)),
@ -67,7 +67,7 @@ impl NbtList {
// arbitrary number to prevent big allocations
let mut strings = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {
strings.push(read_string(data)?.to_owned())
strings.push((*read_string(data)?).to_owned())
}
strings
}),
@ -135,19 +135,23 @@ impl NbtList {
write_with_u32_length(data, 1, slice_i8_into_u8(bytes));
}
NbtList::Short(shorts) => {
write_with_u32_length(data, 2, &slice_into_u8_big_endian(shorts));
write_with_u32_length(data, 2, &slice_into_u8_big_endian(shorts.as_slice().into()));
}
NbtList::Int(ints) => {
write_with_u32_length(data, 4, &slice_into_u8_big_endian(ints));
write_with_u32_length(data, 4, &slice_into_u8_big_endian(ints.as_slice().into()));
}
NbtList::Long(longs) => {
write_with_u32_length(data, 8, &slice_into_u8_big_endian(longs));
write_with_u32_length(data, 8, &slice_into_u8_big_endian(longs.as_slice().into()));
}
NbtList::Float(floats) => {
write_with_u32_length(data, 4, &slice_into_u8_big_endian(floats));
write_with_u32_length(data, 4, &slice_into_u8_big_endian(floats.as_slice().into()));
}
NbtList::Double(doubles) => {
write_with_u32_length(data, 8, &slice_into_u8_big_endian(doubles));
write_with_u32_length(
data,
8,
&slice_into_u8_big_endian(doubles.as_slice().into()),
);
}
NbtList::ByteArray(byte_arrays) => {
write_u32(data, byte_arrays.len() as u32);
@ -173,13 +177,21 @@ impl NbtList {
NbtList::IntArray(int_arrays) => {
write_u32(data, int_arrays.len() as u32);
for array in int_arrays {
write_with_u32_length(data, 4, &slice_into_u8_big_endian(array));
write_with_u32_length(
data,
4,
&slice_into_u8_big_endian(array.as_slice().into()),
);
}
}
NbtList::LongArray(long_arrays) => {
write_u32(data, long_arrays.len() as u32);
for array in long_arrays {
write_with_u32_length(data, 8, &slice_into_u8_big_endian(array));
write_with_u32_length(
data,
8,
&slice_into_u8_big_endian(array.as_slice().into()),
);
}
}
}

View file

@ -48,7 +48,7 @@ impl Nbt {
if root_type != COMPOUND_ID {
return Err(Error::InvalidRootType(root_type));
}
let name = read_string(data)?.to_owned();
let name = (*read_string(data)?).to_owned();
let tag = NbtCompound::read_with_depth(data, 0)?;
Ok(Nbt::Some(BaseNbt { name, tag }))
@ -247,8 +247,8 @@ impl NbtTag {
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())),
BYTE_ARRAY_ID => Ok(NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_vec())),
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,
@ -319,13 +319,13 @@ impl NbtTag {
unsafe {
unchecked_extend(data, &int_array.len().to_be_bytes());
}
data.extend_from_slice(&slice_into_u8_big_endian(int_array));
data.extend_from_slice(&slice_into_u8_big_endian(int_array.as_slice().into()));
}
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));
data.extend_from_slice(&slice_into_u8_big_endian(long_array.as_slice().into()));
}
}
}

View file

@ -1,24 +1,35 @@
use std::{marker::PhantomData, mem};
use std::{
fmt::{self, Debug},
marker::PhantomData,
mem,
};
use crate::swap_endianness::{swap_endianness, swap_endianness_as_u8, SwappableNumber};
use crate::{
swap_endianness::{swap_endianness, swap_endianness_as_u8, SwappableNumber},
thin_slices::Slice32Bit,
};
/// A list of numbers that's kept as big-endian in memory.
#[derive(Debug, PartialEq, Clone)]
// #[derive(Debug, PartialEq, Clone)]
#[repr(transparent)]
pub struct RawList<'a, T> {
data: &'a [u8],
data: Slice32Bit<'a, [u8]>,
_marker: PhantomData<T>,
}
impl<'a, T> RawList<'a, T> {
pub fn new(data: &'a [u8]) -> Self {
pub fn new(data: Slice32Bit<'a, [u8]>) -> Self {
Self {
data,
_marker: PhantomData,
}
}
pub fn new_from_slice(data: &'a [u8]) -> Self {
Self::new(Slice32Bit::new(data.as_ptr(), data.len() as u32))
}
pub fn len(&self) -> usize {
self.data.len() / mem::size_of::<T>()
(self.data.len() / mem::size_of::<T>() as u32) as usize
}
pub fn is_empty(&self) -> bool {
@ -39,7 +50,7 @@ impl<T: SwappableNumber> RawList<'_, T> {
}
pub fn as_big_endian(&self) -> &[u8] {
self.data
&self.data
}
}
@ -65,3 +76,25 @@ where
self.to_vec().into_iter()
}
}
impl<T> Debug for RawList<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.data.fmt(f)
}
}
impl<T> PartialEq for RawList<'_, T>
where
T: PartialEq + Copy + SwappableNumber,
{
fn eq(&self, other: &Self) -> bool {
self.to_vec() == other.to_vec()
}
}
impl<T> Clone for RawList<'_, T> {
fn clone(&self) -> Self {
Self {
data: self.data.clone(),
_marker: PhantomData,
}
}
}

View file

@ -1,5 +1,7 @@
use std::{mem, simd::prelude::*};
use crate::thin_slices::Slice32Bit;
mod private {
pub trait Sealed {}
@ -274,7 +276,7 @@ fn swap_endianness_from_type<T: SwappableNumber>(items: &mut [u8]) {
/// Swaps the endianness of the given data and return it as a `Vec<u8>`.
#[inline]
pub fn swap_endianness_as_u8<T: SwappableNumber>(data: &[u8]) -> Vec<u8> {
pub fn swap_endianness_as_u8<T: SwappableNumber>(data: Slice32Bit<'_, [u8]>) -> Vec<u8> {
let mut items = data.to_vec();
swap_endianness_from_type::<T>(&mut items);
@ -282,22 +284,22 @@ pub fn swap_endianness_as_u8<T: SwappableNumber>(data: &[u8]) -> Vec<u8> {
}
#[inline]
pub fn swap_endianness<T: SwappableNumber>(data: &[u8]) -> Vec<T> {
let width_of_t = mem::size_of::<T>();
pub fn swap_endianness<T: SwappableNumber>(data: Slice32Bit<'_, [u8]>) -> Vec<T> {
let width_of_t = mem::size_of::<T>() as u32;
let length_of_vec_t = data.len() / width_of_t;
// the data must be a multiple of the item width, otherwise it's UB
assert_eq!(data.len() % width_of_t, 0);
// have the vec be of T initially so it's aligned
let mut vec_t = Vec::<T>::with_capacity(length_of_vec_t);
let mut vec_t = Vec::<T>::with_capacity(length_of_vec_t as usize);
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
unsafe { Vec::from_raw_parts(ptr, 0, data.len()) }
unsafe { Vec::from_raw_parts(ptr, 0, data.len() as usize) }
};
vec_u8.extend_from_slice(data);
vec_u8.extend_from_slice(&data);
swap_endianness_from_type::<T>(&mut vec_u8);
@ -306,7 +308,7 @@ pub fn swap_endianness<T: SwappableNumber>(data: &[u8]) -> Vec<T> {
let ptr = vec_u8.as_mut_ptr() as *mut T;
mem::forget(vec_u8);
// SAFETY: The length won't be greater than the length of the original data
unsafe { Vec::from_raw_parts(ptr, length_of_vec_t, length_of_vec_t) }
unsafe { Vec::from_raw_parts(ptr, length_of_vec_t as usize, length_of_vec_t as usize) }
}
#[cfg(test)]
@ -316,21 +318,21 @@ mod tests {
#[test]
fn test_swap_endianness_u16() {
assert_eq!(
swap_endianness_as_u8::<u16>(&[1, 2, 3, 4, 5, 6, 7, 8]),
swap_endianness_as_u8::<u16>([1, 2, 3, 4, 5, 6, 7, 8].as_slice().into()),
[2, 1, 4, 3, 6, 5, 8, 7]
);
}
#[test]
fn test_swap_endianness_u32() {
assert_eq!(
swap_endianness_as_u8::<u32>(&[1, 2, 3, 4, 5, 6, 7, 8]),
swap_endianness_as_u8::<u32>([1, 2, 3, 4, 5, 6, 7, 8].as_slice().into()),
[4, 3, 2, 1, 8, 7, 6, 5]
);
}
#[test]
fn test_swap_endianness_u64() {
assert_eq!(
swap_endianness_as_u8::<u64>(&[1, 2, 3, 4, 5, 6, 7, 8]),
swap_endianness_as_u8::<u64>([1, 2, 3, 4, 5, 6, 7, 8].as_slice().into()),
[8, 7, 6, 5, 4, 3, 2, 1]
);
}
@ -338,7 +340,11 @@ mod tests {
#[test]
fn test_swap_endianness_u64_vec() {
assert_eq!(
swap_endianness::<u64>(&[1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1]),
swap_endianness::<u64>(
[1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1]
.as_slice()
.into()
),
vec![
u64::from_le_bytes([8, 7, 6, 5, 4, 3, 2, 1]),
u64::from_le_bytes([1, 2, 3, 4, 5, 6, 7, 8])

220
simdnbt/src/thin_slices.rs Normal file
View file

@ -0,0 +1,220 @@
use std::{
fmt::{self, Debug, Pointer},
marker::PhantomData,
ops::Deref,
ptr::Pointee,
};
#[repr(packed(2))]
pub struct Slice16Bit<'a, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
ptr: *const u8,
len: u16,
_marker: PhantomData<&'a T>,
}
#[repr(packed(4))]
pub struct Slice32Bit<'a, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
ptr: *const u8,
len: u32,
_marker: PhantomData<&'a T>,
}
impl<'a, T> Slice32Bit<'a, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
pub fn new(ptr: *const u8, len: u32) -> Self {
Self {
ptr,
len,
_marker: PhantomData,
}
}
pub fn len(&self) -> u32 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
}
impl<T> Deref for Slice32Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
type Target = T;
fn deref(&self) -> &Self::Target {
// self.ptr.with_metadata_of(&self.len).as_ptr()
let p = std::ptr::from_raw_parts(self.ptr as *const (), (self.len as usize).into());
unsafe { &*(p as *const T) }
}
}
impl<T> Debug for Slice32Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(&**self).fmt(f)
}
}
impl<T> PartialEq for Slice32Bit<'_, T>
where
T: PartialEq,
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn eq(&self, other: &Self) -> bool {
(&**self).eq(&**other)
}
}
impl<T> Clone for Slice32Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
len: self.len,
_marker: PhantomData,
}
}
}
impl<T> Copy for Slice32Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
}
impl<T> Default for Slice32Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn default() -> Self {
Self {
ptr: std::ptr::null(),
len: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> From<&'a [T]> for Slice32Bit<'a, [T]> {
fn from(slice: &'a [T]) -> Self {
Self {
ptr: slice.as_ptr() as *const u8,
len: slice.len() as u32,
_marker: PhantomData,
}
}
}
impl<'a, T> Slice16Bit<'a, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
pub fn new(ptr: *const u8, len: u16) -> Self {
Self {
ptr,
len,
_marker: PhantomData,
}
}
pub fn len(&self) -> u16 {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
}
impl<T> Deref for Slice16Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
type Target = T;
fn deref(&self) -> &Self::Target {
// self.ptr.with_metadata_of(&self.len).as_ptr()
let p = std::ptr::from_raw_parts(self.ptr as *const (), (self.len as usize).into());
unsafe { &*(p as *const T) }
}
}
impl<T> Debug for Slice16Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(&**self).fmt(f)
}
}
impl<T> PartialEq for Slice16Bit<'_, T>
where
T: PartialEq,
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn eq(&self, other: &Self) -> bool {
(&**self).eq(&**other)
}
}
impl<T> Clone for Slice16Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn clone(&self) -> Self {
Self {
ptr: self.ptr,
len: self.len,
_marker: PhantomData,
}
}
}
impl<T> Copy for Slice16Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
}
impl<T> Default for Slice16Bit<'_, T>
where
T: ?Sized + Pointee,
<T as Pointee>::Metadata: From<usize>,
{
fn default() -> Self {
Self {
ptr: std::ptr::null(),
len: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> From<&'a [T]> for Slice16Bit<'a, [T]> {
fn from(slice: &'a [T]) -> Self {
Self {
ptr: slice.as_ptr() as *const u8,
len: slice.len() as u16,
_marker: PhantomData,
}
}
}

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.len());
let mut hashmap = HashMap::with_capacity(compound.len() as usize);
for (k, v) in compound.iter() {
let k_str = k.to_str();