1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 07:26:04 +00:00
an unnecessarily fast nbt decoder
Find a file
2024-03-09 19:45:29 -06:00
simdnbt fix NbtList::Double write code to be consistent with the others 2024-03-09 19:45:29 -06:00
simdnbt-derive Release 0.4.1 2024-03-09 18:31:46 -06:00
.gitignore use mimalloc 2024-01-19 18:21:44 -06:00
Cargo.toml derive macro 2023-11-18 22:54:15 -06:00
LICENSE add stuff to Cargo.toml 2023-08-29 23:04:24 -05:00
README.md make readme a symlink 2024-01-25 00:13:51 -06:00
rust-toolchain set azalea-nbt commit rev since it was removed 2023-11-28 11:48:07 -06:00

simdnbt

Simdnbt is a very fast NBT 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

cargo add simdnbt

Deserializing

For deserializing, you'll likely want either simdnbt::borrow::Nbt::read or simdnbt::owned::Nbt::read. The difference is that the "borrow" variant requires you to keep a reference to the original buffer, but is significantly faster.

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

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 (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 (though without actually accessing the data):

simdnbt is ~3x faster than the second fastest nbt crate

And here's a benchmark where it accesses the data and makes it owned:

simdnbt is only about 50% faster than the second fastest in this one