mirror of
https://github.com/azalea-rs/simdnbt.git
synced 2025-08-02 07:26:04 +00:00
Use a tape for reading in simdnbt::borrow and probably introduce even more UB (#8)
* write structs and unions for tape * war crimes * except they're not war crimes if you're winning * clippy and uncomment disabled examples * cleanup and fix ub * get(0) -> first() * add fuzzer and fix ub lmao * fix an out of bounds write * fix macros and make it more useable
This commit is contained in:
parent
eb960e076e
commit
150a00cb29
23 changed files with 3263 additions and 1196 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,5 +1,4 @@
|
|||
/target
|
||||
/Cargo.lock
|
||||
.vscode
|
||||
|
||||
# generated by profiling tools
|
||||
|
@ -12,3 +11,6 @@ cachegrind.out.*
|
|||
# sometimes i make these files when benchmarking, don't want to accidentally commit them
|
||||
benchmark_result.txt
|
||||
valgrind.txt
|
||||
|
||||
# thanks rust
|
||||
rustc-ice*.txt
|
||||
|
|
988
Cargo.lock
generated
Normal file
988
Cargo.lock
generated
Normal file
|
@ -0,0 +1,988 @@
|
|||
# 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.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
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.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||
|
||||
[[package]]
|
||||
name = "azalea-buf"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/azalea-rs/azalea?rev=84e036ce3752ecf57904b0f5aff1f33d43e95a32#84e036ce3752ecf57904b0f5aff1f33d43e95a32"
|
||||
dependencies = [
|
||||
"azalea-buf-macros",
|
||||
"byteorder",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-buf-macros"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/azalea-rs/azalea?rev=84e036ce3752ecf57904b0f5aff1f33d43e95a32#84e036ce3752ecf57904b0f5aff1f33d43e95a32"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-nbt"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/azalea-rs/azalea?rev=84e036ce3752ecf57904b0f5aff1f33d43e95a32#84e036ce3752ecf57904b0f5aff1f33d43e95a32"
|
||||
dependencies = [
|
||||
"azalea-buf",
|
||||
"byteorder",
|
||||
"compact_str",
|
||||
"enum-as-inner",
|
||||
"flate2",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||
|
||||
[[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.97"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[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.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
||||
|
||||
[[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.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
|
||||
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.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
|
||||
|
||||
[[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.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastnbt"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d4a73a95dc65551ccd98e1ecd1adb5d1ba5361146963b31f481ca42fc0520a3"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cesu8",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
|
||||
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 = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[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.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"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.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.154"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libmimalloc-sys"
|
||||
version = "0.1.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81eb4061c0582dedea1cbc7aff2240300dd6982e0239d1c99e65c1dbf4a30ba7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "mimalloc"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f41a2280ded0da56c8cf898babb86e8f10651a34adcfff190ae9a1159c6908d"
|
||||
dependencies = [
|
||||
"libmimalloc-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
|
||||
|
||||
[[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.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[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 = "rustversion"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[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 = "serde"
|
||||
version = "1.0.201"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.201"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shen-nbt5"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "027a078cdb13a7d1179dc6a738d7935a4c30d9c2ccfdf11fff1e1367c301b0a1"
|
||||
|
||||
[[package]]
|
||||
name = "simdnbt"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"azalea-nbt",
|
||||
"byteorder",
|
||||
"criterion",
|
||||
"fastnbt",
|
||||
"flate2",
|
||||
"graphite_binary",
|
||||
"hematite-nbt",
|
||||
"mimalloc",
|
||||
"residua-mutf8",
|
||||
"shen-nbt5",
|
||||
"simdnbt-derive",
|
||||
"thiserror",
|
||||
"valence_nbt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdnbt-derive"
|
||||
version = "0.5.2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdnbt-fuzz"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"libfuzzer-sys",
|
||||
"simdnbt",
|
||||
]
|
||||
|
||||
[[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.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
]
|
||||
|
||||
[[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 = "tracing"
|
||||
version = "0.1.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[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.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||
|
||||
[[package]]
|
||||
name = "valence_nbt"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3cddc3222ed5ead4fa446881b3deeeee0dba60b0088b2bf12fedbac7eda2312"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cesu8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.63",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
|
@ -1,3 +1,3 @@
|
|||
[workspace]
|
||||
members = ["simdnbt", "simdnbt-derive"]
|
||||
members = ["simdnbt", "simdnbt-derive", "fuzz"]
|
||||
resolver = "2"
|
||||
|
|
4
fuzz/.gitignore
vendored
Normal file
4
fuzz/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
target
|
||||
corpus
|
||||
artifacts
|
||||
coverage
|
19
fuzz/Cargo.toml
Normal file
19
fuzz/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "simdnbt-fuzz"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.4"
|
||||
simdnbt = { path = "../simdnbt" }
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_target_1"
|
||||
path = "fuzz_targets/fuzz_target_1.rs"
|
||||
test = false
|
||||
doc = false
|
||||
bench = false
|
11
fuzz/fuzz_targets/fuzz_target_1.rs
Normal file
11
fuzz/fuzz_targets/fuzz_target_1.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![no_main]
|
||||
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(r) = simdnbt::borrow::read(&mut std::io::Cursor::new(data)) {
|
||||
if let simdnbt::borrow::Nbt::Some(r) = r {
|
||||
r.as_compound().to_owned();
|
||||
}
|
||||
}
|
||||
});
|
|
@ -64,7 +64,7 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
|||
|
||||
let output = quote! {
|
||||
impl #generics simdnbt::Deserialize for #ident #generics #where_clause {
|
||||
fn from_compound(mut nbt: &simdnbt::borrow::NbtCompound) -> Result<Self, simdnbt::DeserializeError> {
|
||||
fn from_compound(mut nbt: simdnbt::borrow::NbtCompound) -> Result<Self, simdnbt::DeserializeError> {
|
||||
let value = Self {
|
||||
#(#field_deserializers),*
|
||||
};
|
||||
|
@ -168,7 +168,7 @@ pub fn from_nbt_tag_derive(input: proc_macro::TokenStream) -> proc_macro::TokenS
|
|||
|
||||
let output = quote! {
|
||||
impl #generics simdnbt::FromNbtTag for #ident #generics #where_clause {
|
||||
fn from_nbt_tag(tag: &simdnbt::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
|
||||
match tag.string()?.to_str().as_ref() {
|
||||
#(#matchers)*
|
||||
_ => None,
|
||||
|
|
|
@ -26,7 +26,7 @@ fn example(item_bytes: &[u8]) {
|
|||
let skyblock_id: Cow<str> = nbt
|
||||
.list("i")
|
||||
.and_then(|i| i.compounds())
|
||||
.and_then(|i| i.get(0))
|
||||
.and_then(|i| i.first())
|
||||
.and_then(|i| i.compound("tag"))
|
||||
.and_then(|tag| tag.compound("ExtraAttributes"))
|
||||
.and_then(|ea| ea.string("id"))
|
||||
|
|
|
@ -1,105 +1,56 @@
|
|||
use std::{io::Cursor, mem::MaybeUninit};
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
use std::{hint::unreachable_unchecked, mem::MaybeUninit};
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
read_string, unchecked_extend, unchecked_push, unchecked_write_string, write_string,
|
||||
END_ID, MAX_DEPTH,
|
||||
read_int_array, read_long_array, read_string, read_with_u32_length, unchecked_extend,
|
||||
unchecked_push, unchecked_write_string, write_string, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID,
|
||||
DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID,
|
||||
MAX_DEPTH, SHORT_ID, STRING_ID,
|
||||
},
|
||||
reader::Reader,
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use super::{list::NbtList, tag_alloc::TagAllocator, NbtTag};
|
||||
use super::{
|
||||
extra_tapes::ExtraTapes,
|
||||
list::{self, NbtList},
|
||||
tape::{TapeElement, TapeTagKind, TapeTagValue, UnalignedU16},
|
||||
NbtTag, Tapes,
|
||||
};
|
||||
|
||||
/// A list of named tags. The order of the tags is preserved.
|
||||
#[derive(Debug, Default, PartialEq, Clone)]
|
||||
pub struct NbtCompound<'a> {
|
||||
values: &'a [(&'a Mutf8Str, NbtTag<'a>)],
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NbtCompound<'a: 'tape, 'tape> {
|
||||
pub(crate) element: *const TapeElement, // includes the initial compound element
|
||||
pub(crate) extra_tapes: &'tape ExtraTapes<'a>,
|
||||
}
|
||||
|
||||
impl<'a> NbtCompound<'a> {
|
||||
/// # Safety
|
||||
/// The given TagAllocator must be valid for the lifetime of all the tags in this NBT.
|
||||
pub(crate) unsafe fn read(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
alloc: &TagAllocator<'a>,
|
||||
) -> Result<Self, Error> {
|
||||
Self::read_with_depth(data, alloc, 0, 0)
|
||||
}
|
||||
impl<'a: 'tape, 'tape> NbtCompound<'a, 'tape> {
|
||||
pub(crate) fn read(
|
||||
// compounds have no header so nothing to read
|
||||
_data: &mut Reader<'a>,
|
||||
tapes: &'tape mut Tapes<'a>,
|
||||
stack: &mut ParsingStack,
|
||||
) -> Result<(), Error> {
|
||||
let index_of_compound_element = tapes.main.len();
|
||||
|
||||
/// # Safety
|
||||
/// The given TagAllocator must be valid for the lifetime of all the tags in this NBT.
|
||||
pub(crate) unsafe fn read_with_depth(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
alloc: &TagAllocator<'a>,
|
||||
compound_depth: usize,
|
||||
list_depth: usize,
|
||||
) -> Result<Self, Error> {
|
||||
if compound_depth + list_depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
stack.push(ParsingStackElement::Compound {
|
||||
index_of_compound_element: index_of_compound_element as u32,
|
||||
})?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (
|
||||
TapeTagKind::Compound,
|
||||
TapeTagValue {
|
||||
// this gets overwritten later
|
||||
compound: (0.into(), 0.into()),
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
let mut tags = alloc.get().named.start(compound_depth);
|
||||
|
||||
let mut tags_buffer = unsafe {
|
||||
MaybeUninit::<[MaybeUninit<(&Mutf8Str, NbtTag<'a>)>; 4]>::uninit().assume_init()
|
||||
};
|
||||
let mut tags_buffer_len: usize = 0;
|
||||
|
||||
loop {
|
||||
let tag_type = match data.read_u8() {
|
||||
Ok(tag_type) => tag_type,
|
||||
Err(_) => {
|
||||
alloc.get().named.finish(tags, compound_depth);
|
||||
return Err(Error::UnexpectedEof);
|
||||
}
|
||||
};
|
||||
if tag_type == END_ID {
|
||||
break;
|
||||
}
|
||||
|
||||
let tag_name = match read_string(data) {
|
||||
Ok(name) => name,
|
||||
Err(_) => {
|
||||
alloc.get().named.finish(tags, compound_depth);
|
||||
// the only error read_string can return is UnexpectedEof, so this makes it
|
||||
// slightly faster
|
||||
return Err(Error::UnexpectedEof);
|
||||
}
|
||||
};
|
||||
let tag =
|
||||
match NbtTag::read_with_type(data, alloc, tag_type, compound_depth, list_depth) {
|
||||
Ok(tag) => tag,
|
||||
Err(e) => {
|
||||
alloc.get().named.finish(tags, compound_depth);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
tags_buffer[tags_buffer_len] = MaybeUninit::new((tag_name, tag));
|
||||
tags_buffer_len += 1;
|
||||
|
||||
if tags_buffer_len == tags_buffer.len() {
|
||||
// writing the tags in groups like this is slightly faster
|
||||
for i in 0..tags_buffer_len {
|
||||
tags.push(unsafe { tags_buffer.get_unchecked(i).assume_init_read() });
|
||||
}
|
||||
tags_buffer_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..tags_buffer_len {
|
||||
tags.push(unsafe { tags_buffer.get_unchecked(i).assume_init_read() });
|
||||
}
|
||||
|
||||
let values = alloc.get().named.finish(tags, compound_depth);
|
||||
|
||||
Ok(Self { values })
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write(&self, data: &mut Vec<u8>) {
|
||||
for (name, tag) in self.values {
|
||||
for (name, tag) in self.iter() {
|
||||
// reserve 4 bytes extra so we can avoid reallocating for small tags
|
||||
data.reserve(1 + 2 + name.len() + 4);
|
||||
// SAFETY: We just reserved enough space for the tag ID, the name length, the name, and
|
||||
|
@ -108,63 +59,18 @@ impl<'a> NbtCompound<'a> {
|
|||
unchecked_push(data, tag.id());
|
||||
unchecked_write_string(data, name);
|
||||
}
|
||||
match tag {
|
||||
NbtTag::Byte(byte) => unsafe {
|
||||
unchecked_push(data, *byte as u8);
|
||||
},
|
||||
NbtTag::Short(short) => unsafe {
|
||||
unchecked_extend(data, &short.to_be_bytes());
|
||||
},
|
||||
NbtTag::Int(int) => unsafe {
|
||||
unchecked_extend(data, &int.to_be_bytes());
|
||||
},
|
||||
NbtTag::Long(long) => {
|
||||
data.extend_from_slice(&long.to_be_bytes());
|
||||
}
|
||||
NbtTag::Float(float) => unsafe {
|
||||
unchecked_extend(data, &float.to_be_bytes());
|
||||
},
|
||||
NbtTag::Double(double) => {
|
||||
data.extend_from_slice(&double.to_be_bytes());
|
||||
}
|
||||
NbtTag::ByteArray(byte_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(byte_array);
|
||||
}
|
||||
NbtTag::String(string) => {
|
||||
write_string(data, string);
|
||||
}
|
||||
NbtTag::List(list) => {
|
||||
list.write(data);
|
||||
}
|
||||
NbtTag::Compound(compound) => {
|
||||
compound.write(data);
|
||||
}
|
||||
NbtTag::IntArray(int_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(int_array.as_big_endian());
|
||||
}
|
||||
NbtTag::LongArray(long_array) => {
|
||||
unsafe {
|
||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(long_array.as_big_endian());
|
||||
}
|
||||
}
|
||||
|
||||
write_tag(tag, data);
|
||||
}
|
||||
data.push(END_ID);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, name: &str) -> Option<&NbtTag<'a>> {
|
||||
pub fn get(&self, name: &str) -> Option<NbtTag<'a, 'tape>> {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for (key, value) in self.values {
|
||||
if key == &name {
|
||||
for (key, value) in self.iter() {
|
||||
if key == name {
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
|
@ -175,8 +81,8 @@ impl<'a> NbtCompound<'a> {
|
|||
pub fn contains(&self, name: &str) -> bool {
|
||||
let name = Mutf8Str::from_str(name);
|
||||
let name = name.as_ref();
|
||||
for (key, _) in self.values {
|
||||
if key == &name {
|
||||
for key in self.keys() {
|
||||
if key == name {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -201,16 +107,16 @@ impl<'a> NbtCompound<'a> {
|
|||
pub fn double(&self, name: &str) -> Option<f64> {
|
||||
self.get(name).and_then(|tag| tag.double())
|
||||
}
|
||||
pub fn byte_array(&self, name: &str) -> Option<&[u8]> {
|
||||
pub fn byte_array(&self, name: &str) -> Option<&'a [u8]> {
|
||||
self.get(name).and_then(|tag| tag.byte_array())
|
||||
}
|
||||
pub fn string(&self, name: &str) -> Option<&Mutf8Str> {
|
||||
pub fn string(&self, name: &str) -> Option<&'a Mutf8Str> {
|
||||
self.get(name).and_then(|tag| tag.string())
|
||||
}
|
||||
pub fn list(&self, name: &str) -> Option<&NbtList<'a>> {
|
||||
pub fn list(&self, name: &str) -> Option<NbtList<'a, 'tape>> {
|
||||
self.get(name).and_then(|tag| tag.list())
|
||||
}
|
||||
pub fn compound(&self, name: &str) -> Option<&NbtCompound<'a>> {
|
||||
pub fn compound(&self, name: &str) -> Option<NbtCompound<'a, 'tape>> {
|
||||
self.get(name).and_then(|tag| tag.compound())
|
||||
}
|
||||
pub fn int_array(&self, name: &str) -> Option<Vec<i32>> {
|
||||
|
@ -220,26 +126,408 @@ impl<'a> NbtCompound<'a> {
|
|||
self.get(name).and_then(|tag| tag.long_array())
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag<'a>)> {
|
||||
self.values.iter().map(|(k, v)| (*k, v))
|
||||
/// Get the tape element kind and value for this compound.
|
||||
fn element(&self) -> (TapeTagKind, TapeTagValue) {
|
||||
unsafe { (*self.element).kind }
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> CompoundIter<'a, 'tape> {
|
||||
let (kind, value) = self.element();
|
||||
debug_assert_eq!(kind, TapeTagKind::Compound);
|
||||
|
||||
let max_tape_offset = u32::from(unsafe { value.list_list.1 }) as usize;
|
||||
let tape_slice =
|
||||
unsafe { std::slice::from_raw_parts(self.element.add(1), max_tape_offset) };
|
||||
|
||||
CompoundIter {
|
||||
current_tape_offset: 0,
|
||||
max_tape_offset,
|
||||
tape: tape_slice,
|
||||
extra_tapes: self.extra_tapes,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of tags directly in this compound.
|
||||
///
|
||||
/// Note that due to an optimization, this saturates at 2^24. This means if you have a
|
||||
/// compound with more than 2^24 items, then this function will just return 2^24 instead of the
|
||||
/// correct length. If you absolutely need the correct length, you can always just iterate over
|
||||
/// the compound and get the length that way.
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
let (kind, value) = self.element();
|
||||
debug_assert_eq!(kind, TapeTagKind::Compound);
|
||||
unsafe { u32::from(value.list_list.0) as usize }
|
||||
}
|
||||
|
||||
pub fn exact_len(self) -> usize {
|
||||
let len = self.len();
|
||||
if len < 2usize.pow(24) {
|
||||
len
|
||||
} else {
|
||||
self.iter().count()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn keys(&self) -> impl Iterator<Item = &Mutf8Str> {
|
||||
self.values.iter().map(|(k, _)| *k)
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn keys(
|
||||
&self,
|
||||
) -> std::iter::Map<
|
||||
CompoundIter<'a, 'tape>,
|
||||
fn((&'a Mutf8Str, NbtTag<'a, 'tape>)) -> &'a Mutf8Str,
|
||||
> {
|
||||
self.iter().map(|(k, _)| k)
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> crate::owned::NbtCompound {
|
||||
crate::owned::NbtCompound {
|
||||
values: self
|
||||
.values
|
||||
.iter()
|
||||
.map(|(k, v)| ((*k).to_owned(), v.to_owned()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NbtCompound<'_, '_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.iter().eq(other.iter())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompoundIter<'a: 'tape, 'tape> {
|
||||
current_tape_offset: usize,
|
||||
max_tape_offset: usize,
|
||||
tape: &'tape [TapeElement],
|
||||
extra_tapes: &'tape ExtraTapes<'a>,
|
||||
}
|
||||
impl<'a: 'tape, 'tape> Iterator for CompoundIter<'a, 'tape> {
|
||||
type Item = (&'a Mutf8Str, NbtTag<'a, 'tape>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.current_tape_offset + 1 >= self.max_tape_offset {
|
||||
return None;
|
||||
}
|
||||
|
||||
let name_length_ptr = unsafe { self.tape[self.current_tape_offset].name };
|
||||
let name_length_ptr = name_length_ptr as *const UnalignedU16;
|
||||
let name_length = u16::from(unsafe { *name_length_ptr }).swap_bytes();
|
||||
let name_pointer = unsafe { name_length_ptr.add(1) as *const u8 };
|
||||
let name_slice = unsafe { std::slice::from_raw_parts(name_pointer, name_length as usize) };
|
||||
let name = Mutf8Str::from_slice(name_slice);
|
||||
|
||||
self.current_tape_offset += 1;
|
||||
|
||||
let element = unsafe { self.tape.as_ptr().add(self.current_tape_offset) };
|
||||
let tag = NbtTag {
|
||||
element,
|
||||
extra_tapes: self.extra_tapes,
|
||||
};
|
||||
|
||||
self.current_tape_offset += unsafe { (*element).skip_offset() };
|
||||
|
||||
Some((name, tag))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) enum ParsingStackElement {
|
||||
Compound { index_of_compound_element: u32 },
|
||||
ListOfLists { index_of_list_element: u32 },
|
||||
ListOfCompounds { index_of_list_element: u32 },
|
||||
}
|
||||
|
||||
pub struct ParsingStack {
|
||||
stack: [MaybeUninit<ParsingStackElement>; MAX_DEPTH],
|
||||
remaining_elements_in_lists: [u32; MAX_DEPTH],
|
||||
depth: usize,
|
||||
}
|
||||
|
||||
impl ParsingStack {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stack: unsafe { MaybeUninit::uninit().assume_init() },
|
||||
remaining_elements_in_lists: [0; MAX_DEPTH],
|
||||
depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, state: ParsingStackElement) -> Result<(), Error> {
|
||||
unsafe { self.stack.get_unchecked_mut(self.depth).write(state) };
|
||||
self.depth += 1;
|
||||
|
||||
if self.depth >= MAX_DEPTH {
|
||||
Err(Error::MaxDepthExceeded)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_list_length(&mut self, length: u32) {
|
||||
unsafe {
|
||||
*self
|
||||
.remaining_elements_in_lists
|
||||
.get_unchecked_mut(self.depth - 1) = length;
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn decrement_list_length(&mut self) {
|
||||
unsafe {
|
||||
*self
|
||||
.remaining_elements_in_lists
|
||||
.get_unchecked_mut(self.depth - 1) -= 1;
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn remaining_elements_in_list(&self) -> u32 {
|
||||
unsafe {
|
||||
*self
|
||||
.remaining_elements_in_lists
|
||||
.get_unchecked(self.depth - 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pop(&mut self) -> ParsingStackElement {
|
||||
self.depth -= 1;
|
||||
unsafe { self.stack.get_unchecked(self.depth).assume_init() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn peek(&self) -> ParsingStackElement {
|
||||
unsafe { self.stack.get_unchecked(self.depth - 1).assume_init() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.depth == 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn peek_mut(&mut self) -> &mut ParsingStackElement {
|
||||
unsafe {
|
||||
self.stack
|
||||
.get_unchecked_mut(self.depth - 1)
|
||||
.as_mut_ptr()
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn read_tag<'a>(
|
||||
data: &mut Reader<'a>,
|
||||
tapes: &mut Tapes<'a>,
|
||||
stack: &mut ParsingStack,
|
||||
tag_type: u8,
|
||||
) -> Result<(), Error> {
|
||||
match tag_type {
|
||||
COMPOUND_ID => return NbtCompound::read(data, tapes, stack),
|
||||
LIST_ID => return NbtList::read(data, tapes, stack),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
match tag_type {
|
||||
BYTE_ID => {
|
||||
let byte = data.read_i8()?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (TapeTagKind::Byte, TapeTagValue { byte }),
|
||||
});
|
||||
}
|
||||
SHORT_ID => {
|
||||
let short = data.read_i16()?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (TapeTagKind::Short, TapeTagValue { short }),
|
||||
});
|
||||
}
|
||||
INT_ID => {
|
||||
let int = data.read_i32()?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (TapeTagKind::Int, TapeTagValue { int }),
|
||||
});
|
||||
}
|
||||
LONG_ID => {
|
||||
let long = data.read_i64()?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (TapeTagKind::Long, TapeTagValue { long: () }),
|
||||
});
|
||||
tapes.main.push(TapeElement { long });
|
||||
}
|
||||
FLOAT_ID => {
|
||||
let float = data.read_f32()?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (TapeTagKind::Float, TapeTagValue { float }),
|
||||
});
|
||||
}
|
||||
DOUBLE_ID => {
|
||||
let double = data.read_f64()?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (TapeTagKind::Double, TapeTagValue { double: () }),
|
||||
});
|
||||
tapes.main.push(TapeElement { double });
|
||||
}
|
||||
BYTE_ARRAY_ID => {
|
||||
let byte_array_pointer = data.cur as u64;
|
||||
read_with_u32_length(data, 1)?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (
|
||||
TapeTagKind::ByteArray,
|
||||
TapeTagValue {
|
||||
byte_array: byte_array_pointer.into(),
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
STRING_ID => {
|
||||
let string_pointer = data.cur as u64;
|
||||
|
||||
// assert that the top 8 bits of the pointer are 0 (because we rely on this)
|
||||
debug_assert_eq!(string_pointer >> 56, 0);
|
||||
|
||||
read_string(data)?;
|
||||
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (
|
||||
TapeTagKind::String,
|
||||
TapeTagValue {
|
||||
string: string_pointer.into(),
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
INT_ARRAY_ID => {
|
||||
let int_array_pointer = data.cur as u64;
|
||||
read_int_array(data)?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (
|
||||
TapeTagKind::IntArray,
|
||||
TapeTagValue {
|
||||
int_array: int_array_pointer.into(),
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
LONG_ARRAY_ID => {
|
||||
let long_array_pointer = data.cur as u64;
|
||||
read_long_array(data)?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (
|
||||
TapeTagKind::LongArray,
|
||||
TapeTagValue {
|
||||
long_array: long_array_pointer.into(),
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
_ => return Err(Error::UnknownTagId(tag_type)),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn read_tag_in_compound<'a>(
|
||||
data: &mut Reader<'a>,
|
||||
tapes: &mut Tapes<'a>,
|
||||
stack: &mut ParsingStack,
|
||||
) -> Result<(), Error> {
|
||||
let tag_type = data.read_u8()?;
|
||||
if tag_type == END_ID {
|
||||
handle_compound_end(tapes, stack);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let tag_name_pointer = data.cur as u64;
|
||||
debug_assert_eq!(tag_name_pointer >> 56, 0);
|
||||
read_string(data)?;
|
||||
tapes.main.push(TapeElement {
|
||||
name: tag_name_pointer,
|
||||
});
|
||||
|
||||
read_tag(data, tapes, stack, tag_type)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn handle_compound_end(tapes: &mut Tapes, stack: &mut ParsingStack) {
|
||||
let ParsingStackElement::Compound {
|
||||
index_of_compound_element,
|
||||
} = stack.pop()
|
||||
else {
|
||||
unsafe { unreachable_unchecked() };
|
||||
};
|
||||
let index_after_end_element = tapes.main.len();
|
||||
|
||||
unsafe {
|
||||
tapes
|
||||
.main
|
||||
.get_unchecked_mut(index_of_compound_element as usize)
|
||||
.kind
|
||||
.1
|
||||
.compound
|
||||
.1 = (index_after_end_element as u32 - index_of_compound_element).into();
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn write_tag(tag: NbtTag, data: &mut Vec<u8>) {
|
||||
let (kind, value) = tag.element();
|
||||
match kind {
|
||||
TapeTagKind::Byte => unsafe {
|
||||
unchecked_push(data, tag.byte().unwrap() as u8);
|
||||
},
|
||||
TapeTagKind::Short => unsafe {
|
||||
unchecked_extend(data, &tag.short().unwrap().to_be_bytes());
|
||||
},
|
||||
TapeTagKind::Int => unsafe {
|
||||
unchecked_extend(data, &tag.int().unwrap().to_be_bytes());
|
||||
},
|
||||
TapeTagKind::Long => {
|
||||
data.extend_from_slice(&tag.long().unwrap().to_be_bytes());
|
||||
}
|
||||
TapeTagKind::Float => unsafe {
|
||||
unchecked_extend(data, &tag.float().unwrap().to_be_bytes());
|
||||
},
|
||||
TapeTagKind::Double => {
|
||||
data.extend_from_slice(&tag.double().unwrap().to_be_bytes());
|
||||
}
|
||||
TapeTagKind::ByteArray => {
|
||||
let byte_array = tag.byte_array().unwrap();
|
||||
unsafe {
|
||||
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(byte_array);
|
||||
}
|
||||
TapeTagKind::String => {
|
||||
let string = tag.string().unwrap();
|
||||
write_string(data, string);
|
||||
}
|
||||
_ if kind.is_list() => {
|
||||
tag.list().unwrap().write(data);
|
||||
}
|
||||
TapeTagKind::Compound => {
|
||||
tag.compound().unwrap().write(data);
|
||||
}
|
||||
TapeTagKind::IntArray => {
|
||||
let int_array =
|
||||
unsafe { list::u32_prefixed_list_to_rawlist_unchecked::<i32>(value).unwrap() };
|
||||
unsafe {
|
||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(int_array.as_big_endian());
|
||||
}
|
||||
TapeTagKind::LongArray => {
|
||||
let long_array =
|
||||
unsafe { list::u32_prefixed_list_to_rawlist_unchecked::<i64>(value).unwrap() };
|
||||
unsafe {
|
||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||
}
|
||||
data.extend_from_slice(long_array.as_big_endian());
|
||||
}
|
||||
_ => unreachable!("Invalid tag kind {kind:?}"),
|
||||
}
|
||||
}
|
||||
|
|
24
simdnbt/src/borrow/extra_tapes.rs
Normal file
24
simdnbt/src/borrow/extra_tapes.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use std::fmt::{self, Debug};
|
||||
|
||||
use crate::{raw_list::RawList, Mutf8Str};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ExtraTapes<'a> {
|
||||
pub elements: Vec<ExtraTapeElement<'a>>,
|
||||
}
|
||||
|
||||
impl Debug for ExtraTapes<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ExtraTapes")
|
||||
}
|
||||
}
|
||||
|
||||
pub union ExtraTapeElement<'a> {
|
||||
/// An indicator for how long the following list is. This is what we point to from
|
||||
/// `TapeTagValue`.
|
||||
pub length: u32,
|
||||
pub byte_array: &'a [u8],
|
||||
pub string: &'a Mutf8Str,
|
||||
pub int_array: RawList<'a, i32>,
|
||||
pub long_array: RawList<'a, i64>,
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,31 +1,68 @@
|
|||
//! The borrowed variant of NBT. This is useful if you're only reading data and you can keep a reference to the original buffer.
|
||||
|
||||
mod compound;
|
||||
mod extra_tapes;
|
||||
mod list;
|
||||
mod tag_alloc;
|
||||
mod tape;
|
||||
|
||||
use std::{io::Cursor, ops::Deref};
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
io::Cursor,
|
||||
};
|
||||
|
||||
use byteorder::{ReadBytesExt, BE};
|
||||
use byteorder::ReadBytesExt;
|
||||
use tape::UnalignedU32;
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
read_int_array, read_long_array, read_string, read_u32, read_with_u32_length, write_string,
|
||||
BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID,
|
||||
LIST_ID, LONG_ARRAY_ID, LONG_ID, MAX_DEPTH, SHORT_ID, STRING_ID,
|
||||
read_string, write_string, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID,
|
||||
FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID, SHORT_ID, STRING_ID,
|
||||
},
|
||||
raw_list::RawList,
|
||||
reader::{Reader, ReaderFromCursor},
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
use self::tag_alloc::TagAllocator;
|
||||
pub use self::{compound::NbtCompound, list::NbtList};
|
||||
use self::{
|
||||
compound::{read_tag_in_compound, ParsingStack, ParsingStackElement},
|
||||
extra_tapes::ExtraTapes,
|
||||
list::{read_compound_in_list, read_list_in_list},
|
||||
tape::{MainTape, TapeElement, TapeTagKind, TapeTagValue, UnalignedU16},
|
||||
};
|
||||
|
||||
/// Read a normal root NBT compound. This is either empty or has a name and compound tag.
|
||||
///
|
||||
/// Returns `Ok(Nbt::None)` if there is no data.
|
||||
pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
|
||||
Nbt::read(data)
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(Nbt::None);
|
||||
}
|
||||
if root_type != COMPOUND_ID {
|
||||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let mut data = ReaderFromCursor::new(data);
|
||||
let name = read_string(&mut data)?;
|
||||
|
||||
let mut tapes = Tapes::new();
|
||||
let mut stack = ParsingStack::new();
|
||||
|
||||
stack.push(ParsingStackElement::Compound {
|
||||
index_of_compound_element: 0,
|
||||
})?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (
|
||||
TapeTagKind::Compound,
|
||||
TapeTagValue {
|
||||
// this gets overwritten later
|
||||
compound: (0.into(), 0.into()),
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
read_with_stack(&mut data, &mut tapes, &mut stack)?;
|
||||
|
||||
Ok(Nbt::Some(BaseNbt { name, tapes }))
|
||||
}
|
||||
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading
|
||||
/// NBT over the network.
|
||||
|
@ -33,61 +70,221 @@ pub fn read<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
|
|||
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either
|
||||
/// empty or a compound).
|
||||
pub fn read_unnamed<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
|
||||
Nbt::read_unnamed(data)
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(Nbt::None);
|
||||
}
|
||||
if root_type != COMPOUND_ID {
|
||||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let name = Mutf8Str::from_slice(&[]);
|
||||
let BaseNbtCompound { tapes } = read_compound(data)?;
|
||||
Ok(Nbt::Some(BaseNbt { name, tapes }))
|
||||
}
|
||||
/// Read a compound tag. This may have any number of items.
|
||||
pub fn read_compound<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtCompound<'a>, Error> {
|
||||
let tag_alloc = TagAllocator::new();
|
||||
let tag = unsafe { NbtCompound::read(data, &tag_alloc) }?;
|
||||
Ok(BaseNbtCompound {
|
||||
tag,
|
||||
_tag_alloc: tag_alloc,
|
||||
})
|
||||
let mut tapes = Tapes::new();
|
||||
let mut stack = ParsingStack::new();
|
||||
|
||||
let mut data = ReaderFromCursor::new(data);
|
||||
|
||||
stack.push(ParsingStackElement::Compound {
|
||||
index_of_compound_element: 0,
|
||||
})?;
|
||||
tapes.main.push(TapeElement {
|
||||
kind: (
|
||||
TapeTagKind::Compound,
|
||||
TapeTagValue {
|
||||
// this gets overwritten later
|
||||
compound: (0.into(), 0.into()),
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
read_with_stack(&mut data, &mut tapes, &mut stack)?;
|
||||
|
||||
Ok(BaseNbtCompound { tapes })
|
||||
}
|
||||
/// Read an NBT tag, without reading its name. This may be any type of tag except for an end tag. If you need to be able to
|
||||
/// handle end tags, use [`read_optional_tag`].
|
||||
pub fn read_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<BaseNbtTag<'a>, Error> {
|
||||
let tag_alloc = TagAllocator::new();
|
||||
let tag = unsafe { NbtTag::read(data, &tag_alloc) }?;
|
||||
Ok(BaseNbtTag {
|
||||
tag,
|
||||
_tag_alloc: tag_alloc,
|
||||
})
|
||||
let mut tapes = Tapes::new();
|
||||
let mut stack = ParsingStack::new();
|
||||
|
||||
let mut data = ReaderFromCursor::new(data);
|
||||
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if tag_type == END_ID {
|
||||
return Err(Error::InvalidRootType(0));
|
||||
}
|
||||
compound::read_tag(&mut data, &mut tapes, &mut stack, tag_type)?;
|
||||
read_with_stack(&mut data, &mut tapes, &mut stack)?;
|
||||
|
||||
Ok(BaseNbtTag { tapes })
|
||||
}
|
||||
/// Read any NBT tag, without reading its name. This may be any type of tag, including an end tag.
|
||||
///
|
||||
/// Returns `Ok(None)` if there is no data.
|
||||
pub fn read_optional_tag<'a>(data: &mut Cursor<&'a [u8]>) -> Result<Option<BaseNbtTag<'a>>, Error> {
|
||||
let tag_alloc = TagAllocator::new();
|
||||
let tag = unsafe { NbtTag::read_optional(data, &tag_alloc) }?;
|
||||
Ok(tag.map(|tag| BaseNbtTag {
|
||||
tag,
|
||||
_tag_alloc: tag_alloc,
|
||||
}))
|
||||
let mut tapes = Tapes::new();
|
||||
let mut stack = ParsingStack::new();
|
||||
|
||||
let mut data = ReaderFromCursor::new(data);
|
||||
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if tag_type == END_ID {
|
||||
return Ok(None);
|
||||
}
|
||||
compound::read_tag(&mut data, &mut tapes, &mut stack, tag_type)?;
|
||||
read_with_stack(&mut data, &mut tapes, &mut stack)?;
|
||||
|
||||
Ok(Some(BaseNbtTag { tapes }))
|
||||
}
|
||||
|
||||
fn read_with_stack<'a>(
|
||||
data: &mut Reader<'a>,
|
||||
tapes: &mut Tapes<'a>,
|
||||
stack: &mut ParsingStack,
|
||||
) -> Result<(), Error> {
|
||||
while !stack.is_empty() {
|
||||
match stack.peek_mut() {
|
||||
ParsingStackElement::Compound { .. } => read_tag_in_compound(data, tapes, stack)?,
|
||||
ParsingStackElement::ListOfLists { .. } => read_list_in_list(data, tapes, stack)?,
|
||||
ParsingStackElement::ListOfCompounds { .. } => {
|
||||
read_compound_in_list(data, tapes, stack)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Tapes<'a> {
|
||||
main: MainTape,
|
||||
extra: ExtraTapes<'a>,
|
||||
}
|
||||
impl<'a> Tapes<'a> {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
impl Debug for Tapes<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Tapes").finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A complete NBT container. This contains a name and a compound tag.
|
||||
#[derive(Debug)]
|
||||
pub struct BaseNbt<'a> {
|
||||
name: &'a Mutf8Str,
|
||||
tag: NbtCompound<'a>,
|
||||
// we need to keep this around so it's not deallocated
|
||||
_tag_alloc: TagAllocator<'a>,
|
||||
tapes: Tapes<'a>,
|
||||
}
|
||||
impl<'a> BaseNbt<'a> {
|
||||
#[inline]
|
||||
pub fn as_compound<'tape>(&'a self) -> NbtCompound<'a, 'tape>
|
||||
where
|
||||
'a: 'tape,
|
||||
{
|
||||
NbtCompound {
|
||||
element: self.tapes.main.as_ptr(),
|
||||
extra_tapes: &self.tapes.extra,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of the NBT compound. This is often an empty string.
|
||||
pub fn name(&self) -> &'a Mutf8Str {
|
||||
self.name
|
||||
}
|
||||
|
||||
pub fn get<'tape>(&'a self, key: &str) -> Option<NbtTag<'a, 'tape>> {
|
||||
self.as_compound().get(key)
|
||||
}
|
||||
/// Returns whether there is a tag with the given name.
|
||||
pub fn contains(&'a self, key: &str) -> bool {
|
||||
self.as_compound().contains(key)
|
||||
}
|
||||
pub fn byte(&self, name: &str) -> Option<i8> {
|
||||
self.as_compound().byte(name)
|
||||
}
|
||||
pub fn short(&self, name: &str) -> Option<i16> {
|
||||
self.as_compound().short(name)
|
||||
}
|
||||
pub fn int(&self, name: &str) -> Option<i32> {
|
||||
self.as_compound().int(name)
|
||||
}
|
||||
pub fn long(&self, name: &str) -> Option<i64> {
|
||||
self.as_compound().long(name)
|
||||
}
|
||||
pub fn float(&self, name: &str) -> Option<f32> {
|
||||
self.as_compound().float(name)
|
||||
}
|
||||
pub fn double(&self, name: &str) -> Option<f64> {
|
||||
self.as_compound().double(name)
|
||||
}
|
||||
pub fn byte_array(&'a self, name: &str) -> Option<&'a [u8]> {
|
||||
self.as_compound().byte_array(name)
|
||||
}
|
||||
pub fn string(&'a self, name: &str) -> Option<&'a Mutf8Str> {
|
||||
self.as_compound().string(name)
|
||||
}
|
||||
pub fn list<'tape>(&'a self, name: &str) -> Option<NbtList<'a, 'tape>> {
|
||||
self.as_compound().list(name)
|
||||
}
|
||||
pub fn compound<'tape>(&'a self, name: &str) -> Option<NbtCompound<'a, 'tape>> {
|
||||
self.as_compound().compound(name)
|
||||
}
|
||||
pub fn int_array(&self, name: &str) -> Option<Vec<i32>> {
|
||||
self.as_compound().int_array(name)
|
||||
}
|
||||
pub fn long_array(&self, name: &str) -> Option<Vec<i64>> {
|
||||
self.as_compound().long_array(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Debug for BaseNbt<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("BaseNbt").finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A nameless NBT container. This only contains a compound tag. This contains a `TagAllocator`,
|
||||
/// so it can exist independently from a [`BaseNbt`].
|
||||
pub struct BaseNbtCompound<'a> {
|
||||
tag: NbtCompound<'a>,
|
||||
_tag_alloc: TagAllocator<'a>,
|
||||
tapes: Tapes<'a>,
|
||||
}
|
||||
impl<'a, 'tape> From<&'a BaseNbtCompound<'a>> for NbtCompound<'a, 'tape> {
|
||||
fn from(compound: &'a BaseNbtCompound<'a>) -> Self
|
||||
where
|
||||
'a: 'tape,
|
||||
{
|
||||
NbtCompound {
|
||||
element: compound.tapes.main.as_ptr(),
|
||||
extra_tapes: &compound.tapes.extra,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A nameless NBT tag.
|
||||
pub struct BaseNbtTag<'a> {
|
||||
tag: NbtTag<'a>,
|
||||
_tag_alloc: TagAllocator<'a>,
|
||||
tapes: Tapes<'a>,
|
||||
}
|
||||
impl<'a> BaseNbtTag<'a> {
|
||||
pub fn as_tag<'tape>(&'a self) -> NbtTag<'a, 'tape>
|
||||
where
|
||||
'a: 'tape,
|
||||
{
|
||||
NbtTag {
|
||||
element: self.tapes.main.as_ptr(),
|
||||
extra_tapes: &self.tapes.extra,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a, 'tape> From<&'a BaseNbtTag<'a>> for NbtTag<'a, 'tape> {
|
||||
fn from(tag: &'a BaseNbtTag<'a>) -> Self {
|
||||
tag.as_tag()
|
||||
}
|
||||
}
|
||||
|
||||
/// Either a complete NBT container, or nothing.
|
||||
#[derive(Debug, PartialEq, Default)]
|
||||
pub enum Nbt<'a> {
|
||||
|
@ -97,46 +294,6 @@ pub enum Nbt<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Nbt<'a> {
|
||||
/// Reads NBT from the given data. Returns `Ok(Nbt::None)` if there is no data.
|
||||
fn read(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(Nbt::None);
|
||||
}
|
||||
if root_type != COMPOUND_ID {
|
||||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let tag_alloc = TagAllocator::new();
|
||||
|
||||
let name = read_string(data)?;
|
||||
let tag = unsafe { NbtCompound::read(data, &tag_alloc) }?;
|
||||
|
||||
Ok(Nbt::Some(BaseNbt {
|
||||
name,
|
||||
tag,
|
||||
_tag_alloc: tag_alloc,
|
||||
}))
|
||||
}
|
||||
|
||||
fn read_unnamed(data: &mut Cursor<&'a [u8]>) -> Result<Nbt<'a>, Error> {
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(Nbt::None);
|
||||
}
|
||||
if root_type != COMPOUND_ID {
|
||||
return Err(Error::InvalidRootType(root_type));
|
||||
}
|
||||
let tag_alloc = TagAllocator::new();
|
||||
|
||||
let tag = unsafe { NbtCompound::read(data, &tag_alloc) }?;
|
||||
|
||||
Ok(Nbt::Some(BaseNbt {
|
||||
name: Mutf8Str::from_slice(&[]),
|
||||
tag,
|
||||
_tag_alloc: tag_alloc,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn write(&self, data: &mut Vec<u8>) {
|
||||
match self {
|
||||
Nbt::Some(nbt) => nbt.write(data),
|
||||
|
@ -165,40 +322,11 @@ impl<'a> Nbt<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> BaseNbt<'a> {
|
||||
/// Get the name of the NBT compound. This is often an empty string.
|
||||
pub fn name(&self) -> &'a Mutf8Str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BaseNbt<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// we don't need to compare the tag allocator since comparing `tag` will
|
||||
// we don't need to compare the tapes since comparing `tag` will
|
||||
// still compare the values of the tags
|
||||
self.name == other.name && self.tag == other.tag
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for BaseNbt<'a> {
|
||||
type Target = NbtCompound<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tag
|
||||
}
|
||||
}
|
||||
impl<'a> Deref for BaseNbtCompound<'a> {
|
||||
type Target = NbtCompound<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tag
|
||||
}
|
||||
}
|
||||
impl<'a> Deref for BaseNbtTag<'a> {
|
||||
type Target = NbtTag<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.tag
|
||||
self.name == other.name && self.as_compound() == other.as_compound()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -206,203 +334,184 @@ impl<'a> BaseNbt<'a> {
|
|||
pub fn write(&self, data: &mut Vec<u8>) {
|
||||
data.push(COMPOUND_ID);
|
||||
write_string(data, self.name);
|
||||
self.tag.write(data);
|
||||
self.as_compound().write(data);
|
||||
data.push(END_ID);
|
||||
}
|
||||
}
|
||||
|
||||
/// A single NBT tag.
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum NbtTag<'a> {
|
||||
Byte(i8),
|
||||
Short(i16),
|
||||
Int(i32),
|
||||
Long(i64),
|
||||
Float(f32),
|
||||
Double(f64),
|
||||
ByteArray(&'a [u8]),
|
||||
String(&'a Mutf8Str),
|
||||
List(NbtList<'a>),
|
||||
Compound(NbtCompound<'a>),
|
||||
IntArray(RawList<'a, i32>),
|
||||
LongArray(RawList<'a, i64>),
|
||||
#[derive(Debug)]
|
||||
pub struct NbtTag<'a: 'tape, 'tape> {
|
||||
element: *const TapeElement,
|
||||
extra_tapes: &'tape ExtraTapes<'a>,
|
||||
}
|
||||
impl<'a, 'b> NbtTag<'a> {
|
||||
|
||||
impl<'a: 'tape, 'tape> NbtTag<'a, 'tape> {
|
||||
/// Get the numerical ID of the tag type.
|
||||
#[inline]
|
||||
pub fn id(&self) -> u8 {
|
||||
match self {
|
||||
NbtTag::Byte(_) => BYTE_ID,
|
||||
NbtTag::Short(_) => SHORT_ID,
|
||||
NbtTag::Int(_) => INT_ID,
|
||||
NbtTag::Long(_) => LONG_ID,
|
||||
NbtTag::Float(_) => FLOAT_ID,
|
||||
NbtTag::Double(_) => DOUBLE_ID,
|
||||
NbtTag::ByteArray(_) => BYTE_ARRAY_ID,
|
||||
NbtTag::String(_) => STRING_ID,
|
||||
NbtTag::List(_) => LIST_ID,
|
||||
NbtTag::Compound(_) => COMPOUND_ID,
|
||||
NbtTag::IntArray(_) => INT_ARRAY_ID,
|
||||
NbtTag::LongArray(_) => LONG_ARRAY_ID,
|
||||
match self.element().0 {
|
||||
TapeTagKind::Byte => BYTE_ID,
|
||||
TapeTagKind::Short => SHORT_ID,
|
||||
TapeTagKind::Int => INT_ID,
|
||||
TapeTagKind::Long => LONG_ID,
|
||||
TapeTagKind::Float => FLOAT_ID,
|
||||
TapeTagKind::Double => DOUBLE_ID,
|
||||
TapeTagKind::ByteArray => BYTE_ARRAY_ID,
|
||||
TapeTagKind::String => STRING_ID,
|
||||
TapeTagKind::Compound => COMPOUND_ID,
|
||||
TapeTagKind::IntArray => INT_ARRAY_ID,
|
||||
TapeTagKind::LongArray => LONG_ARRAY_ID,
|
||||
t if t.is_list() => LIST_ID,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The given TagAllocator must be valid for the lifetime of all the tags in this NBT.
|
||||
#[inline(always)]
|
||||
unsafe fn read_with_type(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
alloc: &TagAllocator<'a>,
|
||||
tag_type: u8,
|
||||
compound_depth: usize,
|
||||
list_depth: usize,
|
||||
) -> Result<Self, Error> {
|
||||
match tag_type {
|
||||
BYTE_ID => Ok(NbtTag::Byte(
|
||||
data.read_i8().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
SHORT_ID => Ok(NbtTag::Short(
|
||||
data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
INT_ID => Ok(NbtTag::Int(
|
||||
data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
LONG_ID => Ok(NbtTag::Long(
|
||||
data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
FLOAT_ID => Ok(NbtTag::Float(
|
||||
data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
DOUBLE_ID => Ok(NbtTag::Double(
|
||||
data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
BYTE_ARRAY_ID => Ok(NbtTag::ByteArray(read_with_u32_length(data, 1)?)),
|
||||
STRING_ID => Ok(NbtTag::String(read_string(data)?)),
|
||||
LIST_ID => Ok(NbtTag::List(NbtList::read(
|
||||
data,
|
||||
alloc,
|
||||
compound_depth,
|
||||
list_depth + 1,
|
||||
)?)),
|
||||
COMPOUND_ID => Ok(NbtTag::Compound(NbtCompound::read_with_depth(
|
||||
data,
|
||||
alloc,
|
||||
compound_depth + 1,
|
||||
list_depth,
|
||||
)?)),
|
||||
INT_ARRAY_ID => Ok(NbtTag::IntArray(read_int_array(data)?)),
|
||||
LONG_ARRAY_ID => Ok(NbtTag::LongArray(read_long_array(data)?)),
|
||||
_ => Err(Error::UnknownTagId(tag_type)),
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The given TagAllocator must be valid for the lifetime of all the tags in this NBT.
|
||||
unsafe fn read(data: &mut Cursor<&'a [u8]>, alloc: &TagAllocator<'a>) -> Result<Self, Error> {
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
Self::read_with_type(data, alloc, tag_type, 0, 0)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The given TagAllocator must be valid for the lifetime of all the tags in this NBT.
|
||||
unsafe fn read_optional(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
alloc: &TagAllocator<'a>,
|
||||
) -> Result<Option<Self>, Error> {
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if tag_type == END_ID {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(Self::read_with_type(data, alloc, tag_type, 0, 0)?))
|
||||
}
|
||||
|
||||
pub fn byte(&self) -> Option<i8> {
|
||||
match self {
|
||||
NbtTag::Byte(byte) => Some(*byte),
|
||||
_ => None,
|
||||
let (kind, value) = self.element();
|
||||
if kind != TapeTagKind::Byte {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { value.byte })
|
||||
}
|
||||
pub fn short(&self) -> Option<i16> {
|
||||
match self {
|
||||
NbtTag::Short(short) => Some(*short),
|
||||
_ => None,
|
||||
let (kind, value) = self.element();
|
||||
if kind != TapeTagKind::Short {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { value.short })
|
||||
}
|
||||
pub fn int(&self) -> Option<i32> {
|
||||
match self {
|
||||
NbtTag::Int(int) => Some(*int),
|
||||
_ => None,
|
||||
let (kind, value) = unsafe { (*self.element).kind };
|
||||
if kind != TapeTagKind::Int {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { value.int })
|
||||
}
|
||||
pub fn long(&self) -> Option<i64> {
|
||||
match self {
|
||||
NbtTag::Long(long) => Some(*long),
|
||||
_ => None,
|
||||
let (kind, _) = self.element();
|
||||
if kind != TapeTagKind::Long {
|
||||
return None;
|
||||
}
|
||||
// the value is in the next element because longs are too big to fit in a single element
|
||||
let value = unsafe { self.element.add(1) };
|
||||
Some(unsafe { (*value).long })
|
||||
}
|
||||
pub fn float(&self) -> Option<f32> {
|
||||
match self {
|
||||
NbtTag::Float(float) => Some(*float),
|
||||
_ => None,
|
||||
let (kind, value) = self.element();
|
||||
if kind != TapeTagKind::Float {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { value.float })
|
||||
}
|
||||
pub fn double(&self) -> Option<f64> {
|
||||
match self {
|
||||
NbtTag::Double(double) => Some(*double),
|
||||
_ => None,
|
||||
let (kind, _) = self.element();
|
||||
if kind != TapeTagKind::Double {
|
||||
return None;
|
||||
}
|
||||
// the value is in the next element because doubles are too big to fit in a single element
|
||||
let value = unsafe { self.element.add(1) };
|
||||
Some(unsafe { (*value).double })
|
||||
}
|
||||
pub fn byte_array(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
NbtTag::ByteArray(byte_array) => Some(byte_array),
|
||||
_ => None,
|
||||
pub fn byte_array(&self) -> Option<&'a [u8]> {
|
||||
let (kind, value) = self.element();
|
||||
if kind != TapeTagKind::ByteArray {
|
||||
return None;
|
||||
}
|
||||
let length_ptr = unsafe { u64::from(value.byte_array) as *const UnalignedU32 };
|
||||
let length = unsafe { u32::from(*length_ptr).swap_bytes() as usize };
|
||||
let data_ptr = unsafe { length_ptr.add(1) as *const u8 };
|
||||
Some(unsafe { std::slice::from_raw_parts(data_ptr, length) })
|
||||
}
|
||||
pub fn string(&self) -> Option<&Mutf8Str> {
|
||||
match self {
|
||||
NbtTag::String(string) => Some(string),
|
||||
_ => None,
|
||||
pub fn string(&self) -> Option<&'a Mutf8Str> {
|
||||
let (kind, value) = self.element();
|
||||
if kind != TapeTagKind::String {
|
||||
return None;
|
||||
}
|
||||
let length_ptr = unsafe { u64::from(value.string) as usize as *const UnalignedU16 };
|
||||
let length = unsafe { u16::from(*length_ptr).swap_bytes() as usize };
|
||||
let data_ptr = unsafe { length_ptr.add(1) as *const u8 };
|
||||
Some(unsafe { Mutf8Str::from_slice(std::slice::from_raw_parts(data_ptr, length)) })
|
||||
}
|
||||
pub fn list(&self) -> Option<&NbtList<'a>> {
|
||||
match self {
|
||||
NbtTag::List(list) => Some(list),
|
||||
_ => None,
|
||||
pub fn list(&self) -> Option<NbtList<'a, 'tape>> {
|
||||
let (kind, _) = self.element();
|
||||
if !kind.is_list() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NbtList {
|
||||
element: self.element,
|
||||
extra_tapes: self.extra_tapes,
|
||||
})
|
||||
}
|
||||
pub fn compound(&self) -> Option<&NbtCompound<'a>> {
|
||||
match self {
|
||||
NbtTag::Compound(compound) => Some(compound),
|
||||
_ => None,
|
||||
pub fn compound(&self) -> Option<NbtCompound<'a, 'tape>> {
|
||||
let (kind, _) = self.element();
|
||||
if kind != TapeTagKind::Compound {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(NbtCompound {
|
||||
element: self.element,
|
||||
extra_tapes: self.extra_tapes,
|
||||
})
|
||||
}
|
||||
pub fn int_array(&self) -> Option<Vec<i32>> {
|
||||
match self {
|
||||
NbtTag::IntArray(int_array) => Some(int_array.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
list::u32_prefixed_list_to_vec(TapeTagKind::IntArray, self.element)
|
||||
}
|
||||
pub fn long_array(&self) -> Option<Vec<i64>> {
|
||||
match self {
|
||||
NbtTag::LongArray(long_array) => Some(long_array.to_vec()),
|
||||
_ => None,
|
||||
}
|
||||
list::u32_prefixed_list_to_vec(TapeTagKind::LongArray, self.element)
|
||||
}
|
||||
|
||||
/// Get the tape element kind and value for this tag.
|
||||
fn element(&self) -> (TapeTagKind, TapeTagValue) {
|
||||
unsafe { (*self.element).kind }
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> crate::owned::NbtTag {
|
||||
match self {
|
||||
NbtTag::Byte(byte) => crate::owned::NbtTag::Byte(*byte),
|
||||
NbtTag::Short(short) => crate::owned::NbtTag::Short(*short),
|
||||
NbtTag::Int(int) => crate::owned::NbtTag::Int(*int),
|
||||
NbtTag::Long(long) => crate::owned::NbtTag::Long(*long),
|
||||
NbtTag::Float(float) => crate::owned::NbtTag::Float(*float),
|
||||
NbtTag::Double(double) => crate::owned::NbtTag::Double(*double),
|
||||
NbtTag::ByteArray(byte_array) => crate::owned::NbtTag::ByteArray(byte_array.to_vec()),
|
||||
NbtTag::String(string) => crate::owned::NbtTag::String((*string).to_owned()),
|
||||
NbtTag::List(list) => crate::owned::NbtTag::List(list.to_owned()),
|
||||
NbtTag::Compound(compound) => crate::owned::NbtTag::Compound(compound.to_owned()),
|
||||
NbtTag::IntArray(int_array) => crate::owned::NbtTag::IntArray(int_array.to_vec()),
|
||||
NbtTag::LongArray(long_array) => crate::owned::NbtTag::LongArray(long_array.to_vec()),
|
||||
let (kind, _value) = self.element();
|
||||
|
||||
match kind {
|
||||
TapeTagKind::Byte => crate::owned::NbtTag::Byte(self.byte().unwrap()),
|
||||
TapeTagKind::Short => crate::owned::NbtTag::Short(self.short().unwrap()),
|
||||
TapeTagKind::Int => crate::owned::NbtTag::Int(self.int().unwrap()),
|
||||
TapeTagKind::Long => crate::owned::NbtTag::Long(self.long().unwrap()),
|
||||
TapeTagKind::Float => crate::owned::NbtTag::Float(self.float().unwrap()),
|
||||
TapeTagKind::Double => crate::owned::NbtTag::Double(self.double().unwrap()),
|
||||
TapeTagKind::ByteArray => {
|
||||
crate::owned::NbtTag::ByteArray(self.byte_array().unwrap().to_vec())
|
||||
}
|
||||
TapeTagKind::String => crate::owned::NbtTag::String(self.string().unwrap().to_owned()),
|
||||
TapeTagKind::Compound => {
|
||||
crate::owned::NbtTag::Compound(self.compound().unwrap().to_owned())
|
||||
}
|
||||
_ if kind.is_list() => crate::owned::NbtTag::List(self.list().unwrap().to_owned()),
|
||||
TapeTagKind::IntArray => crate::owned::NbtTag::IntArray(self.int_array().unwrap()),
|
||||
TapeTagKind::LongArray => crate::owned::NbtTag::LongArray(self.long_array().unwrap()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NbtTag<'_, '_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let (self_kind, _) = self.element();
|
||||
let (other_kind, _) = other.element();
|
||||
if self_kind != other_kind {
|
||||
return false;
|
||||
}
|
||||
match self_kind {
|
||||
TapeTagKind::Byte => self.byte().unwrap() == other.byte().unwrap(),
|
||||
TapeTagKind::Short => self.short().unwrap() == other.short().unwrap(),
|
||||
TapeTagKind::Int => self.int().unwrap() == other.int().unwrap(),
|
||||
TapeTagKind::Long => self.long().unwrap() == other.long().unwrap(),
|
||||
TapeTagKind::Float => self.float().unwrap() == other.float().unwrap(),
|
||||
TapeTagKind::Double => self.double().unwrap() == other.double().unwrap(),
|
||||
TapeTagKind::ByteArray => self.byte_array().unwrap() == other.byte_array().unwrap(),
|
||||
TapeTagKind::String => self.string().unwrap() == other.string().unwrap(),
|
||||
TapeTagKind::Compound => self.compound().unwrap() == other.compound().unwrap(),
|
||||
TapeTagKind::IntArray => self.int_array().unwrap() == other.int_array().unwrap(),
|
||||
TapeTagKind::LongArray => self.long_array().unwrap() == other.long_array().unwrap(),
|
||||
t if t.is_list() => self.list().unwrap() == other.list().unwrap(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -411,14 +520,14 @@ impl<'a, 'b> NbtTag<'a> {
|
|||
mod tests {
|
||||
use std::io::Read;
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
use byteorder::{WriteBytesExt, BE};
|
||||
use flate2::read::GzDecoder;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let nbt = Nbt::read(&mut Cursor::new(include_bytes!(
|
||||
let nbt = super::read(&mut Cursor::new(include_bytes!(
|
||||
"../../tests/hello_world.nbt"
|
||||
)))
|
||||
.unwrap()
|
||||
|
@ -438,7 +547,9 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
let nbt = Nbt::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(nbt.int("PersistentId"), Some(1946940766));
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
|
@ -451,7 +562,9 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
let nbt = Nbt::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
|
@ -460,7 +573,7 @@ mod tests {
|
|||
#[test]
|
||||
fn read_hypixel() {
|
||||
let src = include_bytes!("../../tests/hypixel.nbt").to_vec();
|
||||
let _nbt = Nbt::read(&mut Cursor::new(&src[..])).unwrap().unwrap();
|
||||
let _nbt = super::read(&mut Cursor::new(&src[..])).unwrap().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -470,11 +583,14 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
let nbt = Nbt::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let mut out = Vec::new();
|
||||
nbt.write(&mut out);
|
||||
let nbt = Nbt::read(&mut Cursor::new(&out)).unwrap().unwrap();
|
||||
|
||||
let nbt = super::read(&mut Cursor::new(&out)).unwrap().unwrap();
|
||||
|
||||
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
|
@ -482,7 +598,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn inttest_1023() {
|
||||
let nbt = Nbt::read(&mut Cursor::new(include_bytes!(
|
||||
let nbt = super::read(&mut Cursor::new(include_bytes!(
|
||||
"../../tests/inttest1023.nbt"
|
||||
)))
|
||||
.unwrap()
|
||||
|
@ -510,7 +626,7 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().ints().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i32, item);
|
||||
|
@ -532,7 +648,7 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().ints().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i32, item);
|
||||
|
@ -554,7 +670,7 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().longs().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i64, item);
|
||||
|
@ -571,12 +687,12 @@ mod tests {
|
|||
data.write_u16::<BE>(0).unwrap(); // first element name length
|
||||
// eof
|
||||
|
||||
let res = Nbt::read(&mut Cursor::new(&data));
|
||||
let res = super::read(&mut Cursor::new(&data));
|
||||
assert_eq!(res, Err(Error::UnexpectedEof));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_complexplayer_with_given_alloc() {
|
||||
fn read_complex_player_as_tag() {
|
||||
let src = include_bytes!("../../tests/complex_player.dat").to_vec();
|
||||
let mut src_slice = src.as_slice();
|
||||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
|
@ -589,9 +705,32 @@ mod tests {
|
|||
decoded_src_as_tag.push(END_ID);
|
||||
|
||||
let nbt = super::read_tag(&mut Cursor::new(&decoded_src_as_tag)).unwrap();
|
||||
let nbt = nbt.compound().unwrap().compound("").unwrap();
|
||||
let nbt = nbt.as_tag().compound().unwrap().compound("").unwrap();
|
||||
|
||||
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_byte_array() {
|
||||
// found from fuzzing
|
||||
let data = [10, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0];
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
nbt.as_compound().to_owned();
|
||||
}
|
||||
#[test]
|
||||
fn list_of_empty_lists() {
|
||||
// found from fuzzing
|
||||
// BaseNbt { name: m"", tag: NbtTag::NbtCompound { m"": NbtTag::List(List::List([List::Empty])) } }
|
||||
let data = [10, 0, 0, 9, 0, 0, 9, 0, 0, 0, 1, 0, 9, 0, 0, 0, 0];
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
nbt.as_compound().to_owned();
|
||||
}
|
||||
#[test]
|
||||
fn list_of_byte_arrays() {
|
||||
// BaseNbt { name: m"", tag: NbtCompound { values: [(m"", List(List([List::ByteArray([])])))] } }
|
||||
let data = [10, 0, 0, 9, 0, 0, 9, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0];
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
nbt.as_compound().to_owned();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,325 +0,0 @@
|
|||
//! Some tags, like compounds and arrays, contain other tags. The naive approach would be to just
|
||||
//! use `Vec`s or `HashMap`s, but this is inefficient and leads to many small allocations.
|
||||
//!
|
||||
//! Instead, the idea for this is essentially that we'd have two big Vec for every tag (one for
|
||||
//! named tags and one for unnamed tags), and then compounds/arrays simply contain a slice of this
|
||||
//! vec.
|
||||
//!
|
||||
//! This almost works. but there's two main issues:
|
||||
//! - compounds aren't length-prefixed, so we can't pre-allocate at the beginning of compounds for
|
||||
//! the rest of that compound
|
||||
//! - resizing a vec might move it in memory, invalidating all of our slices to it
|
||||
//!
|
||||
//! solving the first problem isn't that hard, since we can have a separate vec for every "depth"
|
||||
//! (so compounds in compounds don't share the same vec).
|
||||
//! to solve the second problem, i chose to implement a special data structure
|
||||
//! that relies on low-level allocations so we can guarantee that our allocations don't move in memory.
|
||||
|
||||
use std::{
|
||||
alloc::{self, Layout},
|
||||
cell::UnsafeCell,
|
||||
fmt,
|
||||
mem::MaybeUninit,
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
use crate::{raw_list::RawList, Mutf8Str};
|
||||
|
||||
use super::{NbtCompound, NbtList, NbtTag};
|
||||
|
||||
// this value appears to have the best results on my pc when testing with complex_player.dat
|
||||
const MIN_ALLOC_SIZE: usize = 1024;
|
||||
|
||||
/// The data structure that contains all the parsed NBT tags. This must stay in scope for as long
|
||||
/// as the borrowed NBT exists.
|
||||
#[derive(Default)]
|
||||
pub struct TagAllocator<'a>(UnsafeCell<TagAllocatorImpl<'a>>);
|
||||
impl<'a> TagAllocator<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self(UnsafeCell::new(TagAllocatorImpl::new()))
|
||||
}
|
||||
// shhhhh
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn get(&self) -> &mut TagAllocatorImpl<'a> {
|
||||
unsafe { self.0.get().as_mut().unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TagAllocatorImpl<'a> {
|
||||
pub named: IndividualTagAllocatorWithDepth<(&'a Mutf8Str, NbtTag<'a>)>,
|
||||
|
||||
// lists of lists
|
||||
pub unnamed_list: IndividualTagAllocatorWithDepth<NbtList<'a>>,
|
||||
// lists of compounds
|
||||
pub unnamed_compound: IndividualTagAllocatorWithDepth<NbtCompound<'a>>,
|
||||
|
||||
pub unnamed_bytearray: IndividualTagAllocator<&'a [u8]>,
|
||||
pub unnamed_string: IndividualTagAllocator<&'a Mutf8Str>,
|
||||
pub unnamed_intarray: IndividualTagAllocator<RawList<'a, i32>>,
|
||||
pub unnamed_longarray: IndividualTagAllocator<RawList<'a, i64>>,
|
||||
}
|
||||
|
||||
impl<'a> TagAllocatorImpl<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IndividualTagAllocator<T> {
|
||||
current: TagsAllocation<T>,
|
||||
// we keep track of old allocations so we can deallocate them later
|
||||
previous: Vec<TagsAllocation<T>>,
|
||||
}
|
||||
impl<T> IndividualTagAllocator<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
#[inline]
|
||||
pub fn start(&mut self) -> ContiguousTagsAllocator<T> {
|
||||
let alloc = self.current.clone();
|
||||
|
||||
start_allocating_tags(alloc)
|
||||
}
|
||||
#[inline]
|
||||
pub fn finish<'a>(&mut self, alloc: ContiguousTagsAllocator<T>) -> &'a [T] {
|
||||
finish_allocating_tags(alloc, &mut self.current, &mut self.previous)
|
||||
}
|
||||
}
|
||||
impl<T> Default for IndividualTagAllocator<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
current: Default::default(),
|
||||
previous: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> Drop for IndividualTagAllocator<T> {
|
||||
fn drop(&mut self) {
|
||||
self.current.deallocate();
|
||||
self.previous
|
||||
.iter_mut()
|
||||
.for_each(TagsAllocation::deallocate);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IndividualTagAllocatorWithDepth<T> {
|
||||
// it's a vec because of the depth thing mentioned earlier, index in the vec = depth
|
||||
current: Vec<TagsAllocation<T>>,
|
||||
// we also have to keep track of old allocations so we can deallocate them later
|
||||
previous: Vec<Vec<TagsAllocation<T>>>,
|
||||
}
|
||||
impl<T> IndividualTagAllocatorWithDepth<T>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
#[inline]
|
||||
pub fn start(&mut self, depth: usize) -> ContiguousTagsAllocator<T> {
|
||||
// make sure we have enough space for this depth
|
||||
// (also note that depth is reused for compounds and arrays so we might have to push
|
||||
// more than once)
|
||||
for _ in self.current.len()..=depth {
|
||||
self.current.push(Default::default());
|
||||
self.previous.push(Default::default());
|
||||
}
|
||||
|
||||
let alloc = self.current[depth].clone();
|
||||
|
||||
start_allocating_tags(alloc)
|
||||
}
|
||||
#[inline]
|
||||
pub fn finish<'a>(&mut self, alloc: ContiguousTagsAllocator<T>, depth: usize) -> &'a [T] {
|
||||
finish_allocating_tags(alloc, &mut self.current[depth], &mut self.previous[depth])
|
||||
}
|
||||
}
|
||||
impl<T> Default for IndividualTagAllocatorWithDepth<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
current: Default::default(),
|
||||
previous: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> Drop for IndividualTagAllocatorWithDepth<T> {
|
||||
fn drop(&mut self) {
|
||||
self.current.iter_mut().for_each(TagsAllocation::deallocate);
|
||||
self.previous
|
||||
.iter_mut()
|
||||
.flatten()
|
||||
.for_each(TagsAllocation::deallocate);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn start_allocating_tags<T>(alloc: TagsAllocation<T>) -> ContiguousTagsAllocator<T> {
|
||||
let is_new_allocation = alloc.cap == 0;
|
||||
ContiguousTagsAllocator {
|
||||
alloc,
|
||||
is_new_allocation,
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn finish_allocating_tags<'a, T>(
|
||||
alloc: ContiguousTagsAllocator<T>,
|
||||
current_alloc: &mut TagsAllocation<T>,
|
||||
previous_allocs: &mut Vec<TagsAllocation<T>>,
|
||||
) -> &'a [T] {
|
||||
let slice = unsafe {
|
||||
std::slice::from_raw_parts(
|
||||
alloc
|
||||
.alloc
|
||||
.ptr
|
||||
.as_ptr()
|
||||
.add(alloc.alloc.len)
|
||||
.sub(alloc.size),
|
||||
alloc.size,
|
||||
)
|
||||
};
|
||||
|
||||
let previous_allocation_at_that_depth = std::mem::replace(current_alloc, alloc.alloc);
|
||||
if alloc.is_new_allocation {
|
||||
previous_allocs.push(previous_allocation_at_that_depth);
|
||||
}
|
||||
|
||||
slice
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TagsAllocation<T> {
|
||||
ptr: NonNull<T>,
|
||||
cap: usize,
|
||||
len: usize,
|
||||
}
|
||||
impl<T> Default for TagsAllocation<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
ptr: NonNull::dangling(),
|
||||
cap: 0,
|
||||
len: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> TagsAllocation<T> {
|
||||
fn deallocate(&mut self) {
|
||||
if self.cap == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// call drop on the tags too
|
||||
unsafe {
|
||||
std::ptr::drop_in_place(std::slice::from_raw_parts_mut(
|
||||
self.ptr.as_ptr().cast::<T>(),
|
||||
self.len,
|
||||
));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
alloc::dealloc(
|
||||
self.ptr.as_ptr().cast(),
|
||||
Layout::array::<T>(self.cap).unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is created when we start allocating a compound tag
|
||||
pub struct ContiguousTagsAllocator<T> {
|
||||
alloc: TagsAllocation<T>,
|
||||
/// whether we created a new allocation for this compound (as opposed to reusing an existing
|
||||
/// one).
|
||||
/// this is used to determine whether we're allowed to deallocate it when growing, and whether
|
||||
/// we should add this allocation to `all_allocations`
|
||||
is_new_allocation: bool,
|
||||
/// the size of this individual compound allocation. the size of the full allocation is in
|
||||
/// `alloc.len`.
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl<T> ContiguousTagsAllocator<T> {
|
||||
/// Grow the capacity to the new amount.
|
||||
///
|
||||
/// # Safety
|
||||
/// Must be at least the current capacity.
|
||||
unsafe fn grow_to(&mut self, new_cap: usize) {
|
||||
debug_assert!(new_cap >= self.alloc.cap, "{new_cap} < {}", self.alloc.cap);
|
||||
|
||||
let new_layout = Layout::array::<T>(new_cap).unwrap();
|
||||
|
||||
let new_ptr = if self.is_new_allocation && self.alloc.ptr != NonNull::dangling() {
|
||||
let old_ptr = self.alloc.ptr.as_ptr();
|
||||
let old_cap = self.alloc.cap;
|
||||
let old_layout = Layout::array::<T>(old_cap).unwrap();
|
||||
unsafe { alloc::realloc(old_ptr as *mut u8, old_layout, new_cap) }
|
||||
} else {
|
||||
self.is_new_allocation = true;
|
||||
unsafe { alloc::alloc(new_layout) }
|
||||
} as *mut T;
|
||||
|
||||
// copy the last `size` elements from the old allocation to the new one
|
||||
unsafe {
|
||||
std::ptr::copy_nonoverlapping(
|
||||
self.alloc.ptr.as_ptr().sub(self.size),
|
||||
new_ptr,
|
||||
self.size,
|
||||
)
|
||||
};
|
||||
|
||||
self.alloc.ptr = NonNull::new(new_ptr).unwrap();
|
||||
self.alloc.cap = new_cap;
|
||||
self.alloc.len = self.size;
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn grow(&mut self) {
|
||||
let new_cap = if self.is_new_allocation {
|
||||
// this makes sure we don't allocate 0 bytes
|
||||
std::cmp::max(self.alloc.cap * 2, MIN_ALLOC_SIZE)
|
||||
} else {
|
||||
// reuse the previous cap, since it's not unlikely that we'll have another compound
|
||||
// with a similar
|
||||
self.alloc.cap
|
||||
};
|
||||
|
||||
unsafe { self.grow_to(new_cap) };
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn allocate(&mut self, size: usize) -> &mut [MaybeUninit<T>] {
|
||||
// check if we need to reallocate
|
||||
if self.alloc.len + size > self.alloc.cap {
|
||||
// unsafe { self.grow_to(self.size + size) }
|
||||
let new_cap = std::cmp::max(self.alloc.cap, self.size + size);
|
||||
unsafe { self.grow_to(new_cap) }
|
||||
}
|
||||
|
||||
let start = unsafe { self.alloc.ptr.as_ptr().add(self.alloc.len) };
|
||||
|
||||
self.alloc.len += size;
|
||||
self.size += size;
|
||||
|
||||
unsafe { std::slice::from_raw_parts_mut(start.cast(), size) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn push(&mut self, value: T) {
|
||||
// check if we need to reallocate
|
||||
if self.alloc.len == self.alloc.cap {
|
||||
self.grow();
|
||||
}
|
||||
|
||||
// push the new tag
|
||||
unsafe {
|
||||
let end = self.alloc.ptr.as_ptr().add(self.alloc.len);
|
||||
std::ptr::write(end, value);
|
||||
}
|
||||
self.alloc.len += 1;
|
||||
self.size += 1;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for TagAllocator<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TagAllocator").finish()
|
||||
}
|
||||
}
|
270
simdnbt/src/borrow/tape.rs
Normal file
270
simdnbt/src/borrow/tape.rs
Normal file
|
@ -0,0 +1,270 @@
|
|||
use std::fmt::{self, Debug};
|
||||
|
||||
use crate::common::{
|
||||
BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LONG_ARRAY_ID,
|
||||
LONG_ID, SHORT_ID, STRING_ID,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MainTape {
|
||||
elements: Vec<TapeElement>,
|
||||
}
|
||||
impl MainTape {
|
||||
#[inline]
|
||||
pub fn push(&mut self, element: TapeElement) {
|
||||
self.elements.push(element);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.elements.len()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_unchecked_mut(&mut self, index: usize) -> &mut TapeElement {
|
||||
self.elements.get_unchecked_mut(index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *const TapeElement {
|
||||
self.elements.as_ptr()
|
||||
}
|
||||
}
|
||||
impl Default for MainTape {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
elements: Vec::with_capacity(1024),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub union TapeElement {
|
||||
pub kind: (TapeTagKind, TapeTagValue),
|
||||
|
||||
pub long: i64,
|
||||
pub double: f64,
|
||||
|
||||
pub name: u64, // pointer to the original data
|
||||
}
|
||||
impl TapeElement {
|
||||
/// Returns how much we should increment the tape index to get to the next tag.
|
||||
///
|
||||
/// # Safety
|
||||
/// The element must be a tag and not something else like a continuation of a long or double.
|
||||
pub unsafe fn skip_offset(&self) -> usize {
|
||||
match self.kind {
|
||||
(TapeTagKind::Long | TapeTagKind::Double, _) => 2,
|
||||
(
|
||||
TapeTagKind::Compound,
|
||||
TapeTagValue {
|
||||
compound: (_, offset),
|
||||
},
|
||||
) => u32::from(offset) as usize,
|
||||
(
|
||||
TapeTagKind::ListList,
|
||||
TapeTagValue {
|
||||
list_list: (_, offset),
|
||||
},
|
||||
) => u32::from(offset) as usize,
|
||||
(
|
||||
TapeTagKind::CompoundList,
|
||||
TapeTagValue {
|
||||
compound_list: (_, offset),
|
||||
},
|
||||
) => u32::from(offset) as usize,
|
||||
_ => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Debug for TapeElement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// just writes the u64
|
||||
write!(f, "TapeElement({:#016x})", unsafe { self.name })?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union TapeTagValue {
|
||||
pub byte: i8,
|
||||
pub short: i16,
|
||||
pub int: i32,
|
||||
pub long: (), // value is in next tape element
|
||||
pub float: f32,
|
||||
pub double: (), // value is in next tape element
|
||||
pub byte_array: u56, // pointer to the original data
|
||||
pub string: u56, // pointer to the original data
|
||||
pub compound: (u24, UnalignedU32), // length estimate + tape index offset to the end of the compound
|
||||
pub int_array: u56, // pointer to the original data
|
||||
pub long_array: u56, // pointer to the original data
|
||||
|
||||
// lists
|
||||
pub empty_list: (),
|
||||
pub byte_list: u56, // pointer to the original data
|
||||
pub short_list: u56, // pointer to the original data
|
||||
pub int_list: u56, // pointer to the original data
|
||||
pub long_list: u56, // pointer to the original data
|
||||
pub float_list: u56, // pointer to the original data
|
||||
pub double_list: u56, // pointer to the original data
|
||||
pub byte_array_list: (u24, UnalignedU32), // padding + index to ExtraTapes which has a fat pointer that points to the original data
|
||||
pub string_list: (u24, UnalignedU32), // padding + index to ExtraTapes
|
||||
pub list_list: (u24, UnalignedU32), // length estimate + tape index offset to the end of the list
|
||||
pub compound_list: (u24, UnalignedU32), // length estimate + tape index offset to the end of the list
|
||||
pub int_array_list: (u24, UnalignedU32), // padding + index to ExtraTapes
|
||||
pub long_array_list: (u24, UnalignedU32), // padding + index to ExtraTapes
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct u56 {
|
||||
a: u8,
|
||||
b: u16,
|
||||
c: u32,
|
||||
}
|
||||
impl From<u64> for u56 {
|
||||
#[inline]
|
||||
fn from(value: u64) -> Self {
|
||||
let a = (value >> 48) as u8;
|
||||
let b = (value >> 32) as u16;
|
||||
let c = value as u32;
|
||||
Self { a, b, c }
|
||||
}
|
||||
}
|
||||
impl From<u56> for u64 {
|
||||
#[inline]
|
||||
fn from(value: u56) -> Self {
|
||||
let a = value.a as u64;
|
||||
let b = value.b as u64;
|
||||
let c = value.c as u64;
|
||||
(a << 48) | (b << 32) | c
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct u24 {
|
||||
a: u8,
|
||||
b: u16,
|
||||
}
|
||||
impl From<u32> for u24 {
|
||||
#[inline]
|
||||
fn from(value: u32) -> Self {
|
||||
let a = (value >> 16) as u8;
|
||||
let b = value as u16;
|
||||
Self { a, b }
|
||||
}
|
||||
}
|
||||
impl From<u24> for u32 {
|
||||
#[inline]
|
||||
fn from(value: u24) -> Self {
|
||||
let a = value.a as u32;
|
||||
let b = value.b as u32;
|
||||
(a << 16) | b
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
pub struct UnalignedU32(pub u32);
|
||||
impl From<u32> for UnalignedU32 {
|
||||
#[inline]
|
||||
fn from(value: u32) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl From<UnalignedU32> for u32 {
|
||||
#[inline]
|
||||
fn from(value: UnalignedU32) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
pub struct UnalignedU16(pub u16);
|
||||
impl From<u16> for UnalignedU16 {
|
||||
#[inline]
|
||||
fn from(value: u16) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl From<UnalignedU16> for u16 {
|
||||
#[inline]
|
||||
fn from(value: UnalignedU16) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum TapeTagKind {
|
||||
Byte = BYTE_ID,
|
||||
Short = SHORT_ID,
|
||||
Int = INT_ID,
|
||||
Long = LONG_ID,
|
||||
Float = FLOAT_ID,
|
||||
Double = DOUBLE_ID,
|
||||
ByteArray = BYTE_ARRAY_ID,
|
||||
String = STRING_ID,
|
||||
Compound = COMPOUND_ID,
|
||||
IntArray = INT_ARRAY_ID,
|
||||
LongArray = LONG_ARRAY_ID,
|
||||
|
||||
EmptyList,
|
||||
ByteList,
|
||||
ShortList,
|
||||
IntList,
|
||||
LongList,
|
||||
FloatList,
|
||||
DoubleList,
|
||||
ByteArrayList,
|
||||
StringList,
|
||||
ListList,
|
||||
CompoundList,
|
||||
IntArrayList,
|
||||
LongArrayList,
|
||||
}
|
||||
|
||||
impl TapeTagKind {
|
||||
pub fn is_list(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
TapeTagKind::EmptyList
|
||||
| TapeTagKind::ByteList
|
||||
| TapeTagKind::ShortList
|
||||
| TapeTagKind::IntList
|
||||
| TapeTagKind::LongList
|
||||
| TapeTagKind::FloatList
|
||||
| TapeTagKind::DoubleList
|
||||
| TapeTagKind::ByteArrayList
|
||||
| TapeTagKind::StringList
|
||||
| TapeTagKind::ListList
|
||||
| TapeTagKind::CompoundList
|
||||
| TapeTagKind::IntArrayList
|
||||
| TapeTagKind::LongArrayList
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_u56() {
|
||||
// top 8 bits are cut off
|
||||
let value = 0x1234_5678_9abc_def0;
|
||||
let u56 { a, b, c } = u56::from(value);
|
||||
assert_eq!(a, 0x34);
|
||||
assert_eq!(b, 0x5678);
|
||||
assert_eq!(c, 0x9abc_def0);
|
||||
|
||||
let value: u64 = u56 { a, b, c }.into();
|
||||
assert_eq!(value, 0x34_5678_9abc_def0);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use std::{io::Cursor, mem, slice};
|
||||
use std::{mem, slice};
|
||||
|
||||
use crate::{
|
||||
raw_list::RawList,
|
||||
reader::Reader,
|
||||
swap_endianness::{swap_endianness_as_u8, SwappableNumber},
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
@ -23,83 +24,37 @@ pub const LONG_ARRAY_ID: u8 = 12;
|
|||
pub const MAX_DEPTH: usize = 512;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_u32(data: &mut Cursor<&[u8]>) -> Result<u32, Error> {
|
||||
let remaining_slice = &data.get_ref()[data.position() as usize..data.get_ref().len()];
|
||||
if remaining_slice.len() < 4 {
|
||||
return Err(Error::UnexpectedEof);
|
||||
}
|
||||
|
||||
data.set_position(data.position() + 4);
|
||||
|
||||
Ok(u32::from_be_bytes([
|
||||
remaining_slice[0],
|
||||
remaining_slice[1],
|
||||
remaining_slice[2],
|
||||
remaining_slice[3],
|
||||
]))
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn read_u16(data: &mut Cursor<&[u8]>) -> Result<u16, Error> {
|
||||
let remaining_slice = &data.get_ref()[data.position() as usize..data.get_ref().len()];
|
||||
if remaining_slice.len() < 2 {
|
||||
return Err(Error::UnexpectedEof);
|
||||
}
|
||||
|
||||
data.set_position(data.position() + 2);
|
||||
|
||||
Ok(u16::from_be_bytes([remaining_slice[0], remaining_slice[1]]))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn read_with_u16_length<'a>(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
width: usize,
|
||||
) -> Result<&'a [u8], Error> {
|
||||
let length = read_u16(data)?;
|
||||
pub fn read_with_u16_length<'a>(data: &mut Reader<'a>, width: usize) -> Result<&'a [u8], Error> {
|
||||
let length = data.read_u16()?;
|
||||
let length_in_bytes = length as usize * width;
|
||||
// make sure we don't read more than the length
|
||||
if data.get_ref().len() < data.position() as usize + length_in_bytes {
|
||||
return Err(Error::UnexpectedEof);
|
||||
}
|
||||
let start_position = data.position() as usize;
|
||||
data.set_position(data.position() + length_in_bytes as u64);
|
||||
Ok(&data.get_ref()[start_position..start_position + length_in_bytes])
|
||||
data.read_slice(length_in_bytes)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn read_with_u32_length<'a>(
|
||||
data: &mut Cursor<&'a [u8]>,
|
||||
width: usize,
|
||||
) -> Result<&'a [u8], Error> {
|
||||
let length = read_u32(data)?;
|
||||
pub fn read_with_u32_length<'a>(data: &mut Reader<'a>, width: usize) -> Result<&'a [u8], Error> {
|
||||
let length = data.read_u32()?;
|
||||
let length_in_bytes = length as usize * width;
|
||||
// make sure we don't read more than the length
|
||||
if data.get_ref().len() < data.position() as usize + length_in_bytes {
|
||||
return Err(Error::UnexpectedEof);
|
||||
}
|
||||
let start_position = data.position() as usize;
|
||||
data.set_position(data.position() + length_in_bytes as u64);
|
||||
Ok(&data.get_ref()[start_position..start_position + length_in_bytes])
|
||||
data.read_slice(length_in_bytes)
|
||||
}
|
||||
|
||||
pub fn read_string<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a Mutf8Str, Error> {
|
||||
pub fn read_string<'a>(data: &mut Reader<'a>) -> Result<&'a Mutf8Str, Error> {
|
||||
let data = read_with_u16_length(data, 1)?;
|
||||
Ok(Mutf8Str::from_slice(data))
|
||||
}
|
||||
|
||||
pub fn read_u8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [u8], Error> {
|
||||
pub fn read_u8_array<'a>(data: &mut Reader<'a>) -> Result<&'a [u8], Error> {
|
||||
read_with_u32_length(data, 1)
|
||||
}
|
||||
pub fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<&'a [i8], Error> {
|
||||
pub fn read_i8_array<'a>(data: &mut Reader<'a>) -> Result<&'a [i8], Error> {
|
||||
Ok(slice_u8_into_i8(read_u8_array(data)?))
|
||||
}
|
||||
|
||||
pub fn read_int_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i32>, Error> {
|
||||
pub fn read_int_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i32>, Error> {
|
||||
let array_bytes = read_with_u32_length(data, 4)?;
|
||||
Ok(RawList::new(array_bytes))
|
||||
}
|
||||
|
||||
pub fn read_long_array<'a>(data: &mut Cursor<&'a [u8]>) -> Result<RawList<'a, i64>, Error> {
|
||||
pub fn read_long_array<'a>(data: &mut Reader<'a>) -> Result<RawList<'a, i64>, Error> {
|
||||
let array_bytes = read_with_u32_length(data, 8)?;
|
||||
Ok(RawList::new(array_bytes))
|
||||
}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
#![feature(portable_simd)]
|
||||
#![feature(array_chunks)]
|
||||
#![allow(internal_features)]
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
#[cfg(not(target_pointer_width = "64"))]
|
||||
compile_error!("simdnbt only supports 64-bit platforms");
|
||||
|
||||
pub mod borrow;
|
||||
mod common;
|
||||
|
@ -8,6 +13,7 @@ mod error;
|
|||
mod mutf8;
|
||||
pub mod owned;
|
||||
pub mod raw_list;
|
||||
mod reader;
|
||||
pub mod swap_endianness;
|
||||
mod traits;
|
||||
|
||||
|
|
|
@ -135,13 +135,15 @@ impl fmt::Display for Mutf8Str {
|
|||
|
||||
impl fmt::Debug for Mutf8Str {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Mutf8Str").field(&self.to_str()).finish()
|
||||
f.write_str("m")?;
|
||||
fmt::Debug::fmt(&self.to_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Mutf8String {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Mutf8String").field(&self.to_str()).finish()
|
||||
f.write_str("m")?;
|
||||
fmt::Debug::fmt(&self.to_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
use std::{
|
||||
io::Cursor,
|
||||
mem::{self, MaybeUninit},
|
||||
};
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
use std::mem::{self, MaybeUninit};
|
||||
|
||||
use crate::{
|
||||
common::{read_string, unchecked_push, unchecked_write_string, END_ID, MAX_DEPTH},
|
||||
mutf8::Mutf8String,
|
||||
reader::Reader,
|
||||
Error, Mutf8Str, ToNbtTag,
|
||||
};
|
||||
|
||||
|
@ -28,12 +24,12 @@ impl NbtCompound {
|
|||
Self { values }
|
||||
}
|
||||
|
||||
pub(crate) fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
|
||||
pub(crate) fn read(data: &mut Reader<'_>) -> Result<Self, Error> {
|
||||
Self::read_with_depth(data, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn read_with_depth_and_capacity(
|
||||
data: &mut Cursor<&[u8]>,
|
||||
data: &mut Reader<'_>,
|
||||
depth: usize,
|
||||
capacity: usize,
|
||||
) -> Result<Self, Error> {
|
||||
|
@ -73,7 +69,7 @@ impl NbtCompound {
|
|||
Ok(Self { values })
|
||||
}
|
||||
|
||||
pub(crate) fn read_with_depth(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
pub(crate) fn read_with_depth(data: &mut Reader<'_>, depth: usize) -> Result<Self, Error> {
|
||||
Self::read_with_depth_and_capacity(data, depth, 8)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
use std::io::Cursor;
|
||||
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
read_i8_array, read_int_array, read_long_array, read_string, read_u8_array,
|
||||
|
@ -11,11 +7,12 @@ use crate::{
|
|||
LONG_ID, SHORT_ID, STRING_ID,
|
||||
},
|
||||
mutf8::Mutf8String,
|
||||
reader::Reader,
|
||||
swap_endianness::swap_endianness,
|
||||
Error,
|
||||
};
|
||||
|
||||
use super::{compound::NbtCompound, read_u32, MAX_DEPTH};
|
||||
use super::{compound::NbtCompound, MAX_DEPTH};
|
||||
|
||||
/// A list of NBT tags of a single type.
|
||||
#[repr(u8)]
|
||||
|
@ -37,14 +34,14 @@ pub enum NbtList {
|
|||
LongArray(Vec<Vec<i64>>) = LONG_ARRAY_ID,
|
||||
}
|
||||
impl NbtList {
|
||||
pub(crate) fn read(data: &mut Cursor<&[u8]>, depth: usize) -> Result<Self, Error> {
|
||||
pub(crate) fn read(data: &mut Reader<'_>, depth: usize) -> Result<Self, Error> {
|
||||
if depth > MAX_DEPTH {
|
||||
return Err(Error::MaxDepthExceeded);
|
||||
}
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
Ok(match tag_type {
|
||||
END_ID => {
|
||||
data.set_position(data.position() + 4);
|
||||
data.skip(4)?;
|
||||
NbtList::Empty
|
||||
}
|
||||
BYTE_ID => NbtList::Byte(read_i8_array(data)?.to_owned()),
|
||||
|
@ -54,7 +51,7 @@ impl NbtList {
|
|||
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)?;
|
||||
let length = data.read_u32()?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut arrays = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
|
@ -63,7 +60,7 @@ impl NbtList {
|
|||
arrays
|
||||
}),
|
||||
STRING_ID => NbtList::String({
|
||||
let length = read_u32(data)?;
|
||||
let length = data.read_u32()?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut strings = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
|
@ -72,7 +69,7 @@ impl NbtList {
|
|||
strings
|
||||
}),
|
||||
LIST_ID => NbtList::List({
|
||||
let length = read_u32(data)?;
|
||||
let length = data.read_u32()?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut lists = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
|
@ -81,7 +78,7 @@ impl NbtList {
|
|||
lists
|
||||
}),
|
||||
COMPOUND_ID => NbtList::Compound({
|
||||
let length = read_u32(data)?;
|
||||
let length = data.read_u32()?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut compounds = Vec::with_capacity(length.min(128) as usize);
|
||||
let mut capacity: usize = 8;
|
||||
|
@ -93,7 +90,7 @@ impl NbtList {
|
|||
compounds
|
||||
}),
|
||||
INT_ARRAY_ID => NbtList::IntArray({
|
||||
let length = read_u32(data)?;
|
||||
let length = data.read_u32()?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut arrays = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
|
@ -102,7 +99,7 @@ impl NbtList {
|
|||
arrays
|
||||
}),
|
||||
LONG_ARRAY_ID => NbtList::LongArray({
|
||||
let length = read_u32(data)?;
|
||||
let length = data.read_u32()?;
|
||||
// arbitrary number to prevent big allocations
|
||||
let mut arrays = Vec::with_capacity(length.min(128) as usize);
|
||||
for _ in 0..length {
|
||||
|
|
|
@ -6,16 +6,15 @@ mod list;
|
|||
|
||||
use std::{io::Cursor, ops::Deref};
|
||||
|
||||
use byteorder::{ReadBytesExt, BE};
|
||||
|
||||
use crate::{
|
||||
common::{
|
||||
read_int_array, read_long_array, read_string, read_u32, read_with_u32_length,
|
||||
read_int_array, read_long_array, read_string, read_with_u32_length,
|
||||
slice_into_u8_big_endian, unchecked_extend, unchecked_push, write_string, BYTE_ARRAY_ID,
|
||||
BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID,
|
||||
LONG_ARRAY_ID, LONG_ID, MAX_DEPTH, SHORT_ID, STRING_ID,
|
||||
},
|
||||
mutf8::Mutf8String,
|
||||
reader::{Reader, ReaderFromCursor},
|
||||
Error, Mutf8Str,
|
||||
};
|
||||
|
||||
|
@ -25,7 +24,8 @@ pub use self::{compound::NbtCompound, list::NbtList};
|
|||
///
|
||||
/// Returns `Ok(Nbt::None)` if there is no data.
|
||||
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
|
||||
Nbt::read(data)
|
||||
let mut reader = ReaderFromCursor::new(data);
|
||||
Nbt::read(&mut reader)
|
||||
}
|
||||
/// Read a root NBT compound, but without reading the name. This is used in Minecraft when reading
|
||||
/// NBT over the network.
|
||||
|
@ -33,22 +33,26 @@ pub fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
|
|||
/// This is similar to [`read_tag`], but returns an [`Nbt`] instead (guaranteeing it'll be either
|
||||
/// empty or a compound).
|
||||
pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
|
||||
Nbt::read_unnamed(data)
|
||||
let mut reader = ReaderFromCursor::new(data);
|
||||
Nbt::read_unnamed(&mut reader)
|
||||
}
|
||||
/// Read a compound tag. This may have any number of items.
|
||||
pub fn read_compound(data: &mut Cursor<&[u8]>) -> Result<NbtCompound, Error> {
|
||||
NbtCompound::read(data)
|
||||
let mut reader = ReaderFromCursor::new(data);
|
||||
NbtCompound::read(&mut reader)
|
||||
}
|
||||
/// Read an NBT tag, without reading its name. This may be any type of tag except for an end tag. If you need to be able to
|
||||
/// handle end tags, use [`read_optional_tag`].
|
||||
pub fn read_tag(data: &mut Cursor<&[u8]>) -> Result<NbtTag, Error> {
|
||||
NbtTag::read(data)
|
||||
let mut reader = ReaderFromCursor::new(data);
|
||||
NbtTag::read(&mut reader)
|
||||
}
|
||||
/// Read any NBT tag, without reading its name. This may be any type of tag, including an end tag.
|
||||
///
|
||||
/// Returns `Ok(None)` if there is no data.
|
||||
pub fn read_optional_tag(data: &mut Cursor<&[u8]>) -> Result<Option<NbtTag>, Error> {
|
||||
Ok(NbtTag::read_optional(data)?)
|
||||
let mut reader = ReaderFromCursor::new(data);
|
||||
NbtTag::read_optional(&mut reader)
|
||||
}
|
||||
|
||||
/// A complete NBT container. This contains a name and a compound tag.
|
||||
|
@ -71,7 +75,7 @@ impl Nbt {
|
|||
}
|
||||
|
||||
/// Reads NBT from the given data. Returns `Ok(Nbt::None)` if there is no data.
|
||||
fn read(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
|
||||
fn read(data: &mut Reader<'_>) -> Result<Nbt, Error> {
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(Nbt::None);
|
||||
|
@ -85,7 +89,7 @@ impl Nbt {
|
|||
Ok(Nbt::Some(BaseNbt { name, tag }))
|
||||
}
|
||||
|
||||
fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
|
||||
fn read_unnamed(data: &mut Reader<'_>) -> Result<Nbt, Error> {
|
||||
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if root_type == END_ID {
|
||||
return Ok(Nbt::None);
|
||||
|
@ -258,25 +262,25 @@ impl NbtTag {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn read_with_type(data: &mut Cursor<&[u8]>, tag_type: u8, depth: usize) -> Result<Self, Error> {
|
||||
fn read_with_type(data: &mut Reader<'_>, tag_type: u8, depth: usize) -> Result<Self, Error> {
|
||||
match tag_type {
|
||||
BYTE_ID => Ok(NbtTag::Byte(
|
||||
data.read_i8().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
SHORT_ID => Ok(NbtTag::Short(
|
||||
data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
data.read_i16().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
INT_ID => Ok(NbtTag::Int(
|
||||
data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
data.read_i32().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
LONG_ID => Ok(NbtTag::Long(
|
||||
data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
data.read_i64().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
FLOAT_ID => Ok(NbtTag::Float(
|
||||
data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
data.read_f32().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
DOUBLE_ID => Ok(NbtTag::Double(
|
||||
data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||
data.read_f64().map_err(|_| Error::UnexpectedEof)?,
|
||||
)),
|
||||
BYTE_ARRAY_ID => Ok(NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned())),
|
||||
STRING_ID => Ok(NbtTag::String(read_string(data)?.to_owned())),
|
||||
|
@ -291,12 +295,12 @@ impl NbtTag {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
|
||||
fn read(data: &mut Reader<'_>) -> Result<Self, Error> {
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
Self::read_with_type(data, tag_type, 0)
|
||||
}
|
||||
|
||||
pub fn read_optional(data: &mut Cursor<&[u8]>) -> Result<Option<Self>, Error> {
|
||||
fn read_optional(data: &mut Reader<'_>) -> Result<Option<Self>, Error> {
|
||||
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||
if tag_type == END_ID {
|
||||
return Ok(None);
|
||||
|
@ -621,14 +625,14 @@ impl From<Nbt> for NbtTag {
|
|||
mod tests {
|
||||
use std::io::Read;
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
use byteorder::{WriteBytesExt, BE};
|
||||
use flate2::read::GzDecoder;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hello_world() {
|
||||
let nbt = Nbt::read(&mut Cursor::new(include_bytes!(
|
||||
let nbt = super::read(&mut Cursor::new(include_bytes!(
|
||||
"../../tests/hello_world.nbt"
|
||||
)))
|
||||
.unwrap()
|
||||
|
@ -648,7 +652,9 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
let nbt = Nbt::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(nbt.int("PersistentId"), Some(1946940766));
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
|
@ -661,7 +667,9 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
let nbt = Nbt::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
|
@ -674,11 +682,13 @@ mod tests {
|
|||
let mut decoded_src_decoder = GzDecoder::new(&mut src_slice);
|
||||
let mut decoded_src = Vec::new();
|
||||
decoded_src_decoder.read_to_end(&mut decoded_src).unwrap();
|
||||
let nbt = Nbt::read(&mut Cursor::new(&decoded_src)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&decoded_src))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let mut out = Vec::new();
|
||||
nbt.write(&mut out);
|
||||
let nbt = Nbt::read(&mut Cursor::new(&out)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&out)).unwrap().unwrap();
|
||||
|
||||
assert_eq!(nbt.float("foodExhaustionLevel").unwrap() as u32, 2);
|
||||
assert_eq!(nbt.list("Rotation").unwrap().floats().unwrap().len(), 2);
|
||||
|
@ -686,7 +696,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn inttest_1023() {
|
||||
let nbt = Nbt::read(&mut Cursor::new(include_bytes!(
|
||||
let nbt = super::read(&mut Cursor::new(include_bytes!(
|
||||
"../../tests/inttest1023.nbt"
|
||||
)))
|
||||
.unwrap()
|
||||
|
@ -714,7 +724,7 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().ints().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i32, item);
|
||||
|
@ -736,7 +746,7 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().ints().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i32, item);
|
||||
|
@ -758,7 +768,7 @@ mod tests {
|
|||
}
|
||||
data.write_u8(END_ID).unwrap();
|
||||
|
||||
let nbt = Nbt::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let nbt = super::read(&mut Cursor::new(&data)).unwrap().unwrap();
|
||||
let ints = nbt.list("").unwrap().longs().unwrap();
|
||||
for (i, &item) in ints.iter().enumerate() {
|
||||
assert_eq!(i as i64, item);
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::swap_endianness::{swap_endianness, swap_endianness_as_u8, SwappableNu
|
|||
|
||||
/// A list of numbers that's kept as big-endian in memory.
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub struct RawList<'a, T> {
|
||||
data: &'a [u8],
|
||||
_marker: PhantomData<T>,
|
||||
|
@ -24,6 +24,10 @@ impl<'a, T> RawList<'a, T> {
|
|||
pub fn is_empty(&self) -> bool {
|
||||
self.data.is_empty()
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> &'a [u8] {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SwappableNumber> RawList<'_, T> {
|
||||
|
|
148
simdnbt/src/reader.rs
Normal file
148
simdnbt/src/reader.rs
Normal file
|
@ -0,0 +1,148 @@
|
|||
use std::{
|
||||
io::Cursor,
|
||||
marker::PhantomData,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use crate::Error;
|
||||
|
||||
pub struct Reader<'a> {
|
||||
pub cur: *const u8,
|
||||
/// pointer to after the last byte (so remaining=end-cur)
|
||||
end: *const u8,
|
||||
|
||||
_marker: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
pub fn new(data: &'a [u8]) -> Reader<'a> {
|
||||
Self {
|
||||
cur: data.as_ptr(),
|
||||
end: unsafe { data.as_ptr().add(data.len()) },
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_can_read(&self, size: usize) -> Result<(), Error> {
|
||||
let data_addr = self.cur as usize;
|
||||
let end_addr = self.end as usize;
|
||||
if data_addr + size > end_addr {
|
||||
Err(Error::UnexpectedEof)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn unchecked_read_type<T>(&mut self) -> T {
|
||||
let value = unsafe { self.cur.cast::<T>().read_unaligned() };
|
||||
self.cur = unsafe { self.cur.add(std::mem::size_of::<T>()) };
|
||||
value
|
||||
}
|
||||
|
||||
pub fn read_type<T: Copy>(&mut self) -> Result<T, Error> {
|
||||
self.ensure_can_read(std::mem::size_of::<T>())?;
|
||||
Ok(unsafe { self.unchecked_read_type() })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u8(&mut self) -> Result<u8, Error> {
|
||||
self.read_type()
|
||||
}
|
||||
#[inline]
|
||||
pub fn read_i8(&mut self) -> Result<i8, Error> {
|
||||
self.read_u8().map(|x| x as i8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u16(&mut self) -> Result<u16, Error> {
|
||||
self.read_type::<u16>().map(u16::swap_bytes)
|
||||
}
|
||||
#[inline]
|
||||
pub fn read_i16(&mut self) -> Result<i16, Error> {
|
||||
self.read_u16().map(|x| x as i16)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u32(&mut self) -> Result<u32, Error> {
|
||||
self.read_type::<u32>().map(u32::swap_bytes)
|
||||
}
|
||||
#[inline]
|
||||
pub fn read_i32(&mut self) -> Result<i32, Error> {
|
||||
self.read_u32().map(|x| x as i32)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_u64(&mut self) -> Result<u64, Error> {
|
||||
self.read_type::<u64>().map(u64::swap_bytes)
|
||||
}
|
||||
#[inline]
|
||||
pub fn read_i64(&mut self) -> Result<i64, Error> {
|
||||
self.read_u64().map(|x| x as i64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_f32(&mut self) -> Result<f32, Error> {
|
||||
self.read_u32().map(f32::from_bits)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_f64(&mut self) -> Result<f64, Error> {
|
||||
self.read_u64().map(f64::from_bits)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn skip(&mut self, size: usize) -> Result<(), Error> {
|
||||
self.ensure_can_read(size)?;
|
||||
self.cur = unsafe { self.cur.add(size) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn read_slice(&mut self, size: usize) -> Result<&'a [u8], Error> {
|
||||
self.ensure_can_read(size)?;
|
||||
let slice = unsafe { std::slice::from_raw_parts(self.cur, size) };
|
||||
self.cur = unsafe { self.cur.add(size) };
|
||||
Ok(slice)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for Reader<'a> {
|
||||
fn from(data: &'a [u8]) -> Self {
|
||||
Self::new(data)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ReaderFromCursor<'a: 'cursor, 'cursor> {
|
||||
reader: Reader<'a>,
|
||||
original_cursor: &'cursor mut Cursor<&'a [u8]>,
|
||||
}
|
||||
|
||||
impl<'a, 'cursor> ReaderFromCursor<'a, 'cursor> {
|
||||
pub fn new(cursor: &'cursor mut Cursor<&'a [u8]>) -> Self {
|
||||
let data = cursor.get_ref();
|
||||
Self {
|
||||
reader: Reader::new(data[cursor.position() as usize..].as_ref()),
|
||||
original_cursor: cursor,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Drop for ReaderFromCursor<'_, '_> {
|
||||
fn drop(&mut self) {
|
||||
self.original_cursor.set_position(
|
||||
(self.reader.cur as usize - self.original_cursor.get_ref().as_ptr() as usize) as u64,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for ReaderFromCursor<'a, '_> {
|
||||
type Target = Reader<'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.reader
|
||||
}
|
||||
}
|
||||
impl<'a> DerefMut for ReaderFromCursor<'a, '_> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.reader
|
||||
}
|
||||
}
|
|
@ -4,10 +4,10 @@ use crate::DeserializeError;
|
|||
|
||||
pub trait Deserialize: Sized {
|
||||
fn from_nbt(nbt: &crate::borrow::BaseNbt) -> Result<Self, DeserializeError> {
|
||||
Self::from_compound(nbt)
|
||||
Self::from_compound(nbt.as_compound())
|
||||
}
|
||||
|
||||
fn from_compound(compound: &crate::borrow::NbtCompound) -> Result<Self, DeserializeError>;
|
||||
fn from_compound(compound: crate::borrow::NbtCompound) -> Result<Self, DeserializeError>;
|
||||
}
|
||||
|
||||
pub trait Serialize: Sized {
|
||||
|
@ -19,9 +19,9 @@ pub trait Serialize: Sized {
|
|||
}
|
||||
|
||||
pub trait FromNbtTag: Sized {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self>;
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self>;
|
||||
fn from_optional_nbt_tag(
|
||||
tag: Option<&crate::borrow::NbtTag>,
|
||||
tag: Option<crate::borrow::NbtTag>,
|
||||
) -> Result<Option<Self>, DeserializeError> {
|
||||
match tag {
|
||||
Some(tag) => Ok(Self::from_nbt_tag(tag)),
|
||||
|
@ -38,7 +38,7 @@ pub trait ToNbtTag: Sized {
|
|||
}
|
||||
|
||||
impl<K: Display + FromStr + Eq + Hash, V: FromNbtTag> Deserialize for HashMap<K, V> {
|
||||
fn from_compound(compound: &crate::borrow::NbtCompound) -> Result<Self, DeserializeError> {
|
||||
fn from_compound(compound: crate::borrow::NbtCompound) -> Result<Self, DeserializeError> {
|
||||
let mut hashmap = HashMap::with_capacity(compound.len());
|
||||
|
||||
for (k, v) in compound.iter() {
|
||||
|
@ -70,7 +70,7 @@ impl<K: Display + FromStr + Eq + Hash, V: ToNbtTag> Serialize for HashMap<K, V>
|
|||
}
|
||||
|
||||
impl Deserialize for crate::owned::NbtCompound {
|
||||
fn from_compound(compound: &crate::borrow::NbtCompound) -> Result<Self, DeserializeError> {
|
||||
fn from_compound(compound: crate::borrow::NbtCompound) -> Result<Self, DeserializeError> {
|
||||
Ok(compound.to_owned())
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ impl Serialize for crate::owned::NbtCompound {
|
|||
}
|
||||
|
||||
impl<T: Deserialize> FromNbtTag for T {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.compound().and_then(|c| Self::from_compound(c).ok())
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ impl<T: Serialize> ToNbtTag for T {
|
|||
}
|
||||
|
||||
impl FromNbtTag for crate::owned::NbtTag {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
Some(tag.to_owned())
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ impl ToNbtTag for crate::owned::NbtTag {
|
|||
|
||||
// standard nbt types
|
||||
impl FromNbtTag for i8 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.byte()
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ impl ToNbtTag for i8 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for i16 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.short()
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ impl ToNbtTag for i16 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for i32 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.int()
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ impl ToNbtTag for i32 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for i64 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.long()
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ impl ToNbtTag for i64 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for f32 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.float()
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ impl ToNbtTag for f32 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for f64 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.double()
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +171,7 @@ impl ToNbtTag for f64 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for String {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.string().map(|s| s.to_string())
|
||||
}
|
||||
}
|
||||
|
@ -189,7 +189,7 @@ impl ToNbtTag for &str {
|
|||
|
||||
// unsigned integers
|
||||
impl FromNbtTag for u8 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.byte().map(|b| b as u8)
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ impl ToNbtTag for u8 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for u16 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.short().map(|s| s as u16)
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ impl ToNbtTag for u16 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for u32 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.int().map(|i| i as u32)
|
||||
}
|
||||
}
|
||||
|
@ -222,7 +222,7 @@ impl ToNbtTag for u32 {
|
|||
}
|
||||
|
||||
impl FromNbtTag for u64 {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.long().map(|l| l as u64)
|
||||
}
|
||||
}
|
||||
|
@ -234,7 +234,7 @@ impl ToNbtTag for u64 {
|
|||
|
||||
// lists
|
||||
impl FromNbtTag for Vec<String> {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.list().and_then(|l| {
|
||||
l.strings()
|
||||
.map(|s| s.iter().map(|s| s.to_string()).collect())
|
||||
|
@ -251,11 +251,11 @@ impl ToNbtTag for Vec<String> {
|
|||
|
||||
// slightly less standard types
|
||||
impl<T: FromNbtTag> FromNbtTag for Option<T> {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
Some(T::from_nbt_tag(tag))
|
||||
}
|
||||
fn from_optional_nbt_tag(
|
||||
tag: Option<&crate::borrow::NbtTag>,
|
||||
tag: Option<crate::borrow::NbtTag>,
|
||||
) -> Result<Option<Self>, DeserializeError> {
|
||||
match tag {
|
||||
Some(tag) => Ok(Some(T::from_nbt_tag(tag))),
|
||||
|
@ -274,8 +274,9 @@ impl<T: ToNbtTag> ToNbtTag for Option<T> {
|
|||
|
||||
impl<T: Deserialize> FromNbtTag for Vec<Option<T>> {
|
||||
/// A list of compounds where `None` is an empty compound
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
let list = tag.list()?.compounds()?;
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
let list = tag.list()?;
|
||||
let list = list.compounds()?;
|
||||
let mut vec = Vec::with_capacity(list.len());
|
||||
for tag in list {
|
||||
if tag.is_empty() {
|
||||
|
@ -302,8 +303,9 @@ impl<T: Serialize> ToNbtTag for Vec<Option<T>> {
|
|||
}
|
||||
|
||||
impl<T: Deserialize> FromNbtTag for Vec<T> {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
let list = tag.list()?.compounds()?;
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
let list = tag.list()?;
|
||||
let list = list.compounds()?;
|
||||
let mut vec = Vec::with_capacity(list.len());
|
||||
for tag in list {
|
||||
vec.push(T::from_compound(tag).ok()?);
|
||||
|
@ -321,7 +323,7 @@ impl<T: Serialize> ToNbtTag for Vec<T> {
|
|||
}
|
||||
|
||||
impl FromNbtTag for bool {
|
||||
fn from_nbt_tag(tag: &crate::borrow::NbtTag) -> Option<Self> {
|
||||
fn from_nbt_tag(tag: crate::borrow::NbtTag) -> Option<Self> {
|
||||
tag.byte().map(|b| b != 0)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue