1
0
Fork 0
mirror of https://github.com/azalea-rs/simdnbt.git synced 2025-08-02 07:26:04 +00:00
simdnbt/simdnbt/src/common.rs
2024-03-09 18:04:21 -06:00

207 lines
6.6 KiB
Rust

use std::{io::Cursor, mem, slice};
use crate::{
raw_list::RawList,
swap_endianness::{swap_endianness_as_u8, SwappableNumber},
Error, Mutf8Str,
};
pub const END_ID: u8 = 0;
pub const BYTE_ID: u8 = 1;
pub const SHORT_ID: u8 = 2;
pub const INT_ID: u8 = 3;
pub const LONG_ID: u8 = 4;
pub const FLOAT_ID: u8 = 5;
pub const DOUBLE_ID: u8 = 6;
pub const BYTE_ARRAY_ID: u8 = 7;
pub const STRING_ID: u8 = 8;
pub const LIST_ID: u8 = 9;
pub const COMPOUND_ID: u8 = 10;
pub const INT_ARRAY_ID: u8 = 11;
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)?;
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])
}
#[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)?;
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])
}
pub fn read_string<'a>(data: &mut Cursor<&'a [u8]>) -> 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> {
read_with_u32_length(data, 1)
}
pub fn read_i8_array<'a>(data: &mut Cursor<&'a [u8]>) -> 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> {
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> {
let array_bytes = read_with_u32_length(data, 8)?;
Ok(RawList::new(array_bytes))
}
fn slice_u8_into_i8(s: &[u8]) -> &[i8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const i8, s.len()) }
}
pub fn slice_i8_into_u8(s: &[i8]) -> &[u8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, s.len()) }
}
#[inline(always)]
pub fn write_with_u32_length(data: &mut Vec<u8>, width: usize, value: &[u8]) {
let length = value.len() / width;
data.reserve(4 + value.len());
unsafe {
unchecked_extend(data, &(length as u32).to_be_bytes());
unchecked_extend(data, value);
}
}
pub fn write_u32(data: &mut Vec<u8>, value: u32) {
data.extend_from_slice(&value.to_be_bytes());
}
pub fn write_string(data: &mut Vec<u8>, value: &Mutf8Str) {
data.reserve(2 + value.len());
// SAFETY: We reserved enough capacity
unsafe {
unchecked_write_string(data, value);
}
}
/// Write a string to a Vec<u8> without checking if the Vec has enough capacity.
/// This is unsafe because it can cause a buffer overflow if the Vec doesn't have enough capacity.
///
/// # Safety
///
/// You must reserve enough capacity (2 + value.len()) in the Vec before calling this function.
#[inline]
pub unsafe fn unchecked_write_string(data: &mut Vec<u8>, value: &Mutf8Str) {
unchecked_extend(data, &(value.len() as u16).to_be_bytes());
unchecked_extend(data, value.as_bytes());
}
/// Extend a Vec<u8> with a slice of u8 without checking if the Vec has enough capacity.
///
/// This optimization is barely measurable, but it does make it slightly faster!
///
/// # Safety
///
/// You must reserve enough capacity in the Vec before calling this function.
#[inline]
pub unsafe fn unchecked_extend(data: &mut Vec<u8>, value: &[u8]) {
let ptr = data.as_mut_ptr();
let len = data.len();
std::ptr::copy_nonoverlapping(value.as_ptr(), ptr.add(len), value.len());
data.set_len(len + value.len());
}
#[inline]
pub unsafe fn unchecked_push(data: &mut Vec<u8>, value: u8) {
let ptr = data.as_mut_ptr();
let len = data.len();
std::ptr::write(ptr.add(len), value);
data.set_len(len + 1);
}
/// Convert a slice of any type into a slice of u8. This will probably return the data as little
/// endian! Use [`slice_into_u8_big_endian`] to get big endian (the endianness that's used in NBT).
#[inline]
pub fn slice_into_u8_native_endian<T>(s: &[T]) -> &[u8] {
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, mem::size_of_val(s)) }
}
/// Convert a slice of any type into a Vec<u8>. This will return the data as big endian (the
/// endianness that's used in NBT).
#[inline]
pub fn slice_into_u8_big_endian<T: SwappableNumber>(s: &[T]) -> Vec<u8> {
swap_endianness_as_u8::<T>(slice_into_u8_native_endian(s))
}
#[cfg(test)]
mod tests {
use super::*;
// this test specifically checks with little-endian
#[cfg(target_endian = "little")]
#[test]
fn test_slice_into_u8_native_endian() {
assert_eq!(slice_into_u8_native_endian(&[1u16, 2u16]), [1, 0, 2, 0]);
assert_eq!(
slice_into_u8_native_endian(&[1u32, 2u32]),
[1, 0, 0, 0, 2, 0, 0, 0]
);
}
#[test]
fn test_slice_into_u8_big_endian() {
assert_eq!(slice_into_u8_big_endian(&[1u16, 2u16]), [0, 1, 0, 2]);
assert_eq!(
slice_into_u8_big_endian(&[1u32, 2u32]),
[0, 0, 0, 1, 0, 0, 0, 2]
);
}
}