1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 15:36:03 +00:00

add writing owned data

This commit is contained in:
mat 2023-09-20 20:26:00 -05:00
parent 35d335ffd1
commit d2f3fdefc7
12 changed files with 991 additions and 392 deletions

View file

@ -24,11 +24,63 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) {
let mut group = c.benchmark_group(format!("compare/{filename}"));
group.throughput(Throughput::Bytes(input.len() as u64));
group.bench_function("azalea_parse", |b| {
// group.bench_function("simdnbt_borrow_parse", |b| {
// b.iter(|| {
// let input = black_box(input);
// let nbt = simdnbt::borrow::Nbt::new(&mut Cursor::new(input))
// .unwrap()
// .unwrap();
// // let _ = black_box(nbt.list("").unwrap().ints());
// black_box(nbt);
// })
// });
let nbt = simdnbt::borrow::Nbt::new(&mut Cursor::new(input))
.unwrap()
.unwrap();
group.bench_function("simdnbt_borrow_write", |b| {
b.iter(|| {
let input = black_box(input);
let nbt = azalea_nbt::Nbt::read(&mut Cursor::new(input)).unwrap();
black_box(nbt);
let mut out = Vec::new();
nbt.write(&mut out);
black_box(out);
})
});
// group.bench_function("simdnbt_owned_parse", |b| {
// b.iter(|| {
// let input = black_box(input);
// let nbt = simdnbt::owned::Nbt::new(&mut Cursor::new(input))
// .unwrap()
// .unwrap();
// // let _ = black_box(nbt.list("").unwrap().ints());
// black_box(nbt);
// })
// });
let nbt = simdnbt::owned::Nbt::new(&mut Cursor::new(input))
.unwrap()
.unwrap();
group.bench_function("simdnbt_owned_write", |b| {
b.iter(|| {
let mut out = Vec::new();
nbt.write(&mut out);
black_box(out);
})
});
// group.bench_function("azalea_parse", |b| {
// b.iter(|| {
// let input = black_box(input);
// let nbt = azalea_nbt::Nbt::read(&mut Cursor::new(input)).unwrap();
// black_box(nbt);
// })
// });
let nbt = azalea_nbt::Nbt::read(&mut Cursor::new(input)).unwrap();
group.bench_function("azalea_write", |b| {
b.iter(|| {
let mut out = Vec::new();
nbt.write(&mut out);
black_box(out);
})
});
@ -39,27 +91,13 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) {
// black_box(nbt);
// })
// });
// group.bench_function("simdnbt_parse", |b| {
// let nbt = graphite_binary::nbt::decode::read(&mut &input[..]).unwrap();
// group.bench_function("graphite_write", |b| {
// b.iter(|| {
// let input = black_box(input);
// let nbt = simdnbt::borrow::Nbt::new(&mut Cursor::new(input))
// .unwrap()
// .unwrap();
// // let _ = black_box(nbt.list("").unwrap().ints());
// black_box(nbt);
// let out = graphite_binary::nbt::encode::write(&nbt);
// black_box(out);
// })
// });
group.bench_function("simdnbt_owned_parse", |b| {
b.iter(|| {
let input = black_box(input);
let nbt = simdnbt::owned::Nbt::new(&mut Cursor::new(input))
.unwrap()
.unwrap();
// let _ = black_box(nbt.list("").unwrap().ints());
black_box(nbt);
})
});
// group.bench_function("valence_parse", |b| {
// b.iter(|| {
@ -91,7 +129,7 @@ fn bench(c: &mut Criterion) {
// bench_read_file("bigtest.nbt", c);
// bench_read_file("simple_player.dat", c);
bench_read_file("complex_player.dat", c);
bench_read_file("level.dat", c);
// bench_read_file("level.dat", c);
// bench_read_file("stringtest.nbt", c);
// bench_read_file("inttest1023.nbt", c);
}

View file

@ -106,4 +106,10 @@ fn main() {
let nbt = black_box(nbt.unwrap().unwrap());
black_box(simdnbt_items_from_nbt(nbt));
}
let nbt = Nbt::new(&mut Cursor::new(input)).unwrap().unwrap();
for _ in 0..100000 {
let mut out = Vec::new();
nbt.write(&mut out);
}
}

View file

@ -1,43 +1,45 @@
use std::{io::Cursor, marker::PhantomData};
use std::io::Cursor;
use byteorder::ReadBytesExt;
use crate::{
common::{
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array,
read_with_u32_length, swap_endianness, 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,
read_with_u32_length, write_i8_array, write_string, write_u32, 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, Mutf8Str,
raw_list::RawList,
Mutf8Str, ReadError,
};
use super::{read_u32, CompoundTag, MAX_DEPTH};
/// A list of NBT tags of a single type.
#[repr(u8)]
#[derive(Debug, Default)]
pub enum ListTag<'a> {
#[default]
Empty,
Byte(&'a [i8]),
Short(RawList<'a, i16>),
Int(RawList<'a, i32>),
Long(RawList<'a, i64>),
Float(RawList<'a, f32>),
Double(RawList<'a, f64>),
ByteArray(&'a [u8]),
String(Vec<&'a Mutf8Str>),
List(Vec<ListTag<'a>>),
Compound(Vec<CompoundTag<'a>>),
IntArray(Vec<Vec<i32>>),
LongArray(Vec<Vec<i64>>),
Empty = END_ID,
Byte(&'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 [u8]) = BYTE_ARRAY_ID,
String(Vec<&'a Mutf8Str>) = STRING_ID,
List(Vec<ListTag<'a>>) = LIST_ID,
Compound(Vec<CompoundTag<'a>>) = COMPOUND_ID,
IntArray(Vec<RawList<'a, i32>>) = INT_ARRAY_ID,
LongArray(Vec<RawList<'a, i64>>) = LONG_ARRAY_ID,
}
impl<'a> ListTag<'a> {
pub fn new(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, Error> {
pub fn new(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, ReadError> {
if depth > MAX_DEPTH {
return Err(Error::MaxDepthExceeded);
return Err(ReadError::MaxDepthExceeded);
}
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
let tag_type = data.read_u8().map_err(|_| ReadError::UnexpectedEof)?;
Ok(match tag_type {
END_ID => {
data.set_position(data.position() + 4);
@ -95,10 +97,89 @@ impl<'a> ListTag<'a> {
}
arrays
}),
_ => return Err(Error::UnknownTagId(tag_type)),
_ => return Err(ReadError::UnknownTagId(tag_type)),
})
}
pub fn write(&self, data: &mut Vec<u8>) {
data.push(self.id());
match self {
ListTag::Empty => {
write_u32(data, 0);
}
ListTag::Byte(bytes) => {
write_u32(data, bytes.len() as u32);
write_i8_array(data, bytes);
}
ListTag::Short(shorts) => {
write_u32(data, shorts.len() as u32);
data.extend_from_slice(&shorts.as_big_endian());
}
ListTag::Int(ints) => {
write_u32(data, ints.len() as u32);
data.extend_from_slice(&ints.as_big_endian());
}
ListTag::Long(longs) => {
write_u32(data, longs.len() as u32);
data.extend_from_slice(&longs.as_big_endian());
}
ListTag::Float(floats) => {
write_u32(data, floats.len() as u32);
data.extend_from_slice(&floats.as_big_endian());
}
ListTag::Double(doubles) => {
write_u32(data, doubles.len() as u32);
data.extend_from_slice(&doubles.as_big_endian());
}
ListTag::ByteArray(byte_arrays) => {
write_u32(data, byte_arrays.len() as u32);
data.extend_from_slice(byte_arrays);
}
ListTag::String(strings) => {
write_u32(data, strings.len() as u32);
for &string in strings {
write_string(data, string);
}
}
ListTag::List(lists) => {
write_u32(data, lists.len() as u32);
for list in lists {
list.write(data);
}
}
ListTag::Compound(compounds) => {
write_u32(data, compounds.len() as u32);
for compound in compounds {
compound.write(data);
}
}
ListTag::IntArray(int_arrays) => {
write_u32(data, int_arrays.len() as u32);
for array in int_arrays {
write_u32(data, array.len() as u32);
data.extend_from_slice(&array.as_big_endian());
}
}
ListTag::LongArray(long_arrays) => {
write_u32(data, long_arrays.len() as u32);
for array in long_arrays {
write_u32(data, array.len() as u32);
data.extend_from_slice(&array.as_big_endian());
}
}
}
}
/// Get the numerical ID of the tag type.
#[inline]
pub fn id(&self) -> u8 {
// SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)`
// `union` between `repr(C)` structs, each of which has the `u8`
// discriminant as its first field, so we can read the discriminant
// without offsetting the pointer.
unsafe { *<*const _>::from(self).cast::<u8>() }
}
pub fn bytes(&self) -> Option<&[i8]> {
match self {
ListTag::Byte(bytes) => Some(bytes),
@ -159,39 +240,16 @@ impl<'a> ListTag<'a> {
_ => None,
}
}
pub fn int_arrays(&self) -> Option<&[Vec<i32>]> {
pub fn int_arrays(&self) -> Option<&[RawList<i32>]> {
match self {
ListTag::IntArray(int_arrays) => Some(int_arrays),
_ => None,
}
}
pub fn long_arrays(&self) -> Option<&[Vec<i64>]> {
pub fn long_arrays(&self) -> Option<&[RawList<i64>]> {
match self {
ListTag::LongArray(long_arrays) => Some(long_arrays),
_ => None,
}
}
}
#[derive(Debug)]
pub struct RawList<'a, T> {
data: &'a [u8],
_marker: PhantomData<T>,
}
impl<'a, T> RawList<'a, T> {
pub fn new(data: &'a [u8]) -> Self {
Self {
data,
_marker: PhantomData,
}
}
}
impl<T> RawList<'_, T> {
pub fn to_vec(&self) -> Vec<T>
where
T: Copy,
{
swap_endianness(self.data)
}
}

View file

@ -8,11 +8,12 @@ use byteorder::{ReadBytesExt, BE};
use crate::{
common::{
read_int_array, read_long_array, read_string, read_u32, read_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, MAX_DEPTH, SHORT_ID, STRING_ID,
read_int_array, read_long_array, read_string, read_u32, read_with_u32_length, write_i32,
write_string, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID,
INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID, MAX_DEPTH, SHORT_ID, STRING_ID,
},
Error, Mutf8Str,
raw_list::RawList,
Mutf8Str, ReadError,
};
use self::list::ListTag;
@ -23,6 +24,13 @@ pub struct Nbt<'a> {
name: &'a Mutf8Str,
tag: CompoundTag<'a>,
}
/// A list of named tags. The order of the tags is preserved.
#[derive(Debug, Default)]
pub struct CompoundTag<'a> {
values: Vec<(&'a Mutf8Str, Tag<'a>)>,
}
impl<'a> Nbt<'a> {
/// Get the name of the NBT compound. This is often an empty string.
pub fn name(&self) -> &'a Mutf8Str {
@ -39,35 +47,36 @@ impl<'a> Deref for Nbt<'a> {
impl<'a> Nbt<'a> {
/// Reads NBT from the given data. Returns `Ok(None)` if there is no data.
pub fn new(data: &mut Cursor<&'a [u8]>) -> Result<Option<Nbt<'a>>, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
pub fn new(data: &mut Cursor<&'a [u8]>) -> Result<Option<Nbt<'a>>, ReadError> {
let root_type = data.read_u8().map_err(|_| ReadError::UnexpectedEof)?;
if root_type == END_ID {
return Ok(None);
}
if root_type != COMPOUND_ID {
return Err(Error::InvalidRootType(root_type));
return Err(ReadError::InvalidRootType(root_type));
}
let name = read_string(data)?;
let tag = CompoundTag::new(data, 0)?;
Ok(Some(Nbt { name, tag }))
}
}
/// A list of named tags. The order of the tags is preserved.
#[derive(Debug, Default)]
pub struct CompoundTag<'a> {
values: Vec<(&'a Mutf8Str, Tag<'a>)>,
pub fn write(&self, data: &mut Vec<u8>) {
data.push(COMPOUND_ID);
write_string(data, self.name);
self.tag.write(data);
data.push(END_ID);
}
}
impl<'a> CompoundTag<'a> {
fn new(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, Error> {
fn new(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, ReadError> {
if depth > MAX_DEPTH {
return Err(Error::MaxDepthExceeded);
return Err(ReadError::MaxDepthExceeded);
}
let mut values = Vec::with_capacity(4);
loop {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
let tag_type = data.read_u8().map_err(|_| ReadError::UnexpectedEof)?;
if tag_type == END_ID {
break;
}
@ -76,27 +85,42 @@ impl<'a> CompoundTag<'a> {
match tag_type {
BYTE_ID => values.push((
tag_name,
Tag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
Tag::Byte(data.read_i8().map_err(|_| ReadError::UnexpectedEof)?),
)),
SHORT_ID => values.push((
tag_name,
Tag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Short(
data.read_i16::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
INT_ID => values.push((
tag_name,
Tag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Int(
data.read_i32::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
LONG_ID => values.push((
tag_name,
Tag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Long(
data.read_i64::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
FLOAT_ID => values.push((
tag_name,
Tag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Float(
data.read_f32::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
DOUBLE_ID => values.push((
tag_name,
Tag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Double(
data.read_f64::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
BYTE_ARRAY_ID => {
values.push((tag_name, Tag::ByteArray(read_with_u32_length(data, 1)?)))
@ -108,12 +132,61 @@ impl<'a> CompoundTag<'a> {
}
INT_ARRAY_ID => values.push((tag_name, Tag::IntArray(read_int_array(data)?))),
LONG_ARRAY_ID => values.push((tag_name, Tag::LongArray(read_long_array(data)?))),
_ => return Err(Error::UnknownTagId(tag_type)),
_ => return Err(ReadError::UnknownTagId(tag_type)),
}
}
Ok(Self { values })
}
pub fn write(&self, data: &mut Vec<u8>) {
for (name, tag) in &self.values {
data.push(tag.id());
write_string(data, name);
match tag {
Tag::Byte(byte) => {
data.push(*byte as u8);
}
Tag::Short(short) => {
data.extend_from_slice(&short.to_be_bytes());
}
Tag::Int(int) => {
write_i32(data, *int);
}
Tag::Long(long) => {
data.extend_from_slice(&long.to_be_bytes());
}
Tag::Float(float) => {
data.extend_from_slice(&float.to_be_bytes());
}
Tag::Double(double) => {
data.extend_from_slice(&double.to_be_bytes());
}
Tag::ByteArray(byte_array) => {
write_i32(data, byte_array.len() as i32);
data.extend_from_slice(byte_array);
}
Tag::String(string) => {
write_string(data, string);
}
Tag::List(list) => {
list.write(data);
}
Tag::Compound(compound) => {
compound.write(data);
}
Tag::IntArray(int_array) => {
write_i32(data, int_array.len() as i32);
data.extend_from_slice(&int_array.to_little_endian());
}
Tag::LongArray(long_array) => {
write_i32(data, long_array.len() as i32);
data.extend_from_slice(&long_array.to_little_endian());
}
}
}
data.push(END_ID);
}
#[inline]
pub fn get(&self, name: &str) -> Option<&Tag<'a>> {
let name = Mutf8Str::from_str(name);
@ -168,10 +241,10 @@ impl<'a> CompoundTag<'a> {
pub fn compound(&self, name: &str) -> Option<&CompoundTag<'a>> {
self.get(name).and_then(|tag| tag.compound())
}
pub fn int_array(&self, name: &str) -> Option<&[i32]> {
pub fn int_array(&self, name: &str) -> Option<Vec<i32>> {
self.get(name).and_then(|tag| tag.int_array())
}
pub fn long_array(&self, name: &str) -> Option<&[i64]> {
pub fn long_array(&self, name: &str) -> Option<Vec<i64>> {
self.get(name).and_then(|tag| tag.long_array())
}
@ -181,22 +254,33 @@ impl<'a> CompoundTag<'a> {
}
/// A single NBT tag.
#[repr(u8)]
#[derive(Debug)]
pub enum Tag<'a> {
Byte(i8),
Short(i16),
Int(i32),
Long(i64),
Float(f32),
Double(f64),
ByteArray(&'a [u8]),
String(&'a Mutf8Str),
List(ListTag<'a>),
Compound(CompoundTag<'a>),
IntArray(Vec<i32>),
LongArray(Vec<i64>),
Byte(i8) = BYTE_ID,
Short(i16) = SHORT_ID,
Int(i32) = INT_ID,
Long(i64) = LONG_ID,
Float(f32) = FLOAT_ID,
Double(f64) = DOUBLE_ID,
ByteArray(&'a [u8]) = BYTE_ARRAY_ID,
String(&'a Mutf8Str) = STRING_ID,
List(ListTag<'a>) = LIST_ID,
Compound(CompoundTag<'a>) = COMPOUND_ID,
IntArray(RawList<'a, i32>) = INT_ARRAY_ID,
LongArray(RawList<'a, i64>) = LONG_ARRAY_ID,
}
impl<'a> Tag<'a> {
/// Get the numerical ID of the tag type.
#[inline]
pub fn id(&self) -> u8 {
// SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)`
// `union` between `repr(C)` structs, each of which has the `u8`
// discriminant as its first field, so we can read the discriminant
// without offsetting the pointer.
unsafe { *<*const _>::from(self).cast::<u8>() }
}
pub fn byte(&self) -> Option<i8> {
match self {
Tag::Byte(byte) => Some(*byte),
@ -257,15 +341,15 @@ impl<'a> Tag<'a> {
_ => None,
}
}
pub fn int_array(&self) -> Option<&[i32]> {
pub fn int_array(&self) -> Option<Vec<i32>> {
match self {
Tag::IntArray(int_array) => Some(int_array),
Tag::IntArray(int_array) => Some(int_array.to_vec()),
_ => None,
}
}
pub fn long_array(&self) -> Option<&[i64]> {
pub fn long_array(&self) -> Option<Vec<i64>> {
match self {
Tag::LongArray(long_array) => Some(long_array),
Tag::LongArray(long_array) => Some(long_array.to_vec()),
_ => None,
}
}
@ -311,7 +395,7 @@ mod tests {
}
#[test]
fn complex_player() {
fn read_complex_player() {
let src = include_bytes!("../../tests/complex_player.dat").to_vec();
let mut src_slice = src.as_slice();
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
@ -323,6 +407,23 @@ mod tests {
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
}
#[test]
fn read_write_complex_player() {
let src = include_bytes!("../../tests/complex_player.dat").to_vec();
let mut src_slice = src.as_slice();
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
let mut decoded_src = Vec::new();
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
let nbt = Nbt::new(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let mut out = Vec::new();
nbt.write(&mut out);
let nbt = Nbt::new(&mut Cursor::new(&out)).unwrap().unwrap();
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
}
#[test]
fn inttest_1023() {
let nbt = Nbt::new(&mut Cursor::new(include_bytes!(

View file

@ -1,10 +1,10 @@
use std::{
io::Cursor,
simd::{simd_swizzle, u8x16, u8x32, u8x4, u8x64, u8x8, Simd},
slice,
};
use std::{io::Cursor, slice};
use crate::{Error, Mutf8Str};
use crate::{
raw_list::RawList,
swap_endianness::{swap_endianness_as_u8, SwappableNumber},
Mutf8Str, ReadError,
};
pub const END_ID: u8 = 0;
pub const BYTE_ID: u8 = 1;
@ -23,10 +23,10 @@ pub const LONG_ARRAY_ID: u8 = 12;
pub const MAX_DEPTH: usize = 512;
#[inline(always)]
pub fn read_u32(data: &mut Cursor<&[u8]>) -> Result<u32, Error> {
pub fn read_u32(data: &mut Cursor<&[u8]>) -> Result<u32, ReadError> {
let remaining_slice = &data.get_ref()[data.position() as usize..data.get_ref().len()];
if remaining_slice.len() < 4 {
return Err(Error::UnexpectedEof);
return Err(ReadError::UnexpectedEof);
}
data.set_position(data.position() + 4);
@ -39,10 +39,10 @@ pub fn read_u32(data: &mut Cursor<&[u8]>) -> Result<u32, Error> {
]))
}
#[inline(always)]
pub fn read_u16(data: &mut Cursor<&[u8]>) -> Result<u16, Error> {
pub fn read_u16(data: &mut Cursor<&[u8]>) -> Result<u16, ReadError> {
let remaining_slice = &data.get_ref()[data.position() as usize..data.get_ref().len()];
if remaining_slice.len() < 2 {
return Err(Error::UnexpectedEof);
return Err(ReadError::UnexpectedEof);
}
data.set_position(data.position() + 2);
@ -54,12 +54,12 @@ 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<&'a [u8], ReadError> {
let length = read_u16(data)?;
let length_in_bytes = length as usize * width;
// make sure we don't read more than the length
if data.get_ref().len() < data.position() as usize + length_in_bytes {
return Err(Error::UnexpectedEof);
return Err(ReadError::UnexpectedEof);
}
let start_position = data.position() as usize;
data.set_position(data.position() + length_in_bytes as u64);
@ -70,213 +70,73 @@ pub fn read_with_u16_length<'a>(
pub fn read_with_u32_length<'a>(
data: &mut Cursor<&'a [u8]>,
width: usize,
) -> Result<&'a [u8], Error> {
) -> Result<&'a [u8], ReadError> {
let length = read_u32(data)?;
let length_in_bytes = length as usize * width;
// make sure we don't read more than the length
if data.get_ref().len() < data.position() as usize + length_in_bytes {
return Err(Error::UnexpectedEof);
return Err(ReadError::UnexpectedEof);
}
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])
}
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<&'a Mutf8Str, ReadError> {
let data = read_with_u16_length(data, 1)?;
Ok(Mutf8Str::from_slice(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<&'a [u8], ReadError> {
read_with_u32_length(data, 1)
}
pub fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [i8], Error> {
pub fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [i8], ReadError> {
Ok(slice_u8_into_i8(read_u8_array(data)?))
}
pub fn read_int_array(data: &mut Cursor<&[u8]>) -> Result<Vec<i32>, Error> {
pub fn read_int_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i32>, ReadError> {
let array_bytes = read_with_u32_length(data, 4)?;
let length = array_bytes.len() / 4;
let mut ints = array_bytes.to_vec();
if cfg!(target_endian = "little") {
swap_endianness_32bit(&mut ints, length);
}
let ints = {
let ptr = ints.as_ptr() as *const i32;
std::mem::forget(ints);
// SAFETY: the width provided to read_with_u32_length guarantees that it'll be a multiple of 4
unsafe { Vec::from_raw_parts(ptr as *mut i32, length, length) }
};
Ok(ints)
Ok(RawList::new(array_bytes))
}
pub fn read_long_array(data: &mut Cursor<&[u8]>) -> Result<Vec<i64>, Error> {
pub fn read_long_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i64>, ReadError> {
let array_bytes = read_with_u32_length(data, 8)?;
let length = array_bytes.len() / 8;
let mut ints = array_bytes.to_vec();
if cfg!(target_endian = "little") {
swap_endianness_64bit(&mut ints, length);
}
let ints = {
let ptr = ints.as_ptr() as *const i64;
std::mem::forget(ints);
// SAFETY: the width provided to read_with_u32_length guarantees that it'll be a multiple of 8
unsafe { Vec::from_raw_parts(ptr as *mut i64, length, length) }
};
Ok(ints)
}
fn swap_endianness_32bit(bytes: &mut [u8], num: usize) {
for i in 0..num / 16 {
let simd: u8x64 = Simd::from_slice(bytes[i * 16 * 4..(i + 1) * 16 * 4].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
11, 10, 9, 8,
15, 14, 13, 12,
19, 18, 17, 16,
23, 22, 21, 20,
27, 26, 25, 24,
31, 30, 29, 28,
35, 34, 33, 32,
39, 38, 37, 36,
43, 42, 41, 40,
47, 46, 45, 44,
51, 50, 49, 48,
55, 54, 53, 52,
59, 58, 57, 56,
63, 62, 61, 60,
]);
bytes[i * 16 * 4..(i + 1) * 16 * 4].copy_from_slice(simd.as_array());
}
let mut i = num / 16 * 16;
if i + 8 <= num {
let simd: u8x32 = Simd::from_slice(bytes[i * 4..i * 4 + 32].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
11, 10, 9, 8,
15, 14, 13, 12,
19, 18, 17, 16,
23, 22, 21, 20,
27, 26, 25, 24,
31, 30, 29, 28,
]);
bytes[i * 4..i * 4 + 32].copy_from_slice(simd.as_array());
i += 8;
}
if i + 4 <= num {
let simd: u8x16 = Simd::from_slice(bytes[i * 4..i * 4 + 16].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
11, 10, 9, 8,
15, 14, 13, 12,
]);
bytes[i * 4..i * 4 + 16].copy_from_slice(simd.as_array());
i += 4;
}
if i + 2 <= num {
let simd: u8x8 = Simd::from_slice(bytes[i * 4..i * 4 + 8].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
]);
bytes[i * 4..i * 4 + 8].copy_from_slice(simd.as_array());
i += 2;
}
if i < num {
let simd: u8x4 = Simd::from_slice(bytes[i * 4..i * 4 + 4].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
]);
bytes[i * 4..i * 4 + 4].copy_from_slice(simd.as_array());
}
}
fn swap_endianness_64bit(bytes: &mut [u8], num: usize) {
for i in 0..num / 8 {
let simd: u8x64 = Simd::from_slice(bytes[i * 64..i * 64 + 64].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
15, 14, 13, 12, 11, 10, 9, 8,
23, 22, 21, 20, 19, 18, 17, 16,
31, 30, 29, 28, 27, 26, 25, 24,
39, 38, 37, 36, 35, 34, 33, 32,
47, 46, 45, 44, 43, 42, 41, 40,
55, 54, 53, 52, 51, 50, 49, 48,
63, 62, 61, 60, 59, 58, 57, 56,
]);
bytes[i * 64..i * 64 + 64].copy_from_slice(simd.as_array());
}
let mut i = num / 8 * 8;
if i + 4 <= num {
let simd: u8x32 = Simd::from_slice(bytes[i * 8..i * 8 + 32].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
15, 14, 13, 12, 11, 10, 9, 8,
23, 22, 21, 20, 19, 18, 17, 16,
31, 30, 29, 28, 27, 26, 25, 24,
]);
bytes[i * 8..i * 8 + 32].copy_from_slice(simd.as_array());
i += 4;
}
if i + 2 <= num {
let simd: u8x16 = Simd::from_slice(bytes[i * 8..i * 8 + 16].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
15, 14, 13, 12, 11, 10, 9, 8,
]);
bytes[i * 8..i * 8 + 16].copy_from_slice(simd.as_array());
i += 2;
}
if i < num {
let simd: u8x8 = Simd::from_slice(bytes[i * 8..i * 8 + 8].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
]);
bytes[i * 8..i * 8 + 8].copy_from_slice(simd.as_array());
}
Ok(RawList::new(array_bytes))
}
fn slice_u8_into_i8(s: &[u8]) -> &[i8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
}
#[inline]
pub fn swap_endianness<T>(data: &[u8]) -> Vec<T> {
let length = data.len() / std::mem::size_of::<T>();
let mut items = data.to_vec();
if cfg!(target_endian = "little") {
match std::mem::size_of::<T>() {
4 => swap_endianness_32bit(&mut items, length),
8 => swap_endianness_64bit(&mut items, length),
_ => panic!("unsupported size of type"),
}
}
{
let ptr = items.as_ptr() as *const T;
std::mem::forget(items);
// SAFETY: the width provided to read_with_u32_length guarantees that it'll be a multiple of 4
unsafe { Vec::from_raw_parts(ptr as *mut T, length, length) }
}
fn slice_i8_into_u8(s: &[i8]) -> &[u8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, s.len()) }
}
pub fn write_u32(data: &mut Vec<u8>, value: u32) {
data.extend_from_slice(&value.to_be_bytes());
}
pub fn write_i32(data: &mut Vec<u8>, value: i32) {
data.extend_from_slice(&value.to_be_bytes());
}
pub fn write_u16(data: &mut Vec<u8>, value: u16) {
data.extend_from_slice(&value.to_be_bytes());
}
pub fn write_i8_array(data: &mut Vec<u8>, value: &[i8]) {
data.extend_from_slice(slice_i8_into_u8(value));
}
pub fn write_string(data: &mut Vec<u8>, value: &Mutf8Str) {
write_u16(data, value.len() as u16);
data.extend_from_slice(value.as_bytes());
}
/// 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).
pub fn slice_into_u8_native_endian<T>(s: &[T]) -> &[u8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, s.len() * std::mem::size_of::<T>()) }
}
/// 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).
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

@ -1,7 +1,7 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
pub enum ReadError {
#[error("Invalid root type {0}")]
InvalidRootType(u8),
#[error("Unknown tag id {0}")]

View file

@ -20,6 +20,8 @@ mod common;
mod error;
mod mutf8;
pub mod owned;
pub mod raw_list;
pub mod swap_endianness;
pub use error::Error;
pub use error::ReadError;
pub use mutf8::Mutf8Str;

View file

@ -107,6 +107,14 @@ impl Mutf8Str {
}
}
}
pub fn len(&self) -> usize {
self.slice.len()
}
pub fn as_bytes(&self) -> &[u8] {
&self.slice
}
}
impl fmt::Display for Mutf8Str {
@ -147,6 +155,10 @@ impl Mutf8String {
}
}
}
pub fn len(&self) -> usize {
self.vec.len()
}
}
impl Deref for Mutf8String {
type Target = Mutf8Str;

View file

@ -5,40 +5,42 @@ use byteorder::ReadBytesExt;
use crate::{
common::{
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array,
read_with_u32_length, swap_endianness, 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,
read_with_u32_length, slice_into_u8_big_endian, write_i8_array, write_string, write_u32,
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,
},
mutf8::Mutf8String,
Error,
swap_endianness::swap_endianness,
ReadError,
};
use super::{read_u32, CompoundTag, MAX_DEPTH};
/// A list of NBT tags of a single type.
#[repr(u8)]
#[derive(Debug, Default)]
pub enum ListTag {
#[default]
Empty,
Byte(Vec<i8>),
Short(Vec<i16>),
Int(Vec<i32>),
Long(Vec<i64>),
Float(Vec<f32>),
Double(Vec<f64>),
ByteArray(Vec<u8>),
String(Vec<Mutf8String>),
List(Vec<ListTag>),
Compound(Vec<CompoundTag>),
IntArray(Vec<Vec<i32>>),
LongArray(Vec<Vec<i64>>),
Empty = END_ID,
Byte(Vec<i8>) = BYTE_ID,
Short(Vec<i16>) = SHORT_ID,
Int(Vec<i32>) = INT_ID,
Long(Vec<i64>) = LONG_ID,
Float(Vec<f32>) = FLOAT_ID,
Double(Vec<f64>) = DOUBLE_ID,
ByteArray(Vec<u8>) = BYTE_ARRAY_ID,
String(Vec<Mutf8String>) = STRING_ID,
List(Vec<ListTag>) = LIST_ID,
Compound(Vec<CompoundTag>) = COMPOUND_ID,
IntArray(Vec<Vec<i32>>) = INT_ARRAY_ID,
LongArray(Vec<Vec<i64>>) = LONG_ARRAY_ID,
}
impl ListTag {
pub fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
pub fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, ReadError> {
if depth > MAX_DEPTH {
return Err(Error::MaxDepthExceeded);
return Err(ReadError::MaxDepthExceeded);
}
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
let tag_type = data.read_u8().map_err(|_| ReadError::UnexpectedEof)?;
Ok(match tag_type {
END_ID => {
data.set_position(data.position() + 4);
@ -83,7 +85,7 @@ impl ListTag {
// arbitrary number to prevent big allocations
let mut arrays = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {
arrays.push(read_int_array(data)?)
arrays.push(read_int_array(data)?.to_vec())
}
arrays
}),
@ -92,14 +94,93 @@ impl ListTag {
// arbitrary number to prevent big allocations
let mut arrays = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {
arrays.push(read_long_array(data)?)
arrays.push(read_long_array(data)?.to_vec())
}
arrays
}),
_ => return Err(Error::UnknownTagId(tag_type)),
_ => return Err(ReadError::UnknownTagId(tag_type)),
})
}
pub fn write(&self, data: &mut Vec<u8>) {
data.push(self.id());
match self {
ListTag::Empty => {
write_u32(data, 0);
}
ListTag::Byte(bytes) => {
write_u32(data, bytes.len() as u32);
write_i8_array(data, bytes);
}
ListTag::Short(shorts) => {
write_u32(data, shorts.len() as u32);
data.extend_from_slice(&slice_into_u8_big_endian(shorts));
}
ListTag::Int(ints) => {
write_u32(data, ints.len() as u32);
data.extend_from_slice(&slice_into_u8_big_endian(ints));
}
ListTag::Long(longs) => {
write_u32(data, longs.len() as u32);
data.extend_from_slice(&slice_into_u8_big_endian(longs));
}
ListTag::Float(floats) => {
write_u32(data, floats.len() as u32);
data.extend_from_slice(&slice_into_u8_big_endian(floats));
}
ListTag::Double(doubles) => {
write_u32(data, doubles.len() as u32);
data.extend_from_slice(&slice_into_u8_big_endian(doubles));
}
ListTag::ByteArray(byte_arrays) => {
write_u32(data, byte_arrays.len() as u32);
data.extend_from_slice(byte_arrays);
}
ListTag::String(strings) => {
write_u32(data, strings.len() as u32);
for string in strings {
write_string(data, string);
}
}
ListTag::List(lists) => {
write_u32(data, lists.len() as u32);
for list in lists {
list.write(data);
}
}
ListTag::Compound(compounds) => {
write_u32(data, compounds.len() as u32);
for compound in compounds {
compound.write(data);
}
}
ListTag::IntArray(int_arrays) => {
write_u32(data, int_arrays.len() as u32);
for array in int_arrays {
write_u32(data, array.len() as u32);
data.extend_from_slice(&slice_into_u8_big_endian(array));
}
}
ListTag::LongArray(long_arrays) => {
write_u32(data, long_arrays.len() as u32);
for array in long_arrays {
write_u32(data, array.len() as u32);
data.extend_from_slice(&slice_into_u8_big_endian(array));
}
}
}
}
/// Get the numerical ID of the tag type.
#[inline]
pub fn id(&self) -> u8 {
// SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)`
// `union` between `repr(C)` structs, each of which has the `u8`
// discriminant as its first field, so we can read the discriminant
// without offsetting the pointer.
unsafe { *<*const _>::from(self).cast::<u8>() }
}
pub fn bytes(&self) -> Option<&[i8]> {
match self {
ListTag::Byte(bytes) => Some(bytes),

View file

@ -9,11 +9,12 @@ use byteorder::{ReadBytesExt, BE};
use crate::{
common::{
read_int_array, read_long_array, read_string, read_u32, read_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, MAX_DEPTH, SHORT_ID, STRING_ID,
slice_into_u8_big_endian, write_i32, write_string, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID,
DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID,
MAX_DEPTH, SHORT_ID, STRING_ID,
},
mutf8::Mutf8String,
Error, Mutf8Str,
Mutf8Str, ReadError,
};
use self::list::ListTag;
@ -40,19 +41,26 @@ impl Deref for Nbt {
impl Nbt {
/// Reads NBT from the given data. Returns `Ok(None)` if there is no data.
pub fn new(data: &mut Cursor<&[u8]>) -> Result<Option<Nbt>, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
pub fn new(data: &mut Cursor<&[u8]>) -> Result<Option<Nbt>, ReadError> {
let root_type = data.read_u8().map_err(|_| ReadError::UnexpectedEof)?;
if root_type == END_ID {
return Ok(None);
}
if root_type != COMPOUND_ID {
return Err(Error::InvalidRootType(root_type));
return Err(ReadError::InvalidRootType(root_type));
}
let name = read_string(data)?.to_owned();
let tag = CompoundTag::new(data, 0)?;
Ok(Some(Nbt { name, tag }))
}
/// Writes the NBT to the given buffer.
pub fn write(&self, data: &mut Vec<u8>) {
data.push(COMPOUND_ID);
write_string(data, &self.name);
self.tag.write(data);
}
}
/// A list of named tags. The order of the tags is preserved.
@ -62,13 +70,13 @@ pub struct CompoundTag {
}
impl CompoundTag {
fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, ReadError> {
if depth > MAX_DEPTH {
return Err(Error::MaxDepthExceeded);
return Err(ReadError::MaxDepthExceeded);
}
let mut values = Vec::with_capacity(4);
loop {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
let tag_type = data.read_u8().map_err(|_| ReadError::UnexpectedEof)?;
if tag_type == END_ID {
break;
}
@ -77,27 +85,42 @@ impl CompoundTag {
match tag_type {
BYTE_ID => values.push((
tag_name,
Tag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
Tag::Byte(data.read_i8().map_err(|_| ReadError::UnexpectedEof)?),
)),
SHORT_ID => values.push((
tag_name,
Tag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Short(
data.read_i16::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
INT_ID => values.push((
tag_name,
Tag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Int(
data.read_i32::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
LONG_ID => values.push((
tag_name,
Tag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Long(
data.read_i64::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
FLOAT_ID => values.push((
tag_name,
Tag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Float(
data.read_f32::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
DOUBLE_ID => values.push((
tag_name,
Tag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
Tag::Double(
data.read_f64::<BE>()
.map_err(|_| ReadError::UnexpectedEof)?,
),
)),
BYTE_ARRAY_ID => values.push((
tag_name,
@ -108,14 +131,67 @@ impl CompoundTag {
COMPOUND_ID => {
values.push((tag_name, Tag::Compound(CompoundTag::new(data, depth + 1)?)))
}
INT_ARRAY_ID => values.push((tag_name, Tag::IntArray(read_int_array(data)?))),
LONG_ARRAY_ID => values.push((tag_name, Tag::LongArray(read_long_array(data)?))),
_ => return Err(Error::UnknownTagId(tag_type)),
INT_ARRAY_ID => {
values.push((tag_name, Tag::IntArray(read_int_array(data)?.to_vec())))
}
LONG_ARRAY_ID => {
values.push((tag_name, Tag::LongArray(read_long_array(data)?.to_vec())))
}
_ => return Err(ReadError::UnknownTagId(tag_type)),
}
}
Ok(Self { values })
}
pub fn write(&self, data: &mut Vec<u8>) {
for (name, tag) in &self.values {
data.push(tag.id());
write_string(data, name);
match tag {
Tag::Byte(byte) => {
data.push(*byte as u8);
}
Tag::Short(short) => {
data.extend_from_slice(&short.to_be_bytes());
}
Tag::Int(int) => {
write_i32(data, *int);
}
Tag::Long(long) => {
data.extend_from_slice(&long.to_be_bytes());
}
Tag::Float(float) => {
data.extend_from_slice(&float.to_be_bytes());
}
Tag::Double(double) => {
data.extend_from_slice(&double.to_be_bytes());
}
Tag::ByteArray(byte_array) => {
write_i32(data, byte_array.len() as i32);
data.extend_from_slice(byte_array);
}
Tag::String(string) => {
write_string(data, string);
}
Tag::List(list) => {
list.write(data);
}
Tag::Compound(compound) => {
compound.write(data);
}
Tag::IntArray(int_array) => {
write_i32(data, int_array.len() as i32);
data.extend_from_slice(&slice_into_u8_big_endian(int_array));
}
Tag::LongArray(long_array) => {
write_i32(data, long_array.len() as i32);
data.extend_from_slice(&slice_into_u8_big_endian(long_array));
}
}
}
data.push(END_ID);
}
#[inline]
pub fn get(&self, name: &str) -> Option<&Tag> {
let name = Mutf8Str::from_str(name);
@ -271,22 +347,33 @@ impl CompoundTag {
}
/// A single NBT tag.
#[repr(u8)]
#[derive(Debug)]
pub enum Tag {
Byte(i8),
Short(i16),
Int(i32),
Long(i64),
Float(f32),
Double(f64),
ByteArray(Vec<u8>),
String(Mutf8String),
List(ListTag),
Compound(CompoundTag),
IntArray(Vec<i32>),
LongArray(Vec<i64>),
Byte(i8) = BYTE_ID,
Short(i16) = SHORT_ID,
Int(i32) = INT_ID,
Long(i64) = LONG_ID,
Float(f32) = FLOAT_ID,
Double(f64) = DOUBLE_ID,
ByteArray(Vec<u8>) = BYTE_ARRAY_ID,
String(Mutf8String) = STRING_ID,
List(ListTag) = LIST_ID,
Compound(CompoundTag) = COMPOUND_ID,
IntArray(Vec<i32>) = INT_ARRAY_ID,
LongArray(Vec<i64>) = LONG_ARRAY_ID,
}
impl Tag {
/// Get the numerical ID of the tag type.
#[inline]
pub fn id(&self) -> u8 {
// SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)`
// `union` between `repr(C)` structs, each of which has the `u8`
// discriminant as its first field, so we can read the discriminant
// without offsetting the pointer.
unsafe { *<*const _>::from(self).cast::<u8>() }
}
pub fn byte(&self) -> Option<i8> {
match self {
Tag::Byte(byte) => Some(*byte),
@ -485,6 +572,23 @@ mod tests {
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
}
#[test]
fn read_write_complex_player() {
let src = include_bytes!("../../tests/complex_player.dat").to_vec();
let mut src_slice = src.as_slice();
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
let mut decoded_src = Vec::new();
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
let nbt = Nbt::new(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let mut out = Vec::new();
nbt.write(&mut out);
let nbt = Nbt::new(&mut Cursor::new(&out)).unwrap().unwrap();
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
}
#[test]
fn inttest_1023() {
let nbt = Nbt::new(&mut Cursor::new(include_bytes!(
@ -566,37 +670,4 @@ mod tests {
}
assert_eq!(ints.len(), 1023);
}
// #[test]
// fn generate_inttest() {
// use byteorder::WriteBytesExt;
// let mut out = Vec::new();
// out.write_u8(COMPOUND_ID).unwrap();
// out.write_u16::<BE>(0).unwrap();
// out.write_u8(LIST_ID).unwrap();
// out.write_u16::<BE>(0).unwrap();
// out.write_u8(INT_ID).unwrap();
// out.write_i32::<BE>(1023).unwrap();
// for i in 0..1023 {
// out.write_i32::<BE>(i).unwrap();
// }
// out.write_u8(END_ID).unwrap();
// std::fs::write("tests/inttest1023.nbt", out).unwrap();
// }
// #[test]
// fn generate_stringtest() {
// let mut out = Vec::new();
// out.write_u8(COMPOUND_ID).unwrap();
// out.write_u16::<BE>(0).unwrap();
// out.write_u8(LIST_ID).unwrap();
// out.write_u16::<BE>(0).unwrap();
// out.write_u8(STRING_ID).unwrap();
// out.write_i32::<BE>(16).unwrap();
// out.extend_from_slice(&std::fs::read("tests/stringtest.nbt").unwrap().as_slice()[13..]);
// out.write_u8(END_ID).unwrap();
// std::fs::write("tests/stringtest2.nbt", out).unwrap();
// }
}

63
src/raw_list.rs Normal file
View file

@ -0,0 +1,63 @@
use std::marker::PhantomData;
use crate::swap_endianness::{swap_endianness, swap_endianness_as_u8, SwappableNumber};
/// A list of numbers that's kept as big-endian in memory.
#[derive(Debug)]
pub struct RawList<'a, T> {
data: &'a [u8],
_marker: PhantomData<T>,
}
impl<'a, T> RawList<'a, T> {
pub fn new(data: &'a [u8]) -> Self {
Self {
data,
_marker: PhantomData,
}
}
pub fn len(&self) -> usize {
self.data.len() / std::mem::size_of::<T>()
}
}
impl<T: SwappableNumber> RawList<'_, T> {
pub fn to_vec(&self) -> Vec<T>
where
T: Copy + SwappableNumber,
{
swap_endianness(self.data)
}
pub fn to_little_endian(&self) -> Vec<u8> {
swap_endianness_as_u8::<T>(self.data)
}
pub fn as_big_endian(&self) -> &[u8] {
self.data
}
}
impl<T> IntoIterator for RawList<'_, T>
where
T: Copy + SwappableNumber,
{
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.to_vec().into_iter()
}
}
impl<T> IntoIterator for &RawList<'_, T>
where
T: Copy + SwappableNumber,
{
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.to_vec().into_iter()
}
}

307
src/swap_endianness.rs Normal file
View file

@ -0,0 +1,307 @@
use std::simd::prelude::*;
pub trait SwappableNumber {}
impl SwappableNumber for u16 {}
impl SwappableNumber for u32 {}
impl SwappableNumber for u64 {}
impl SwappableNumber for i16 {}
impl SwappableNumber for i32 {}
impl SwappableNumber for i64 {}
impl SwappableNumber for f32 {}
impl SwappableNumber for f64 {}
fn swap_endianness_16bit(bytes: &mut [u8], num: usize) {
for i in 0..num / 32 {
let simd: u8x64 = Simd::from_slice(bytes[i * 32 * 2..(i + 1) * 32 * 2].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
1, 0,
3, 2,
5, 4,
7, 6,
9, 8,
11, 10,
13, 12,
15, 14,
17, 16,
19, 18,
21, 20,
23, 22,
25, 24,
27, 26,
29, 28,
31, 30,
33, 32,
35, 34,
37, 36,
39, 38,
41, 40,
43, 42,
45, 44,
47, 46,
49, 48,
51, 50,
53, 52,
55, 54,
57, 56,
59, 58,
61, 60,
63, 62,
]);
bytes[i * 32 * 2..(i + 1) * 32 * 2].copy_from_slice(simd.as_array());
}
let mut i = num / 32 * 32;
if i + 16 <= num {
let simd: u8x32 = Simd::from_slice(bytes[i * 2..i * 2 + 32].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
1, 0,
3, 2,
5, 4,
7, 6,
9, 8,
11, 10,
13, 12,
15, 14,
17, 16,
19, 18,
21, 20,
23, 22,
25, 24,
27, 26,
29, 28,
31, 30,
]);
bytes[i * 2..i * 2 + 32].copy_from_slice(simd.as_array());
i += 16;
}
if i + 8 <= num {
let simd: u8x16 = Simd::from_slice(bytes[i * 2..i * 2 + 16].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
1, 0,
3, 2,
5, 4,
7, 6,
9, 8,
11, 10,
13, 12,
15, 14,
]);
bytes[i * 2..i * 2 + 16].copy_from_slice(simd.as_array());
i += 8;
}
if i + 4 <= num {
let simd: u8x8 = Simd::from_slice(bytes[i * 2..i * 2 + 8].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
1, 0,
3, 2,
5, 4,
7, 6,
]);
bytes[i * 2..i * 2 + 8].copy_from_slice(simd.as_array());
i += 4;
}
if i + 2 <= num {
let simd: u8x4 = Simd::from_slice(bytes[i * 2..i * 2 + 4].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
1, 0,
3, 2,
]);
bytes[i * 2..i * 2 + 4].copy_from_slice(simd.as_array());
i += 2;
}
if i < num {
let simd: u8x2 = Simd::from_slice(bytes[i * 2..i * 2 + 2].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
1, 0,
]);
bytes[i * 2..i * 2 + 2].copy_from_slice(simd.as_array());
}
}
fn swap_endianness_32bit(bytes: &mut [u8], num: usize) {
for i in 0..num / 16 {
let simd: u8x64 = Simd::from_slice(bytes[i * 16 * 4..(i + 1) * 16 * 4].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
11, 10, 9, 8,
15, 14, 13, 12,
19, 18, 17, 16,
23, 22, 21, 20,
27, 26, 25, 24,
31, 30, 29, 28,
35, 34, 33, 32,
39, 38, 37, 36,
43, 42, 41, 40,
47, 46, 45, 44,
51, 50, 49, 48,
55, 54, 53, 52,
59, 58, 57, 56,
63, 62, 61, 60,
]);
bytes[i * 16 * 4..(i + 1) * 16 * 4].copy_from_slice(simd.as_array());
}
let mut i = num / 16 * 16;
if i + 8 <= num {
let simd: u8x32 = Simd::from_slice(bytes[i * 4..i * 4 + 32].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
11, 10, 9, 8,
15, 14, 13, 12,
19, 18, 17, 16,
23, 22, 21, 20,
27, 26, 25, 24,
31, 30, 29, 28,
]);
bytes[i * 4..i * 4 + 32].copy_from_slice(simd.as_array());
i += 8;
}
if i + 4 <= num {
let simd: u8x16 = Simd::from_slice(bytes[i * 4..i * 4 + 16].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
11, 10, 9, 8,
15, 14, 13, 12,
]);
bytes[i * 4..i * 4 + 16].copy_from_slice(simd.as_array());
i += 4;
}
if i + 2 <= num {
let simd: u8x8 = Simd::from_slice(bytes[i * 4..i * 4 + 8].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
7, 6, 5, 4,
]);
bytes[i * 4..i * 4 + 8].copy_from_slice(simd.as_array());
i += 2;
}
if i < num {
let simd: u8x4 = Simd::from_slice(bytes[i * 4..i * 4 + 4].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
3, 2, 1, 0,
]);
bytes[i * 4..i * 4 + 4].copy_from_slice(simd.as_array());
}
}
fn swap_endianness_64bit(bytes: &mut [u8], num: usize) {
for i in 0..num / 8 {
let simd: u8x64 = Simd::from_slice(bytes[i * 64..i * 64 + 64].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
15, 14, 13, 12, 11, 10, 9, 8,
23, 22, 21, 20, 19, 18, 17, 16,
31, 30, 29, 28, 27, 26, 25, 24,
39, 38, 37, 36, 35, 34, 33, 32,
47, 46, 45, 44, 43, 42, 41, 40,
55, 54, 53, 52, 51, 50, 49, 48,
63, 62, 61, 60, 59, 58, 57, 56,
]);
bytes[i * 64..i * 64 + 64].copy_from_slice(simd.as_array());
}
let mut i = num / 8 * 8;
if i + 4 <= num {
let simd: u8x32 = Simd::from_slice(bytes[i * 8..i * 8 + 32].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
15, 14, 13, 12, 11, 10, 9, 8,
23, 22, 21, 20, 19, 18, 17, 16,
31, 30, 29, 28, 27, 26, 25, 24,
]);
bytes[i * 8..i * 8 + 32].copy_from_slice(simd.as_array());
i += 4;
}
if i + 2 <= num {
let simd: u8x16 = Simd::from_slice(bytes[i * 8..i * 8 + 16].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
15, 14, 13, 12, 11, 10, 9, 8,
]);
bytes[i * 8..i * 8 + 16].copy_from_slice(simd.as_array());
i += 2;
}
if i < num {
let simd: u8x8 = Simd::from_slice(bytes[i * 8..i * 8 + 8].as_ref());
#[rustfmt::skip]
let simd = simd_swizzle!(simd, [
7, 6, 5, 4, 3, 2, 1, 0,
]);
bytes[i * 8..i * 8 + 8].copy_from_slice(simd.as_array());
}
}
#[inline]
pub fn swap_endianness_as_u8<T: SwappableNumber>(data: &[u8]) -> Vec<u8> {
let length = data.len() / std::mem::size_of::<T>();
let mut items = data.to_vec();
if cfg!(target_endian = "little") {
match std::mem::size_of::<T>() {
2 => swap_endianness_16bit(&mut items, length),
4 => swap_endianness_32bit(&mut items, length),
8 => swap_endianness_64bit(&mut items, length),
_ => panic!("unsupported size of type"),
}
}
items
}
#[inline]
pub fn swap_endianness<T: SwappableNumber>(data: &[u8]) -> Vec<T> {
let length = data.len() / std::mem::size_of::<T>();
let items = swap_endianness_as_u8::<T>(data);
{
let ptr = items.as_ptr() as *const T;
std::mem::forget(items);
// SAFETY: The length won't be greater than the length of the original data
unsafe { Vec::from_raw_parts(ptr as *mut T, length, length) }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_swap_endianness_u16() {
assert_eq!(
swap_endianness_as_u8::<u16>(&[1, 2, 3, 4, 5, 6, 7, 8]),
[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]),
[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]),
[8, 7, 6, 5, 4, 3, 2, 1]
);
}
}