diff options
| author | mat <github@matdoes.dev> | 2023-03-22 23:18:55 -0500 |
|---|---|---|
| committer | mat <github@matdoes.dev> | 2023-03-22 23:18:55 -0500 |
| commit | c3b63ad129abf956f2171b66980fc94d728fa08b (patch) | |
| tree | 3e987a404b7b808d774eee7bbc7c8eb89b84fa11 | |
| parent | 350bbac2828ebd5b2a5abac5c54014d271e8603a (diff) | |
| download | azalea-drasl-c3b63ad129abf956f2171b66980fc94d728fa08b.tar.xz | |
binary search map
| -rw-r--r-- | Cargo.lock | 18 | ||||
| -rw-r--r-- | azalea-nbt/Cargo.toml | 3 | ||||
| -rwxr-xr-x | azalea-nbt/README.md | 7 | ||||
| -rwxr-xr-x | azalea-nbt/benches/compare.rs | 54 | ||||
| -rwxr-xr-x | azalea-nbt/benches/nbt.rs | 14 | ||||
| -rwxr-xr-x | azalea-nbt/src/decode.rs | 3 | ||||
| -rwxr-xr-x | azalea-nbt/src/encode.rs | 2 | ||||
| -rwxr-xr-x | azalea-nbt/src/lib.rs | 14 | ||||
| -rwxr-xr-x | azalea-nbt/src/tag.rs | 73 | ||||
| -rwxr-xr-x | azalea-nbt/tests/tests.rs | 9 |
10 files changed, 122 insertions, 75 deletions
@@ -40,19 +40,6 @@ dependencies = [ ] [[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "serde", - "version_check", -] - -[[package]] name = "aho-corasick" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -349,7 +336,6 @@ dependencies = [ name = "azalea-nbt" version = "0.6.0" dependencies = [ - "ahash 0.8.3", "azalea-buf", "byteorder", "compact_str", @@ -656,7 +642,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04d90ce493910ad9af3b4220ea6864c7d1472761086a98230ecac59c8d547e95" dependencies = [ - "ahash 0.7.6", + "ahash", "bevy_utils_proc_macros", "getrandom", "hashbrown", @@ -1329,7 +1315,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash 0.7.6", + "ahash", "serde", ] diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml index e14c501f..76c57663 100644 --- a/azalea-nbt/Cargo.toml +++ b/azalea-nbt/Cargo.toml @@ -9,7 +9,6 @@ repository = "https://github.com/mat-1/azalea/tree/main/azalea-nbt" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ahash = { version = "^0.8.3" } azalea-buf = { path = "../azalea-buf", version = "^0.6.0" } byteorder = "^1.4.3" compact_str = { version = "0.7.0", features = ["serde"] } @@ -26,7 +25,7 @@ fastnbt = "2.4.3" [features] default = [] -serde = ["dep:serde", "ahash/serde"] +serde = ["dep:serde"] [profile.release] lto = true diff --git a/azalea-nbt/README.md b/azalea-nbt/README.md index 63e0bfe7..5e20b008 100755 --- a/azalea-nbt/README.md +++ b/azalea-nbt/README.md @@ -7,17 +7,16 @@ Note: Running your code with `RUSTFLAGS="-C target-cpu=native"` will result in s # Examples ``` -use ahash::AHashMap; -use azalea_nbt::Tag; +use azalea_nbt::{Tag, NbtCompound}; use std::io::Cursor; let buf = include_bytes!("../tests/hello_world.nbt"); let tag = Tag::read(&mut Cursor::new(&buf[..])).unwrap(); assert_eq!( tag, - Tag::Compound(AHashMap::from_iter(vec![( + Tag::Compound(NbtCompound::from_iter(vec![( "hello world".into(), - Tag::Compound(AHashMap::from_iter(vec![( + Tag::Compound(NbtCompound::from_iter(vec![( "name".into(), Tag::String("Bananrama".into()), )])) diff --git a/azalea-nbt/benches/compare.rs b/azalea-nbt/benches/compare.rs index 7aede267..bed77527 100755 --- a/azalea-nbt/benches/compare.rs +++ b/azalea-nbt/benches/compare.rs @@ -48,40 +48,40 @@ pub fn bench_read_file(filename: &str, c: &mut Criterion) { // // writing - // let nbt = azalea_nbt::Tag::read_from(&mut Cursor::new(input)).unwrap(); - // group.bench_function("azalea_write", |b| { - // b.iter(|| { - // let nbt = black_box(&nbt); - // let mut written = Vec::new(); - // nbt.write(&mut written).unwrap(); - // black_box(written); - // }) - // }); + let nbt = azalea_nbt::Tag::read_from(&mut Cursor::new(input)).unwrap(); + group.bench_function("azalea_write", |b| { + b.iter(|| { + let nbt = black_box(&nbt); + let mut written = Vec::new(); + nbt.write(&mut written).unwrap(); + black_box(written); + }) + }); - // let nbt = graphite_binary::nbt::decode::read(&mut &input[..]).unwrap(); - // group.bench_function("graphite_write", |b| { - // b.iter(|| { - // let nbt = black_box(&nbt); - // let written = graphite_binary::nbt::encode::write(nbt); - // black_box(written); - // }) - // }); + let nbt = graphite_binary::nbt::decode::read(&mut &input[..]).unwrap(); + group.bench_function("graphite_write", |b| { + b.iter(|| { + let nbt = black_box(&nbt); + let written = graphite_binary::nbt::encode::write(nbt); + 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); - // }) - // }); + 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) { 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/complex_player.dat", c); // bench_read_file("tests/level.dat", c); // bench_read_file("tests/stringtest.nbt", c); // bench_read_file("tests/inttest.nbt", c); diff --git a/azalea-nbt/benches/nbt.rs b/azalea-nbt/benches/nbt.rs index 5eeb733f..3d56cad9 100755 --- a/azalea-nbt/benches/nbt.rs +++ b/azalea-nbt/benches/nbt.rs @@ -26,16 +26,16 @@ 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(Tag::read(&mut decoded_src_stream).unwrap()); - // decoded_src_stream.set_position(0); - // }) - // }); + group.bench_function("Decode", |b| { + b.iter(|| { + black_box(Tag::read(&mut decoded_src_stream).unwrap()); + decoded_src_stream.set_position(0); + }) + }); group.bench_function("Encode", |b| { b.iter(|| { - nbt.write(&mut io::sink()).unwrap(); + nbt.write(&mut black_box(Vec::new())).unwrap(); }) }); group.finish(); diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs index 635e2f6d..e2e9c7c9 100755 --- a/azalea-nbt/src/decode.rs +++ b/azalea-nbt/src/decode.rs @@ -1,6 +1,5 @@ use crate::tag::*; use crate::Error; -use ahash::AHashMap; use azalea_buf::{BufReadError, McBufReadable}; use byteorder::{ReadBytesExt, BE}; use flate2::read::{GzDecoder, ZlibDecoder}; @@ -265,7 +264,7 @@ impl Tag { } let name = read_string(stream)?; let tag = Tag::read_known(stream, tag_id)?; - let mut map = AHashMap::with_capacity(1); + let mut map = NbtCompound::with_capacity(1); map.insert(name, tag); Ok(Tag::Compound(map)) diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index ba2b77a5..03715691 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -15,7 +15,7 @@ fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> { #[inline] fn write_compound(writer: &mut dyn Write, value: &NbtCompound, end_tag: bool) -> Result<(), Error> { - for (key, tag) in value { + for (key, tag) in value.inner() { match tag { Tag::End => {} Tag::Byte(value) => { diff --git a/azalea-nbt/src/lib.rs b/azalea-nbt/src/lib.rs index 048e466e..3c8aa2cd 100755 --- a/azalea-nbt/src/lib.rs +++ b/azalea-nbt/src/lib.rs @@ -6,23 +6,23 @@ mod error; mod tag; pub use error::Error; -pub use tag::NbtList; -pub use tag::Tag; +pub use tag::{NbtCompound, NbtList, Tag}; #[cfg(test)] mod tests { use std::io::Cursor; + use crate::tag::NbtCompound; + use super::*; - use ahash::AHashMap; use azalea_buf::{McBufReadable, McBufWritable}; #[test] fn mcbuf_nbt() { let mut buf = Vec::new(); - let tag = Tag::Compound(AHashMap::from_iter(vec![( + let tag = Tag::Compound(NbtCompound::from_iter(vec![( "hello world".into(), - Tag::Compound(AHashMap::from_iter(vec![( + Tag::Compound(NbtCompound::from_iter(vec![( "name".into(), Tag::String("Bananrama".into()), )])), @@ -34,9 +34,9 @@ mod tests { let result = Tag::read_from(&mut buf).unwrap(); assert_eq!( result, - Tag::Compound(AHashMap::from_iter(vec![( + Tag::Compound(NbtCompound::from_iter(vec![( "hello world".into(), - Tag::Compound(AHashMap::from_iter(vec![( + Tag::Compound(NbtCompound::from_iter(vec![( "name".into(), Tag::String("Bananrama".into()), )])), diff --git a/azalea-nbt/src/tag.rs b/azalea-nbt/src/tag.rs index e1aecb91..e23db913 100755 --- a/azalea-nbt/src/tag.rs +++ b/azalea-nbt/src/tag.rs @@ -1,9 +1,7 @@ -use ahash::AHashMap; - use compact_str::CompactString; use enum_as_inner::EnumAsInner; #[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; +use serde::{ser::SerializeMap, Deserialize, Serialize}; pub type NbtByte = i8; pub type NbtShort = i16; @@ -13,7 +11,6 @@ pub type NbtFloat = f32; pub type NbtDouble = f64; pub type NbtByteArray = Vec<u8>; pub type NbtString = CompactString; -pub type NbtCompound = AHashMap<CompactString, Tag>; pub type NbtIntArray = Vec<i32>; pub type NbtLongArray = Vec<i64>; @@ -94,3 +91,71 @@ impl NbtList { unsafe { *<*const _>::from(self).cast::<u8>() } } } + +// thanks to Moulberry/Graphite for the idea to use a vec and binary search +#[derive(Debug, Clone, PartialEq)] +pub struct NbtCompound { + inner: Vec<(NbtString, Tag)>, +} +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)) + } + + #[inline] + pub fn get(&self, key: &NbtString) -> Option<&Tag> { + self.binary_search(key).ok().map(|i| &self.inner[i].1) + } + + #[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 + } + } + } + + #[inline] + pub fn inner(&self) -> &Vec<(NbtString, Tag)> { + &self.inner + } +} +#[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, Tag> as Deserialize>::deserialize(deserializer)?; + Ok(Self { + inner: map.into_iter().collect(), + }) + } +} + +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 } + } +} diff --git a/azalea-nbt/tests/tests.rs b/azalea-nbt/tests/tests.rs index 62852578..22fc1074 100755 --- a/azalea-nbt/tests/tests.rs +++ b/azalea-nbt/tests/tests.rs @@ -1,5 +1,4 @@ -use ahash::AHashMap; -use azalea_nbt::{NbtList, Tag}; +use azalea_nbt::{NbtCompound, NbtList, Tag}; use std::io::Cursor; #[test] @@ -9,9 +8,9 @@ fn test_decode_hello_world() { let tag = Tag::read(&mut Cursor::new(&buf[..])).unwrap(); assert_eq!( tag, - Tag::Compound(AHashMap::from_iter(vec![( + Tag::Compound(NbtCompound::from_iter(vec![( "hello world".into(), - Tag::Compound(AHashMap::from_iter(vec![( + Tag::Compound(NbtCompound::from_iter(vec![( "name".into(), Tag::String("Bananrama".into()), )])) @@ -51,7 +50,7 @@ fn test_bigtest() { #[test] fn test_stringtest() { - let correct_tag = Tag::Compound(AHashMap::from_iter(vec