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:
parent
35d335ffd1
commit
d2f3fdefc7
12 changed files with 991 additions and 392 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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!(
|
||||
|
|
246
src/common.rs
246
src/common.rs
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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}")]
|
||||
|
|
|
@ -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;
|
||||
|
|
12
src/mutf8.rs
12
src/mutf8.rs
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
|
|
197
src/owned/mod.rs
197
src/owned/mod.rs
|
@ -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
63
src/raw_list.rs
Normal 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
307
src/swap_endianness.rs
Normal 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]
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue