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

except they're not war crimes if you're winning

This commit is contained in:
mat 2024-05-19 06:50:01 +00:00
parent 1cc234c7fd
commit d420d3fb37
14 changed files with 1459 additions and 1115 deletions

View file

@ -19,20 +19,20 @@ The difference is that the "borrow" variant requires you to keep a reference to
use std::borrow::Cow;
use std::io::Cursor;
// fn example(item_bytes: &[u8]) {
// let nbt = simdnbt::borrow::read(&mut Cursor::new(item_bytes))
// .unwrap()
// .unwrap();
// let skyblock_id: Cow<str> = nbt
// .list("i")
// .and_then(|i| i.compounds())
// .and_then(|i| i.get(0))
// .and_then(|i| i.compound("tag"))
// .and_then(|tag| tag.compound("ExtraAttributes"))
// .and_then(|ea| ea.string("id"))
// .map(|id| id.to_string_lossy())
// .unwrap_or_default();
// }
fn example(item_bytes: &[u8]) {
let nbt = simdnbt::borrow::read(&mut Cursor::new(item_bytes))
.unwrap()
.unwrap();
let skyblock_id: Cow<str> = nbt
.list("i")
.and_then(|i| i.compounds())
.and_then(|i| i.get(0))
.and_then(|i| i.compound("tag"))
.and_then(|tag| tag.compound("ExtraAttributes"))
.and_then(|ea| ea.string("id"))
.map(|id| id.to_string_lossy())
.unwrap_or_default();
}
```
### Serializing

View file

@ -8,7 +8,6 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughpu
use flate2::read::GzDecoder;
pub fn bench_read_file(filename: &str, c: &mut Criterion) {
return;
let mut file = File::open(format!("tests/{filename}")).unwrap();
let mut contents = Vec::new();
file.read_to_end(&mut contents).unwrap();
@ -99,75 +98,73 @@ pub struct ItemDisplay {
}
fn simdnbt_items_from_nbt(nbt: simdnbt::borrow::BaseNbt) -> Option<Vec<Option<Item>>> {
// let mut items = Vec::new();
// for item_nbt in nbt
// .compound()
// .list("i")
// .and_then(|list| list.compounds())
// .unwrap_or_default()
// {
// // check if "id" is present, if not, skip
// if !item_nbt.contains("id") {
// // this just means the item isn't present
// items.push(None);
// continue;
// }
let mut items = Vec::new();
for item_nbt in nbt
.list("i")
.and_then(|list| list.compounds())
.unwrap_or_default()
{
// check if "id" is present, if not, skip
if !item_nbt.contains("id") {
// this just means the item isn't present
items.push(None);
continue;
}
// let item_tag = item_nbt.compound("tag")?;
// let item_extra_attributes = item_tag.compound("ExtraAttributes");
// let item_display = item_tag.compound("display");
let item_tag = item_nbt.compound("tag")?;
let item_extra_attributes = item_tag.compound("ExtraAttributes");
let item_display = item_tag.compound("display");
// items.push(Some(Item {
// id: item_nbt.short("id")?,
// damage: item_nbt.short("Damage")?,
// count: item_nbt.byte("Count")?,
items.push(Some(Item {
id: item_nbt.short("id")?,
damage: item_nbt.short("Damage")?,
count: item_nbt.byte("Count")?,
// head_texture_id: item_tag
// .compound("SkullOwner")
// .and_then(|skull_owner| skull_owner.compound("Properties"))
// .and_then(|properties| properties.list("textures"))
// .and_then(|textures| textures.compounds())
// .and_then(|textures| textures.next())
// .and_then(|texture| texture.string("Value"))
// // the real program does some base64+json decoding here but that's unnecessary for the benchmark
// .map(|value| value.to_string()),
// skyblock_id: item_extra_attributes
// .and_then(|e| e.string("id"))
// .map(|id| id.to_string()),
// reforge: item_extra_attributes
// .and_then(|e| e.string("modifier"))
// .map(|id| id.to_string()),
head_texture_id: item_tag
.compound("SkullOwner")
.and_then(|skull_owner| skull_owner.compound("Properties"))
.and_then(|properties| properties.list("textures"))
.and_then(|textures| textures.compounds())
.and_then(|textures| textures.get(0))
.and_then(|texture| texture.string("Value"))
// the real program does some base64+json decoding here but that's unnecessary for the benchmark
.map(|value| value.to_string()),
skyblock_id: item_extra_attributes
.and_then(|e| e.string("id"))
.map(|id| id.to_string()),
reforge: item_extra_attributes
.and_then(|e| e.string("modifier"))
.map(|id| id.to_string()),
// display: ItemDisplay {
// name: item_display
// .and_then(|d| d.string("Name"))
// .map(|n| n.to_string())
// .unwrap_or_default(),
// lore: item_display
// .and_then(|d| d.list("Lore"))
// .and_then(|l| l.strings())
// .map(|l| l.iter().map(|s| s.to_string()).collect())
// .unwrap_or_default(),
// color: item_display.and_then(|d| d.int("color")),
// has_glint: item_extra_attributes
// .map(|e| e.contains("ench"))
// .unwrap_or_default(),
// },
// enchantments: item_extra_attributes
// .and_then(|e| e.compound("enchantments"))
// .map(|e| {
// e.iter()
// .map(|(k, v)| (k.to_string(), v.int().unwrap_or_default()))
// .collect()
// })
// .unwrap_or_default(),
// timestamp: item_extra_attributes
// .and_then(|e| e.string("timestamp"))
// .map(|t| t.to_string()),
// }));
// }
// Some(items)
None
display: ItemDisplay {
name: item_display
.and_then(|d| d.string("Name"))
.map(|n| n.to_string())
.unwrap_or_default(),
lore: item_display
.and_then(|d| d.list("Lore"))
.and_then(|l| l.strings())
.map(|l| l.iter().map(|s| s.to_string()).collect())
.unwrap_or_default(),
color: item_display.and_then(|d| d.int("color")),
has_glint: item_extra_attributes
.map(|e| e.contains("ench"))
.unwrap_or_default(),
},
enchantments: item_extra_attributes
.and_then(|e| e.compound("enchantments"))
.map(|e| {
e.iter()
.map(|(k, v)| (k.to_string(), v.int().unwrap_or_default()))
.collect()
})
.unwrap_or_default(),
timestamp: item_extra_attributes
.and_then(|e| e.string("timestamp"))
.map(|t| t.to_string()),
}));
}
Some(items)
}
fn azalea_items_from_nbt(nbt: azalea_nbt::Nbt) -> Option<Vec<Option<Item>>> {

View file

@ -35,7 +35,7 @@ fn bench_file(filename: &str, c: &mut Criterion) {
let nbt = simdnbt::borrow::read(&mut input_stream).unwrap().unwrap();
group.bench_function("Get", |b| {
b.iter(|| {
let level = nbt.compound().compound("abilities").unwrap();
let level = nbt.compound("abilities").unwrap();
for (k, _) in level.iter() {
black_box(level.get(black_box(&k.to_str())));
}

View file

@ -1,23 +1,24 @@
use std::{io::Cursor, mem::MaybeUninit};
use byteorder::ReadBytesExt;
use std::{hint::unreachable_unchecked, mem::MaybeUninit};
use crate::{
common::{
read_string, skip_string, unchecked_extend, unchecked_push, unchecked_write_string,
write_string, END_ID, MAX_DEPTH,
read_int_array, read_long_array, read_string, read_with_u32_length, unchecked_extend,
unchecked_push, unchecked_write_string, 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,
},
reader::Reader,
Error, Mutf8Str,
};
use super::{
extra_tapes::ExtraTapes,
list::NbtList,
tape::{MainTape, TapeElement, TapeTagKind, TapeTagValue, UnalignedU16},
list::{self, NbtList},
tape::{TapeElement, TapeTagKind, TapeTagValue, UnalignedU16},
NbtTag, Tapes,
};
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct NbtCompound<'a: 'tape, 'tape> {
pub(crate) element: *const TapeElement, // includes the initial compound element
pub(crate) extra_tapes: &'tape ExtraTapes<'a>,
@ -25,136 +26,138 @@ pub struct NbtCompound<'a: 'tape, 'tape> {
impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
pub(crate) fn read(
data: &mut Cursor<&'a [u8]>,
// compounds have no header so nothing to read
_data: &mut Reader<'a>,
tapes: &'tape mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
Self::read_with_depth(data, tapes, 0)
}
let index_of_compound_element = tapes.main.len();
pub(crate) fn read_with_depth(
data: &mut Cursor<&'a [u8]>,
tapes: &'tape mut Tapes<'a>,
depth: usize,
) -> Result<(), Error> {
if depth > MAX_DEPTH {
return Err(Error::MaxDepthExceeded);
}
let index_of_compound_element = tapes.main.elements.len();
tapes.main.elements.push(TapeElement {
stack.push(ParsingStackElement::Compound {
index_of_compound_element: index_of_compound_element as u32,
})?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::Compound,
TapeTagValue {
// this gets overridden later
// this gets overwritten later
compound: (0.into(), 0.into()),
},
),
});
loop {
let tag_type = match data.read_u8() {
Ok(tag_type) => tag_type,
Err(_) => {
return Err(Error::UnexpectedEof);
}
};
if tag_type == END_ID {
break;
}
let tag_name_pointer = data.get_ref().as_ptr() as u64 + data.position();
debug_assert_eq!(tag_name_pointer >> 56, 0);
if let Err(e) = skip_string(data) {
return Err(e);
};
tapes.main.elements.push(TapeElement {
name: tag_name_pointer,
});
match NbtTag::read_with_type(data, tapes, tag_type, depth) {
Ok(tag) => tag,
Err(e) => {
return Err(e);
}
};
}
let index_after_end_element = tapes.main.elements.len();
unsafe {
tapes
.main
.elements
.get_unchecked_mut(index_of_compound_element)
.kind
.1
.compound = (
0.into(),
((index_after_end_element - index_of_compound_element) as u32).into(),
);
};
Ok(())
}
// pub fn write(&self, data: &mut Vec<u8>) {
// for (name, tag) in self.values {
// // reserve 4 bytes extra so we can avoid reallocating for small tags
// data.reserve(1 + 2 + name.len() + 4);
// // SAFETY: We just reserved enough space for the tag ID, the name length, the name, and
// // 4 bytes of tag data.
// unsafe {
// unchecked_push(data, tag.id());
// unchecked_write_string(data, name);
// }
// match tag {
// NbtTag::Byte(byte) => unsafe {
// unchecked_push(data, *byte as u8);
// },
// NbtTag::Short(short) => unsafe {
// unchecked_extend(data, &short.to_be_bytes());
// },
// NbtTag::Int(int) => unsafe {
// unchecked_extend(data, &int.to_be_bytes());
// },
// NbtTag::Long(long) => {
// data.extend_from_slice(&long.to_be_bytes());
// }
// NbtTag::Float(float) => unsafe {
// unchecked_extend(data, &float.to_be_bytes());
// },
// NbtTag::Double(double) => {
// data.extend_from_slice(&double.to_be_bytes());
// }
// NbtTag::ByteArray(byte_array) => {
// unsafe {
// unchecked_extend(data, &byte_array.len().to_be_bytes());
// }
// data.extend_from_slice(byte_array);
// }
// NbtTag::String(string) => {
// write_string(data, string);
// }
// NbtTag::List(list) => {
// list.write(data);
// }
// NbtTag::Compound(compound) => {
// compound.write(data);
// }
// NbtTag::IntArray(int_array) => {
// unsafe {
// unchecked_extend(data, &int_array.len().to_be_bytes());
// }
// data.extend_from_slice(int_array.as_big_endian());
// }
// NbtTag::LongArray(long_array) => {
// unsafe {
// unchecked_extend(data, &long_array.len().to_be_bytes());
// }
// data.extend_from_slice(long_array.as_big_endian());
// }
// }
// }
// data.push(END_ID);
// }
pub fn write(&self, data: &mut Vec<u8>) {
for (name, tag) in self.iter() {
// reserve 4 bytes extra so we can avoid reallocating for small tags
data.reserve(1 + 2 + name.len() + 4);
// SAFETY: We just reserved enough space for the tag ID, the name length, the name, and
// 4 bytes of tag data.
unsafe {
unchecked_push(data, tag.id());
unchecked_write_string(data, name);
}
let (kind, _) = tag.element();
match kind {
// NbtTag::Byte(byte) => unsafe {
// unchecked_push(data, *byte as u8);
// },
TapeTagKind::Byte => unsafe {
unchecked_push(data, tag.byte().unwrap() as u8);
}, // NbtTag::Short(short) => unsafe {
// unchecked_extend(data, &short.to_be_bytes());
// },
TapeTagKind::Short => unsafe {
unchecked_extend(data, &tag.short().unwrap().to_be_bytes());
}, // NbtTag::Int(int) => unsafe {
// unchecked_extend(data, &int.to_be_bytes());
// },
TapeTagKind::Int => unsafe {
unchecked_extend(data, &tag.int().unwrap().to_be_bytes());
}, // NbtTag::Long(long) => {
// data.extend_from_slice(&long.to_be_bytes());
// }
TapeTagKind::Long => {
data.extend_from_slice(&tag.long().unwrap().to_be_bytes());
} // NbtTag::Float(float) => unsafe {
// unchecked_extend(data, &float.to_be_bytes());
// },
TapeTagKind::Float => unsafe {
unchecked_extend(data, &tag.float().unwrap().to_be_bytes());
}, // NbtTag::Double(double) => {
// data.extend_from_slice(&double.to_be_bytes());
// }
TapeTagKind::Double => {
data.extend_from_slice(&tag.double().unwrap().to_be_bytes());
} // NbtTag::ByteArray(byte_array) => {
// unsafe {
// unchecked_extend(data, &byte_array.len().to_be_bytes());
// }
// data.extend_from_slice(byte_array);
// }
TapeTagKind::ByteArray => {
let byte_array = tag.byte_array().unwrap();
unsafe {
unchecked_extend(data, &byte_array.len().to_be_bytes());
}
data.extend_from_slice(byte_array);
} // NbtTag::String(string) => {
// write_string(data, string);
// }
TapeTagKind::String => {
let string = tag.string().unwrap();
write_string(data, string);
} // NbtTag::List(list) => {
// list.write(data);
// }
_ if kind.is_list() => {
tag.list().unwrap().write(data);
} // NbtTag::Compound(compound) => {
// compound.write(data);
// }
TapeTagKind::Compound => {
tag.compound().unwrap().write(data);
} // NbtTag::IntArray(int_array) => {
// unsafe {
// unchecked_extend(data, &int_array.len().to_be_bytes());
// }
// data.extend_from_slice(int_array.as_big_endian());
// }
TapeTagKind::IntArray => {
let int_array = list::u32_prefixed_list_to_rawlist::<i32>(
TapeTagKind::IntArray,
self.element,
)
.unwrap();
unsafe {
unchecked_extend(data, &int_array.len().to_be_bytes());
}
data.extend_from_slice(int_array.as_big_endian());
} // NbtTag::LongArray(long_array) => {
// unsafe {
// unchecked_extend(data, &long_array.len().to_be_bytes());
// }
// data.extend_from_slice(long_array.as_big_endian());
// }
TapeTagKind::LongArray => {
let long_array = list::u32_prefixed_list_to_rawlist::<i64>(
TapeTagKind::LongArray,
self.element,
)
.unwrap();
unsafe {
unchecked_extend(data, &long_array.len().to_be_bytes());
}
data.extend_from_slice(long_array.as_big_endian());
}
_ => unreachable!("Invalid tag kind {kind:?}"),
}
}
data.push(END_ID);
}
#[inline]
pub fn get(&self, name: &str) -> Option<NbtTag<'a, 'tape>> {
@ -198,10 +201,10 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
pub fn double(&self, name: &str) -> Option<f64> {
self.get(name).and_then(|tag| tag.double())
}
pub fn byte_array(&self, name: &str) -> Option<&[u8]> {
pub fn byte_array(&self, name: &str) -> Option<&'a [u8]> {
self.get(name).and_then(|tag| tag.byte_array())
}
pub fn string(&self, name: &str) -> Option<&Mutf8Str> {
pub fn string(&self, name: &str) -> Option<&'a Mutf8Str> {
self.get(name).and_then(|tag| tag.string())
}
pub fn list(&self, name: &str) -> Option<NbtList<'a, 'tape>> {
@ -227,9 +230,8 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
debug_assert_eq!(kind, TapeTagKind::Compound);
let max_tape_offset = u32::from(unsafe { value.list_list.1 }) as usize;
let tape_slice = unsafe {
std::slice::from_raw_parts((self.element as *const TapeElement).add(1), max_tape_offset)
};
let tape_slice =
unsafe { std::slice::from_raw_parts(self.element.add(1), max_tape_offset) };
CompoundIter {
current_tape_offset: 0,
@ -263,6 +265,7 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[allow(clippy::type_complexity)]
pub fn keys(
&self,
) -> std::iter::Map<
@ -282,6 +285,12 @@ impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
}
}
impl PartialEq for NbtCompound<'_, '_> {
fn eq(&self, other: &Self) -> bool {
self.iter().eq(other.iter())
}
}
pub struct CompoundIter<'a: 'tape, 'tape> {
current_tape_offset: usize,
max_tape_offset: usize,
@ -292,30 +301,270 @@ impl<'a: 'tape, 'tape> Iterator for CompoundIter<'a, 'tape> {
type Item = (&'a Mutf8Str, NbtTag<'a, 'tape>);
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.current_tape_offset + 1 >= self.max_tape_offset {
return None;
}
if self.current_tape_offset + 1 >= self.max_tape_offset {
return None;
}
let name_length_ptr = unsafe { self.tape[self.current_tape_offset].name };
let name_length_ptr = name_length_ptr as *const UnalignedU16;
let name_length = u16::from(unsafe { *name_length_ptr }).swap_bytes();
let name_pointer = unsafe { name_length_ptr.add(1) as *const u8 };
let name_slice =
unsafe { std::slice::from_raw_parts(name_pointer, name_length as usize) };
let name = Mutf8Str::from_slice(name_slice);
let name_length_ptr = unsafe { self.tape[self.current_tape_offset].name };
let name_length_ptr = name_length_ptr as *const UnalignedU16;
let name_length = u16::from(unsafe { *name_length_ptr }).swap_bytes();
let name_pointer = unsafe { name_length_ptr.add(1) as *const u8 };
let name_slice = unsafe { std::slice::from_raw_parts(name_pointer, name_length as usize) };
let name = Mutf8Str::from_slice(name_slice);
self.current_tape_offset += 1;
self.current_tape_offset += 1;
let element = unsafe { self.tape.as_ptr().add(self.current_tape_offset as usize) };
let tag = NbtTag {
element,
extra_tapes: self.extra_tapes,
};
let element = unsafe { self.tape.as_ptr().add(self.current_tape_offset) };
let tag = NbtTag {
element,
extra_tapes: self.extra_tapes,
};
self.current_tape_offset += unsafe { (*element).skip_offset() };
self.current_tape_offset += unsafe { (*element).skip_offset() };
return Some((name, tag));
Some((name, tag))
}
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum ParsingStackElement {
Compound { index_of_compound_element: u32 },
ListOfLists { index_of_list_element: u32 },
ListOfCompounds { index_of_list_element: u32 },
}
pub struct ParsingStack {
stack: [MaybeUninit<ParsingStackElement>; MAX_DEPTH],
remaining_elements_in_lists: [u32; MAX_DEPTH],
depth: usize,
}
impl ParsingStack {
pub fn new() -> Self {
Self {
stack: unsafe { MaybeUninit::uninit().assume_init() },
remaining_elements_in_lists: [0; MAX_DEPTH],
depth: 0,
}
}
#[inline]
pub fn push(&mut self, state: ParsingStackElement) -> Result<(), Error> {
// self.stack[self.depth] = MaybeUninit::new(state);
unsafe { self.stack.get_unchecked_mut(self.depth).write(state) };
self.depth += 1;
if self.depth > MAX_DEPTH {
Err(Error::MaxDepthExceeded)
} else {
Ok(())
}
}
#[inline]
pub fn set_list_length(&mut self, length: u32) {
unsafe {
*self
.remaining_elements_in_lists
.get_unchecked_mut(self.depth - 1) = length;
};
}
#[inline]
pub fn decrement_list_length(&mut self) {
unsafe {
*self
.remaining_elements_in_lists
.get_unchecked_mut(self.depth - 1) -= 1;
};
}
#[inline]
pub fn remaining_elements_in_list(&self) -> u32 {
unsafe {
*self
.remaining_elements_in_lists
.get_unchecked(self.depth - 1)
}
}
#[inline]
pub fn pop(&mut self) -> ParsingStackElement {
self.depth -= 1;
unsafe { self.stack.get_unchecked(self.depth).assume_init() }
}
#[inline]
pub fn peek(&self) -> ParsingStackElement {
unsafe { self.stack.get_unchecked(self.depth - 1).assume_init() }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.depth == 0
}
#[inline]
pub fn peek_mut(&mut self) -> &mut ParsingStackElement {
unsafe {
self.stack
.get_unchecked_mut(self.depth - 1)
.as_mut_ptr()
.as_mut()
.unwrap_unchecked()
}
}
}
#[inline(always)]
pub(crate) fn read_tag<'a>(
data: &mut Reader<'a>,
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
tag_type: u8,
) -> Result<(), Error> {
match tag_type {
COMPOUND_ID => return NbtCompound::read(data, tapes, stack),
LIST_ID => return NbtList::read(data, tapes, stack),
_ => {}
}
match tag_type {
BYTE_ID => {
let byte = data.read_i8()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Byte, TapeTagValue { byte }),
});
}
SHORT_ID => {
let short = data.read_i16()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Short, TapeTagValue { short }),
});
}
INT_ID => {
let int = data.read_i32()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Int, TapeTagValue { int }),
});
}
LONG_ID => {
let long = data.read_i64()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Long, TapeTagValue { long: () }),
});
tapes.main.push(TapeElement { long });
}
FLOAT_ID => {
let float = data.read_f32()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Float, TapeTagValue { float }),
});
}
DOUBLE_ID => {
let double = data.read_f64()?;
tapes.main.push(TapeElement {
kind: (TapeTagKind::Double, TapeTagValue { double: () }),
});
tapes.main.push(TapeElement { double });
}
BYTE_ARRAY_ID => {
let byte_array_pointer = data.cur as u64;
read_with_u32_length(data, 1)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::ByteArray,
TapeTagValue {
byte_array: byte_array_pointer.into(),
},
),
});
}
STRING_ID => {
let string_pointer = data.cur as u64;
// assert that the top 8 bits of the pointer are 0 (because we rely on this)
debug_assert_eq!(string_pointer >> 56, 0);
read_string(data)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::String,
TapeTagValue {
string: string_pointer.into(),
},
),
});
}
INT_ARRAY_ID => {
let int_array_pointer = data.cur as u64;
read_int_array(data)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::IntArray,
TapeTagValue {
int_array: int_array_pointer.into(),
},
),
});
}
LONG_ARRAY_ID => {
let long_array_pointer = data.cur as u64;
read_long_array(data)?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::LongArray,
TapeTagValue {
long_array: long_array_pointer.into(),
},
),
});
}
_ => return Err(Error::UnknownTagId(tag_type)),
};
Ok(())
}
#[inline(always)]
pub(crate) fn read_tag_in_compound<'a>(
data: &mut Reader<'a>,
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
let tag_type = data.read_u8()?;
if tag_type == END_ID {
handle_compound_end(tapes, stack);
return Ok(());
}
let tag_name_pointer = data.cur as u64;
debug_assert_eq!(tag_name_pointer >> 56, 0);
read_string(data)?;
tapes.main.push(TapeElement {
name: tag_name_pointer,
});
read_tag(data, tapes, stack, tag_type)
}
#[inline(always)]
fn handle_compound_end(tapes: &mut Tapes, stack: &mut ParsingStack) {
let ParsingStackElement::Compound {
index_of_compound_element,
} = stack.pop()
else {
unsafe { unreachable_unchecked() };
};
let index_after_end_element = tapes.main.len();
unsafe {
tapes
.main
.get_unchecked_mut(index_of_compound_element as usize)
.kind
.1
.compound
.1 = (index_after_end_element as u32 - index_of_compound_element).into();
};
}

File diff suppressed because it is too large Load diff

View file

@ -8,24 +8,24 @@ mod tape;
use std::{
fmt::{self, Debug},
io::Cursor,
ops::Deref,
};
use byteorder::{ReadBytesExt, BE};
use byteorder::ReadBytesExt;
use crate::{
common::{
read_int_array, read_long_array, read_string, read_u32, read_with_u32_length, 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,
read_string, 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, SHORT_ID, STRING_ID,
},
raw_list::RawList,
reader::{Reader, ReaderFromCursor},
Error, Mutf8Str,
};
pub use self::{compound::NbtCompound, list::NbtList};
use self::{
compound::{read_tag_in_compound, ParsingStack, ParsingStackElement},
extra_tapes::ExtraTapes,
list::{read_compound_in_list, read_list_in_list},
tape::{MainTape, TapeElement, TapeTagKind, TapeTagValue, UnalignedU16},
};
@ -33,7 +33,35 @@ use self::{
///
/// Returns `Ok(Nbt::None)` if there is no data.
pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
Nbt::read(data)
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
return Ok(Nbt::None);
}
if root_type != COMPOUND_ID {
return Err(Error::InvalidRootType(root_type));
}
let mut data = ReaderFromCursor::new(data);
let name = read_string(&mut data)?;
let mut tapes = Tapes::new();
let mut stack = ParsingStack::new();
stack.push(ParsingStackElement::Compound {
index_of_compound_element: 0,
})?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::Compound,
TapeTagValue {
// this gets overwritten later
compound: (0.into(), 0.into()),
},
),
});
read_with_stack(&mut data, &mut tapes, &mut stack)?;
Ok(Nbt::Some(BaseNbt { name, tapes }))
}
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading
/// NBT over the network.
@ -41,19 +69,56 @@ pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either
/// empty or a compound).
pub fn read_unnamed<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
Nbt::read_unnamed(data)
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
return Ok(Nbt::None);
}
if root_type != COMPOUND_ID {
return Err(Error::InvalidRootType(root_type));
}
let name = Mutf8Str::from_slice(&[]);
let BaseNbtCompound { tapes } = read_compound(data)?;
Ok(Nbt::Some(BaseNbt { name, tapes }))
}
/// Read a compound tag. This may have any number of items.
pub fn read_compound<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtCompound<'a>, Error> {
let mut tapes = Tapes::new();
NbtCompound::read(data, &mut tapes)?;
let mut stack = ParsingStack::new();
let mut data = ReaderFromCursor::new(data);
stack.push(ParsingStackElement::Compound {
index_of_compound_element: 0,
})?;
tapes.main.push(TapeElement {
kind: (
TapeTagKind::Compound,
TapeTagValue {
// this gets overwritten later
compound: (0.into(), 0.into()),
},
),
});
read_with_stack(&mut data, &mut tapes, &mut stack)?;
Ok(BaseNbtCompound { tapes })
}
/// Read an NBT tag, without reading its name. This may be any type of tag except for an end tag. If you need to be able to
/// handle end tags, use [`read_optional_tag`].
pub fn read_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtTag<'a>, Error> {
let mut tapes = Tapes::new();
NbtTag::read(data, &mut tapes)?;
let mut stack = ParsingStack::new();
let mut data = ReaderFromCursor::new(data);
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if tag_type == END_ID {
return Err(Error::InvalidRootType(0));
}
compound::read_tag(&mut data, &mut tapes, &mut stack, tag_type)?;
read_with_stack(&mut data, &mut tapes, &mut stack)?;
Ok(BaseNbtTag { tapes })
}
/// Read any NBT tag, without reading its name. This may be any type of tag, including an end tag.
@ -61,12 +126,36 @@ pub fn read_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtTag<'a>, Error
/// Returns `Ok(None)` if there is no data.
pub fn read_optional_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Option<BaseNbtTag<'a>>, Error> {
let mut tapes = Tapes::new();
let tag = NbtTag::read_optional(data, &mut tapes)?;
Ok(if tag {
Some(BaseNbtTag { tapes })
} else {
None
})
let mut stack = ParsingStack::new();
let mut data = ReaderFromCursor::new(data);
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if tag_type == END_ID {
return Ok(None);
}
compound::read_tag(&mut data, &mut tapes, &mut stack, tag_type)?;
read_with_stack(&mut data, &mut tapes, &mut stack)?;
Ok(Some(BaseNbtTag { tapes }))
}
fn read_with_stack<'a>(
data: &mut Reader<'a>,
tapes: &mut Tapes<'a>,
stack: &mut ParsingStack,
) -> Result<(), Error> {
while !stack.is_empty() {
match stack.peek_mut() {
ParsingStackElement::Compound { .. } => read_tag_in_compound(data, tapes, stack)?,
ParsingStackElement::ListOfLists { .. } => read_list_in_list(data, tapes, stack)?,
ParsingStackElement::ListOfCompounds { .. } => {
read_compound_in_list(data, tapes, stack)?
}
}
}
Ok(())
}
#[derive(Default)]
@ -91,12 +180,13 @@ pub struct BaseNbt<'a> {
tapes: Tapes<'a>,
}
impl<'a> BaseNbt<'a> {
pub fn compound<'tape>(&'a self) -> NbtCompound<'a, 'tape>
#[inline]
pub fn as_compound<'tape>(&'a self) -> NbtCompound<'a, 'tape>
where
'a: 'tape,
{
NbtCompound {
element: self.tapes.main.elements.as_ptr(),
element: self.tapes.main.as_ptr(),
extra_tapes: &self.tapes.extra,
}
}
@ -105,7 +195,52 @@ impl<'a> BaseNbt<'a> {
pub fn name(&self) -> &'a Mutf8Str {
self.name
}
pub fn get<'tape>(&'a self, key: &str) -> Option<NbtTag<'a, 'tape>> {
self.as_compound().get(key)
}
/// Returns whether there is a tag with the given name.
pub fn contains<'tape>(&'a self, key: &str) -> bool {
self.as_compound().contains(key)
}
pub fn byte(&self, name: &str) -> Option<i8> {
self.as_compound().byte(name)
}
pub fn short(&self, name: &str) -> Option<i16> {
self.as_compound().short(name)
}
pub fn int(&self, name: &str) -> Option<i32> {
self.as_compound().int(name)
}
pub fn long(&self, name: &str) -> Option<i64> {
self.as_compound().long(name)
}
pub fn float(&self, name: &str) -> Option<f32> {
self.as_compound().float(name)
}
pub fn double(&self, name: &str) -> Option<f64> {
self.as_compound().double(name)
}
pub fn byte_array(&'a self, name: &str) -> Option<&'a [u8]> {
self.as_compound().byte_array(name)
}
pub fn string(&'a self, name: &str) -> Option<&'a Mutf8Str> {
self.as_compound().string(name)
}
pub fn list<'tape>(&'a self, name: &str) -> Option<NbtList<'a, 'tape>> {
self.as_compound().list(name)
}
pub fn compound<'tape>(&'a self, name: &str) -> Option<NbtCompound<'a, 'tape>> {
self.as_compound().compound(name)
}
pub fn int_array(&self, name: &str) -> Option<Vec<i32>> {
self.as_compound().int_array(name)
}
pub fn long_array(&self, name: &str) -> Option<Vec<i64>> {
self.as_compound().long_array(name)
}
}
impl<'a> Debug for BaseNbt<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BaseNbt").finish()
@ -128,7 +263,7 @@ impl<'a> BaseNbtTag<'a> {
'a: 'tape,
{
NbtCompound {
element: self.tapes.main.elements.as_ptr(),
element: self.tapes.main.as_ptr(),
extra_tapes: &self.tapes.extra,
}
}
@ -143,50 +278,13 @@ pub enum Nbt<'a> {
}
impl<'a> Nbt<'a> {
/// Reads NBT from the given data. Returns `Ok(Nbt::None)` if there is no data.
fn read(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
return Ok(Nbt::None);
}
if root_type != COMPOUND_ID {
return Err(Error::InvalidRootType(root_type));
}
let mut tapes = Tapes::new();
let name = read_string(data)?;
NbtCompound::read(data, &mut tapes)?;
Ok(Nbt::Some(BaseNbt { name, tapes }))
}
fn read_unnamed(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
return Ok(Nbt::None);
}
if root_type != COMPOUND_ID {
return Err(Error::InvalidRootType(root_type));
}
let mut tapes = Tapes::new();
NbtCompound::read(data, &mut tapes)?;
Ok(Nbt::Some(BaseNbt {
name: Mutf8Str::from_slice(&[]),
tapes,
}))
}
pub fn write(&self, data: &mut Vec<u8>) {
todo!();
// match self {
// Nbt::Some(nbt) => nbt.write(data),
// Nbt::None => {
// data.push(END_ID);
// }
// }
match self {
Nbt::Some(nbt) => nbt.write(data),
Nbt::None => {
data.push(END_ID);
}
}
}
pub fn unwrap(self) -> BaseNbt<'a> {
@ -210,19 +308,18 @@ impl<'a> Nbt<'a> {
impl PartialEq for BaseNbt<'_> {
fn eq(&self, other: &Self) -> bool {
todo!();
// we don't need to compare the tapes since comparing `tag` will
// still compare the values of the tags
// self.name == other.name && self.tag == other.tag
self.name == other.name && self.as_compound() == other.as_compound()
}
}
impl<'a> BaseNbt<'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);
data.push(COMPOUND_ID);
write_string(data, self.name);
self.as_compound().write(data);
data.push(END_ID);
}
}
@ -253,142 +350,7 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
}
}
#[inline(always)]
fn read_with_type(
data: &mut Cursor<&'a [u8]>,
tapes: &'tape mut Tapes<'a>,
tag_type: u8,
depth: usize,
) -> Result<(), Error> {
match tag_type {
BYTE_ID => {
let byte = data.read_i8().map_err(|_| Error::UnexpectedEof)?;
tapes.main.elements.push(TapeElement {
kind: (TapeTagKind::Byte, TapeTagValue { byte }),
});
Ok(())
}
SHORT_ID => {
let short = data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?;
tapes.main.elements.push(TapeElement {
kind: (TapeTagKind::Short, TapeTagValue { short }),
});
Ok(())
}
INT_ID => {
let int = data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?;
tapes.main.elements.push(TapeElement {
kind: (TapeTagKind::Int, TapeTagValue { int }),
});
Ok(())
}
LONG_ID => {
let long = data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?;
tapes.main.elements.push(TapeElement {
kind: (TapeTagKind::Long, TapeTagValue { long: () }),
});
tapes.main.elements.push(TapeElement { long });
Ok(())
}
FLOAT_ID => {
let float = data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?;
tapes.main.elements.push(TapeElement {
kind: (TapeTagKind::Float, TapeTagValue { float }),
});
Ok(())
}
DOUBLE_ID => {
let double = data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?;
tapes.main.elements.push(TapeElement {
kind: (TapeTagKind::Double, TapeTagValue { double: () }),
});
tapes.main.elements.push(TapeElement { double });
Ok(())
}
BYTE_ARRAY_ID => {
let byte_array_pointer = data.get_ref().as_ptr() as u64 + data.position();
read_with_u32_length(data, 1)?;
tapes.main.elements.push(TapeElement {
kind: (
TapeTagKind::ByteArray,
TapeTagValue {
byte_array: byte_array_pointer.into(),
},
),
});
Ok(())
}
STRING_ID => {
let string_pointer = data.get_ref().as_ptr() as u64 + data.position();
// assert that the top 8 bits of the pointer are 0 (because we rely on this)
debug_assert_eq!(string_pointer >> 56, 0);
read_string(data)?;
tapes.main.elements.push(TapeElement {
kind: (
TapeTagKind::String,
TapeTagValue {
string: string_pointer.into(),
},
),
});
Ok(())
}
LIST_ID => NbtList::read(data, tapes, depth + 1),
COMPOUND_ID => NbtCompound::read_with_depth(data, tapes, depth + 1),
INT_ARRAY_ID => {
let int_array_pointer = data.get_ref().as_ptr() as u64 + data.position();
// let int_array = read_int_array(data)?;
tapes.main.elements.push(TapeElement {
kind: (
TapeTagKind::IntArray,
TapeTagValue {
int_array: int_array_pointer.into(),
},
),
});
Ok(())
}
LONG_ARRAY_ID => {
let long_array_pointer = data.get_ref().as_ptr() as u64 + data.position();
read_long_array(data)?;
tapes.main.elements.push(TapeElement {
kind: (
TapeTagKind::LongArray,
TapeTagValue {
long_array: long_array_pointer.into(),
},
),
});
Ok(())
}
_ => Err(Error::UnknownTagId(tag_type)),
}
}
fn read(data: &mut Cursor<&'a [u8]>, tapes: &'tape mut Tapes<'a>) -> Result<(), Error> {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
Self::read_with_type(data, tapes, tag_type, 0)
}
fn read_optional(
data: &mut Cursor<&'a [u8]>,
tapes: &'tape mut Tapes<'a>,
) -> Result<bool, Error> {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if tag_type == END_ID {
return Ok(false);
}
Self::read_with_type(data, tapes, tag_type, 0)?;
Ok(true)
}
pub fn byte(&self) -> Option<i8> {
// match self {
// NbtTag::Byte(byte) => Some(*byte),
// _ => None,
// }
let (kind, value) = self.element();
if kind != TapeTagKind::Byte {
return None;
@ -396,10 +358,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { value.byte })
}
pub fn short(&self) -> Option<i16> {
// match self {
// NbtTag::Short(short) => Some(*short),
// _ => None,
// }
let (kind, value) = self.element();
if kind != TapeTagKind::Short {
return None;
@ -407,10 +365,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { value.short })
}
pub fn int(&self) -> Option<i32> {
// match self {
// NbtTag::Int(int) => Some(*int),
// _ => None,
// }
let (kind, value) = unsafe { (*self.element).kind };
if kind != TapeTagKind::Int {
return None;
@ -418,10 +372,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { value.int })
}
pub fn long(&self) -> Option<i64> {
// match self {
// NbtTag::Long(long) => Some(*long),
// _ => None,
// }
let (kind, _) = self.element();
if kind != TapeTagKind::Long {
return None;
@ -431,10 +381,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { (*value).long })
}
pub fn float(&self) -> Option<f32> {
// match self {
// NbtTag::Float(float) => Some(*float),
// _ => None,
// }
let (kind, value) = self.element();
if kind != TapeTagKind::Float {
return None;
@ -442,10 +388,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { value.float })
}
pub fn double(&self) -> Option<f64> {
// match self {
// NbtTag::Double(double) => Some(*double),
// _ => None,
// }
let (kind, _) = self.element();
if kind != TapeTagKind::Double {
return None;
@ -455,10 +397,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { (*value).double })
}
pub fn byte_array(&self) -> Option<&'a [u8]> {
// match self {
// NbtTag::ByteArray(byte_array) => Some(byte_array),
// _ => None,
// }
let (kind, value) = self.element();
if kind != TapeTagKind::ByteArray {
return None;
@ -469,10 +407,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { std::slice::from_raw_parts(data_ptr, length) })
}
pub fn string(&self) -> Option<&'a Mutf8Str> {
// match self {
// NbtTag::String(string) => Some(string),
// _ => None,
// }
let (kind, value) = self.element();
if kind != TapeTagKind::String {
return None;
@ -483,10 +417,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
Some(unsafe { Mutf8Str::from_slice(std::slice::from_raw_parts(data_ptr, length)) })
}
pub fn list(&self) -> Option<NbtList<'a, 'tape>> {
// match self {
// NbtTag::List(list) => Some(list),
// _ => None,
// }
let (kind, _) = self.element();
if !kind.is_list() {
return None;
@ -498,10 +428,6 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
})
}
pub fn compound(&self) -> Option<NbtCompound<'a, 'tape>> {
// match self {
// NbtTag::Compound(compound) => Some(compound),
// _ => None,
// }
let (kind, _) = self.element();
if kind != TapeTagKind::Compound {
return None;
@ -525,21 +451,52 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
}
pub fn to_owned(&self) -> crate::owned::NbtTag {
todo!()
// match self {
// NbtTag::Byte(byte) => crate::owned::NbtTag::Byte(*byte),
// NbtTag::Short(short) => crate::owned::NbtTag::Short(*short),
// NbtTag::Int(int) => crate::owned::NbtTag::Int(*int),
// NbtTag::Long(long) => crate::owned::NbtTag::Long(*long),
// NbtTag::Float(float) => crate::owned::NbtTag::Float(*float),
// NbtTag::Double(double) => crate::owned::NbtTag::Double(*double),
// NbtTag::ByteArray(byte_array) => crate::owned::NbtTag::ByteArray(byte_array.to_vec()),
// NbtTag::String(string) => crate::owned::NbtTag::String((*string).to_owned()),
// NbtTag::List(list) => crate::owned::NbtTag::List(list.to_owned()),
// NbtTag::Compound(compound) => crate::owned::NbtTag::Compound(compound.to_owned()),
// NbtTag::IntArray(int_array) => crate::owned::NbtTag::IntArray(int_array.to_vec()),
// NbtTag::LongArray(long_array) => crate::owned::NbtTag::LongArray(long_array.to_vec()),
// }
let (kind, _value) = self.element();
match kind {
TapeTagKind::Byte => crate::owned::NbtTag::Byte(self.byte().unwrap()),
TapeTagKind::Short => crate::owned::NbtTag::Short(self.short().unwrap()),
TapeTagKind::Int => crate::owned::NbtTag::Int(self.int().unwrap()),
TapeTagKind::Long => crate::owned::NbtTag::Long(self.long().unwrap()),
TapeTagKind::Float => crate::owned::NbtTag::Float(self.float().unwrap()),
TapeTagKind::Double => crate::owned::NbtTag::Double(self.double().unwrap()),
TapeTagKind::ByteArray => {
crate::owned::NbtTag::ByteArray(self.byte_array().unwrap().to_vec())
}
TapeTagKind::String => crate::owned::NbtTag::String(self.string().unwrap().to_owned()),
TapeTagKind::Compound => {
crate::owned::NbtTag::Compound(self.compound().unwrap().to_owned())
}
_ if kind.is_list() => crate::owned::NbtTag::List(self.list().unwrap().to_owned()),
TapeTagKind::IntArray => crate::owned::NbtTag::IntArray(self.int_array().unwrap()),
TapeTagKind::LongArray => crate::owned::NbtTag::LongArray(self.long_array().unwrap()),
_ => unreachable!(),
}
}
}
impl PartialEq for NbtTag<'_, '_> {
fn eq(&self, other: &Self) -> bool {
let (self_kind, _) = self.element();
let (other_kind, _) = other.element();
if self_kind != other_kind {
return false;
}
match self_kind {
TapeTagKind::Byte => self.byte().unwrap() == other.byte().unwrap(),
TapeTagKind::Short => self.short().unwrap() == other.short().unwrap(),
TapeTagKind::Int => self.int().unwrap() == other.int().unwrap(),
TapeTagKind::Long => self.long().unwrap() == other.long().unwrap(),
TapeTagKind::Float => self.float().unwrap() == other.float().unwrap(),
TapeTagKind::Double => self.double().unwrap() == other.double().unwrap(),
TapeTagKind::ByteArray => self.byte_array().unwrap() == other.byte_array().unwrap(),
TapeTagKind::String => self.string().unwrap() == other.string().unwrap(),
TapeTagKind::Compound => self.compound().unwrap() == other.compound().unwrap(),
TapeTagKind::IntArray => self.int_array().unwrap() == other.int_array().unwrap(),
TapeTagKind::LongArray => self.long_array().unwrap() == other.long_array().unwrap(),
t if t.is_list() => self.list().unwrap() == other.list().unwrap(),
_ => unreachable!(),
}
}
}
@ -547,21 +504,21 @@ impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
mod tests {
use std::io::Read;
use byteorder::WriteBytesExt;
use byteorder::{WriteBytesExt, BE};
use flate2::read::GzDecoder;
use super::*;
#[test]
fn hello_world() {
let nbt = Nbt::read(&mut Cursor::new(include_bytes!(
let nbt = super::read(&mut Cursor::new(include_bytes!(
"../../tests/hello_world.nbt"
)))
.unwrap()
.unwrap();
assert_eq!(
nbt.compound().string("name"),
nbt.string("name"),
Some(Mutf8Str::from_str("Bananrama").as_ref())
);
assert_eq!(nbt.name().to_str(), "hello world");
@ -574,18 +531,12 @@ mod tests {
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::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let nbt = super::read(&mut Cursor::new(&decoded_src))
.unwrap()
.unwrap();
assert_eq!(nbt.compound().int("PersistentId"), Some(1946940766));
assert_eq!(
nbt.compound()
.list("Rotation")
.unwrap()
.floats()
.unwrap()
.len(),
2
);
assert_eq!(nbt.int("PersistentId"), Some(1946940766));
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
}
#[test]
@ -595,67 +546,49 @@ mod tests {
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::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let nbt = super::read(&mut Cursor::new(&decoded_src))
.unwrap()
.unwrap();
assert_eq!(
nbt.compound().float("foodExhaustionLevel").unwrap() as u32,
2
);
assert_eq!(
nbt.compound()
.list("Rotation")
.unwrap()
.floats()
.unwrap()
.len(),
2
);
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
}
#[test]
fn read_hypixel() {
let src = include_bytes!("../../tests/hypixel.nbt").to_vec();
let _nbt = Nbt::read(&mut Cursor::new(&src[..])).unwrap().unwrap();
let _nbt = super::read(&mut Cursor::new(&src[..])).unwrap().unwrap();
}
#[test]
fn read_write_complex_player() {
return;
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::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let nbt = super::read(&mut Cursor::new(&decoded_src))
.unwrap()
.unwrap();
let mut out = Vec::new();
nbt.write(&mut out);
let nbt = Nbt::read(&mut Cursor::new(&out)).unwrap().unwrap();
assert_eq!(
nbt.compound().float("foodExhaustionLevel").unwrap() as u32,
2
);
assert_eq!(
nbt.compound()
.list("Rotation")
.unwrap()
.floats()
.unwrap()
.len(),
2
);
let nbt = super::read(&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::read(&mut Cursor::new(include_bytes!(
let nbt = super::read(&mut Cursor::new(include_bytes!(
"../../tests/inttest1023.nbt"
)))
.unwrap()
.unwrap();
let ints = nbt.compound().list("").unwrap().ints().unwrap();
let ints = nbt.list("").unwrap().ints().unwrap();
for (i, &item) in ints.iter().enumerate() {
assert_eq!(i as i32, item);
@ -677,8 +610,8 @@ mod tests {
}
data.write_u8(END_ID).unwrap();
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
let ints = nbt.compound().list("").unwrap().ints().unwrap();
let nbt = super::read(&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);
}
@ -699,8 +632,8 @@ mod tests {
}
data.write_u8(END_ID).unwrap();
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
let ints = nbt.compound().list("").unwrap().ints().unwrap();
let nbt = super::read(&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);
}
@ -721,8 +654,8 @@ mod tests {
}
data.write_u8(END_ID).unwrap();
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
let ints = nbt.compound().list("").unwrap().longs().unwrap();
let nbt = super::read(&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);
}
@ -738,7 +671,7 @@ mod tests {
data.write_u16::<BE>(0).unwrap(); // first element name length
// eof
let res = Nbt::read(&mut Cursor::new(&data));
let res = super::read(&mut Cursor::new(&data));
assert_eq!(res, Err(Error::UnexpectedEof));
}

View file

@ -1,7 +1,33 @@
use std::fmt::{self, Debug};
use crate::common::{
BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LONG_ARRAY_ID,
LONG_ID, SHORT_ID, STRING_ID,
};
pub struct MainTape {
pub elements: Vec<TapeElement>,
elements: Vec<TapeElement>,
}
impl MainTape {
#[inline]
pub fn push(&mut self, element: TapeElement) {
self.elements.push(element);
}
#[inline]
pub fn len(&self) -> usize {
self.elements.len()
}
#[inline]
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut TapeElement {
self.elements.get_unchecked_mut(index)
}
#[inline]
pub fn as_ptr(&self) -> *const TapeElement {
self.elements.as_ptr()
}
}
impl Debug for MainTape {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -178,17 +204,17 @@ impl From<UnalignedU16> for u16 {
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum TapeTagKind {
Byte,
Short,
Int,
Long,
Float,
Double,
ByteArray,
String,
Compound,
IntArray,
LongArray,
Byte = BYTE_ID,
Short = SHORT_ID,
Int = INT_ID,
Long = LONG_ID,
Float = FLOAT_ID,
Double = DOUBLE_ID,
ByteArray = BYTE_ARRAY_ID,
String = STRING_ID,
Compound = COMPOUND_ID,
IntArray = INT_ARRAY_ID,
LongArray = LONG_ARRAY_ID,
EmptyList,
ByteList,

View file

@ -1,7 +1,8 @@
use std::{io::Cursor, mem, slice};
use std::{mem, slice};
use crate::{
raw_list::RawList,
reader::Reader,
swap_endianness::{swap_endianness_as_u8, SwappableNumber},
Error, Mutf8Str,
};
@ -23,100 +24,37 @@ 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> {
let remaining_slice = &data.get_ref()[data.position() as usize..data.get_ref().len()];
if remaining_slice.len() < 4 {
return Err(Error::UnexpectedEof);
}
data.set_position(data.position() + 4);
Ok(u32::from_be_bytes([
remaining_slice[0],
remaining_slice[1],
remaining_slice[2],
remaining_slice[3],
]))
}
#[inline(always)]
pub fn read_u16(data: &mut Cursor<&[u8]>) -> Result<u16, Error> {
let remaining_slice = &data.get_ref()[data.position() as usize..data.get_ref().len()];
if remaining_slice.len() < 2 {
return Err(Error::UnexpectedEof);
}
data.set_position(data.position() + 2);
Ok(u16::from_be_bytes([remaining_slice[0], remaining_slice[1]]))
}
#[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)?;
pub fn read_with_u16_length<'a>(data: &mut Reader<'a>, width: usize) -> Result<&'a [u8], Error> {
let length = data.read_u16()?;
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 skip_string<'a>(data: &mut Cursor<&'a [u8]>) -> Result<(), Error> {
let remaining_slice = &data.get_ref()[data.position() as usize..data.get_ref().len()];
if remaining_slice.len() < 2 {
return Err(Error::UnexpectedEof);
}
let length = u16::from_be_bytes([remaining_slice[0], remaining_slice[1]]);
let length_in_bytes = length as usize;
// make sure we don't read more than the length
if data.get_ref().len() < data.position() as usize + length_in_bytes + 2 {
return Err(Error::UnexpectedEof);
}
data.set_position(data.position() + 2 + length_in_bytes as u64);
Ok(())
data.read_slice(length_in_bytes)
}
#[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)?;
pub fn read_with_u32_length<'a>(data: &mut Reader<'a>, width: usize) -> Result<&'a [u8], Error> {
let length = data.read_u32()?;
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])
data.read_slice(length_in_bytes)
}
pub fn read_string<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a Mutf8Str, Error> {
pub fn read_string<'a>(data: &mut Reader<'a>) -> 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> {
pub fn read_u8_array<'a>(data: &mut Reader<'a>) -> 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> {
pub fn read_i8_array<'a>(data: &mut Reader<'a>) -> Result<&'a [i8], Error> {
Ok(slice_u8_into_i8(read_u8_array(data)?))
}
pub fn read_int_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i32>, Error> {
pub fn read_int_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i32>, Error> {
let array_bytes = read_with_u32_length(data, 4)?;
Ok(RawList::new(array_bytes))
}
pub fn read_long_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i64>, Error> {
pub fn read_long_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i64>, Error> {
let array_bytes = read_with_u32_length(data, 8)?;
Ok(RawList::new(array_bytes))
}

View file

@ -1,6 +1,11 @@
#![doc = include_str!("../README.md")]
#![feature(portable_simd)]
#![feature(array_chunks)]
#![allow(internal_features)]
#![feature(core_intrinsics)]
#[cfg(not(target_pointer_width = "64"))]
compile_error!("simdnbt only supports 64-bit platforms");
pub mod borrow;
mod common;
@ -8,6 +13,7 @@ mod error;
mod mutf8;
pub mod owned;
pub mod raw_list;
mod reader;
pub mod swap_endianness;
mod traits;

View file

@ -1,13 +1,9 @@
use std::{
io::Cursor,
mem::{self, MaybeUninit},
};
use byteorder::ReadBytesExt;
use std::mem::{self, MaybeUninit};
use crate::{
common::{read_string, unchecked_push, unchecked_write_string, END_ID, MAX_DEPTH},
mutf8::Mutf8String,
reader::Reader,
Error, Mutf8Str, ToNbtTag,
};
@ -28,12 +24,12 @@ impl NbtCompound {
Self { values }
}
pub(crate) fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
pub(crate) fn read(data: &mut Reader<'_>) -> Result<Self, Error> {
Self::read_with_depth(data, 0)
}
pub(crate) fn read_with_depth_and_capacity(
data: &mut Cursor<&[u8]>,
data: &mut Reader<'_>,
depth: usize,
capacity: usize,
) -> Result<Self, Error> {
@ -73,7 +69,7 @@ impl NbtCompound {
Ok(Self { values })
}
pub(crate) fn read_with_depth(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
pub(crate) fn read_with_depth(data: &mut Reader<'_>, depth: usize) -> Result<Self, Error> {
Self::read_with_depth_and_capacity(data, depth, 8)
}

View file

@ -1,7 +1,3 @@
use std::io::Cursor;
use byteorder::ReadBytesExt;
use crate::{
common::{
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array,
@ -11,11 +7,12 @@ use crate::{
LONG_ID, SHORT_ID, STRING_ID,
},
mutf8::Mutf8String,
reader::Reader,
swap_endianness::swap_endianness,
Error,
};
use super::{compound::NbtCompound, read_u32, MAX_DEPTH};
use super::{compound::NbtCompound, MAX_DEPTH};
/// A list of NBT tags of a single type.
#[repr(u8)]
@ -37,14 +34,14 @@ pub enum NbtList {
LongArray(Vec<Vec<i64>>) = LONG_ARRAY_ID,
}
impl NbtList {
pub(crate) fn read(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
pub(crate) fn read(data: &mut Reader<'_>, 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);
data.skip(4)?;
NbtList::Empty
}
BYTE_ID => NbtList::Byte(read_i8_array(data)?.to_owned()),
@ -54,7 +51,7 @@ impl NbtList {
FLOAT_ID => NbtList::Float(swap_endianness(read_with_u32_length(data, 4)?)),
DOUBLE_ID => NbtList::Double(swap_endianness(read_with_u32_length(data, 8)?)),
BYTE_ARRAY_ID => NbtList::ByteArray({
let length = read_u32(data)?;
let length = data.read_u32()?;
// arbitrary number to prevent big allocations
let mut arrays = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {
@ -63,7 +60,7 @@ impl NbtList {
arrays
}),
STRING_ID => NbtList::String({
let length = read_u32(data)?;
let length = data.read_u32()?;
// arbitrary number to prevent big allocations
let mut strings = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {
@ -72,7 +69,7 @@ impl NbtList {
strings
}),
LIST_ID => NbtList::List({
let length = read_u32(data)?;
let length = data.read_u32()?;
// arbitrary number to prevent big allocations
let mut lists = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {
@ -81,7 +78,7 @@ impl NbtList {
lists
}),
COMPOUND_ID => NbtList::Compound({
let length = read_u32(data)?;
let length = data.read_u32()?;
// arbitrary number to prevent big allocations
let mut compounds = Vec::with_capacity(length.min(128) as usize);
let mut capacity: usize = 8;
@ -93,7 +90,7 @@ impl NbtList {
compounds
}),
INT_ARRAY_ID => NbtList::IntArray({
let length = read_u32(data)?;
let length = data.read_u32()?;
// arbitrary number to prevent big allocations
let mut arrays = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {
@ -102,7 +99,7 @@ impl NbtList {
arrays
}),
LONG_ARRAY_ID => NbtList::LongArray({
let length = read_u32(data)?;
let length = data.read_u32()?;
// arbitrary number to prevent big allocations
let mut arrays = Vec::with_capacity(length.min(128) as usize);
for _ in 0..length {

View file

@ -6,16 +6,15 @@ 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,
read_int_array, read_long_array, read_string, read_with_u32_length,
slice_into_u8_big_endian, unchecked_extend, unchecked_push, 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,
reader::{Reader, ReaderFromCursor},
Error, Mutf8Str,
};
@ -25,7 +24,8 @@ pub use self::{compound::NbtCompound, list::NbtList};
///
/// Returns `Ok(Nbt::None)` if there is no data.
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
Nbt::read(data)
let mut reader = ReaderFromCursor::new(data);
Nbt::read(&mut reader)
}
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading
/// NBT over the network.
@ -33,22 +33,26 @@ pub fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either
/// empty or a compound).
pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
Nbt::read_unnamed(data)
let mut reader = ReaderFromCursor::new(data);
Nbt::read_unnamed(&mut reader)
}
/// Read a compound tag. This may have any number of items.
pub fn read_compound(data: &mut Cursor<&[u8]>) -> Result<NbtCompound, Error> {
NbtCompound::read(data)
let mut reader = ReaderFromCursor::new(data);
NbtCompound::read(&mut reader)
}
/// Read an NBT tag, without reading its name. This may be any type of tag except for an end tag. If you need to be able to
/// handle end tags, use [`read_optional_tag`].
pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<NbtTag, Error> {
NbtTag::read(data)
let mut reader = ReaderFromCursor::new(data);
NbtTag::read(&mut reader)
}
/// Read any NBT tag, without reading its name. This may be any type of tag, including an end tag.
///
/// Returns `Ok(None)` if there is no data.
pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<Option<NbtTag>, Error> {
Ok(NbtTag::read_optional(data)?)
let mut reader = ReaderFromCursor::new(data);
NbtTag::read_optional(&mut reader)
}
/// A complete NBT container. This contains a name and a compound tag.
@ -71,7 +75,7 @@ impl Nbt {
}
/// Reads NBT from the given data. Returns `Ok(Nbt::None)` if there is no data.
fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
fn read(data: &mut Reader<'_>) -> Result<Nbt, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
return Ok(Nbt::None);
@ -85,7 +89,7 @@ impl Nbt {
Ok(Nbt::Some(BaseNbt { name, tag }))
}
fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
fn read_unnamed(data: &mut Reader<'_>) -> Result<Nbt, Error> {
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if root_type == END_ID {
return Ok(Nbt::None);
@ -258,25 +262,25 @@ impl NbtTag {
}
#[inline(always)]
fn read_with_type(data: &mut Cursor<&[u8]>, tag_type: u8, depth: usize) -> Result<Self, Error> {
fn read_with_type(data: &mut Reader<'_>, tag_type: u8, depth: usize) -> Result<Self, Error> {
match tag_type {
BYTE_ID => Ok(NbtTag::Byte(
data.read_i8().map_err(|_| Error::UnexpectedEof)?,
)),
SHORT_ID => Ok(NbtTag::Short(
data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?,
data.read_i16().map_err(|_| Error::UnexpectedEof)?,
)),
INT_ID => Ok(NbtTag::Int(
data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?,
data.read_i32().map_err(|_| Error::UnexpectedEof)?,
)),
LONG_ID => Ok(NbtTag::Long(
data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?,
data.read_i64().map_err(|_| Error::UnexpectedEof)?,
)),
FLOAT_ID => Ok(NbtTag::Float(
data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?,
data.read_f32().map_err(|_| Error::UnexpectedEof)?,
)),
DOUBLE_ID => Ok(NbtTag::Double(
data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?,
data.read_f64().map_err(|_| Error::UnexpectedEof)?,
)),
BYTE_ARRAY_ID => Ok(NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned())),
STRING_ID => Ok(NbtTag::String(read_string(data)?.to_owned())),
@ -291,12 +295,12 @@ impl NbtTag {
}
}
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
fn read(data: &mut Reader<'_>) -> Result<Self, Error> {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
Self::read_with_type(data, tag_type, 0)
}
pub fn read_optional(data: &mut Cursor<&[u8]>) -> Result<Option<Self>, Error> {
fn read_optional(data: &mut Reader<'_>) -> Result<Option<Self>, Error> {
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
if tag_type == END_ID {
return Ok(None);
@ -621,14 +625,14 @@ impl From<Nbt> for NbtTag {
mod tests {
use std::io::Read;
use byteorder::WriteBytesExt;
use byteorder::{WriteBytesExt, BE};
use flate2::read::GzDecoder;
use super::*;
#[test]
fn hello_world() {
let nbt = Nbt::read(&mut Cursor::new(include_bytes!(
let nbt = super::read(&mut Cursor::new(include_bytes!(
"../../tests/hello_world.nbt"
)))
.unwrap()
@ -648,7 +652,9 @@ mod tests {
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::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let nbt = super::read(&mut Cursor::new(&decoded_src))
.unwrap()
.unwrap();
assert_eq!(nbt.int("PersistentId"), Some(1946940766));
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
@ -661,7 +667,9 @@ mod tests {
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::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let nbt = super::read(&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);
@ -674,11 +682,13 @@ mod tests {
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::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
let nbt = super::read(&mut Cursor::new(&decoded_src))
.unwrap()
.unwrap();
let mut out = Vec::new();
nbt.write(&mut out);
let nbt = Nbt::read(&mut Cursor::new(&out)).unwrap().unwrap();
let nbt = super::read(&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);
@ -686,7 +696,7 @@ mod tests {
#[test]
fn inttest_1023() {
let nbt = Nbt::read(&mut Cursor::new(include_bytes!(
let nbt = super::read(&mut Cursor::new(include_bytes!(
"../../tests/inttest1023.nbt"
)))
.unwrap()
@ -714,7 +724,7 @@ mod tests {
}
data.write_u8(END_ID).unwrap();
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
let nbt = super::read(&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);
@ -736,7 +746,7 @@ mod tests {
}
data.write_u8(END_ID).unwrap();
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
let nbt = super::read(&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);
@ -758,7 +768,7 @@ mod tests {
}
data.write_u8(END_ID).unwrap();
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
let nbt = super::read(&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);

148
simdnbt/src/reader.rs Normal file
View file

@ -0,0 +1,148 @@
use std::{
io::Cursor,
marker::PhantomData,
ops::{Deref, DerefMut},
};
use crate::Error;
pub struct Reader<'a> {
pub cur: *const u8,
/// pointer to after the last byte (so remaining=end-cur)
end: *const u8,
_marker: PhantomData<&'a ()>,
}
impl<'a> Reader<'a> {
pub fn new(data: &'a [u8]) -> Reader<'a> {
Self {
cur: data.as_ptr(),
end: unsafe { data.as_ptr().add(data.len()) },
_marker: PhantomData,
}
}
pub fn ensure_can_read(&self, size: usize) -> Result<(), Error> {
let data_addr = self.cur as usize;
let end_addr = self.end as usize;
if data_addr + size > end_addr {
Err(Error::UnexpectedEof)
} else {
Ok(())
}
}
pub unsafe fn unchecked_read_type<T>(&mut self) -> T {
let value = unsafe { self.cur.cast::<T>().read_unaligned() };
self.cur = unsafe { self.cur.add(std::mem::size_of::<T>()) };
value
}
pub fn read_type<T: Copy>(&mut self) -> Result<T, Error> {
self.ensure_can_read(std::mem::size_of::<T>())?;
Ok(unsafe { self.unchecked_read_type() })
}
#[inline]
pub fn read_u8(&mut self) -> Result<u8, Error> {
self.read_type()
}
#[inline]
pub fn read_i8(&mut self) -> Result<i8, Error> {
self.read_u8().map(|x| x as i8)
}
#[inline]
pub fn read_u16(&mut self) -> Result<u16, Error> {
self.read_type::<u16>().map(u16::swap_bytes)
}
#[inline]
pub fn read_i16(&mut self) -> Result<i16, Error> {
self.read_u16().map(|x| x as i16)
}
#[inline]
pub fn read_u32(&mut self) -> Result<u32, Error> {
self.read_type::<u32>().map(u32::swap_bytes)
}
#[inline]
pub fn read_i32(&mut self) -> Result<i32, Error> {
self.read_u32().map(|x| x as i32)
}
#[inline]
pub fn read_u64(&mut self) -> Result<u64, Error> {
self.read_type::<u64>().map(u64::swap_bytes)
}
#[inline]
pub fn read_i64(&mut self) -> Result<i64, Error> {
self.read_u64().map(|x| x as i64)
}
#[inline]
pub fn read_f32(&mut self) -> Result<f32, Error> {
self.read_u32().map(f32::from_bits)
}
#[inline]
pub fn read_f64(&mut self) -> Result<f64, Error> {
self.read_u64().map(f64::from_bits)
}
#[inline]
pub fn skip(&mut self, size: usize) -> Result<(), Error> {
self.ensure_can_read(size)?;
self.cur = unsafe { self.cur.add(size) };
Ok(())
}
#[inline]
pub fn read_slice(&mut self, size: usize) -> Result<&'a [u8], Error> {
self.ensure_can_read(size)?;
let slice = unsafe { std::slice::from_raw_parts(self.cur, size) };
self.cur = unsafe { self.cur.add(size) };
Ok(slice)
}
}
impl<'a> From<&'a [u8]> for Reader<'a> {
fn from(data: &'a [u8]) -> Self {
Self::new(data)
}
}
pub struct ReaderFromCursor<'a: 'cursor, 'cursor> {
reader: Reader<'a>,
original_cursor: &'cursor mut Cursor<&'a [u8]>,
}
impl<'a, 'cursor> ReaderFromCursor<'a, 'cursor> {
pub fn new(cursor: &'cursor mut Cursor<&'a [u8]>) -> Self {
let data = cursor.get_ref();
Self {
reader: Reader::new(data[cursor.position() as usize..].as_ref()),
original_cursor: cursor,
}
}
}
impl Drop for ReaderFromCursor<'_, '_> {
fn drop(&mut self) {
self.original_cursor.set_position(
(self.reader.cur as usize - self.original_cursor.get_ref().as_ptr() as usize) as u64,
);
}
}
impl<'a> Deref for ReaderFromCursor<'a, '_> {
type Target = Reader<'a>;
fn deref(&self) -> &Self::Target {
&self.reader
}
}
impl<'a> DerefMut for ReaderFromCursor<'a, '_> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.reader
}
}

View file

@ -4,7 +4,7 @@ use crate::DeserializeError;
pub trait Deserialize: Sized {
fn from_nbt(nbt: &crate::borrow::BaseNbt) -> Result<Self, DeserializeError> {
Self::from_compound(nbt.compound())
Self::from_compound(nbt.as_compound())
}
fn from_compound(compound: crate::borrow::NbtCompound) -> Result<Self, DeserializeError>;