aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock17
-rw-r--r--azalea-nbt/Cargo.toml1
-rwxr-xr-xazalea-nbt/benches/compare.rs2
-rwxr-xr-xazalea-nbt/benches/nbt.rs33
-rwxr-xr-xazalea-nbt/src/encode.rs73
-rwxr-xr-xazalea-nbt/src/tag.rs90
-rw-r--r--azalea-nbt/tests/inttest1023.nbtbin0 -> 4104 bytes
-rwxr-xr-xazalea-nbt/tests/inttest16.nbt (renamed from azalea-nbt/tests/inttest.nbt)bin95 -> 95 bytes
-rw-r--r--azalea-nbt/tests/inttest3.nbtbin0 -> 24 bytes
-rwxr-xr-xazalea-nbt/tests/tests.rs32
10 files changed, 222 insertions, 26 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3ba21574..493cc6a1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -345,6 +345,7 @@ dependencies = [
"flate2",
"graphite_binary",
"log",
+ "packed_simd_2",
"serde",
"valence_nbt",
]
@@ -1515,6 +1516,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
+name = "libm"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
+
+[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1741,6 +1748,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
+name = "packed_simd_2"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282"
+dependencies = [
+ "cfg-if",
+ "libm",
+]
+
+[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml
index 2c35c3da..bead5ba3 100644
--- a/azalea-nbt/Cargo.toml
+++ b/azalea-nbt/Cargo.toml
@@ -15,6 +15,7 @@ compact_str = { version = "0.7.0", features = ["serde"] }
enum-as-inner = "0.5.1"
flate2 = "^1.0.25"
log = "0.4.17"
+packed_simd_2 = "0.3.8"
serde = { version = "1.0.152", features = ["derive"], optional = true }
[dev-dependencies]
diff --git a/azalea-nbt/benches/compare.rs b/azalea-nbt/benches/compare.rs
index f0fa17c0..23e133da 100755
--- a/azalea-nbt/benches/compare.rs
+++ b/azalea-nbt/benches/compare.rs
@@ -79,7 +79,7 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) {
}
fn bench(c: &mut Criterion) {
- bench_read_file("tests/bigtest.nbt", c);
+ // bench_read_file("tests/bigtest.nbt", c);
// bench_read_file("tests/simple_player.dat", c);
// bench_read_file("tests/complex_player.dat", c);
// bench_read_file("tests/level.dat", c);
diff --git a/azalea-nbt/benches/nbt.rs b/azalea-nbt/benches/nbt.rs
index 0d413103..a219a46d 100755
--- a/azalea-nbt/benches/nbt.rs
+++ b/azalea-nbt/benches/nbt.rs
@@ -15,7 +15,10 @@ fn bench_file(filename: &str, c: &mut Criterion) {
// decode the original src so most of the time isn't spent on unzipping
let mut decoded_src_decoder = GzDecoder::new(&mut src);
let mut decoded_src = Vec::new();
- decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
+ if decoded_src_decoder.read_to_end(&mut decoded_src).is_err() {
+ // oh probably wasn't gzipped then
+ decoded_src = contents;
+ }
let mut decoded_src_stream = Cursor::new(&decoded_src[..]);
@@ -26,12 +29,12 @@ fn bench_file(filename: &str, c: &mut Criterion) {
group.throughput(Throughput::Bytes(decoded_src.len() as u64));
- group.bench_function("Decode", |b| {
- b.iter(|| {
- black_box(Nbt::read(&mut decoded_src_stream).unwrap());
- decoded_src_stream.set_position(0);
- })
- });
+ // group.bench_function("Decode", |b| {
+ // b.iter(|| {
+ // black_box(Nbt::read(&mut decoded_src_stream).unwrap());
+ // decoded_src_stream.set_position(0);
+ // })
+ // });
// group.bench_function("Encode", |b| {
// b.iter(|| {
@@ -41,7 +44,16 @@ fn bench_file(filename: &str, c: &mut Criterion) {
group.bench_function("Get", |b| {
b.iter(|| {
- black_box(nbt.as_compound().unwrap().get(""));
+ let level = nbt
+ .as_compound()
+ .unwrap()
+ .get("Level")
+ .unwrap()
+ .as_compound()
+ .unwrap();
+ for (k, _) in level.iter() {
+ black_box(level.get(black_box(k)));
+ }
})
});
group.finish();
@@ -53,7 +65,10 @@ fn bench(c: &mut Criterion) {
// 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/inttest16.nbt", c);
+
+ // bench_file("tests/inttest1023.nbt", c);
+ // bench_file("tests/inttest3.nbt", c);
}
criterion_group!(benches, bench);
diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs
index bf56c223..f5195327 100755
--- a/azalea-nbt/src/encode.rs
+++ b/azalea-nbt/src/encode.rs
@@ -2,6 +2,7 @@ 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(always)]
@@ -81,14 +82,78 @@ fn write_list(writer: &mut impl Write, value: &NbtList) {
}
NbtList::Int(l) => {
writer.write_i32::<BE>(l.len() as i32).unwrap();
- for &v in l {
- writer.write_i32::<BE>(v).unwrap();
+ // flip the bits to big endian with simd
+ let l = l.as_slice();
+ let mut position = 0;
+ // x16
+ while l.len() - position >= 16 {
+ let l = unsafe { i32x16::from_slice_unaligned_unchecked(&l[position..]) };
+ l.to_be();
+ let l = unsafe { std::mem::transmute::<i32x16, [u8; 64]>(l) };
+ writer.write_all(&l).unwrap();
+ position += 16;
+ }
+ // x8
+ if l.len() - position >= 8 {
+ let l = unsafe { i32x8::from_slice_unaligned_unchecked(&l[position..]) };
+ l.to_be();
+ let l = unsafe { std::mem::transmute::<i32x8, [u8; 32]>(l) };
+ writer.write_all(&l).unwrap();
+ position += 8;
+ }
+ // x4
+ if l.len() - position >= 4 {
+ let l = unsafe { i32x4::from_slice_unaligned_unchecked(&l[position..]) };
+ l.to_be();
+ let l = unsafe { std::mem::transmute::<i32x4, [u8; 16]>(l) };
+ writer.write_all(&l).unwrap();
+ position += 4;
+ }
+ // x2
+ if l.len() - position >= 2 {
+ let l = unsafe { i32x2::from_slice_unaligned_unchecked(&l[position..]) };
+ l.to_be();
+ let l = unsafe { std::mem::transmute::<i32x2, [u8; 8]>(l) };
+ writer.write_all(&l).unwrap();
+ position += 2;
+ }
+ // x1 ... just a normal write_i32
+ if l.len() - position >= 1 {
+ writer.write_i32::<BE>(l[position]).unwrap();
}
}
NbtList::Long(l) => {
writer.write_i32::<BE>(l.len() as i32).unwrap();
- for &v in l {
- writer.write_i64::<BE>(v).unwrap();
+ // flip the bits to big endian with simd
+ let l = l.as_slice();
+ 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();
}
}
NbtList::Float(l) => {
diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs
index 620d0577..efb6bdd2 100755
--- a/azalea-nbt/src/tag.rs
+++ b/azalea-nbt/src/tag.rs
@@ -48,6 +48,17 @@ pub enum Nbt {
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)]
@@ -69,7 +80,7 @@ pub enum NbtList {
LongArray(Vec<NbtLongArray>) = LONG_ARRAY_ID,
}
-impl Nbt {
+impl NbtList {
/// Get the numerical ID of the tag type.
#[inline]
pub fn id(&self) -> u8 {
@@ -80,15 +91,64 @@ impl Nbt {
unsafe { *<*const _>::from(self).cast::<u8>() }
}
}
-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)
}
}
@@ -161,7 +221,9 @@ impl NbtCompound {
#[inline]
fn is_worth_sorting(&self) -> bool {
- self.inner.len() > 128
+ // i don't actually know when binary search starts being better, but it's at
+ // least more than 12
+ self.inner.len() >= 32
}
}
#[cfg(feature = "serde")]
@@ -191,3 +253,9 @@ impl FromIterator<(NbtString, Nbt)> for NbtCompound {
Self { inner }
}
}
+
+impl From<Vec<(NbtString, Nbt)>> for NbtCompound {
+ fn from(inner: Vec<(NbtString, Nbt)>) -> Self {
+ Self { inner }
+ }
+}
diff --git a/azalea-nbt/tests/inttest1023.nbt b/azalea-nbt/tests/inttest1023.nbt
new file mode 100644
index 00000000..481dde19
--- /dev/null
+++ b/azalea-nbt/tests/inttest1023.nbt
Binary files differ
diff --git a/azalea-nbt/tests/inttest.nbt b/azalea-nbt/tests/inttest16.nbt
index ad9255f2..ad9255f2 100755
--- a/azalea-nbt/tests/inttest.nbt
+++ b/azalea-nbt/tests/inttest16.nbt
Binary files differ
diff --git a/azalea-nbt/tests/inttest3.nbt b/azalea-nbt/tests/inttest3.nbt
new file mode 100644
index 00000000..2890f577
--- /dev/null
+++ b/azalea-nbt/tests/inttest3.nbt
Binary files differ
diff --git a/azalea-nbt/tests/tests.rs b/azalea-nbt/tests/tests.rs
index a7f6912f..5c82c3c7 100755
--- a/azalea-nbt/tests/tests.rs
+++ b/azalea-nbt/tests/tests.rs
@@ -1,4 +1,4 @@
-use azalea_nbt::{NbtCompound, NbtList, Nbt};
+use azalea_nbt::{Nbt, NbtCompound, NbtList};
use std::io::Cursor;
#[test]
@@ -108,3 +108,33 @@ fn test_simple_player() {
assert_eq!(decoded_tag, original_tag);
}
+
+// #[test]
+// fn test_inttest() {
+// let original = include_bytes!("inttest.nbt").to_vec();
+
+// let mut original_stream = Cursor::new(original);
+// let original_tag = Nbt::read_gzip(&mut original_stream).unwrap();
+
+// let mut result = Vec::new();
+// original_tag.write(&mut result);
+
+// let decoded_tag = Nbt::read(&mut Cursor::new(&result)).unwrap();
+
+// assert_eq!(decoded_tag, original_tag);
+// }
+
+#[test]
+fn test_inttest1023() {
+ let original = include_bytes!("inttest1023.nbt").to_vec();
+
+ let mut original_stream = Cursor::new(original.as_slice());
+ let original_tag = Nbt::read(&mut original_stream).unwrap();
+
+ let mut result = Vec::new();
+ original_tag.write(&mut result);
+
+ let decoded_tag = Nbt::read(&mut Cursor::new(&result)).unwrap();
+
+ assert_eq!(decoded_tag, original_tag);
+}