# simdnbt
Simdnbt is a very fast [NBT](https://minecraft.wiki/w/NBT_format) serializer and deserializer.
It was originally made as a joke but it ended up being too good of a joke so it's actually a thing now.
## Usage
```sh
cargo add simdnbt
```
### Deserializing
For deserializing, you'll likely want either [simdnbt::borrow::Nbt::read](https://docs.rs/simdnbt/latest/simdnbt/borrow/enum.Nbt.html#method.read) or [simdnbt::owned::Nbt::read](https://docs.rs/simdnbt/latest/simdnbt/owned/enum.Nbt.html#method.read).
The difference is that the "borrow" variant requires you to keep a reference to the original buffer, but is significantly faster.
```rust,no_run
use std::borrow::Cow;
use std::io::Cursor;
use simdnbt::borrow::Nbt;
fn example(item_bytes: &[u8]) {
let nbt = Nbt::read(&mut Cursor::new(item_bytes))
.unwrap()
.unwrap();
let skyblock_id: Cow<str> = nbt
.list("i")
.and_then(|i| i.compounds())
.and_then(|i| i.get(0))
.and_then(|i| i.compound("tag"))
.and_then(|tag| tag.compound("ExtraAttributes"))
.and_then(|ea| ea.string("id"))
.map(|id| id.to_string_lossy())
.unwrap_or_default();
}
```
### Serializing
```rust
use simdnbt::owned::{BaseNbt, Nbt, NbtCompound, NbtTag};
let nbt = Nbt::Some(BaseNbt::new(
"",
NbtCompound::from_values(vec![
("key".into(), NbtTag::String("value".into())),
]),
));
let mut buffer = Vec::new();
nbt.write(&mut buffer);
```
## Performance guide
Use the borrow variant of `Nbt` if possible, and avoid allocating unnecessarily (for example, keep strings as `Cow<str>` if you can).
The most significant and simple optimization you can do is switching to an allocator like [mimalloc](https://docs.rs/mimalloc/latest/mimalloc/) (it's ~20% faster on my machine). Setting `RUSTFLAGS='-C target-cpu=native'` when running your code may also help a little bit.
## Implementation details
Simdnbt currently makes use of SIMD instructions for two things:
- swapping the endianness of int arrays
- checking if a string is plain ascii for faster mutf8 to utf8 conversion
Simdnbt ~~cheats~~ takes some shortcuts to be this fast:
1. it requires a reference to the original data (to avoid cloning)
2. it doesn't validate/decode the mutf-8 strings at decode-time
## Benchmarks
Simdnbt is likely the fastest NBT decoder currently in existence.
Here's a benchmark comparing Simdnbt against a few of the other fastest NBT crates for decoding [`complex_player.dat`](https://github.com/azalea-rs/simdnbt/blob/master/simdnbt/tests/complex_player.dat):
| [simdnbt::borrow](https://docs.rs/simdnbt/latest/simdnbt/borrow/index.html) | 717.45 MiB/s |
| [simdnbt::owned](https://docs.rs/simdnbt/latest/simdnbt/owned/index.html) | 329.10 MiB/s |
| [shen_nbt5](https://docs.rs/shen-nbt5/latest/shen_nbt5/) | 306.58 MiB/s |
| [azalea_nbt](https://docs.rs/azalea-nbt/latest/azalea_nbt/) | 297.28 MiB/s |
| [valence_nbt](https://docs.rs/valence_nbt/latest/valence_nbt/) | 236.42 MiB/s |
| [graphite_binary](https://docs.rs/graphite_binary/latest/graphite_binary/) | 210.51 MiB/s |
| [fastnbt](https://docs.rs/fastnbt/latest/fastnbt/) | 115.54 MiB/s |
| [hematite_nbt](https://docs.rs/hematite-nbt/latest/nbt/) | 108.91 MiB/s |
And for writing `complex_player.dat`:
| simdnbt::borrow | 2.5914 GiB/s |
| azalea_nbt | 2.1096 GiB/s |
| simdnbt::owned | 1.9508 GiB/s |
| graphite_binary | 1.7745 GiB/s |
The tables above were made from the [compare benchmark](https://github.com/azalea-rs/simdnbt/tree/master/simdnbt/benches) in this repo.
Note that the benchmark is somewhat unfair, since `simdnbt::borrow` doesn't fully decode some things like strings and integer arrays until they're used.
Also keep in mind that if you run your own benchmark you'll get different numbers, but the speeds should be about the same relative to each other.