1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 07:26:04 +00:00

derive macro

This commit is contained in:
mat 2023-11-18 22:54:15 -06:00
parent 1526a21276
commit 7d4b7b9a22
36 changed files with 1564 additions and 155 deletions

View file

@ -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"

View file

@ -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

14
simdnbt-derive/Cargo.toml Normal file
View file

@ -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

View file

@ -0,0 +1,42 @@
use syn::parse::{Parse, ParseStream};
#[derive(Default, Debug)]
pub struct FieldAttrs {
pub rename: Option<String>,
}
impl Parse for FieldAttrs {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut attrs = Self::default();
while !input.is_empty() {
let attr = input.parse::<proc_macro2::Ident>()?;
match attr.to_string().as_str() {
"rename" => {
input.parse::<syn::Token![=]>()?;
let rename = input.parse::<syn::LitStr>()?;
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::<FieldAttrs>()
.expect("invalid simdnbt attr");
if let Some(rename) = new_attr.rename {
field_attrs.rename = Some(rename);
}
}
field_attrs
}

53
simdnbt-derive/src/lib.rs Normal file
View file

@ -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::<proc_macro2::TokenStream>::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<Self, simdnbt::DeserializeError> {
Ok(Self {
#(#field_deserializers),*
})
}
}
};
output.into()
}

952
simdnbt/Cargo.lock generated Normal file
View file

@ -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"

52
simdnbt/Cargo.toml Normal file
View file

@ -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"

View file

@ -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<SkullOwner>,
#[simdnbt(rename = "ExtraAttributes")]
pub extra_attributes: ExtraAttributes,
pub display: ItemDisplay,
}
#[derive(Deserialize, Debug)]
pub struct ExtraAttributes {
pub id: Option<String>,
pub modifier: Option<String>,
pub ench: Option<simdnbt::owned::NbtCompound>,
pub enchantments: Option<HashMap<String, i32>>,
pub timestamp: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct SkullOwner {
pub properties: Properties,
}
#[derive(Deserialize, Debug)]
pub struct Properties {
pub textures: Vec<Texture>,
}
#[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<String>,
pub color: Option<i32>,
}
#[derive(Deserialize, Debug)]
pub struct Base {
#[simdnbt(rename = "i")]
pub items: Vec<Option<Item>>,
}
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:?}");
}
}

View file

@ -102,9 +102,10 @@ fn simdnbt_items_from_nbt(nbt: BaseNbt) -> Option<Vec<Option<Item>>> {
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));
}
}

View file

