diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2023-11-19 22:07:38 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-19 22:07:38 -0600 |
| commit | 2c610826fc9f8e16897f52313faa8e0602d1dc3d (patch) | |
| tree | 5aad79ecab3b68511a10ebd7eba07af0bd8a5905 /azalea-nbt/src | |
| parent | 84e036ce3752ecf57904b0f5aff1f33d43e95a32 (diff) | |
| download | azalea-drasl-2c610826fc9f8e16897f52313faa8e0602d1dc3d.tar.xz | |
Replace azalea-nbt with simdnbt (#111)
* delete azalea-nbt and replace with simdnbt
* use simdnbt from crates.io
* remove serde dependency on azalea-registry
Diffstat (limited to 'azalea-nbt/src')
| -rwxr-xr-x | azalea-nbt/src/decode.rs | 314 | ||||
| -rwxr-xr-x | azalea-nbt/src/encode.rs | 296 | ||||
| -rwxr-xr-x | azalea-nbt/src/error.rs | 15 | ||||
| -rwxr-xr-x | azalea-nbt/src/lib.rs | 46 | ||||
| -rwxr-xr-x | azalea-nbt/src/tag.rs | 271 |
5 files changed, 0 insertions, 942 deletions
diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs deleted file mode 100755 index 23247b74..00000000 --- a/azalea-nbt/src/decode.rs +++ /dev/null @@ -1,314 +0,0 @@ -use crate::tag::*; -use crate::Error; -use azalea_buf::{BufReadError, McBufReadable}; -use byteorder::{ReadBytesExt, BE}; -use flate2::read::{GzDecoder, ZlibDecoder}; -use std::io::{BufRead, Cursor, Read}; -use tracing::warn; - -#[inline] -fn read_bytes<'a>(buf: &'a mut Cursor<&[u8]>, length: usize) -> Result<&'a [u8], Error> { - if length > (buf.get_ref().len() - buf.position() as usize) { - return Err(Error::UnexpectedEof); - } - let initial_position = buf.position() as usize; - buf.set_position(buf.position() + length as u64); - let data = &buf.get_ref()[initial_position..initial_position + length]; - Ok(data) -} - -#[inline] -fn read_string(stream: &mut Cursor<&[u8]>) -> Result<NbtString, Error> { - let length = stream.read_u16::<BE>()? as usize; - - let buf = read_bytes(stream, length)?; - - Ok(if let Ok(string) = std::str::from_utf8(buf) { - string.into() - } else { - let lossy_string = String::from_utf8_lossy(buf).into_owned(); - warn!("Error decoding utf8 (bytes: {buf:?}, lossy: \"{lossy_string})\""); - lossy_string.into() - }) -} - -#[inline] -fn read_byte_array(stream: &mut Cursor<&[u8]>) -> Result<NbtByteArray, Error> { - let length = stream.read_u32::<BE>()? as usize; - let bytes = read_bytes(stream, length)?.to_vec(); - Ok(bytes) -} - -// https://stackoverflow.com/a/59707887 -fn vec_u8_into_i8(v: Vec<u8>) -> Vec<i8> { - // ideally we'd use Vec::into_raw_parts, but it's unstable, - // so we have to do it manually: - - // first, make sure v's destructor doesn't free the data - // it thinks it owns when it goes out of scope - let mut v = std::mem::ManuallyDrop::new(v); - - // then, pick apart the existing Vec - let p = v.as_mut_ptr(); - let len = v.len(); - let cap = v.capacity(); - - // finally, adopt the data into a new Vec - unsafe { Vec::from_raw_parts(p as *mut i8, len, cap) } -} - -#[inline] -fn read_list(stream: &mut Cursor<&[u8]>) -> Result<NbtList, Error> { - let type_id = stream.read_u8()?; - let length = stream.read_u32::<BE>()?; - let list = match type_id { - END_ID => NbtList::Empty, - BYTE_ID => NbtList::Byte(vec_u8_into_i8( - read_bytes(stream, length as usize)?.to_vec(), - )), - SHORT_ID => NbtList::Short({ - if ((length * 2) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| stream.read_i16::<BE>()) - .collect::<Result<Vec<_>, _>>()? - }), - INT_ID => NbtList::Int({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| stream.read_i32::<BE>()) - .collect::<Result<Vec<_>, _>>()? - }), - LONG_ID => NbtList::Long({ - if ((length * 8) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| stream.read_i64::<BE>()) - .collect::<Result<Vec<_>, _>>()? - }), - FLOAT_ID => NbtList::Float({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| stream.read_f32::<BE>()) - .collect::<Result<Vec<_>, _>>()? - }), - DOUBLE_ID => NbtList::Double({ - if ((length * 8) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| stream.read_f64::<BE>()) - .collect::<Result<Vec<_>, _>>()? - }), - BYTE_ARRAY_ID => NbtList::ByteArray({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| read_byte_array(stream)) - .collect::<Result<Vec<_>, _>>()? - }), - STRING_ID => NbtList::String({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| read_string(stream)) - .collect::<Result<Vec<_>, _>>()? - }), - LIST_ID => NbtList::List({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| read_list(stream)) - .collect::<Result<Vec<_>, _>>()? - }), - COMPOUND_ID => NbtList::Compound({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| read_compound(stream)) - .collect::<Result<Vec<_>, _>>()? - }), - INT_ARRAY_ID => NbtList::IntArray({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| read_int_array(stream)) - .collect::<Result<Vec<_>, _>>()? - }), - LONG_ARRAY_ID => NbtList::LongArray({ - if ((length * 4) as usize) > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - (0..length) - .map(|_| read_long_array(stream)) - .collect::<Result<Vec<_>, _>>()? - }), - _ => return Err(Error::InvalidTagType(type_id)), - }; - Ok(list) -} - -#[inline] -fn read_compound(stream: &mut Cursor<&[u8]>) -> Result<NbtCompound, Error> { - // we default to capacity 4 because it'll probably not be empty - let mut map = NbtCompound::with_capacity(4); - loop { - let tag_id = stream.read_u8().unwrap_or(0); - if tag_id == 0 { - break; - } - let name = read_string(stream)?; - let tag = Nbt::read_known(stream, tag_id)?; - map.insert_unsorted(name, tag); - } - map.sort(); - Ok(map) -} - -#[inline] -fn read_int_array(stream: &mut Cursor<&[u8]>) -> Result<NbtIntArray, Error> { - let length = stream.read_u32::<BE>()? as usize; - if length * 4 > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - let mut ints = NbtIntArray::with_capacity(length); - for _ in 0..length { - ints.push(stream.read_i32::<BE>()?); - } - Ok(ints) -} - -#[inline] -fn read_long_array(stream: &mut Cursor<&[u8]>) -> Result<NbtLongArray, Error> { - let length = stream.read_u32::<BE>()? as usize; - if length * 8 > (stream.get_ref().len() - stream.position() as usize) { - return Err(Error::UnexpectedEof); - } - let mut longs = NbtLongArray::with_capacity(length); - for _ in 0..length { - longs.push(stream.read_i64::<BE>()?); - } - Ok(longs) -} - -impl Nbt { - /// Read the NBT data when you already know the ID of the tag. You usually - /// want [`Nbt::read`] if you're reading an NBT file. - #[inline] - fn read_known(stream: &mut Cursor<&[u8]>, id: u8) -> Result<Nbt, Error> { - Ok(match id { - // Signifies the end of a TAG_Compound. It is only ever used inside - // a TAG_Compound, and is not named despite being in a TAG_Compound - END_ID => Nbt::End, - // A single signed byte - BYTE_ID => Nbt::Byte(stream.read_i8()?), - // A single signed, big endian 16 bit integer - SHORT_ID => Nbt::Short(stream.read_i16::<BE>()?), - // A single signed, big endian 32 bit integer - INT_ID => Nbt::Int(stream.read_i32::<BE>()?), - // A single signed, big endian 64 bit integer - LONG_ID => Nbt::Long(stream.read_i64::<BE>()?), - // A single, big endian IEEE-754 single-precision floating point - // number (NaN possible) - FLOAT_ID => Nbt::Float(stream.read_f32::<BE>()?), - // A single, big endian IEEE-754 double-precision floating point - // number (NaN possible) - DOUBLE_ID => Nbt::Double(stream.read_f64::<BE>()?), - // A length-prefixed array of signed bytes. The prefix is a signed - // integer (thus 4 bytes) - BYTE_ARRAY_ID => Nbt::ByteArray(read_byte_array(stream)?), - // A length-prefixed modified UTF-8 string. The prefix is an - // unsigned short (thus 2 bytes) signifying the length of the - // string in bytes - STRING_ID => Nbt::String(read_string(stream)?), - // A list of nameless tags, all of the same type. The list is - // prefixed with the Type ID of the items it contains (thus 1 - // byte), and the length of the list as a signed integer (a further - // 4 bytes). If the length of the list is 0 or negative, the type - // may be 0 (TAG_End) but otherwise it must be any other type. (The - // notchian implementation uses TAG_End in that situation, but - // another reference implementation by Mojang uses 1 instead; - // parsers should accept any type if the length is <= 0). - LIST_ID => Nbt::List(read_list(stream)?), - // Effectively a list of a named tags. Order is not guaranteed. - COMPOUND_ID => Nbt::Compound(read_compound(stream)?), - // A length-prefixed array of signed integers. The prefix is a - // signed integer (thus 4 bytes) and indicates the number of 4 byte - // integers. - INT_ARRAY_ID => Nbt::IntArray(read_int_array(stream)?), - // A length-prefixed array of signed longs. The prefix is a signed - // integer (thus 4 bytes) and indicates the number of 8 byte longs. - LONG_ARRAY_ID => Nbt::LongArray(read_long_array(stream)?), - _ => return Err(Error::InvalidTagType(id)), - }) - } - - /// Read the NBT data. This will return a compound tag with a single item. - /// - /// Minecraft usually uses this function when reading from files. - /// [`Nbt::read_any_tag`] is used when reading from the network. - pub fn read(stream: &mut Cursor<&[u8]>) -> Result<Nbt, Error> { - // default to compound tag - - // the parent compound only ever has one item - let tag_id = stream.read_u8().unwrap_or(0); - if tag_id == 0 { - return Ok(Nbt::End); - } - let name = read_string(stream)?; - let tag = Nbt::read_known(stream, tag_id)?; - let mut map = NbtCompound::with_capacity(1); - map.insert_unsorted(name, tag); - - Ok(Nbt::Compound(map)) - } - - /// Read the NBT data. There is no guarantee that the tag will be a compound - /// with a single item. - /// - /// The Minecraft protocol uses this function when reading from the network. - /// [`Nbt::read`] is usually used when reading from files. - pub fn read_any_tag(stream: &mut Cursor<&[u8]>) -> Result<Nbt, Error> { - let tag_id = stream.read_u8().unwrap_or(0); - let tag = Nbt::read_known(stream, tag_id)?; - Ok(tag) - } - - /// Read the NBT data compressed wtih zlib. - pub fn read_zlib(stream: &mut impl BufRead) -> Result<Nbt, Error> { - let mut gz = ZlibDecoder::new(stream); - let mut buf = Vec::new(); - gz.read_to_end(&mut buf)?; - Nbt::read(&mut Cursor::new(&buf)) - } - - /// Read the NBT data compressed wtih gzip. - pub fn read_gzip(stream: &mut Cursor<Vec<u8>>) -> Result<Nbt, Error> { - let mut gz = GzDecoder::new(stream); - let mut buf = Vec::new(); - gz.read_to_end(&mut buf)?; - Nbt::read(&mut Cursor::new(&buf)) - } -} - -impl McBufReadable for Nbt { - fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> { - Ok(Nbt::read_any_tag(buf)?) - } -} -impl From<Error> for BufReadError { - fn from(e: Error) -> Self { - BufReadError::Custom(e.to_string()) - } -} diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs deleted file mode 100755 index 34c451d2..00000000 --- a/azalea-nbt/src/encode.rs +++ /dev/null @@ -1,296 +0,0 @@ -use crate::tag::*; -use azalea_buf::McBufWritable; -use byteorder::{WriteBytesExt, BE}; -use flate2::write::{GzEncoder, ZlibEncoder}; -// use packed_simd_2::{i32x16, i32x2, i32x4, i32x8, i64x2, i64x4, i64x8}; -use std::io::Write; - -#[inline] -fn write_string(writer: &mut impl Write, string: &NbtString) { - writer.write_u16::<BE>(string.len() as u16).unwrap(); - writer.write_all(string.as_bytes()).unwrap(); -} - -#[inline] -fn write_compound(writer: &mut impl Write, value: &NbtCompound, end_tag: bool) { - for (key, tag) in value.iter() { - writer.write_u8(tag.id()).unwrap(); - write_string(writer, key); - write_known(writer, tag); - } - if end_tag { - writer.write_u8(END_ID).unwrap(); - } -} - -fn write_known(writer: &mut impl Write, tag: &Nbt) { - match tag { - Nbt::End => {} - Nbt::Byte(value) => { - writer.write_i8(*value).unwrap(); - } - Nbt::Short(value) => { - writer.write_i16::<BE>(*value).unwrap(); - } - Nbt::Int(value) => { - writer.write_i32::<BE>(*value).unwrap(); - } - Nbt::Long(value) => { - writer.write_i64::<BE>(*value).unwrap(); - } - Nbt::Float(value) => { - writer.write_f32::<BE>(*value).unwrap(); - } - Nbt::Double(value) => { - writer.write_f64::<BE>(*value).unwrap(); - } - Nbt::ByteArray(value) => { - write_byte_array(writer, value); - } - Nbt::String(value) => { - write_string(writer, value); - } - Nbt::List(value) => { - write_list(writer, value); - } - Nbt::Compound(value) => { - write_compound(writer, value, true); - } - Nbt::IntArray(value) => { - write_int_array(writer, value); - } - Nbt::LongArray(value) => { - write_long_array(writer, value); - } - } -} - -#[inline] -fn write_list(writer: &mut impl Write, value: &NbtList) { - writer.write_u8(value.id()).unwrap(); - match value { - NbtList::Empty => writer.write_all(&[0; 4]).unwrap(), - NbtList::Byte(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - let l = l.as_slice(); - writer - // convert [i8] into [u8] - .write_all(unsafe { std::slice::from_raw_parts(l.as_ptr() as *const u8, l.len()) }) - .unwrap(); - } - NbtList::Short(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for &v in l { - writer.write_i16::<BE>(v).unwrap(); - } - } - NbtList::Int(l) => write_int_array(writer, l), - NbtList::Long(l) => write_long_array(writer, l), - NbtList::Float(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for &v in l { - writer.write_f32::<BE>(v).unwrap(); - } - } - NbtList::Double(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for &v in l { - writer.write_f64::<BE>(v).unwrap(); - } - } - NbtList::ByteArray(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for v in l { - write_byte_array(writer, v); - } - } - NbtList::String(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for v in l { - write_string(writer, v); - } - } - NbtList::List(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for v in l { - write_list(writer, v); - } - } - NbtList::Compound(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for v in l { - write_compound(writer, v, true); - } - } - NbtList::IntArray(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for v in l { - write_int_array(writer, v); - } - } - NbtList::LongArray(l) => { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - for v in l { - write_long_array(writer, v); - } - } - } -} - -#[inline] -fn write_byte_array(writer: &mut impl Write, value: &[u8]) { - writer.write_u32::<BE>(value.len() as u32).unwrap(); - writer.write_all(value).unwrap(); -} - -#[inline] -fn write_int_array(writer: &mut impl Write, array: &[i32]) { - writer.write_i32::<BE>(array.len() as i32).unwrap(); - - for &item in array { - writer.write_i32::<BE>(item).unwrap(); - } - - // (disabled for now since i realized packed_simd to_be does not work as - // expected) // flip the bits to big endian with simd - // let mut position = 0; - // // x16 - // while array.len() - position >= 16 { - // let l = unsafe { - // i32x16::from_slice_unaligned_unchecked(&array[position..]) }; let - // l = l.to_be(); let l = unsafe { std::mem::transmute::<i32x16, - // [u8; 64]>(l) }; writer.write_all(&l).unwrap(); - // position += 16; - // } - // // x8 - // if array.len() - position >= 8 { - // let l = unsafe { - // i32x8::from_slice_unaligned_unchecked(&array[position..]) }; - // let l = l.to_be(); - // let l = unsafe { std::mem::transmute::<i32x8, [u8; 32]>(l) }; - // writer.write_all(&l).unwrap(); - // position += 8; - // } - // // x4 - // if array.len() - position >= 4 { - // let l = unsafe { - // i32x4::from_slice_unaligned_unchecked(&array[position..]) }; - // let l = l.to_be(); - // let l = unsafe { std::mem::transmute::<i32x4, [u8; 16]>(l) }; - // writer.write_all(&l).unwrap(); - // position += 4; - // } - // // x2 - // if array.len() - position >= 2 { - // let l = unsafe { - // i32x2::from_slice_unaligned_unchecked(&array[position..]) }; - // let l = l.to_be(); - // let l = l.swap_bytes(); - // let l = unsafe { std::mem::transmute::<i32x2, [u8; 8]>(l) }; - // writer.write_all(&l).unwrap(); - // position += 2; - // } - // // x1 ... just a normal write_i32 - // if array.len() - position >= 1 { - // writer.write_i32::<BE>(array[position]).unwrap(); - // } -} - -#[inline] -fn write_long_array(writer: &mut impl Write, l: &[i64]) { - writer.write_i32::<BE>(l.len() as i32).unwrap(); - - for &item in l { - writer.write_i64::<BE>(item).unwrap(); - } - - // (disabled for now since i realized packed_simd to_be does not work as - // expected) - - // // flip the bits to big endian with simd - // let mut position = 0; - // // x16 - // while l.len() - position >= 8 { - // let l = unsafe { - // i64x8::from_slice_unaligned_unchecked(&l[position..]) }; - // l.to_be(); - // let l = unsafe { std::mem::transmute::<i64x8, [u8; 64]>(l) }; - // writer.write_all(&l).unwrap(); - // position += 8; - // } - // // x4 - // if l.len() - position >= 4 { - // let l = unsafe { - // i64x4::from_slice_unaligned_unchecked(&l[position..]) }; - // l.to_be(); - // let l = unsafe { std::mem::transmute::<i64x4, [u8; 32]>(l) }; - // writer.write_all(&l).unwrap(); - // position += 4; - // } - // // x2 - // if l.len() - position >= 2 { - // let l = unsafe { - // i64x2::from_slice_unaligned_unchecked(&l[position..]) }; - // l.to_be(); - // let l = unsafe { std::mem::transmute::<i64x2, [u8; 16]>(l) }; - // writer.write_all(&l).unwrap(); - // position += 2; - // } - // // x1 ... just a normal write_i32 - // if l.len() - position >= 1 { - // writer.write_i64::<BE>(l[position]).unwrap(); - // } -} - -impl Nbt { - /// Write the compound tag as NBT data. - /// - /// # Panics - /// - /// Will panic if the tag is not a Compound or End tag. - pub fn write(&self, writer: &mut impl Write) { - match self { - Nbt::Compound(value) => { - write_compound(writer, value, false); - } - Nbt::End => { - END_ID.write_into(writer).unwrap(); - } - _ => panic!("Not a compound tag"), - } - } - - /// Write any tag as NBT data. This is used by Minecraft when writing to the - /// network, otherwise [`Nbt::write`] is usually used instead. - pub fn write_any(&self, writer: &mut impl Write) { - writer.write_u8(self.id()).unwrap(); - write_known(writer, self); - } - - /// Write the compound tag as NBT data compressed wtih zlib. - /// - /// # Errors - /// - /// Returns an `Err` if it's not a Compound or End tag. - pub fn write_zlib(&self, writer: &mut impl Write) { - let mut encoder = ZlibEncoder::new(writer, flate2::Compression::default()); - self.write(&mut encoder) - } - - /// Write the compound tag as NBT data compressed wtih gzip. - /// - /// # Errors - /// - /// Returns an `Err` if it's not a Compound or End tag. - pub fn write_gzip(&self, writer: &mut impl Write) { - let mut encoder = GzEncoder::new(writer, flate2::Compression::default()); - self.write(&mut encoder) - } -} - -impl McBufWritable for Nbt { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - self.write_any(buf); - Ok(()) - } -} diff --git a/azalea-nbt/src/error.rs b/azalea-nbt/src/error.rs deleted file mode 100755 index ace7fcd3..00000000 --- a/azalea-nbt/src/error.rs +++ /dev/null @@ -1,15 +0,0 @@ -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum Error { - #[error("Invalid tag type: {0}")] - InvalidTagType(u8), - #[error("Invalid tag")] - InvalidTag, - #[error("Write error: {0}")] - WriteError(#[from] std::io::Error), - #[error("Utf8 error: {0}")] - Utf8Error(#[from] std::str::Utf8Error), - #[error("Unexpected EOF")] - UnexpectedEof, -} diff --git a/azalea-nbt/src/lib.rs b/azalea-nbt/src/lib.rs deleted file mode 100755 index 1a636520..00000000 --- a/azalea-nbt/src/lib.rs +++ /dev/null @@ -1,46 +0,0 @@ -#![doc = include_str!("../README.md")] - -mod decode; -mod encode; -mod error; -mod tag; - -pub use error::Error; -pub use tag::*; - -#[cfg(test)] -mod tests { - use std::io::Cursor; - - use crate::tag::NbtCompound; - - use super::*; - use azalea_buf::{McBufReadable, McBufWritable}; - - #[test] - fn mcbuf_nbt() { - let mut buf = Vec::new(); - let tag = Nbt::Compound(NbtCompound::from_iter(vec![( - "hello world".into(), - Nbt::Compound(NbtCompound::from_iter(vec![( - "name".into(), - Nbt::String("Bananrama".into()), - )])), - )])); - tag.write_into(&mut buf).unwrap(); - - let mut buf = Cursor::new(&buf[..]); - - let result = Nbt::read_from(&mut buf).unwrap(); - assert_eq!( - result, - Nbt::Compound(NbtCompound::from_iter(vec![( - "hello world".into(), - Nbt::Compound(NbtCompound::from_iter(vec![( - "name".into(), - Nbt::String("Bananrama".into()), - )])), - )])) - ); - } -} diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs deleted file mode 100755 index 224db2d3..00000000 --- a/azalea-nbt/src/tag.rs +++ /dev/null @@ -1,271 +0,0 @@ -use compact_str::CompactString; -use enum_as_inner::EnumAsInner; -#[cfg(feature = "serde")] -use serde::{ser::SerializeMap, Deserialize, Serialize}; - -pub type NbtByte = i8; -pub type NbtShort = i16; -pub type NbtInt = i32; -pub type NbtLong = i64; -pub type NbtFloat = f32; -pub type NbtDouble = f64; -pub type NbtByteArray = Vec<u8>; -pub type NbtString = CompactString; -pub type NbtIntArray = Vec<i32>; -pub type NbtLongArray = Vec<i64>; - -pub const END_ID: u8 = 0; -pub const BYTE_ID: u8 = 1; -pub const SHORT_ID: u8 = 2; -pub const INT_ID: u8 = 3; -pub const LONG_ID: u8 = 4; -pub const FLOAT_ID: u8 = 5; -pub const DOUBLE_ID: u8 = 6; -pub const BYTE_ARRAY_ID: u8 = 7; -pub const STRING_ID: u8 = 8; -pub const LIST_ID: u8 = 9; -pub const COMPOUND_ID: u8 = 10; -pub const INT_ARRAY_ID: u8 = 11; -pub const LONG_ARRAY_ID: u8 = 12; - -/// An NBT value. -#[derive(Clone, Debug, PartialEq, Default, EnumAsInner)] -#[repr(u8)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))] -pub enum Nbt { - #[default] - End = END_ID, - Byte(NbtByte) = BYTE_ID, - Short(NbtShort) = SHORT_ID, - Int(NbtInt) = INT_ID, - Long(NbtLong) = LONG_ID, - Float(NbtFloat) = FLOAT_ID, - Double(NbtDouble) = DOUBLE_ID, - ByteArray(NbtByteArray) = BYTE_ARRAY_ID, - String(NbtString) = STRING_ID, - List(NbtList) = LIST_ID, - Compound(NbtCompound) = COMPOUND_ID, - IntArray(NbtIntArray) = INT_ARRAY_ID, - LongArray(NbtLongArray) = LONG_ARRAY_ID, -} -impl Nbt { - /// Get the numerical ID of the tag type. - #[inline] - pub fn id(&self) -> u8 { - // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` - // `union` between `repr(C)` structs, each of which has the `u8` - // discriminant as its first field, so we can read the discriminant - // without offsetting the pointer. - unsafe { *<*const _>::from(self).cast::<u8>() } - } -} - -/// An NBT value. -#[derive(Clone, Debug, PartialEq)] -#[repr(u8)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))] -pub enum NbtList { - Empty = END_ID, - Byte(Vec<NbtByte>) = BYTE_ID, - Short(Vec<NbtShort>) = SHORT_ID, - Int(Vec<NbtInt>) = INT_ID, - Long(Vec<NbtLong>) = LONG_ID, - Float(Vec<NbtFloat>) = FLOAT_ID, - Double(Vec<NbtDouble>) = DOUBLE_ID, - ByteArray(Vec<NbtByteArray>) = BYTE_ARRAY_ID, - String(Vec<NbtString>) = STRING_ID, - List(Vec<NbtList>) = LIST_ID, - Compound(Vec<NbtCompound>) = COMPOUND_ID, - IntArray(Vec<NbtIntArray>) = INT_ARRAY_ID, - LongArray(Vec<NbtLongArray>) = LONG_ARRAY_ID, -} - -impl NbtList { - /// Get the numerical ID of the tag type. - #[inline] - pub fn id(&self) -> u8 { - // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` - // `union` between `repr(C)` structs, each of which has the `u8` - // discriminant as its first field, so we can read the discriminant - // without offsetting the pointer. - unsafe { *<*const _>::from(self).cast::<u8>() } - } -} -impl From<Vec<NbtByte>> for NbtList { - fn from(v: Vec<NbtByte>) -> Self { - Self::Byte(v) - } -} -impl From<Vec<NbtShort>> for NbtList { - fn from(v: Vec<NbtShort>) -> Self { - Self::Short(v) - } -} -impl From<Vec<NbtInt>> for NbtList { - fn from(v: Vec<NbtInt>) -> Self { - Self::Int(v) - } -} -impl From<Vec<NbtLong>> for NbtList { - fn from(v: Vec<NbtLong>) -> Self { - Self::Long(v) - } -} -impl From<Vec<NbtFloat>> for NbtList { - fn from(v: Vec<NbtFloat>) -> Self { - Self::Float(v) - } -} -impl From<Vec<NbtDouble>> for NbtList { - fn from(v: Vec<NbtDouble>) -> Self { - Self::Double(v) - } -} -impl From<Vec<NbtByteArray>> for NbtList { - fn from(v: Vec<NbtByteArray>) -> Self { - Self::ByteArray(v) - } -} -impl From<Vec<NbtString>> for NbtList { - fn from(v: Vec<NbtString>) -> Self { - Self::String(v) - } -} -impl From<Vec<NbtList>> for NbtList { - fn from(v: Vec<NbtList>) -> Self { - Self::List(v) - } -} -impl From<Vec<NbtCompound>> for NbtList { - fn from(v: Vec<NbtCompound>) -> Self { - Self::Compound(v) - } -} -impl From<Vec<NbtIntArray>> for NbtList { - fn from(v: Vec<NbtIntArray>) -> Self { - Self::IntArray(v) - } -} -impl From<Vec<NbtLongArray>> for NbtList { - fn from(v: Vec<NbtLongArray>) -> Self { - Self::LongArray(v) - } -} - -// thanks to Moulberry/Graphite for the idea to use a vec and binary search -#[derive(Debug, Clone, Default, PartialEq)] -pub struct NbtCompound { - inner: Vec<(NbtString, Nbt)>, -} -impl NbtCompound { - #[inline] - pub fn with_capacity(capacity: usize) -> Self { - Self { - inner: Vec::with_capacity(capacity), - } - } - - #[inline] - fn binary_search(&self, key: &NbtString) -> Result<usize, usize> { - self.inner.binary_search_by(|(k, _)| k.cmp(key)) - } - - /// Get a reference to the value corresponding to the key in this compound. - /// - /// If you previously used [`Self::insert_unsorted`] without [`Self::sort`], - /// this function may return incorrect results. - #[inline] - pub fn get(&self, key: &str) -> Option<&Nbt> { - if self.is_worth_sorting() { - let key = NbtString::from(key); - self.binary_search(&key).ok().map(|i| &self.inner[i].1) - } else { - for (k, v) in &self.inner { - if &key == k { - return Some(v); - } - } - None - } - } - - #[inline] - pub fn insert_unsorted(&mut self, key: NbtString, value: Nbt) { - self.inner.push((key, value)); - } - - /// Insert an item into the compound, returning the previous value if it - /// existed. - /// - /// If you're adding many items at once, it's more efficient to use - /// [`Self::insert_unsorted`] and then [`Self::sort`] after everything is - /// inserted. - #[inline] - pub fn insert(&mut self, key: NbtString, value: Nbt) { - self.inner.push((key, value)); - self.sort() - } - - #[inline] - pub fn sort(&mut self) { - if !self.is_worth_sorting() { - return; - } - self.inner.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); - } - - #[inline] - pub fn iter(&self) -> std::slice::Iter<'_, (CompactString, Nbt)> { - self.inner.iter() - } - - #[inline] - fn is_worth_sorting(&self) -> bool { - // i don't actually know when binary search starts being better, but it's at - // least more than 12 - self.inner.len() >= 32 - } -} - -impl IntoIterator for NbtCompound { - type Item = (NbtString, Nbt); - type IntoIter = std::vec::IntoIter<Self::Item>; - - fn into_iter(self) -> Self::IntoIter { - self.inner.into_iter() - } -} - -#[cfg(feature = "serde")] -impl Serialize for NbtCompound { - fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { - let mut map = serializer.serialize_map(Some(self.inner.len()))?; - for (key, value) in &self.inner { - map.serialize_entry(key, value)?; - } - map.end() - } -} -#[cfg(feature = "serde")] -impl<'de> Deserialize<'de> for NbtCompound { - fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { - use std::collections::BTreeMap; - let map = <BTreeMap<NbtString, Nbt> as Deserialize>::deserialize(deserializer)?; - Ok(Self { - inner: map.into_iter().collect(), - }) - } -} - -impl FromIterator<(NbtString, Nbt)> for NbtCompound { - fn from_iter<T: IntoIterator<Item = (NbtString, Nbt)>>(iter: T) -> Self { - let inner = iter.into_iter().collect::<Vec<_>>(); - Self { inner } - } -} - -impl From<Vec<(NbtString, Nbt)>> for NbtCompound { - fn from(inner: Vec<(NbtString, Nbt)>) -> Self { - Self { inner } - } -} |
