na_nbt 0.2.1

High-performance NBT (Named Binary Tag) library with zero-copy parsing and serde support
Documentation
mod compound_mut;
mod compound_own;
mod compound_ref;
mod config;
mod into_nbt;
mod list_mut;
mod list_own;
mod list_ref;
mod read;
mod size;
mod string_ref;
mod typed_list_mut;
mod typed_list_own;
mod typed_list_ref;
mod value_mut;
mod value_own;
mod value_ref;
mod write;

use std::{any::TypeId, io::Read};

pub use compound_own::CompoundOwn;
pub use into_nbt::IntoNBT;
pub use list_own::ListOwn;
pub use typed_list_own::TypedListOwn;
pub use value_own::ValueOwn;
use zerocopy::byteorder;

use crate::{
    ByteOrder, Error, Result, cold_path,
    mutable::read::{read_unsafe, read_unsafe_fallback},
};

pub fn read_owned<SOURCE: ByteOrder, STORE: ByteOrder>(source: &[u8]) -> Result<ValueOwn<STORE>> {
    unsafe {
        macro_rules! check_bounds {
            ($required:expr) => {
                if source.len() < $required {
                    cold_path();
                    return Err(Error::EOF);
                }
            };
        }

        let mut current_pos = source.as_ptr();
        let end_pos = source.as_ptr().add(source.len());

        check_bounds!(1);

        let tag_id = *current_pos;
        current_pos = current_pos.add(1);

        if tag_id == 0 {
            cold_path();
            return Ok(ValueOwn::End(()));
        }

        check_bounds!(1 + 2);
        let name_len = byteorder::U16::<SOURCE>::from_bytes(*current_pos.cast()).get();

        check_bounds!(1 + 2 + name_len as usize);
        current_pos = current_pos.add(2 + name_len as usize);

        let value = if TypeId::of::<SOURCE>() == TypeId::of::<STORE>() {
            let result = read_unsafe::<SOURCE>(tag_id, &mut current_pos, end_pos)?;
            Ok(std::mem::transmute::<ValueOwn<SOURCE>, ValueOwn<STORE>>(
                result,
            ))
        } else {
            read_unsafe_fallback::<SOURCE, STORE>(tag_id, &mut current_pos, end_pos)
        }?;

        if current_pos < end_pos {
            cold_path();
            return Err(Error::REMAIN(
                end_pos.byte_offset_from_unsigned(current_pos),
            ));
        }

        Ok(value)
    }
}

pub fn read_owned_from_reader<SOURCE: ByteOrder, STORE: ByteOrder>(
    mut reader: impl Read,
) -> Result<ValueOwn<STORE>> {
    let mut buf = Vec::new();
    reader.read_to_end(&mut buf).map_err(Error::IO)?;

    read_owned::<SOURCE, STORE>(&buf)
}