From 7d4b7b9a222a8ef6dc3112226a63a2595f7b3bee Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 18 Nov 2023 22:54:15 -0600 Subject: [PATCH] derive macro --- Cargo.toml | 50 +- README.md | 2 +- simdnbt-derive/Cargo.toml | 14 + simdnbt-derive/src/attrs.rs | 42 + simdnbt-derive/src/lib.rs | 53 + simdnbt/Cargo.lock | 952 ++++++++++++++++++ simdnbt/Cargo.toml | 52 + {benches => simdnbt/benches}/compare.rs | 0 .../benches}/compare_realworld.rs | 0 {benches => simdnbt/benches}/mutf8.rs | 0 {benches => simdnbt/benches}/nbt.rs | 0 simdnbt/examples/hypixel.rs | 78 ++ .../examples/hypixel_no_derive.rs | 5 +- .../examples}/read_and_write.rs | 0 {src => simdnbt/src}/borrow/compound.rs | 6 +- {src => simdnbt/src}/borrow/list.rs | 88 +- {src => simdnbt/src}/borrow/mod.rs | 6 +- {src => simdnbt/src}/common.rs | 0 {src => simdnbt/src}/error.rs | 8 + {src => simdnbt/src}/lib.rs | 6 +- {src => simdnbt/src}/mutf8.rs | 0 {src => simdnbt/src}/owned/compound.rs | 24 +- {src => simdnbt/src}/owned/list.rs | 171 +++- {src => simdnbt/src}/owned/mod.rs | 14 +- {src => simdnbt/src}/raw_list.rs | 0 {src => simdnbt/src}/swap_endianness.rs | 0 simdnbt/src/traits.rs | 148 +++ {tests => simdnbt/tests}/bigtest.nbt | Bin {tests => simdnbt/tests}/complex_player.dat | Bin {tests => simdnbt/tests}/hello_world.nbt | Bin {tests => simdnbt/tests}/inttest1023.nbt | Bin {tests => simdnbt/tests}/level.dat | Bin {tests => simdnbt/tests}/realworld.nbt | Bin {tests => simdnbt/tests}/simple_player.dat | Bin {tests => simdnbt/tests}/stringtest.nbt | Bin {tests => simdnbt/tests}/tests.rs | 0 36 files changed, 1564 insertions(+), 155 deletions(-) create mode 100644 simdnbt-derive/Cargo.toml create mode 100644 simdnbt-derive/src/attrs.rs create mode 100644 simdnbt-derive/src/lib.rs create mode 100644 simdnbt/Cargo.lock create mode 100644 simdnbt/Cargo.toml rename {benches => simdnbt/benches}/compare.rs (100%) rename {benches => simdnbt/benches}/compare_realworld.rs (100%) rename {benches => simdnbt/benches}/mutf8.rs (100%) rename {benches => simdnbt/benches}/nbt.rs (100%) create mode 100644 simdnbt/examples/hypixel.rs rename examples/hypixel.rs => simdnbt/examples/hypixel_no_derive.rs (96%) rename {examples => simdnbt/examples}/read_and_write.rs (100%) rename {src => simdnbt/src}/borrow/compound.rs (97%) rename {src => simdnbt/src}/borrow/list.rs (77%) rename {src => simdnbt/src}/borrow/mod.rs (98%) rename {src => simdnbt/src}/common.rs (100%) rename {src => simdnbt/src}/error.rs (70%) rename {src => simdnbt/src}/lib.rs (83%) rename {src => simdnbt/src}/mutf8.rs (100%) rename {src => simdnbt/src}/owned/compound.rs (93%) rename {src => simdnbt/src}/owned/list.rs (64%) rename {src => simdnbt/src}/owned/mod.rs (98%) rename {src => simdnbt/src}/raw_list.rs (100%) rename {src => simdnbt/src}/swap_endianness.rs (100%) create mode 100644 simdnbt/src/traits.rs rename {tests => simdnbt/tests}/bigtest.nbt (100%) rename {tests => simdnbt/tests}/complex_player.dat (100%) rename {tests => simdnbt/tests}/hello_world.nbt (100%) rename {tests => simdnbt/tests}/inttest1023.nbt (100%) rename {tests => simdnbt/tests}/level.dat (100%) rename {tests => simdnbt/tests}/realworld.nbt (100%) rename {tests => simdnbt/tests}/simple_player.dat (100%) rename {tests => simdnbt/tests}/stringtest.nbt (100%) rename {tests => simdnbt/tests}/tests.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index a540993..a176a21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,47 +1,3 @@ -[package] -name = "simdnbt" -version = "0.1.2" -edition = "2021" -description = "an unnecessarily fast nbt decoder" -license = "MIT" -repository = "https://github.com/mat-1/simdnbt" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -byteorder = "1.4.3" -flate2 = "^1.0.27" -residua-mutf8 = "2.0.0" -thiserror = "1.0.47" - -[dev-dependencies] -criterion = { version = "^0.5.1", features = ["html_reports"] } -graphite_binary = "0.1.0" -valence_nbt = { version = "0.6.1", features = ["binary"] } -fastnbt = "2.4.4" -azalea-nbt = { git = "https://github.com/mat-1/azalea" } -hematite-nbt = { version = "0.5.2", default-features = false } - -[profile.release] -lto = true -debug = false - -[profile.bench] -lto = true -debug = false - -[[bench]] -harness = false -name = "nbt" - -[[bench]] -harness = false -name = "compare" - -[[bench]] -harness = false -name = "compare_realworld" - -[[bench]] -harness = false -name = "mutf8" +[workspace] +members = ["simdnbt", "simdnbt-derive"] +resolver = "2" diff --git a/README.md b/README.md index 5c61652..4be5351 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # simdnbt -an unnecessarily fast nbt decoder. like seriously you probably don't need this unless you're trying to win benchmarks. +a very fast nbt serializer and deserializer. simdnbt currently makes use of simd instructions for two things: - swapping the endianness of int arrays diff --git a/simdnbt-derive/Cargo.toml b/simdnbt-derive/Cargo.toml new file mode 100644 index 0000000..d369170 --- /dev/null +++ b/simdnbt-derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "simdnbt-derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = "1.0.69" +quote = "1.0.33" +syn = "2.0.38" + +[lib] +proc-macro = true diff --git a/simdnbt-derive/src/attrs.rs b/simdnbt-derive/src/attrs.rs new file mode 100644 index 0000000..531348c --- /dev/null +++ b/simdnbt-derive/src/attrs.rs @@ -0,0 +1,42 @@ +use syn::parse::{Parse, ParseStream}; + +#[derive(Default, Debug)] +pub struct FieldAttrs { + pub rename: Option, +} + +impl Parse for FieldAttrs { + fn parse(input: ParseStream) -> syn::Result { + let mut attrs = Self::default(); + + while !input.is_empty() { + let attr = input.parse::()?; + match attr.to_string().as_str() { + "rename" => { + input.parse::()?; + let rename = input.parse::()?; + + attrs.rename = Some(rename.value()); + } + _ => todo!(), + } + } + + Ok(attrs) + } +} + +pub fn parse_field_attrs(attrs: &[syn::Attribute]) -> FieldAttrs { + let mut field_attrs = FieldAttrs::default(); + + for attr in attrs.iter().filter(|attr| attr.path().is_ident("simdnbt")) { + let new_attr = attr + .parse_args::() + .expect("invalid simdnbt attr"); + if let Some(rename) = new_attr.rename { + field_attrs.rename = Some(rename); + } + } + + field_attrs +} diff --git a/simdnbt-derive/src/lib.rs b/simdnbt-derive/src/lib.rs new file mode 100644 index 0000000..612739a --- /dev/null +++ b/simdnbt-derive/src/lib.rs @@ -0,0 +1,53 @@ +mod attrs; + +use attrs::parse_field_attrs; +use quote::quote; +use syn::{parse_macro_input, DeriveInput}; + +#[proc_macro_derive(Deserialize, attributes(simdnbt))] +pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + + let ident = input.ident; + + let mut field_deserializers = Vec::::new(); + + match input.data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => match fields { + syn::Fields::Named(syn::FieldsNamed { named, .. }) => { + for field in named { + let struct_field_name = field.ident.unwrap(); + + let mut field_attrs = parse_field_attrs(&field.attrs); + + let field_name = field_attrs + .rename + .take() + .unwrap_or_else(|| struct_field_name.to_string()); + + field_deserializers.push(quote! { + #struct_field_name: simdnbt::FromNbtTag::from_optional_nbt_tag( + nbt.take(#field_name) + )?.ok_or(simdnbt::DeserializeError::MismatchedFieldType)? + }); + } + } + syn::Fields::Unnamed(_) => todo!(), + syn::Fields::Unit => todo!(), + }, + syn::Data::Enum(_) => todo!(), + syn::Data::Union(_) => todo!(), + } + + let output = quote! { + impl simdnbt::Deserialize for #ident { + fn from_compound(mut nbt: simdnbt::owned::NbtCompound) -> Result { + Ok(Self { + #(#field_deserializers),* + }) + } + } + }; + + output.into() +} diff --git a/simdnbt/Cargo.lock b/simdnbt/Cargo.lock new file mode 100644 index 0000000..0c339e5 --- /dev/null +++ b/simdnbt/Cargo.lock @@ -0,0 +1,952 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "azalea-buf" +version = "0.8.0" +source = "git+https://github.com/mat-1/azalea#5977f79400e46de6a7af413a51bc1afd8e0dc9f6" +dependencies = [ + "azalea-buf-macros", + "byteorder", + "log", + "thiserror", + "uuid", +] + +[[package]] +name = "azalea-buf-macros" +version = "0.8.0" +source = "git+https://github.com/mat-1/azalea#5977f79400e46de6a7af413a51bc1afd8e0dc9f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "azalea-nbt" +version = "0.8.0" +source = "git+https://github.com/mat-1/azalea#5977f79400e46de6a7af413a51bc1afd8e0dc9f6" +dependencies = [ + "azalea-buf", + "byteorder", + "compact_str", + "enum-as-inner", + "flate2", + "log", + "thiserror", +] + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "serde", + "static_assertions", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastnbt" +version = "2.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369bd70629bccfda7e344883c9ae3ab7f3b10a357bcf8b0f69caa7256bcf188" +dependencies = [ + "byteorder", + "cesu8", + "serde", + "serde_bytes", +] + +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "graphite_binary" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dc8b44c673c50a2b3e6ec6652b8c8d26532254a3a182cc43b76d1b6e4cd1572" +dependencies = [ + "anyhow", + "bytes", + "cesu8", + "graphite_binary_macros", + "thiserror", +] + +[[package]] +name = "graphite_binary_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30667bf8d368a37fa37f4165d90ee84362e360d83d85924898c41cfe3d097521" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hematite-nbt" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670d0784ee67cfb57393dc1837867d2951f9a59ca7db99a653499c854f745739" +dependencies = [ + "byteorder", + "cesu8", + "flate2", +] + +[[package]] +name = "hermit-abi" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "residua-cesu8" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca29b145d9861719b5505602d881afc46705200144153ca9dbc0802be2938ea" + +[[package]] +name = "residua-mutf8" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adba843a48e520e7dad6d1e9c367a4f818787eaccf4530c6b90dd1f035e630d" +dependencies = [ + "residua-cesu8", +] + +[[package]] +name = "rustix" +version = "0.38.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_bytes" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "simdnbt" +version = "0.1.2" +dependencies = [ + "azalea-nbt", + "byteorder", + "criterion", + "fastnbt", + "flate2", + "graphite_binary", + "hematite-nbt", + "residua-mutf8", + "thiserror", + "valence_nbt", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + +[[package]] +name = "valence_nbt" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d8921a6ebbb93ddbcd0e152611bd94fb928199efe8528077ab36787b298be4" +dependencies = [ + "byteorder", + "cesu8", +] + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.37", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/simdnbt/Cargo.toml b/simdnbt/Cargo.toml new file mode 100644 index 0000000..4d09f4f --- /dev/null +++ b/simdnbt/Cargo.toml @@ -0,0 +1,52 @@ +[package] +name = "simdnbt" +version = "0.1.2" +edition = "2021" +description = "an unnecessarily fast nbt decoder" +license = "MIT" +repository = "https://github.com/mat-1/simdnbt" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +byteorder = "1.4.3" +flate2 = "^1.0.27" +residua-mutf8 = "2.0.0" +simdnbt-derive = { version = "0.1.0", path = "../simdnbt-derive", optional = true } +thiserror = "1.0.47" + +[dev-dependencies] +criterion = { version = "^0.5.1", features = ["html_reports"] } +graphite_binary = "0.1.0" +valence_nbt = { version = "0.8.0", features = ["binary"] } +fastnbt = "2.4.4" +azalea-nbt = { git = "https://github.com/mat-1/azalea" } +hematite-nbt = { version = "0.5.2", default-features = false } + +[features] +default = ["derive"] +derive = ["dep:simdnbt-derive"] + +[profile.release] +lto = true +debug = false + +[profile.bench] +lto = true +debug = false + +[[bench]] +harness = false +name = "nbt" + +[[bench]] +harness = false +name = "compare" + +[[bench]] +harness = false +name = "compare_realworld" + +[[bench]] +harness = false +name = "mutf8" diff --git a/benches/compare.rs b/simdnbt/benches/compare.rs similarity index 100% rename from benches/compare.rs rename to simdnbt/benches/compare.rs diff --git a/benches/compare_realworld.rs b/simdnbt/benches/compare_realworld.rs similarity index 100% rename from benches/compare_realworld.rs rename to simdnbt/benches/compare_realworld.rs diff --git a/benches/mutf8.rs b/simdnbt/benches/mutf8.rs similarity index 100% rename from benches/mutf8.rs rename to simdnbt/benches/mutf8.rs diff --git a/benches/nbt.rs b/simdnbt/benches/nbt.rs similarity index 100% rename from benches/nbt.rs rename to simdnbt/benches/nbt.rs diff --git a/simdnbt/examples/hypixel.rs b/simdnbt/examples/hypixel.rs new file mode 100644 index 0000000..4e232ed --- /dev/null +++ b/simdnbt/examples/hypixel.rs @@ -0,0 +1,78 @@ +use std::{collections::HashMap, hint::black_box, io::Cursor}; + +use simdnbt::{owned::Nbt, Deserialize}; + +#[derive(Deserialize, Debug)] +pub struct Item { + pub id: i16, + #[simdnbt(rename = "Damage")] + pub damage: i16, + #[simdnbt(rename = "Count")] + pub count: i8, + + pub tag: ItemTag, +} + +#[derive(Deserialize, Debug)] +pub struct ItemTag { + #[simdnbt(rename = "SkullOwner")] + pub skull_owner: Option, + #[simdnbt(rename = "ExtraAttributes")] + pub extra_attributes: ExtraAttributes, + pub display: ItemDisplay, +} + +#[derive(Deserialize, Debug)] +pub struct ExtraAttributes { + pub id: Option, + pub modifier: Option, + + pub ench: Option, + pub enchantments: Option>, + pub timestamp: Option, +} + +#[derive(Deserialize, Debug)] +pub struct SkullOwner { + pub properties: Properties, +} + +#[derive(Deserialize, Debug)] +pub struct Properties { + pub textures: Vec, +} + +#[derive(Deserialize, Debug)] +pub struct Texture { + #[simdnbt(rename = "Value")] + pub value: String, +} + +#[derive(Deserialize, Debug)] +pub struct ItemDisplay { + #[simdnbt(rename = "Name")] + pub name: String, + #[simdnbt(rename = "Lore")] + pub lore: Vec, + + pub color: Option, +} + +#[derive(Deserialize, Debug)] +pub struct Base { + #[simdnbt(rename = "i")] + pub items: Vec>, +} + +fn main() { + let input = black_box(include_bytes!("../tests/realworld.nbt")); + + for _ in 0..1 { + let nbt = Nbt::read(&mut Cursor::new(input)); + let nbt = black_box(nbt.unwrap().unwrap()); + + let data = Base::from_nbt(nbt).unwrap().items; + + println!("data: {data:?}"); + } +} diff --git a/examples/hypixel.rs b/simdnbt/examples/hypixel_no_derive.rs similarity index 96% rename from examples/hypixel.rs rename to simdnbt/examples/hypixel_no_derive.rs index f875be1..9ebe392 100644 --- a/examples/hypixel.rs +++ b/simdnbt/examples/hypixel_no_derive.rs @@ -102,9 +102,10 @@ fn simdnbt_items_from_nbt(nbt: BaseNbt) -> Option>> { fn main() { let input = black_box(include_bytes!("../tests/realworld.nbt")); - for _ in 0..10000 { + for _ in 0..1 { let nbt = Nbt::read(&mut Cursor::new(input)); let nbt = black_box(nbt.unwrap().unwrap()); - black_box(simdnbt_items_from_nbt(nbt)); + println!("{:?}", simdnbt_items_from_nbt(nbt)); + // black_box(simdnbt_items_from_nbt(nbt)); } } diff --git a/examples/read_and_write.rs b/simdnbt/examples/read_and_write.rs similarity index 100% rename from examples/read_and_write.rs rename to simdnbt/examples/read_and_write.rs diff --git a/src/borrow/compound.rs b/simdnbt/src/borrow/compound.rs similarity index 97% rename from src/borrow/compound.rs rename to simdnbt/src/borrow/compound.rs index bc06fd9..f929797 100644 --- a/src/borrow/compound.rs +++ b/simdnbt/src/borrow/compound.rs @@ -12,7 +12,7 @@ use crate::{ Error, Mutf8Str, }; -use super::{list::ListTag, NbtTag}; +use super::{list::NbtList, NbtTag}; /// A list of named tags. The order of the tags is preserved. #[derive(Debug, Default, PartialEq)] @@ -66,7 +66,7 @@ impl<'a> NbtCompound<'a> { values.push((tag_name, NbtTag::ByteArray(read_with_u32_length(data, 1)?))) } STRING_ID => values.push((tag_name, NbtTag::String(read_string(data)?))), - LIST_ID => values.push((tag_name, NbtTag::List(ListTag::read(data, depth + 1)?))), + LIST_ID => values.push((tag_name, NbtTag::List(NbtList::read(data, depth + 1)?))), COMPOUND_ID => values.push(( tag_name, NbtTag::Compound(NbtCompound::read_with_depth(data, depth + 1)?), @@ -188,7 +188,7 @@ impl<'a> NbtCompound<'a> { pub fn string(&self, name: &str) -> Option<&Mutf8Str> { self.get(name).and_then(|tag| tag.string()) } - pub fn list(&self, name: &str) -> Option<&ListTag<'a>> { + pub fn list(&self, name: &str) -> Option<&NbtList<'a>> { self.get(name).and_then(|tag| tag.list()) } pub fn compound(&self, name: &str) -> Option<&NbtCompound<'a>> { diff --git a/src/borrow/list.rs b/simdnbt/src/borrow/list.rs similarity index 77% rename from src/borrow/list.rs rename to simdnbt/src/borrow/list.rs index d5a534d..4f8f321 100644 --- a/src/borrow/list.rs +++ b/simdnbt/src/borrow/list.rs @@ -18,7 +18,7 @@ use super::{read_u32, NbtCompound, MAX_DEPTH}; /// A list of NBT tags of a single type. #[repr(u8)] #[derive(Debug, Default, PartialEq)] -pub enum ListTag<'a> { +pub enum NbtList<'a> { #[default] Empty = END_ID, Byte(&'a [i8]) = BYTE_ID, @@ -29,12 +29,12 @@ pub enum ListTag<'a> { Double(RawList<'a, f64>) = DOUBLE_ID, ByteArray(Vec<&'a [u8]>) = BYTE_ARRAY_ID, String(Vec<&'a Mutf8Str>) = STRING_ID, - List(Vec>) = LIST_ID, + List(Vec>) = LIST_ID, Compound(Vec>) = COMPOUND_ID, IntArray(Vec>) = INT_ARRAY_ID, LongArray(Vec>) = LONG_ARRAY_ID, } -impl<'a> ListTag<'a> { +impl<'a> NbtList<'a> { pub fn read(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result { if depth > MAX_DEPTH { return Err(Error::MaxDepthExceeded); @@ -43,15 +43,15 @@ impl<'a> ListTag<'a> { Ok(match tag_type { END_ID => { data.set_position(data.position() + 4); - ListTag::Empty + NbtList::Empty } - BYTE_ID => ListTag::Byte(read_i8_array(data)?), - SHORT_ID => ListTag::Short(RawList::new(read_with_u32_length(data, 2)?)), - INT_ID => ListTag::Int(RawList::new(read_with_u32_length(data, 4)?)), - LONG_ID => ListTag::Long(RawList::new(read_with_u32_length(data, 8)?)), - FLOAT_ID => ListTag::Float(RawList::new(read_with_u32_length(data, 4)?)), - DOUBLE_ID => ListTag::Double(RawList::new(read_with_u32_length(data, 8)?)), - BYTE_ARRAY_ID => ListTag::ByteArray({ + BYTE_ID => NbtList::Byte(read_i8_array(data)?), + SHORT_ID => NbtList::Short(RawList::new(read_with_u32_length(data, 2)?)), + INT_ID => NbtList::Int(RawList::new(read_with_u32_length(data, 4)?)), + LONG_ID => NbtList::Long(RawList::new(read_with_u32_length(data, 8)?)), + FLOAT_ID => NbtList::Float(RawList::new(read_with_u32_length(data, 4)?)), + DOUBLE_ID => NbtList::Double(RawList::new(read_with_u32_length(data, 8)?)), + BYTE_ARRAY_ID => NbtList::ByteArray({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut arrays = Vec::with_capacity(length.min(128) as usize); @@ -60,7 +60,7 @@ impl<'a> ListTag<'a> { } arrays }), - STRING_ID => ListTag::String({ + STRING_ID => NbtList::String({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut strings = Vec::with_capacity(length.min(128) as usize); @@ -69,16 +69,16 @@ impl<'a> ListTag<'a> { } strings }), - LIST_ID => ListTag::List({ + LIST_ID => NbtList::List({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut lists = Vec::with_capacity(length.min(128) as usize); for _ in 0..length { - lists.push(ListTag::read(data, depth + 1)?) + lists.push(NbtList::read(data, depth + 1)?) } lists }), - COMPOUND_ID => ListTag::Compound({ + COMPOUND_ID => NbtList::Compound({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut compounds = Vec::with_capacity(length.min(128) as usize); @@ -87,7 +87,7 @@ impl<'a> ListTag<'a> { } compounds }), - INT_ARRAY_ID => ListTag::IntArray({ + INT_ARRAY_ID => NbtList::IntArray({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut arrays = Vec::with_capacity(length.min(128) as usize); @@ -96,7 +96,7 @@ impl<'a> ListTag<'a> { } arrays }), - LONG_ARRAY_ID => ListTag::LongArray({ + LONG_ARRAY_ID => NbtList::LongArray({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut arrays = Vec::with_capacity(length.min(128) as usize); @@ -111,7 +111,7 @@ impl<'a> ListTag<'a> { pub fn write(&self, data: &mut Vec) { // fast path for compound since it's very common to have lists of compounds - if let ListTag::Compound(compounds) = self { + if let NbtList::Compound(compounds) = self { data.reserve(5); // SAFETY: we just reserved 5 bytes unsafe { @@ -126,55 +126,55 @@ impl<'a> ListTag<'a> { data.push(self.id()); match self { - ListTag::Empty => { + NbtList::Empty => { data.extend(&0u32.to_be_bytes()); } - ListTag::Byte(bytes) => { + NbtList::Byte(bytes) => { write_with_u32_length(data, 1, slice_i8_into_u8(bytes)); } - ListTag::Short(shorts) => { + NbtList::Short(shorts) => { write_with_u32_length(data, 2, shorts.as_big_endian()); } - ListTag::Int(ints) => { + NbtList::Int(ints) => { write_with_u32_length(data, 4, ints.as_big_endian()); } - ListTag::Long(longs) => { + NbtList::Long(longs) => { write_with_u32_length(data, 8, longs.as_big_endian()); } - ListTag::Float(floats) => { + NbtList::Float(floats) => { write_with_u32_length(data, 4, floats.as_big_endian()); } - ListTag::Double(doubles) => { + NbtList::Double(doubles) => { write_with_u32_length(data, 8, doubles.as_big_endian()); } - ListTag::ByteArray(byte_arrays) => { + NbtList::ByteArray(byte_arrays) => { write_u32(data, byte_arrays.len() as u32); for array in byte_arrays.iter() { write_with_u32_length(data, 1, array); } } - ListTag::String(strings) => { + NbtList::String(strings) => { write_u32(data, strings.len() as u32); for string in strings { write_string(data, string); } } - ListTag::List(lists) => { + NbtList::List(lists) => { write_u32(data, lists.len() as u32); for list in lists { list.write(data); } } - ListTag::Compound(_) => { + NbtList::Compound(_) => { unreachable!("fast path for compound should have been taken") } - ListTag::IntArray(int_arrays) => { + NbtList::IntArray(int_arrays) => { write_u32(data, int_arrays.len() as u32); for array in int_arrays { write_with_u32_length(data, 4, array.as_big_endian()); } } - ListTag::LongArray(long_arrays) => { + NbtList::LongArray(long_arrays) => { write_u32(data, long_arrays.len() as u32); for array in long_arrays { write_with_u32_length(data, 8, array.as_big_endian()); @@ -195,73 +195,73 @@ impl<'a> ListTag<'a> { pub fn bytes(&self) -> Option<&[i8]> { match self { - ListTag::Byte(bytes) => Some(bytes), + NbtList::Byte(bytes) => Some(bytes), _ => None, } } pub fn shorts(&self) -> Option> { match self { - ListTag::Short(shorts) => Some(shorts.to_vec()), + NbtList::Short(shorts) => Some(shorts.to_vec()), _ => None, } } pub fn ints(&self) -> Option> { match self { - ListTag::Int(ints) => Some(ints.to_vec()), + NbtList::Int(ints) => Some(ints.to_vec()), _ => None, } } pub fn longs(&self) -> Option> { match self { - ListTag::Long(longs) => Some(longs.to_vec()), + NbtList::Long(longs) => Some(longs.to_vec()), _ => None, } } pub fn floats(&self) -> Option> { match self { - ListTag::Float(floats) => Some(floats.to_vec()), + NbtList::Float(floats) => Some(floats.to_vec()), _ => None, } } pub fn doubles(&self) -> Option> { match self { - ListTag::Double(doubles) => Some(doubles.to_vec()), + NbtList::Double(doubles) => Some(doubles.to_vec()), _ => None, } } pub fn byte_arrays(&self) -> Option<&Vec<&[u8]>> { match self { - ListTag::ByteArray(byte_arrays) => Some(byte_arrays), + NbtList::ByteArray(byte_arrays) => Some(byte_arrays), _ => None, } } pub fn strings(&self) -> Option<&[&Mutf8Str]> { match self { - ListTag::String(strings) => Some(strings), + NbtList::String(strings) => Some(strings), _ => None, } } - pub fn lists(&self) -> Option<&[ListTag]> { + pub fn lists(&self) -> Option<&[NbtList]> { match self { - ListTag::List(lists) => Some(lists), + NbtList::List(lists) => Some(lists), _ => None, } } pub fn compounds(&self) -> Option<&[NbtCompound]> { match self { - ListTag::Compound(compounds) => Some(compounds), + NbtList::Compound(compounds) => Some(compounds), _ => None, } } pub fn int_arrays(&self) -> Option<&[RawList]> { match self { - ListTag::IntArray(int_arrays) => Some(int_arrays), + NbtList::IntArray(int_arrays) => Some(int_arrays), _ => None, } } pub fn long_arrays(&self) -> Option<&[RawList]> { match self { - ListTag::LongArray(long_arrays) => Some(long_arrays), + NbtList::LongArray(long_arrays) => Some(long_arrays), _ => None, } } diff --git a/src/borrow/mod.rs b/simdnbt/src/borrow/mod.rs similarity index 98% rename from src/borrow/mod.rs rename to simdnbt/src/borrow/mod.rs index 7ccf688..34efa86 100644 --- a/src/borrow/mod.rs +++ b/simdnbt/src/borrow/mod.rs @@ -17,7 +17,7 @@ use crate::{ Error, Mutf8Str, }; -pub use self::{compound::NbtCompound, list::ListTag}; +pub use self::{compound::NbtCompound, list::NbtList}; /// A complete NBT container. This contains a name and a compound tag. #[derive(Debug, PartialEq)] @@ -111,7 +111,7 @@ pub enum NbtTag<'a> { Double(f64) = DOUBLE_ID, ByteArray(&'a [u8]) = BYTE_ARRAY_ID, String(&'a Mutf8Str) = STRING_ID, - List(ListTag<'a>) = LIST_ID, + List(NbtList<'a>) = LIST_ID, Compound(NbtCompound<'a>) = COMPOUND_ID, IntArray(RawList<'a, i32>) = INT_ARRAY_ID, LongArray(RawList<'a, i64>) = LONG_ARRAY_ID, @@ -175,7 +175,7 @@ impl<'a> NbtTag<'a> { _ => None, } } - pub fn list(&self) -> Option<&ListTag<'a>> { + pub fn list(&self) -> Option<&NbtList<'a>> { match self { NbtTag::List(list) => Some(list), _ => None, diff --git a/src/common.rs b/simdnbt/src/common.rs similarity index 100% rename from src/common.rs rename to simdnbt/src/common.rs diff --git a/src/error.rs b/simdnbt/src/error.rs similarity index 70% rename from src/error.rs rename to simdnbt/src/error.rs index 2d2b270..f36c627 100644 --- a/src/error.rs +++ b/simdnbt/src/error.rs @@ -13,3 +13,11 @@ pub enum Error { #[error("Tried to read NBT tag with too high complexity, depth > {MAX_DEPTH}")] MaxDepthExceeded, } + +#[derive(Error, Debug)] +pub enum DeserializeError { + #[error("Missing field")] + MissingField, + #[error("Mismatched type")] + MismatchedFieldType, +} diff --git a/src/lib.rs b/simdnbt/src/lib.rs similarity index 83% rename from src/lib.rs rename to simdnbt/src/lib.rs index d4419d3..99d43df 100644 --- a/src/lib.rs +++ b/simdnbt/src/lib.rs @@ -22,6 +22,10 @@ mod mutf8; pub mod owned; pub mod raw_list; pub mod swap_endianness; +mod traits; -pub use error::Error; +pub use error::{DeserializeError, Error}; pub use mutf8::Mutf8Str; +pub use traits::{Deserialize, FromNbtTag}; + +pub use simdnbt_derive::*; diff --git a/src/mutf8.rs b/simdnbt/src/mutf8.rs similarity index 100% rename from src/mutf8.rs rename to simdnbt/src/mutf8.rs diff --git a/src/owned/compound.rs b/simdnbt/src/owned/compound.rs similarity index 93% rename from src/owned/compound.rs rename to simdnbt/src/owned/compound.rs index 3eebf38..91cdf7f 100644 --- a/src/owned/compound.rs +++ b/simdnbt/src/owned/compound.rs @@ -1,4 +1,4 @@ -use std::io::Cursor; +use std::{io::Cursor, mem}; use byteorder::{ReadBytesExt, BE}; @@ -13,7 +13,7 @@ use crate::{ Error, Mutf8Str, }; -use super::{list::ListTag, NbtTag}; +use super::{list::NbtList, NbtTag}; /// A list of named tags. The order of the tags is preserved. #[derive(Debug, Default, Clone, PartialEq)] @@ -72,7 +72,7 @@ impl NbtCompound { NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned()), )), STRING_ID => values.push((tag_name, NbtTag::String(read_string(data)?.to_owned()))), - LIST_ID => values.push((tag_name, NbtTag::List(ListTag::read(data, depth + 1)?))), + LIST_ID => values.push((tag_name, NbtTag::List(NbtList::read(data, depth + 1)?))), COMPOUND_ID => values.push(( tag_name, NbtTag::Compound(NbtCompound::read_with_depth(data, depth + 1)?), @@ -174,6 +174,20 @@ impl NbtCompound { None } + /// Get an owned tag from the compound by swapping it with a dummy tag. + pub fn take(&mut self, name: &str) -> Option { + let name = Mutf8Str::from_str(name); + let name = name.as_ref(); + for i in 0..self.values.len() { + if self.values[i].0.as_str() == name { + let mut value = NbtTag::Byte(0); + mem::swap(&mut self.values[i].1, &mut value); + return Some(value); + } + } + None + } + /// Returns whether there is a tag with the given name. pub fn contains(&self, name: &str) -> bool { let name = Mutf8Str::from_str(name); @@ -234,10 +248,10 @@ impl NbtCompound { pub fn string_mut(&mut self, name: &str) -> Option<&mut Mutf8String> { self.get_mut(name).and_then(|tag| tag.string_mut()) } - pub fn list(&self, name: &str) -> Option<&ListTag> { + pub fn list(&self, name: &str) -> Option<&NbtList> { self.get(name).and_then(|tag| tag.list()) } - pub fn list_mut(&mut self, name: &str) -> Option<&mut ListTag> { + pub fn list_mut(&mut self, name: &str) -> Option<&mut NbtList> { self.get_mut(name).and_then(|tag| tag.list_mut()) } pub fn compound(&self, name: &str) -> Option<&NbtCompound> { diff --git a/src/owned/list.rs b/simdnbt/src/owned/list.rs similarity index 64% rename from src/owned/list.rs rename to simdnbt/src/owned/list.rs index 8265e38..a71cfc4 100644 --- a/src/owned/list.rs +++ b/simdnbt/src/owned/list.rs @@ -20,7 +20,7 @@ use super::{compound::NbtCompound, read_u32, MAX_DEPTH}; /// A list of NBT tags of a single type. #[repr(u8)] #[derive(Debug, Default, Clone, PartialEq)] -pub enum ListTag { +pub enum NbtList { #[default] Empty = END_ID, Byte(Vec) = BYTE_ID, @@ -31,12 +31,12 @@ pub enum ListTag { Double(Vec) = DOUBLE_ID, ByteArray(Vec>) = BYTE_ARRAY_ID, String(Vec) = STRING_ID, - List(Vec) = LIST_ID, + List(Vec) = LIST_ID, Compound(Vec) = COMPOUND_ID, IntArray(Vec>) = INT_ARRAY_ID, LongArray(Vec>) = LONG_ARRAY_ID, } -impl ListTag { +impl NbtList { pub fn read(data: &mut Cursor<&[u8]>, depth: usize) -> Result { if depth > MAX_DEPTH { return Err(Error::MaxDepthExceeded); @@ -45,15 +45,15 @@ impl ListTag { Ok(match tag_type { END_ID => { data.set_position(data.position() + 4); - ListTag::Empty + NbtList::Empty } - BYTE_ID => ListTag::Byte(read_i8_array(data)?.to_owned()), - SHORT_ID => ListTag::Short(swap_endianness(read_with_u32_length(data, 2)?)), - INT_ID => ListTag::Int(swap_endianness(read_with_u32_length(data, 4)?)), - LONG_ID => ListTag::Long(swap_endianness(read_with_u32_length(data, 8)?)), - FLOAT_ID => ListTag::Float(swap_endianness(read_with_u32_length(data, 4)?)), - DOUBLE_ID => ListTag::Double(swap_endianness(read_with_u32_length(data, 8)?)), - BYTE_ARRAY_ID => ListTag::ByteArray({ + BYTE_ID => NbtList::Byte(read_i8_array(data)?.to_owned()), + SHORT_ID => NbtList::Short(swap_endianness(read_with_u32_length(data, 2)?)), + INT_ID => NbtList::Int(swap_endianness(read_with_u32_length(data, 4)?)), + LONG_ID => NbtList::Long(swap_endianness(read_with_u32_length(data, 8)?)), + 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)?; // arbitrary number to prevent big allocations let mut arrays = Vec::with_capacity(length.min(128) as usize); @@ -62,7 +62,7 @@ impl ListTag { } arrays }), - STRING_ID => ListTag::String({ + STRING_ID => NbtList::String({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut strings = Vec::with_capacity(length.min(128) as usize); @@ -71,16 +71,16 @@ impl ListTag { } strings }), - LIST_ID => ListTag::List({ + LIST_ID => NbtList::List({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut lists = Vec::with_capacity(length.min(128) as usize); for _ in 0..length { - lists.push(ListTag::read(data, depth + 1)?) + lists.push(NbtList::read(data, depth + 1)?) } lists }), - COMPOUND_ID => ListTag::Compound({ + COMPOUND_ID => NbtList::Compound({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut compounds = Vec::with_capacity(length.min(128) as usize); @@ -89,7 +89,7 @@ impl ListTag { } compounds }), - INT_ARRAY_ID => ListTag::IntArray({ + INT_ARRAY_ID => NbtList::IntArray({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut arrays = Vec::with_capacity(length.min(128) as usize); @@ -98,7 +98,7 @@ impl ListTag { } arrays }), - LONG_ARRAY_ID => ListTag::LongArray({ + LONG_ARRAY_ID => NbtList::LongArray({ let length = read_u32(data)?; // arbitrary number to prevent big allocations let mut arrays = Vec::with_capacity(length.min(128) as usize); @@ -113,7 +113,7 @@ impl ListTag { pub fn write(&self, data: &mut Vec) { // fast path for compound since it's very common to have lists of compounds - if let ListTag::Compound(compounds) = self { + if let NbtList::Compound(compounds) = self { data.reserve(5); // SAFETY: we just reserved 5 bytes unsafe { @@ -128,55 +128,55 @@ impl ListTag { data.push(self.id()); match self { - ListTag::Empty => { + NbtList::Empty => { data.extend(&0u32.to_be_bytes()); } - ListTag::Byte(bytes) => { + NbtList::Byte(bytes) => { write_with_u32_length(data, 1, slice_i8_into_u8(bytes)); } - ListTag::Short(shorts) => { + NbtList::Short(shorts) => { write_with_u32_length(data, 2, &slice_into_u8_big_endian(shorts)); } - ListTag::Int(ints) => { + NbtList::Int(ints) => { write_with_u32_length(data, 4, &slice_into_u8_big_endian(ints)); } - ListTag::Long(longs) => { + NbtList::Long(longs) => { write_with_u32_length(data, 8, &slice_into_u8_big_endian(longs)); } - ListTag::Float(floats) => { + NbtList::Float(floats) => { write_with_u32_length(data, 4, &slice_into_u8_big_endian(floats)); } - ListTag::Double(doubles) => { + NbtList::Double(doubles) => { write_with_u32_length(data, 8, &slice_into_u8_big_endian(doubles)); } - ListTag::ByteArray(byte_arrays) => { + NbtList::ByteArray(byte_arrays) => { write_u32(data, byte_arrays.len() as u32); for array in byte_arrays { write_with_u32_length(data, 1, array); } } - ListTag::String(strings) => { + NbtList::String(strings) => { write_u32(data, strings.len() as u32); for string in strings { write_string(data, string); } } - ListTag::List(lists) => { + NbtList::List(lists) => { write_u32(data, lists.len() as u32); for list in lists { list.write(data); } } - ListTag::Compound(_) => { + NbtList::Compound(_) => { unreachable!("fast path for compound should have been taken") } - ListTag::IntArray(int_arrays) => { + NbtList::IntArray(int_arrays) => { write_u32(data, int_arrays.len() as u32); for array in int_arrays { write_with_u32_length(data, 4, &slice_into_u8_big_endian(array)); } } - ListTag::LongArray(long_arrays) => { + NbtList::LongArray(long_arrays) => { write_u32(data, long_arrays.len() as u32); for array in long_arrays { write_with_u32_length(data, 8, &slice_into_u8_big_endian(array)); @@ -197,73 +197,156 @@ impl ListTag { pub fn bytes(&self) -> Option<&[i8]> { match self { - ListTag::Byte(bytes) => Some(bytes), + NbtList::Byte(bytes) => Some(bytes), _ => None, } } + pub fn into_bytes(self) -> Option> { + match self { + NbtList::Byte(bytes) => Some(bytes), + _ => None, + } + } + pub fn shorts(&self) -> Option> { match self { - ListTag::Short(shorts) => Some(shorts.to_vec()), + NbtList::Short(shorts) => Some(shorts.to_vec()), _ => None, } } + pub fn into_shorts(self) -> Option> { + match self { + NbtList::Short(shorts) => Some(shorts), + _ => None, + } + } + pub fn ints(&self) -> Option> { match self { - ListTag::Int(ints) => Some(ints.to_vec()), + NbtList::Int(ints) => Some(ints.to_vec()), _ => None, } } + pub fn into_ints(self) -> Option> { + match self { + NbtList::Int(ints) => Some(ints), + _ => None, + } + } + pub fn longs(&self) -> Option> { match self { - ListTag::Long(longs) => Some(longs.to_vec()), + NbtList::Long(longs) => Some(longs.to_vec()), _ => None, } } + pub fn into_longs(self) -> Option> { + match self { + NbtList::Long(longs) => Some(longs), + _ => None, + } + } + pub fn floats(&self) -> Option> { match self { - ListTag::Float(floats) => Some(floats.to_vec()), + NbtList::Float(floats) => Some(floats.to_vec()), _ => None, } } + pub fn into_floats(self) -> Option> { + match self { + NbtList::Float(floats) => Some(floats), + _ => None, + } + } + pub fn doubles(&self) -> Option> { match self { - ListTag::Double(doubles) => Some(doubles.to_vec()), + NbtList::Double(doubles) => Some(doubles.to_vec()), _ => None, } } + pub fn into_doubles(self) -> Option> { + match self { + NbtList::Double(doubles) => Some(doubles), + _ => None, + } + } + pub fn byte_arrays(&self) -> Option<&[Vec]> { match self { - ListTag::ByteArray(byte_arrays) => Some(byte_arrays), + NbtList::ByteArray(byte_arrays) => Some(byte_arrays), _ => None, } } + pub fn into_byte_arrays(self) -> Option>> { + match self { + NbtList::ByteArray(byte_arrays) => Some(byte_arrays), + _ => None, + } + } + pub fn strings(&self) -> Option<&[Mutf8String]> { match self { - ListTag::String(strings) => Some(strings), + NbtList::String(strings) => Some(strings), _ => None, } } - pub fn lists(&self) -> Option<&[ListTag]> { + pub fn into_strings(self) -> Option> { match self { - ListTag::List(lists) => Some(lists), + NbtList::String(strings) => Some(strings), _ => None, } } + + pub fn lists(&self) -> Option<&[NbtList]> { + match self { + NbtList::List(lists) => Some(lists), + _ => None, + } + } + pub fn into_lists(self) -> Option> { + match self { + NbtList::List(lists) => Some(lists), + _ => None, + } + } + pub fn compounds(&self) -> Option<&[NbtCompound]> { match self { - ListTag::Compound(compounds) => Some(compounds), + NbtList::Compound(compounds) => Some(compounds), _ => None, } } + pub fn into_compounds(self) -> Option> { + match self { + NbtList::Compound(compounds) => Some(compounds), + _ => None, + } + } + pub fn int_arrays(&self) -> Option<&[Vec]> { match self { - ListTag::IntArray(int_arrays) => Some(int_arrays), + NbtList::IntArray(int_arrays) => Some(int_arrays), _ => None, } } + pub fn into_int_arrays(self) -> Option>> { + match self { + NbtList::IntArray(int_arrays) => Some(int_arrays), + _ => None, + } + } + pub fn long_arrays(&self) -> Option<&[Vec]> { match self { - ListTag::LongArray(long_arrays) => Some(long_arrays), + NbtList::LongArray(long_arrays) => Some(long_arrays), + _ => None, + } + } + pub fn into_long_arrays(self) -> Option>> { + match self { + NbtList::LongArray(long_arrays) => Some(long_arrays), _ => None, } } diff --git a/src/owned/mod.rs b/simdnbt/src/owned/mod.rs similarity index 98% rename from src/owned/mod.rs rename to simdnbt/src/owned/mod.rs index fe6332e..4eeaea1 100644 --- a/src/owned/mod.rs +++ b/simdnbt/src/owned/mod.rs @@ -17,7 +17,7 @@ use crate::{ Error, Mutf8Str, }; -pub use self::{compound::NbtCompound, list::ListTag}; +pub use self::{compound::NbtCompound, list::NbtList}; /// A complete NBT container. This contains a name and a compound tag. #[derive(Debug, Clone, PartialEq, Default)] @@ -134,6 +134,10 @@ impl BaseNbt { pub fn into_iter(self) -> impl Iterator { self.tag.into_iter() } + + pub fn into_inner(self) -> NbtCompound { + self.tag + } } impl Deref for BaseNbt { type Target = NbtCompound; @@ -155,7 +159,7 @@ pub enum NbtTag { Double(f64) = DOUBLE_ID, ByteArray(Vec) = BYTE_ARRAY_ID, String(Mutf8String) = STRING_ID, - List(ListTag) = LIST_ID, + List(NbtList) = LIST_ID, Compound(NbtCompound) = COMPOUND_ID, IntArray(Vec) = INT_ARRAY_ID, LongArray(Vec) = LONG_ARRAY_ID, @@ -323,19 +327,19 @@ impl NbtTag { } } - pub fn list(&self) -> Option<&ListTag> { + pub fn list(&self) -> Option<&NbtList> { match self { NbtTag::List(list) => Some(list), _ => None, } } - pub fn list_mut(&mut self) -> Option<&mut ListTag> { + pub fn list_mut(&mut self) -> Option<&mut NbtList> { match self { NbtTag::List(list) => Some(list), _ => None, } } - pub fn into_list(self) -> Option { + pub fn into_list(self) -> Option { match self { NbtTag::List(list) => Some(list), _ => None, diff --git a/src/raw_list.rs b/simdnbt/src/raw_list.rs similarity index 100% rename from src/raw_list.rs rename to simdnbt/src/raw_list.rs diff --git a/src/swap_endianness.rs b/simdnbt/src/swap_endianness.rs similarity index 100% rename from src/swap_endianness.rs rename to simdnbt/src/swap_endianness.rs diff --git a/simdnbt/src/traits.rs b/simdnbt/src/traits.rs new file mode 100644 index 0000000..cf70fdb --- /dev/null +++ b/simdnbt/src/traits.rs @@ -0,0 +1,148 @@ +use std::collections::HashMap; + +use crate::DeserializeError; + +pub trait Deserialize: Sized { + fn from_nbt(nbt: crate::owned::BaseNbt) -> Result { + Self::from_compound(nbt.into_inner()) + } + + fn from_compound(compound: crate::owned::NbtCompound) -> Result; +} + +impl Deserialize for HashMap { + fn from_compound(compound: crate::owned::NbtCompound) -> Result { + let mut hashmap = HashMap::with_capacity(compound.values.len()); + + for (k, v) in compound.values { + hashmap.insert( + k.to_string(), + T::from_nbt_tag(v).ok_or(DeserializeError::MismatchedFieldType)?, + ); + } + + Ok(hashmap) + } +} + +impl Deserialize for crate::owned::NbtCompound { + fn from_compound(compound: crate::owned::NbtCompound) -> Result { + Ok(compound) + } +} + +pub trait FromNbtTag: Sized { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option; + fn from_optional_nbt_tag( + tag: Option, + ) -> Result, DeserializeError> { + match tag { + Some(tag) => Ok(Self::from_nbt_tag(tag)), + None => Err(DeserializeError::MissingField), + } + } +} + +impl FromNbtTag for T { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.into_compound() + .and_then(|c| Self::from_compound(c).ok()) + } +} + +// standard nbt types +impl FromNbtTag for i8 { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.byte() + } +} +impl FromNbtTag for i16 { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.short() + } +} +impl FromNbtTag for i32 { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.int() + } +} +impl FromNbtTag for i64 { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.long() + } +} +impl FromNbtTag for f32 { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.float() + } +} +impl FromNbtTag for f64 { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.double() + } +} +impl FromNbtTag for String { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.string().map(|s| s.to_string()) + } +} + +// lists +impl FromNbtTag for Vec { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.list().and_then(|l| { + l.strings() + .map(|s| s.iter().map(|s| s.to_string()).collect()) + }) + } +} + +// slightly less standard types +impl FromNbtTag for Option { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + Some(T::from_nbt_tag(tag)) + } + fn from_optional_nbt_tag( + tag: Option, + ) -> Result, DeserializeError> { + match tag { + Some(tag) => Ok(Some(T::from_nbt_tag(tag))), + None => Ok(Some(None)), + } + } +} + +impl FromNbtTag for Vec> { + /// A list of compounds where `None` is an empty compound + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + let list = tag.into_list()?.into_compounds()?; + let mut vec = Vec::with_capacity(list.len()); + for tag in list { + if tag.values.is_empty() { + vec.push(None); + } else { + vec.push(Some(T::from_compound(tag).ok()?)); + } + } + + Some(vec) + } +} + +impl FromNbtTag for Vec { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + let list = tag.into_list()?.into_compounds()?; + let mut vec = Vec::with_capacity(list.len()); + for tag in list { + vec.push(T::from_compound(tag).ok()?); + } + + Some(vec) + } +} + +impl FromNbtTag for bool { + fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option { + tag.byte().map(|b| b != 0) + } +} diff --git a/tests/bigtest.nbt b/simdnbt/tests/bigtest.nbt similarity index 100% rename from tests/bigtest.nbt rename to simdnbt/tests/bigtest.nbt diff --git a/tests/complex_player.dat b/simdnbt/tests/complex_player.dat similarity index 100% rename from tests/complex_player.dat rename to simdnbt/tests/complex_player.dat diff --git a/tests/hello_world.nbt b/simdnbt/tests/hello_world.nbt similarity index 100% rename from tests/hello_world.nbt rename to simdnbt/tests/hello_world.nbt diff --git a/tests/inttest1023.nbt b/simdnbt/tests/inttest1023.nbt similarity index 100% rename from tests/inttest1023.nbt rename to simdnbt/tests/inttest1023.nbt diff --git a/tests/level.dat b/simdnbt/tests/level.dat similarity index 100% rename from tests/level.dat rename to simdnbt/tests/level.dat diff --git a/tests/realworld.nbt b/simdnbt/tests/realworld.nbt similarity index 100% rename from tests/realworld.nbt rename to simdnbt/tests/realworld.nbt diff --git a/tests/simple_player.dat b/simdnbt/tests/simple_player.dat similarity index 100% rename from tests/simple_player.dat rename to simdnbt/tests/simple_player.dat diff --git a/tests/stringtest.nbt b/simdnbt/tests/stringtest.nbt similarity index 100% rename from tests/stringtest.nbt rename to simdnbt/tests/stringtest.nbt diff --git a/tests/tests.rs b/simdnbt/tests/tests.rs similarity index 100% rename from tests/tests.rs rename to simdnbt/tests/tests.rs