diff options
| author | mat <github@matdoes.dev> | 2023-03-23 00:19:00 -0500 |
|---|---|---|
| committer | mat <github@matdoes.dev> | 2023-03-23 00:19:00 -0500 |
| commit | d08bf9b943a6578990668f7f073309f3bb9678ab (patch) | |
| tree | 63ca75ed080b79d26bbff12642e6f5539399a2fa | |
| parent | c3b63ad129abf956f2171b66980fc94d728fa08b (diff) | |
| download | azalea-drasl-d08bf9b943a6578990668f7f073309f3bb9678ab.tar.xz | |
make encode not a Result and small optimizations
| -rwxr-xr-x | azalea-nbt/README.md | 2 | ||||
| -rwxr-xr-x | azalea-nbt/benches/compare.rs | 34 | ||||
| -rwxr-xr-x | azalea-nbt/benches/nbt.rs | 12 | ||||
| -rwxr-xr-x | azalea-nbt/src/encode.rs | 244 | ||||
| -rwxr-xr-x | azalea-nbt/src/tag.rs | 82 | ||||
| -rwxr-xr-x | azalea-nbt/tests/tests.rs | 8 |
6 files changed, 210 insertions, 172 deletions
diff --git a/azalea-nbt/README.md b/azalea-nbt/README.md index 5e20b008..97da90cc 100755 --- a/azalea-nbt/README.md +++ b/azalea-nbt/README.md @@ -2,8 +2,6 @@ A fast NBT serializer and deserializer. -Note: Running your code with `RUSTFLAGS="-C target-cpu=native"` will result in significant performance improvements. - # Examples ``` diff --git a/azalea-nbt/benches/compare.rs b/azalea-nbt/benches/compare.rs index bed77527..91fba9f5 100755 --- a/azalea-nbt/benches/compare.rs +++ b/azalea-nbt/benches/compare.rs @@ -38,13 +38,13 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) { }) }); - group.bench_function("valence_parse", |b| { - b.iter(|| { - let input = black_box(input); - let nbt = valence_nbt::from_binary_slice(&mut &input[..]).unwrap(); - black_box(nbt); - }) - }); + // group.bench_function("valence_parse", |b| { + // b.iter(|| { + // let input = black_box(input); + // let nbt = valence_nbt::from_binary_slice(&mut &input[..]).unwrap(); + // black_box(nbt); + // }) + // }); // // writing @@ -53,7 +53,7 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) { b.iter(|| { let nbt = black_box(&nbt); let mut written = Vec::new(); - nbt.write(&mut written).unwrap(); + nbt.write(&mut written); black_box(written); }) }); @@ -67,15 +67,15 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) { }) }); - let nbt = valence_nbt::from_binary_slice(&mut &input[..]).unwrap(); - group.bench_function("valence_write", |b| { - b.iter(|| { - let nbt = black_box(&nbt); - let mut written = Vec::new(); - valence_nbt::to_binary_writer(&mut written, &nbt.0, &nbt.1).unwrap(); - black_box(written); - }) - }); + // let nbt = valence_nbt::from_binary_slice(&mut &input[..]).unwrap(); + // group.bench_function("valence_write", |b| { + // b.iter(|| { + // let nbt = black_box(&nbt); + // let mut written = Vec::new(); + // valence_nbt::to_binary_writer(&mut written, &nbt.0, + // &nbt.1).unwrap(); black_box(written); + // }) + // }); } fn bench(c: &mut Criterion) { diff --git a/azalea-nbt/benches/nbt.rs b/azalea-nbt/benches/nbt.rs index 3d56cad9..cfa05a9d 100755 --- a/azalea-nbt/benches/nbt.rs +++ b/azalea-nbt/benches/nbt.rs @@ -35,19 +35,19 @@ fn bench_file(filename: &str, c: &mut Criterion) { group.bench_function("Encode", |b| { b.iter(|| { - nbt.write(&mut black_box(Vec::new())).unwrap(); + nbt.write(&mut black_box(Vec::new())); }) }); group.finish(); } fn bench(c: &mut Criterion) { - // bench_file("tests/bigtest.nbt", c); - // bench_file("tests/simple_player.dat", c); + bench_file("tests/bigtest.nbt", c); + bench_file("tests/simple_player.dat", c); bench_file("tests/complex_player.dat", c); - // bench_file("tests/level.dat", c); - // bench_file("tests/stringtest.nbt", c); - // bench_file("tests/inttest.nbt", c); + bench_file("tests/level.dat", c); + bench_file("tests/stringtest.nbt", c); + bench_file("tests/inttest.nbt", c); } criterion_group!(benches, bench); diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index 03715691..4d76fede 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -1,207 +1,199 @@ use crate::tag::*; -use crate::Error; use azalea_buf::McBufWritable; use byteorder::{WriteBytesExt, BE}; use flate2::write::{GzEncoder, ZlibEncoder}; use std::io::Write; -#[inline] -fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { - writer.write_u16::<BE>(string.len() as u16)?; - writer.write_all(string.as_bytes())?; - - Ok(()) +#[inline(always)] +fn write_string(writer: &mut dyn Write, string: &str) { + writer.write_u16::<BE>(string.len() as u16).unwrap(); + writer.write_all(string.as_bytes()).unwrap(); } #[inline] -fn write_compound(writer: &mut dyn Write, value: &NbtCompound, end_tag: bool) -> Result<(), Error> { - for (key, tag) in value.inner() { +fn write_compound(writer: &mut dyn Write, value: &NbtCompound, end_tag: bool) { + for (key, tag) in value.iter() { match tag { Tag::End => {} Tag::Byte(value) => { - writer.write_u8(BYTE_ID)?; - write_string(writer, key)?; - writer.write_i8(*value)?; + writer.write_u8(BYTE_ID).unwrap(); + write_string(writer, key); + writer.write_i8(*value).unwrap(); } Tag::Short(value) => { - writer.write_u8(SHORT_ID)?; - write_string(writer, key)?; - writer.write_i16::<BE>(*value)?; + writer.write_u8(SHORT_ID).unwrap(); + write_string(writer, key); + writer.write_i16::<BE>(*value).unwrap(); } Tag::Int(value) => { - writer.write_u8(INT_ID)?; - write_string(writer, key)?; - writer.write_i32::<BE>(*value)?; + writer.write_u8(INT_ID).unwrap(); + write_string(writer, key); + writer.write_i32::<BE>(*value).unwrap(); } Tag::Long(value) => { - writer.write_u8(LONG_ID)?; - write_string(writer, key)?; - writer.write_i64::<BE>(*value)?; + writer.write_u8(LONG_ID).unwrap(); + write_string(writer, key); + writer.write_i64::<BE>(*value).unwrap(); } Tag::Float(value) => { - writer.write_u8(FLOAT_ID)?; - write_string(writer, key)?; - writer.write_f32::<BE>(*value)?; + writer.write_u8(FLOAT_ID).unwrap(); + write_string(writer, key); + writer.write_f32::<BE>(*value).unwrap(); } Tag::Double(value) => { - writer.write_u8(DOUBLE_ID)?; - write_string(writer, key)?; - writer.write_f64::<BE>(*value)?; + writer.write_u8(DOUBLE_ID).unwrap(); + write_string(writer, key); + writer.write_f64::<BE>(*value).unwrap(); } Tag::ByteArray(value) => { - writer.write_u8(BYTE_ARRAY_ID)?; - write_string(writer, key)?; - write_byte_array(writer, value)?; + writer.write_u8(BYTE_ARRAY_ID).unwrap(); + write_string(writer, key); + write_byte_array(writer, value); } Tag::String(value) => { - writer.write_u8(STRING_ID)?; - write_string(writer, key)?; - write_string(writer, value)?; + writer.write_u8(STRING_ID).unwrap(); + write_string(writer, key); + write_string(writer, value); } Tag::List(value) => { - writer.write_u8(LIST_ID)?; - write_string(writer, key)?; - write_list(writer, value)?; + writer.write_u8(LIST_ID).unwrap(); + write_string(writer, key); + write_list(writer, value); } Tag::Compound(value) => { - writer.write_u8(COMPOUND_ID)?; - write_string(writer, key)?; - write_compound(writer, value, true)?; + writer.write_u8(COMPOUND_ID).unwrap(); + write_string(writer, key); + write_compound(writer, value, true); } Tag::IntArray(value) => { - writer.write_u8(INT_ARRAY_ID)?; - write_string(writer, key)?; - write_int_array(writer, value)?; + writer.write_u8(INT_ARRAY_ID).unwrap(); + write_string(writer, key); + write_int_array(writer, value); } Tag::LongArray(value) => { - writer.write_u8(LONG_ARRAY_ID)?; - write_string(writer, key)?; - write_long_array(writer, value)?; + writer.write_u8(LONG_ARRAY_ID).unwrap(); + write_string(writer, key); + write_long_array(writer, value); } } } if end_tag { - writer.write_u8(Tag::End.id())?; + writer.write_u8(END_ID).unwrap(); } - Ok(()) } #[inline] -fn write_list(writer: &mut dyn Write, value: &NbtList) -> Result<(), Error> { +fn write_list(writer: &mut dyn Write, value: &NbtList) { match value { - NbtList::Empty => writer.write_all(&[0; 5])?, + NbtList::Empty => writer.write_all(&[0; 5]).unwrap(), NbtList::Byte(l) => { - writer.write_u8(BYTE_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; - for v in l { - writer.write_i8(*v)?; - } + writer.write_u8(BYTE_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); + let l = l.as_slice(); + writer + .write_all(unsafe { std::slice::from_raw_parts(l.as_ptr() as *const u8, l.len()) }) + .unwrap(); } NbtList::Short(l) => { - writer.write_u8(SHORT_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(SHORT_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - writer.write_i16::<BE>(*v)?; + writer.write_i16::<BE>(*v).unwrap(); } } NbtList::Int(l) => { - writer.write_u8(INT_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(INT_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - writer.write_i32::<BE>(*v)?; + writer.write_i32::<BE>(*v).unwrap(); } } NbtList::Long(l) => { - writer.write_u8(LONG_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(LONG_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - writer.write_i64::<BE>(*v)?; + writer.write_i64::<BE>(*v).unwrap(); } } NbtList::Float(l) => { - writer.write_u8(FLOAT_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(FLOAT_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - writer.write_f32::<BE>(*v)?; + writer.write_f32::<BE>(*v).unwrap(); } } NbtList::Double(l) => { - writer.write_u8(DOUBLE_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(DOUBLE_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - writer.write_f64::<BE>(*v)?; + writer.write_f64::<BE>(*v).unwrap(); } } NbtList::ByteArray(l) => { - writer.write_u8(BYTE_ARRAY_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(BYTE_ARRAY_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - write_byte_array(writer, v)?; + write_byte_array(writer, v); } } NbtList::String(l) => { - writer.write_u8(STRING_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(STRING_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - write_string(writer, v)?; + write_string(writer, v); } } NbtList::List(l) => { - writer.write_u8(LIST_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(LIST_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - write_list(writer, v)?; + write_list(writer, v); } } NbtList::Compound(l) => { - writer.write_u8(COMPOUND_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(COMPOUND_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - write_compound(writer, v, true)?; + write_compound(writer, v, true); } } NbtList::IntArray(l) => { - writer.write_u8(INT_ARRAY_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(INT_ARRAY_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - write_int_array(writer, v)?; + write_int_array(writer, v); } } NbtList::LongArray(l) => { - writer.write_u8(LONG_ARRAY_ID)?; - writer.write_i32::<BE>(l.len() as i32)?; + writer.write_u8(LONG_ARRAY_ID).unwrap(); + writer.write_i32::<BE>(l.len() as i32).unwrap(); for v in l { - write_long_array(writer, v)?; + write_long_array(writer, v); } } } - - Ok(()) } #[inline] -fn write_byte_array(writer: &mut dyn Write, value: &Vec<u8>) -> Result<(), Error> { - writer.write_u32::<BE>(value.len() as u32)?; - writer.write_all(value)?; - Ok(()) +fn write_byte_array(writer: &mut dyn Write, value: &Vec<u8>) { + writer.write_u32::<BE>(value.len() as u32).unwrap(); + writer.write_all(value).unwrap(); } #[inline] -fn write_int_array(writer: &mut dyn Write, value: &Vec<i32>) -> Result<(), Error> { - writer.write_u32::<BE>(value.len() as u32)?; +fn write_int_array(writer: &mut dyn Write, value: &Vec<i32>) { + writer.write_u32::<BE>(value.len() as u32).unwrap(); for &int in value { - writer.write_i32::<BE>(int)?; + writer.write_i32::<BE>(int).unwrap(); } - Ok(()) } #[inline] -fn write_long_array(writer: &mut dyn Write, value: &Vec<i64>) -> Result<(), Error> { - writer.write_u32::<BE>(value.len() as u32)?; +fn write_long_array(writer: &mut dyn Write, value: &Vec<i64>) { + writer.write_u32::<BE>(value.len() as u32).unwrap(); for &long in value { - writer.write_i64::<BE>(long)?; + writer.write_i64::<BE>(long).unwrap(); } - Ok(()) } impl Tag { @@ -210,42 +202,38 @@ impl Tag { /// [`Tag::write`] to avoid the `End` tag (this is used when writing NBT to /// a file). #[inline] - pub fn write_without_end(&self, writer: &mut dyn Write) -> Result<(), Error> { + pub fn write_without_end(&self, writer: &mut dyn Write) { match self { Tag::End => {} - Tag::Byte(value) => writer.write_i8(*value)?, - Tag::Short(value) => writer.write_i16::<BE>(*value)?, - Tag::Int(value) => writer.write_i32::<BE>(*value)?, - Tag::Long(value) => writer.write_i64::<BE>(*value)?, - Tag::Float(value) => writer.write_f32::<BE>(*value)?, - Tag::Double(value) => writer.write_f64::<BE>(*value)?, - Tag::ByteArray(value) => write_byte_array(writer, value)?, - Tag::String(value) => write_string(writer, value)?, - Tag::List(value) => write_list(writer, value)?, - Tag::Compound(value) => write_compound(writer, value, true)?, - Tag::IntArray(value) => write_int_array(writer, value)?, - Tag::LongArray(value) => write_long_array(writer, value)?, + Tag::Byte(value) => writer.write_i8(*value).unwrap(), + Tag::Short(value) => writer.write_i16::<BE>(*value).unwrap(), + Tag::Int(value) => writer.write_i32::<BE>(*value).unwrap(), + Tag::Long(value) => writer.write_i64::<BE>(*value).unwrap(), + Tag::Float(value) => writer.write_f32::<BE>(*value).unwrap(), + Tag::Double(value) => writer.write_f64::<BE>(*value).unwrap(), + Tag::ByteArray(value) => write_byte_array(writer, value), + Tag::String(value) => write_string(writer, value), + Tag::List(value) => write_list(writer, value), + Tag::Compound(value) => write_compound(writer, value, true), + Tag::IntArray(value) => write_int_array(writer, value), + Tag::LongArray(value) => write_long_array(writer, value), } - - Ok(()) } /// Write the compound tag as NBT data. /// - /// # Errors + /// # Panics /// - /// Returns an `Err` if it's not a Compound or End tag. - pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> { + /// Will panic if the tag is not a Compound or End tag. + pub fn write(&self, writer: &mut impl Write) { match self { Tag::Compound(value) => { - write_compound(writer, value, false)?; - Ok(()) + write_compound(writer, value, false); } Tag::End => { - 0u8.write_into(writer)?; - Ok(()) + END_ID.write_into(writer).unwrap(); } - _ => Err(Error::InvalidTag), + _ => panic!("Not a compound tag"), } } @@ -254,7 +242,7 @@ impl Tag { /// # Errors /// /// Returns an `Err` if it's not a Compound or End tag. - pub fn write_zlib(&self, writer: &mut impl Write) -> Result<(), Error> { + pub fn write_zlib(&self, writer: &mut impl Write) { let mut encoder = ZlibEncoder::new(writer, flate2::Compression::default()); self.write(&mut encoder) } @@ -264,7 +252,7 @@ impl Tag { /// # Errors /// /// Returns an `Err` if it's not a Compound or End tag. - pub fn write_gzip(&self, writer: &mut impl Write) -> Result<(), Error> { + pub fn write_gzip(&self, writer: &mut impl Write) { let mut encoder = GzEncoder::new(writer, flate2::Compression::default()); self.write(&mut encoder) } @@ -272,7 +260,7 @@ impl Tag { impl McBufWritable for Tag { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - self.write(buf) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + self.write(buf); + Ok(()) } } diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs index e23db913..fb44c559 100755 --- a/azalea-nbt/src/tag.rs +++ b/azalea-nbt/src/tag.rs @@ -93,14 +93,16 @@ impl NbtList { } // thanks to Moulberry/Graphite for the idea to use a vec and binary search -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct NbtCompound { + sorted: bool, inner: Vec<(NbtString, Tag)>, } impl NbtCompound { #[inline] pub fn with_capacity(capacity: usize) -> Self { Self { + sorted: false, inner: Vec::with_capacity(capacity), } } @@ -110,25 +112,38 @@ impl NbtCompound { 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: &NbtString) -> Option<&Tag> { + pub fn get(&mut self, key: &NbtString) -> Option<&Tag> { + if !self.sorted { + self.sort() + } self.binary_search(key).ok().map(|i| &self.inner[i].1) } + /// 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: Tag) -> Option<Tag> { - match self.binary_search(&key) { - Ok(i) => Some(std::mem::replace(&mut self.inner[i].1, value)), - Err(i) => { - self.inner.insert(i, (key, value)); - None - } - } + pub fn insert(&mut self, key: NbtString, value: Tag) { + self.inner.push((key, value)); } #[inline] - pub fn inner(&self) -> &Vec<(NbtString, Tag)> { - &self.inner + pub fn sort(&mut self) { + self.sorted = true; + self.inner.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); + } + + #[inline] + pub fn iter(&self) -> std::slice::Iter<'_, (CompactString, Tag)> { + self.inner.iter() } } #[cfg(feature = "serde")] @@ -148,14 +163,51 @@ impl<'de> Deserialize<'de> for NbtCompound { let map = <BTreeMap<NbtString, Tag> as Deserialize>::deserialize(deserializer)?; Ok(Self { inner: map.into_iter().collect(), + sorted: false, }) } } impl FromIterator<(NbtString, Tag)> for NbtCompound { fn from_iter<T: IntoIterator<Item = (NbtString, Tag)>>(iter: T) -> Self { - let mut inner = iter.into_iter().collect::<Vec<_>>(); - inner.sort_unstable_by_key(|(k, _)| k.clone()); - Self { inner } + let inner = iter.into_iter().collect::<Vec<_>>(); + Self { + inner, + sorted: false, + } + } +} + +impl PartialEq for NbtCompound { + /// Compare two NBT compounds for equality, ignoring the order of the keys. + /// Note that this will execute fastest if the keys are already sorted with + /// [`Self::sort`]. + fn eq(&self, other: &Self) -> bool { + if self.inner.len() != other.inner.len() { + return false; + } + if self.inner == other.inner { + return true; + } + if !self.sorted && !other.sorted { + // neither are sorted, so sort both + let mut a = self.clone(); + let mut b = other.clone(); + a.sort(); + b.sort(); + a == b + } else if !self.sorted { + // only self is sorted, so sort self + let mut a = self.clone(); + a.sort(); + a == *other + } else if !other.sorted { + // only other is sorted, so sort other + let mut b = other.clone(); + b.sort(); + *self == b + } else { + self.inner == other.inner + } } } diff --git a/azalea-nbt/tests/tests.rs b/azalea-nbt/tests/tests.rs index 22fc1074..1555d7fa 100755 --- a/azalea-nbt/tests/tests.rs +++ b/azalea-nbt/tests/tests.rs @@ -27,7 +27,7 @@ fn test_roundtrip_hello_world() { // write hello_world.nbt let mut result = Vec::new(); - tag.write(&mut result).unwrap(); + tag.write(&mut result); assert_eq!(result, original); } @@ -41,7 +41,7 @@ fn test_bigtest() { let original_tag = Tag::read_gzip(&mut original_stream).unwrap(); let mut result = Vec::new(); - original_tag.write(&mut result).unwrap(); + original_tag.write(&mut result); let decoded_tag = Tag::read(&mut Cursor::new(&result)).unwrap(); @@ -87,7 +87,7 @@ fn test_complex_player() { let original_tag = Tag::read_gzip(&mut original_stream).unwrap(); let mut result = Vec::new(); - original_tag.write(&mut result).unwrap(); + original_tag.write(&mut result); let decoded_tag = Tag::read(&mut Cursor::new(&result)).unwrap(); @@ -102,7 +102,7 @@ fn test_simple_player() { let original_tag = Tag::read_gzip(&mut original_stream).unwrap(); let mut result = Vec::new(); - original_tag.write(&mut result).unwrap(); + original_tag.write(&mut result); let decoded_tag = Tag::read(&mut Cursor::new(&result)).unwrap(); |
