mirror of
https://github.com/azalea-rs/simdnbt.git
synced 2025-08-02 15:36:03 +00:00
base support for owned data
This commit is contained in:
parent
46173b6bdc
commit
fb2f3770d2
6 changed files with 927 additions and 245 deletions
|
@ -1,16 +1,18 @@
|
|||
use std::{io::Cursor, marker::PhantomData, simd::prelude::*, slice};
|
||||
use std::{io::Cursor, marker::PhantomData};
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
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_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,
|
||||
},
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use super::{read_string, read_u32, read_with_u32_length, CompoundTag, MAX_DEPTH};
|
||||
use super::{read_u32, CompoundTag, MAX_DEPTH};
|
||||
|
||||
/// A list of NBT tags of a single type.
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -190,196 +192,6 @@ impl<T> RawList<'_, T> {
|
|||
where
|
||||
T: Copy,
|
||||
{
|
||||
let data = self.data;
|
||||
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) }
|
||||
}
|
||||
swap_endianness(self.data)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_u8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [u8], Error> {
|
||||
read_with_u32_length(data, 1)
|
||||
}
|
||||
fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [i8], Error> {
|
||||
Ok(slice_u8_into_i8(read_u8_array(data)?))
|
||||
}
|
||||
pub fn read_int_array(data: &mut Cursor<&[u8]>) -> Result<Vec<i32>, Error> {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn read_long_array(data: &mut Cursor<&[u8]>) -> Result<Vec<i64>, Error> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
fn slice_u8_into_i8(s: &[u8]) -> &[i8] {
|
||||
unsafe { slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
|
||||
}
|
||||
|
|
|
@ -19,13 +19,14 @@ use byteorder::{ReadBytesExt, BE};
|
|||
|
||||
use crate::{
|
||||
common::{
|
||||
read_u16, read_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, MAX_DEPTH, SHORT_ID, STRING_ID,
|
||||
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,
|
||||
},
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use self::list::{read_int_array, read_long_array, ListTag};
|
||||
use self::list::ListTag;
|
||||
|
||||
/// A complete NBT container. This contains a name and a compound tag.
|
||||
#[derive(Debug)]
|
||||
|
@ -47,37 +48,6 @@ impl<'a> Deref for Nbt<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_with_u16_length<'a>(data: &mut Cursor<&'a [u8]>, width: usize) -> Result<&'a [u8], Error> {
|
||||
let length = read_u16(data)?;
|
||||
let length_in_bytes = length as usize * width;
|
||||
// make sure we don't read more than the length
|
||||
if data.get_ref().len() < data.position() as usize + length_in_bytes {
|
||||
return Err(Error::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])
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn read_with_u32_length<'a>(data: &mut Cursor<&'a [u8]>, width: usize) -> Result<&'a [u8], Error> {
|
||||
let length = read_u32(data)?;
|
||||
let length_in_bytes = length as usize * width;
|
||||
// make sure we don't read more than the length
|
||||
if data.get_ref().len() < data.position() as usize + length_in_bytes {
|
||||
return Err(Error::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])
|
||||
}
|
||||
|
||||
fn read_string<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a Mutf8Str, Error> {
|
||||
let data = read_with_u16_length(data, 1)?;
|
||||
Ok(Mutf8Str::from_slice(data))
|
||||
}
|
||||
|
||||
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> {
|
||||
|
|
267
src/common.rs
267
src/common.rs
|
@ -1,6 +1,26 @@
|
|||
use std::io::Cursor;
|
||||
use std::{
|
||||
io::Cursor,
|
||||
simd::{simd_swizzle, u8x16, u8x32, u8x4, u8x64, u8x8, Simd},
|
||||
slice,
|
||||
};
|
||||
|
||||
use crate::Error;
|
||||
use crate::{Error, Mutf8Str};
|
||||
|
||||
pub const END_ID: u8 = 0;
|
||||
pub const BYTE_ID: u8 = 1;
|
||||
pub const SHORT_ID: u8 = 2;
|
||||
pub const INT_ID: u8 = 3;
|
||||
pub const LONG_ID: u8 = 4;
|
||||
pub const FLOAT_ID: u8 = 5;
|
||||
pub const DOUBLE_ID: u8 = 6;
|
||||
pub const BYTE_ARRAY_ID: u8 = 7;
|
||||
pub const STRING_ID: u8 = 8;
|
||||
pub const LIST_ID: u8 = 9;
|
||||
pub const COMPOUND_ID: u8 = 10;
|
||||
pub const INT_ARRAY_ID: u8 = 11;
|
||||
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> {
|
||||
|
@ -30,18 +50,233 @@ pub fn read_u16(data: &mut Cursor<&[u8]>) -> Result<u16, Error> {
|
|||
Ok(u16::from_be_bytes([remaining_slice[0], remaining_slice[1]]))
|
||||
}
|
||||
|
||||
pub const END_ID: u8 = 0;
|
||||
pub const BYTE_ID: u8 = 1;
|
||||
pub const SHORT_ID: u8 = 2;
|
||||
pub const INT_ID: u8 = 3;
|
||||
pub const LONG_ID: u8 = 4;
|
||||
pub const FLOAT_ID: u8 = 5;
|
||||
pub const DOUBLE_ID: u8 = 6;
|
||||
pub const BYTE_ARRAY_ID: u8 = 7;
|
||||
pub const STRING_ID: u8 = 8;
|
||||
pub const LIST_ID: u8 = 9;
|
||||
pub const COMPOUND_ID: u8 = 10;
|
||||
pub const INT_ARRAY_ID: u8 = 11;
|
||||
pub const LONG_ARRAY_ID: u8 = 12;
|
||||
#[inline(always)]
|
||||
pub fn read_with_u16_length<'a>(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
width: usize,
|
||||
) -> Result<&'a [u8], Error> {
|
||||
let length = read_u16(data)?;
|
||||
let length_in_bytes = length as usize * width;
|
||||
// make sure we don't read more than the length
|
||||
if data.get_ref().len() < data.position() as usize + length_in_bytes {
|
||||
return Err(Error::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 const MAX_DEPTH: usize = 512;
|
||||
#[inline(never)]
|
||||
pub fn read_with_u32_length<'a>(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
width: usize,
|
||||
) -> Result<&'a [u8], Error> {
|
||||
let length = read_u32(data)?;
|
||||
let length_in_bytes = length as usize * width;
|
||||
// make sure we don't read more than the length
|
||||
if data.get_ref().len() < data.position() as usize + length_in_bytes {
|
||||
return Err(Error::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> {
|
||||
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> {
|
||||
read_with_u32_length(data, 1)
|
||||
}
|
||||
pub fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [i8], Error> {
|
||||
Ok(slice_u8_into_i8(read_u8_array(data)?))
|
||||
}
|
||||
pub fn read_int_array(data: &mut Cursor<&[u8]>) -> Result<Vec<i32>, Error> {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn read_long_array(data: &mut Cursor<&[u8]>) -> Result<Vec<i64>, Error> {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ pub mod borrow;
|
|||
mod common;
|
||||
mod error;
|
||||
mod mutf8;
|
||||
pub mod owned;
|
||||
|
||||
pub use error::Error;
|
||||
pub use mutf8::Mutf8Str;
|
||||
|
|
175
src/owned/list.rs
Normal file
175
src/owned/list.rs
Normal file
|
@ -0,0 +1,175 @@
|
|||
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,
|
||||
},
|
||||
mutf8::Mutf8String,
|
||||
Error,
|
||||
};
|
||||
|
||||
use super::{read_u32, CompoundTag, MAX_DEPTH};
|
||||
|
||||
/// A list of NBT tags of a single type.
|
||||
#[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>>),
|
||||
}
|
||||
impl ListTag {
|
||||
pub fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
if depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
Ok(match tag_type {
|
||||
END_ID => {
|
||||
data.set_position(data.position() + 4);
|
||||
ListTag::Empty
|
||||
}
|
||||
BYTE_ID => ListTag::Byte(read_i8_array(data)?.to_owned()),
|
||||
SHORT_ID => ListTag::Short(swap_endianness(read_with_u32_length(data, 2)?)),
|
||||
INT_ID => ListTag::Int(swap_endianness(read_with_u32_length(data, 4)?)),
|
||||
LONG_ID => ListTag::Long(swap_endianness(read_with_u32_length(data, 8)?)),
|
||||
FLOAT_ID => ListTag::Float(swap_endianness(read_with_u32_length(data, 4)?)),
|
||||
DOUBLE_ID => ListTag::Double(swap_endianness(read_with_u32_length(data, 8)?)),
|
||||
BYTE_ARRAY_ID => ListTag::ByteArray(read_u8_array(data)?.to_owned()),
|
||||
STRING_ID => ListTag::String({
|
||||
let length = read_u32(data)?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut strings = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
strings.push(read_string(data)?.to_owned())
|
||||
}
|
||||
strings
|
||||
}),
|
||||
LIST_ID => ListTag::List({
|
||||
let length = read_u32(data)?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut lists = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
lists.push(ListTag::new(data, depth + 1)?)
|
||||
}
|
||||
lists
|
||||
}),
|
||||
COMPOUND_ID => ListTag::Compound({
|
||||
let length = read_u32(data)?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut compounds = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
compounds.push(CompoundTag::new(data, depth + 1)?)
|
||||
}
|
||||
compounds
|
||||
}),
|
||||
INT_ARRAY_ID => ListTag::IntArray({
|
||||
let length = read_u32(data)?;
|
||||
// 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
|
||||
}),
|
||||
LONG_ARRAY_ID => ListTag::LongArray({
|
||||
let length = read_u32(data)?;
|
||||
// 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
|
||||
}),
|
||||
_ => return Err(Error::UnknownTagId(tag_type)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bytes(&self) -> Option<&[i8]> {
|
||||
match self {
|
||||
ListTag::Byte(bytes) => Some(bytes),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn shorts(&self) -> Option<Vec<i16>> {
|
||||
match self {
|
||||
ListTag::Short(shorts) => Some(shorts.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn ints(&self) -> Option<Vec<i32>> {
|
||||
match self {
|
||||
ListTag::Int(ints) => Some(ints.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn longs(&self) -> Option<Vec<i64>> {
|
||||
match self {
|
||||
ListTag::Long(longs) => Some(longs.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn floats(&self) -> Option<Vec<f32>> {
|
||||
match self {
|
||||
ListTag::Float(floats) => Some(floats.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn doubles(&self) -> Option<Vec<f64>> {
|
||||
match self {
|
||||
ListTag::Double(doubles) => Some(doubles.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn byte_arrays(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
ListTag::ByteArray(byte_arrays) => Some(byte_arrays),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn strings(&self) -> Option<&[Mutf8String]> {
|
||||
match self {
|
||||
ListTag::String(strings) => Some(strings),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn lists(&self) -> Option<&[ListTag]> {
|
||||
match self {
|
||||
ListTag::List(lists) => Some(lists),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compounds(&self) -> Option<&[CompoundTag]> {
|
||||
match self {
|
||||
ListTag::Compound(compounds) => Some(compounds),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int_arrays(&self) -> Option<&[Vec<i32>]> {
|
||||
match self {
|
||||
ListTag::IntArray(int_arrays) => Some(int_arrays),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long_arrays(&self) -> Option<&[Vec<i64>]> {
|
||||
match self {
|
||||
ListTag::LongArray(long_arrays) => Some(long_arrays),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
489
src/owned/mod.rs
Normal file
489
src/owned/mod.rs
Normal file
|
@ -0,0 +1,489 @@
|
|||
//! an unnecessarily fast nbt decoder.
|
||||
//!
|
||||
//! afaik, this is currently the fastest nbt decoder in existence.
|
||||
//!
|
||||
//! ```
|
||||
//! use simdnbt::borrow::Nbt;
|
||||
//! use std::io::Cursor;
|
||||
//!
|
||||
//! let nbt = Nbt::new(&mut Cursor::new(include_bytes!("../../tests/hello_world.nbt"))).unwrap().unwrap();
|
||||
//! assert_eq!(nbt.name().to_str(), "hello world");
|
||||
//! assert_eq!(nbt.string("name").unwrap().to_str(), "Bananrama");
|
||||
//! ```
|
||||
|
||||
pub mod list;
|
||||
|
||||
use std::{io::Cursor, ops::Deref};
|
||||
|
||||
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,
|
||||
},
|
||||
mutf8::Mutf8String,
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use self::list::ListTag;
|
||||
|
||||
/// A complete NBT container. This contains a name and a compound tag.
|
||||
#[derive(Debug)]
|
||||
pub struct Nbt {
|
||||
name: Mutf8String,
|
||||
tag: CompoundTag,
|
||||
}
|
||||
impl Nbt {
|
||||
/// Get the name of the NBT compound. This is often an empty string.
|
||||
pub fn name(&self) -> &Mutf8Str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
impl Deref for Nbt {
|
||||
type Target = CompoundTag;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tag
|
||||
}
|
||||
}
|
||||
|
||||
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)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(None);
|
||||
}
|
||||
if root_type != COMPOUND_ID {
|
||||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let name = read_string(data)?.to_owned();
|
||||
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 {
|
||||
values: Vec<(Mutf8String, Tag)>,
|
||||
}
|
||||
|
||||
impl CompoundTag {
|
||||
fn new(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
if depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
let mut values = Vec::with_capacity(4);
|
||||
loop {
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if tag_type == END_ID {
|
||||
break;
|
||||
}
|
||||
let tag_name = read_string(data)?.to_owned();
|
||||
|
||||
match tag_type {
|
||||
BYTE_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
SHORT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
INT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
LONG_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
FLOAT_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
DOUBLE_ID => values.push((
|
||||
tag_name,
|
||||
Tag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
||||
)),
|
||||
BYTE_ARRAY_ID => values.push((
|
||||
tag_name,
|
||||
Tag::ByteArray(read_with_u32_length(data, 1)?.to_owned()),
|
||||
)),
|
||||
STRING_ID => values.push((tag_name, Tag::String(read_string(data)?.to_owned()))),
|
||||
LIST_ID => values.push((tag_name, Tag::List(ListTag::new(data, depth + 1)?))),
|
||||
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)),
|
||||
}
|
||||
}
|
||||
Ok(Self { values })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, name: &str) -> Option<&Tag> {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for (key, value) in &self.values {
|
||||
if key.as_str() == name {
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns whether there is a tag with the given name.
|
||||
pub fn contains(&self, name: &str) -> bool {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for (key, _) in &self.values {
|
||||
if key.as_str() == name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn byte(&self, name: &str) -> Option<i8> {
|
||||
match self.get(name) {
|
||||
Some(Tag::Byte(byte)) => Some(*byte),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn short(&self, name: &str) -> Option<i16> {
|
||||
match self.get(name) {
|
||||
Some(Tag::Short(short)) => Some(*short),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int(&self, name: &str) -> Option<i32> {
|
||||
match self.get(name) {
|
||||
Some(Tag::Int(int)) => Some(*int),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long(&self, name: &str) -> Option<i64> {
|
||||
match self.get(name) {
|
||||
Some(Tag::Long(long)) => Some(*long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn float(&self, name: &str) -> Option<f32> {
|
||||
match self.get(name) {
|
||||
Some(Tag::Float(float)) => Some(*float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn double(&self, name: &str) -> Option<&f64> {
|
||||
match self.get(name) {
|
||||
Some(Tag::Double(double)) => Some(double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn byte_array(&self, name: &str) -> Option<&[u8]> {
|
||||
match self.get(name) {
|
||||
Some(Tag::ByteArray(byte_array)) => Some(byte_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn string(&self, name: &str) -> Option<&Mutf8Str> {
|
||||
match self.get(name) {
|
||||
Some(Tag::String(string)) => Some(string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn list(&self, name: &str) -> Option<&ListTag> {
|
||||
match self.get(name) {
|
||||
Some(Tag::List(list)) => Some(list),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compound(&self, name: &str) -> Option<&CompoundTag> {
|
||||
match self.get(name) {
|
||||
Some(Tag::Compound(compound)) => Some(compound),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int_array(&self, name: &str) -> Option<&[i32]> {
|
||||
match self.get(name) {
|
||||
Some(Tag::IntArray(int_array)) => Some(int_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long_array(&self, name: &str) -> Option<&[i64]> {
|
||||
match self.get(name) {
|
||||
Some(Tag::LongArray(long_array)) => Some(long_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &Tag)> {
|
||||
self.values.iter().map(|(k, v)| (k.as_str(), v))
|
||||
}
|
||||
}
|
||||
|
||||
/// A single NBT tag.
|
||||
#[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>),
|
||||
}
|
||||
impl Tag {
|
||||
pub fn byte(&self) -> Option<i8> {
|
||||
match self {
|
||||
Tag::Byte(byte) => Some(*byte),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn short(&self) -> Option<i16> {
|
||||
match self {
|
||||
Tag::Short(short) => Some(*short),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int(&self) -> Option<i32> {
|
||||
match self {
|
||||
Tag::Int(int) => Some(*int),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long(&self) -> Option<i64> {
|
||||
match self {
|
||||
Tag::Long(long) => Some(*long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn float(&self) -> Option<f32> {
|
||||
match self {
|
||||
Tag::Float(float) => Some(*float),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn double(&self) -> Option<f64> {
|
||||
match self {
|
||||
Tag::Double(double) => Some(*double),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn byte_array(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
Tag::ByteArray(byte_array) => Some(byte_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn string(&self) -> Option<&Mutf8Str> {
|
||||
match self {
|
||||
Tag::String(string) => Some(string),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn list(&self) -> Option<&ListTag> {
|
||||
match self {
|
||||
Tag::List(list) => Some(list),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn compound(&self) -> Option<&CompoundTag> {
|
||||
match self {
|
||||
Tag::Compound(compound) => Some(compound),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn int_array(&self) -> Option<&[i32]> {
|
||||
match self {
|
||||
Tag::IntArray(int_array) => Some(int_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn long_array(&self) -> Option<&[i64]> {
|
||||
match self {
|
||||
Tag::LongArray(long_array) => Some(long_array),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::Read;
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
use flate2::read::GzDecoder;
|
||||
|
||||
use crate::common::{INT_ID, LIST_ID, LONG_ID};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let nbt = Nbt::new(&mut Cursor::new(include_bytes!(
|
||||
"../../tests/hello_world.nbt"
|
||||
)))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
nbt.string("name"),
|
||||
Some(Mutf8Str::from_str("Bananrama").as_ref())
|
||||
);
|
||||
assert_eq!(nbt.name().to_str(), "hello world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_player() {
|
||||
let src = include_bytes!("../../tests/simple_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();
|
||||
|
||||
assert_eq!(nbt.int("PersistentId"), Some(1946940766));
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn 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();
|
||||
|
||||
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!(
|
||||
"../../tests/inttest1023.nbt"
|
||||
)))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let ints = nbt.list("").unwrap().ints().unwrap();
|
||||
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i32, item);
|
||||
}
|
||||
assert_eq!(ints.len(), 1023);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inttest_1024() {
|
||||
let mut data = Vec::new();
|
||||
data.write_u8(COMPOUND_ID).unwrap();
|
||||
data.write_u16::<BE>(0).unwrap();
|
||||
data.write_u8(LIST_ID).unwrap();
|
||||
data.write_u16::<BE>(0).unwrap();
|
||||
data.write_u8(INT_ID).unwrap();
|
||||
data.write_i32::<BE>(1024).unwrap();
|
||||
for i in 0..1024 {
|
||||
data.write_i32::<BE>(i).unwrap();
|
||||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::new(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().ints().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i32, item);
|
||||
}
|
||||
assert_eq!(ints.len(), 1024);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inttest_1021() {
|
||||
let mut data = Vec::new();
|
||||
data.write_u8(COMPOUND_ID).unwrap();
|
||||
data.write_u16::<BE>(0).unwrap();
|
||||
data.write_u8(LIST_ID).unwrap();
|
||||
data.write_u16::<BE>(0).unwrap();
|
||||
data.write_u8(INT_ID).unwrap();
|
||||
data.write_i32::<BE>(1021).unwrap();
|
||||
for i in 0..1021 {
|
||||
data.write_i32::<BE>(i).unwrap();
|
||||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::new(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().ints().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i32, item);
|
||||
}
|
||||
assert_eq!(ints.len(), 1021);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn longtest_1023() {
|
||||
let mut data = Vec::new();
|
||||
data.write_u8(COMPOUND_ID).unwrap();
|
||||
data.write_u16::<BE>(0).unwrap();
|
||||
data.write_u8(LIST_ID).unwrap();
|
||||
data.write_u16::<BE>(0).unwrap();
|
||||
data.write_u8(LONG_ID).unwrap();
|
||||
data.write_i32::<BE>(1023).unwrap();
|
||||
for i in 0..1023 {
|
||||
data.write_i64::<BE>(i).unwrap();
|
||||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::new(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().longs().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i64, item);
|
||||
}
|
||||
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();
|
||||
// }
|
||||
}
|
Loading…
Add table
Reference in a new issue