@ -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>> {

View file

@ -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<ListTag<'a>>) = LIST_ID,
List(Vec<NbtList<'a>>) = LIST_ID,
Compound(Vec<NbtCompound<'a>>) = COMPOUND_ID,
IntArray(Vec<RawList<'a, i32>>) = INT_ARRAY_ID,
LongArray(Vec<RawList<'a, i64>>) = LONG_ARRAY_ID,
}
impl<'a> ListTag<'a> {
impl<'a> NbtList<'a> {
pub fn read(data: &mut Cursor<&'a [u8]>, depth: usize) -> Result<Self, Error> {
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<u8>) {
// 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<Vec<i16>> {
match self {
ListTag::Short(shorts) => Some(shorts.to_vec()),
NbtList::Short(shorts) => Some(shorts.to_vec()),
_ => None,
}
}
pub fn ints(&self) -> Option<Vec<i32>> {
match self {
ListTag::Int(ints) => Some(ints.to_vec()),
NbtList::Int(ints) => Some(ints.to_vec()),
_ => None,
}
}
pub fn longs(&self) -> Option<Vec<i64>> {
match self {
ListTag::Long(longs) => Some(longs.to_vec()),
NbtList::Long(longs) => Some(longs.to_vec()),
_ => None,
}
}
pub fn floats(&self) -> Option<Vec<f32>> {
match self {
ListTag::Float(floats) => Some(floats.to_vec()),
NbtList::Float(floats) => Some(floats.to_vec()),
_ => None,
}
}
pub fn doubles(&self) -> Option<Vec<f64>> {
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<i32>]> {
match self {
ListTag::IntArray(int_arrays) => Some(int_arrays),
NbtList::IntArray(int_arrays) => Some(int_arrays),
_ => None,
}
}
pub fn long_arrays(&self) -> Option<&[RawList<i64>]> {
match self {
ListTag::LongArray(long_arrays) => Some(long_arrays),
NbtList::LongArray(long_arrays) => Some(long_arrays),
_ => None,
}
}

View file

@ -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,

View file

@ -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,
}

View file

@ -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::*;

View file

@ -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<NbtTag> {
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> {

View file

@ -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<i8>) = BYTE_ID,
@ -31,12 +31,12 @@ pub enum ListTag {
Double(Vec<f64>) = DOUBLE_ID,
ByteArray(Vec<Vec<u8>>) = BYTE_ARRAY_ID,
String(Vec<Mutf8String>) = STRING_ID,
List(Vec<ListTag>) = LIST_ID,
List(Vec<NbtList>) = LIST_ID,
Compound(Vec<NbtCompound>) = COMPOUND_ID,
IntArray(Vec<Vec<i32>>) = INT_ARRAY_ID,
LongArray(Vec<Vec<i64>>) = LONG_ARRAY_ID,
}
impl ListTag {
impl NbtList {
pub fn read(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
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<u8>) {
// 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<Vec<i8>> {
match self {
NbtList::Byte(bytes) => Some(bytes),
_ => None,
}
}
pub fn shorts(&self) -> Option<Vec<i16>> {
match self {
ListTag::Short(shorts) => Some(shorts.to_vec()),
NbtList::Short(shorts) => Some(shorts.to_vec()),
_ => None,
}
}
pub fn into_shorts(self) -> Option<Vec<i16>> {
match self {
NbtList::Short(shorts) => Some(shorts),
_ => None,
}
}
pub fn ints(&self) -> Option<Vec<i32>> {
match self {
ListTag::Int(ints) => Some(ints.to_vec()),
NbtList::Int(ints) => Some(ints.to_vec()),
_ => None,
}
}
pub fn into_ints(self) -> Option<Vec<i32>> {
match self {
NbtList::Int(ints) => Some(ints),
_ => None,
}
}
pub fn longs(&self) -> Option<Vec<i64>> {
match self {
ListTag::Long(longs) => Some(longs.to_vec()),
NbtList::Long(longs) => Some(longs.to_vec()),
_ => None,
}
}
pub fn into_longs(self) -> Option<Vec<i64>> {
match self {
NbtList::Long(longs) => Some(longs),
_ => None,
}
}
pub fn floats(&self) -> Option<Vec<f32>> {
match self {
ListTag::Float(floats) => Some(floats.to_vec()),
NbtList::Float(floats) => Some(floats.to_vec()),
_ => None,
}
}
pub fn into_floats(self) -> Option<Vec<f32>> {
match self {
NbtList::Float(floats) => Some(floats),
_ => None,
}
}
pub fn doubles(&self) -> Option<Vec<f64>> {
match self {
ListTag::Double(doubles) => Some(doubles.to_vec()),
NbtList::Double(doubles) => Some(doubles.to_vec()),
_ => None,
}
}
pub fn into_doubles(self) -> Option<Vec<f64>> {
match self {
NbtList::Double(doubles) => Some(doubles),
_ => 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 into_byte_arrays(self) -> Option<Vec<Vec<u8>>> {
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<Vec<Mutf8String>> {
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<Vec<NbtList>> {
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<Vec<NbtCompound>> {
match self {
NbtList::Compound(compounds) => Some(compounds),
_ => None,
}
}
pub fn int_arrays(&self) -> Option<&[Vec<i32>]> {
match self {
ListTag::IntArray(int_arrays) => Some(int_arrays),
NbtList::IntArray(int_arrays) => Some(int_arrays),
_ => None,
}
}
pub fn into_int_arrays(self) -> Option<Vec<Vec<i32>>> {
match self {
NbtList::IntArray(int_arrays) => Some(int_arrays),
_ => None,
}
}
pub fn long_arrays(&self) -> Option<&[Vec<i64>]> {
match self {
ListTag::LongArray(long_arrays) => Some(long_arrays),
NbtList::LongArray(long_arrays) => Some(long_arrays),
_ => None,
}
}
pub fn into_long_arrays(self) -> Option<Vec<Vec<i64>>> {
match self {
NbtList::LongArray(long_arrays) => Some(long_arrays),
_ => None,
}
}

View file

@ -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<Item = (Mutf8String, NbtTag)> {
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<u8>) = BYTE_ARRAY_ID,
String(Mutf8String) = STRING_ID,
List(ListTag) = LIST_ID,
List(NbtList) = LIST_ID,
Compound(NbtCompound) = COMPOUND_ID,
IntArray(Vec<i32>) = INT_ARRAY_ID,
LongArray(Vec<i64>) = 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<ListTag> {
pub fn into_list(self) -> Option<NbtList> {
match self {
NbtTag::List(list) => Some(list),
_ => None,

148
simdnbt/src/traits.rs Normal file
View file

@ -0,0 +1,148 @@
use std::collections::HashMap;
use crate::DeserializeError;
pub trait Deserialize: Sized {
fn from_nbt(nbt: crate::owned::BaseNbt) -> Result<Self, DeserializeError> {
Self::from_compound(nbt.into_inner())
}
fn from_compound(compound: crate::owned::NbtCompound) -> Result<Self, DeserializeError>;
}
impl<T: FromNbtTag> Deserialize for HashMap<String, T> {
fn from_compound(compound: crate::owned::NbtCompound) -> Result<Self, DeserializeError> {
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<Self, DeserializeError> {
Ok(compound)
}
}
pub trait FromNbtTag: Sized {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self>;
fn from_optional_nbt_tag(
tag: Option<crate::owned::NbtTag>,
) -> Result<Option<Self>, DeserializeError> {
match tag {
Some(tag) => Ok(Self::from_nbt_tag(tag)),
None => Err(DeserializeError::MissingField),
}
}
}
impl<T: Deserialize> FromNbtTag for T {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
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<Self> {
tag.byte()
}
}
impl FromNbtTag for i16 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.short()
}
}
impl FromNbtTag for i32 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.int()
}
}
impl FromNbtTag for i64 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.long()
}
}
impl FromNbtTag for f32 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.float()
}
}
impl FromNbtTag for f64 {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.double()
}
}
impl FromNbtTag for String {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.string().map(|s| s.to_string())
}
}
// lists
impl FromNbtTag for Vec<String> {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
tag.list().and_then(|l| {
l.strings()
.map(|s| s.iter().map(|s| s.to_string()).collect())
})
}
}
// slightly less standard types
impl<T: FromNbtTag> FromNbtTag for Option<T> {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
Some(T::from_nbt_tag(tag))
}
fn from_optional_nbt_tag(
tag: Option<crate::owned::NbtTag>,
) -> Result<Option<Self>, DeserializeError> {
match tag {
Some(tag) => Ok(Some(T::from_nbt_tag(tag))),
None => Ok(Some(None)),
}
}
}
impl<T: Deserialize> FromNbtTag for Vec<Option<T>> {
/// A list of compounds where `None` is an empty compound
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
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<T: Deserialize> FromNbtTag for Vec<T> {
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
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<Self> {
tag.byte().map(|b| b != 0)
}
}