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:
parent
1cc234c7fd
commit
d420d3fb37
14 changed files with 1459 additions and 1115 deletions
|
@ -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
|
||||
|
|
|
@ -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>>> {
|
||||
|
|
|
@ -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())));
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
148
simdnbt/src/reader.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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>;
|
||||
|
|
Loading…
Add table
Reference in a new issue