-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add binary reader to improve reading of the binary nbt
- Loading branch information
Showing
11 changed files
with
281 additions
and
262 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use std::string::FromUtf8Error; | ||
|
||
macro_rules! impl_read_number { | ||
($fn_name:ident, $type:ty) => { | ||
pub fn $fn_name(&mut self) -> $type { | ||
let size = std::mem::size_of::<$type>(); | ||
let bytes = &self.raw[self.index..self.index + size]; | ||
let integer = <$type>::from_be_bytes(bytes.try_into().unwrap()); | ||
self.index += size; | ||
integer | ||
} | ||
}; | ||
} | ||
|
||
macro_rules! impl_read_array { | ||
($fn_name:ident, $type:ty, $reader:ident) => { | ||
pub fn $fn_name(&mut self) -> Vec<$type> { | ||
let size = self.read_i32(); | ||
let mut values = Vec::new(); | ||
|
||
for _ in 0..size { | ||
let next_tag = self.$reader(); | ||
values.push(next_tag); | ||
} | ||
|
||
values | ||
} | ||
}; | ||
} | ||
|
||
pub struct BinaryReader<'a> { | ||
raw: &'a [u8], | ||
index: usize, | ||
} | ||
|
||
impl<'a> BinaryReader<'a> { | ||
pub fn new(raw: &'a [u8]) -> Self { | ||
Self { raw, index: 0 } | ||
} | ||
|
||
pub fn read_string(&mut self) -> Result<String, FromUtf8Error> { | ||
let size = self.read_u16() as usize; | ||
let bytes = &self.raw[self.index..self.index + size]; | ||
self.index += size; | ||
String::from_utf8(Vec::from(bytes)) | ||
} | ||
|
||
pub fn read_name(&mut self) -> Option<String> { | ||
self.read_string().ok().filter(|s| !s.is_empty()) | ||
} | ||
|
||
pub fn read_type(&mut self) -> u8 { | ||
self.read_u8() | ||
} | ||
|
||
impl_read_number!(read_i8, i8); | ||
impl_read_number!(read_u8, u8); | ||
impl_read_number!(read_i16, i16); | ||
impl_read_number!(read_u16, u16); | ||
impl_read_number!(read_i32, i32); | ||
impl_read_number!(read_i64, i64); | ||
impl_read_number!(read_f32, f32); | ||
impl_read_number!(read_f64, f64); | ||
impl_read_array!(read_byte_array, i8, read_i8); | ||
impl_read_array!(read_int_array, i32, read_i32); | ||
impl_read_array!(read_long_array, i64, read_i64); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_read_i8() { | ||
let data = [0x7F]; | ||
let mut reader = BinaryReader::new(&data); | ||
assert_eq!(reader.read_i8(), 127); | ||
} | ||
|
||
#[test] | ||
fn test_read_i16() { | ||
let data = [0x7F, 0xFF]; | ||
let mut reader = BinaryReader::new(&data); | ||
assert_eq!(reader.read_i16(), 32767); | ||
} | ||
|
||
#[test] | ||
fn test_read_u16() { | ||
let data = [0x0F, 0xFF]; | ||
let mut reader = BinaryReader::new(&data); | ||
assert_eq!(reader.read_u16(), 4095); | ||
} | ||
|
||
#[test] | ||
fn test_read_i32() { | ||
let data = [0x7F, 0xFF, 0xFF, 0xFF]; | ||
let mut reader = BinaryReader::new(&data); | ||
assert_eq!(reader.read_i32(), 2147483647); | ||
} | ||
|
||
#[test] | ||
fn test_read_f32() { | ||
let data = [0x3F, 0x80, 0x00, 0x00]; | ||
let mut reader = BinaryReader::new(&data); | ||
assert_eq!(reader.read_f32(), 1.0); | ||
} | ||
|
||
#[test] | ||
fn test_read_string() { | ||
let data = [0, 5, 72, 69, 76, 76, 79]; | ||
let mut reader = BinaryReader::new(&data); | ||
let parsed = reader.read_string().unwrap(); | ||
|
||
assert_eq!(parsed, "HELLO"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub mod binary_reader; | ||
pub mod parse; | ||
mod readers; | ||
mod parsers; | ||
pub mod tag; | ||
mod writers; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pub mod parse_compound_tag; | ||
pub mod parse_list_tag; | ||
pub mod parse_with_type; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
use crate::nbt::binary_reader::BinaryReader; | ||
use crate::nbt::parse::parse_tag; | ||
use crate::nbt::tag::Tag; | ||
|
||
pub fn parse_compound_tag(reader: &mut BinaryReader) -> Vec<Tag> { | ||
let mut values = Vec::new(); | ||
|
||
loop { | ||
let next_tag = parse_tag(reader); | ||
if next_tag == Tag::End { | ||
break; | ||
} | ||
values.push(next_tag); | ||
} | ||
|
||
values | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use crate::nbt::binary_reader::BinaryReader; | ||
use crate::nbt::parsers::parse_with_type::parse_with_type; | ||
use crate::nbt::tag::Tag; | ||
|
||
pub fn parse_list_tag(reader: &mut BinaryReader) -> (u8, Vec<Tag>) { | ||
let mut values = Vec::new(); | ||
|
||
let tag_type = reader.read_type(); | ||
let list_length = reader.read_i32(); | ||
if list_length <= 0 && tag_type == 0 { | ||
return (tag_type, values); | ||
} | ||
|
||
for _ in 0..list_length { | ||
let next_tag = parse_with_type(reader, tag_type, true); | ||
values.push(next_tag); | ||
} | ||
|
||
(tag_type, values) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use crate::nbt::binary_reader::BinaryReader; | ||
use crate::nbt::parsers::parse_compound_tag::parse_compound_tag; | ||
use crate::nbt::parsers::parse_list_tag::parse_list_tag; | ||
use crate::nbt::tag::Tag; | ||
|
||
pub fn parse_with_type(reader: &mut BinaryReader, tag_type: u8, skip_name: bool) -> Tag { | ||
let name = if skip_name || tag_type == 0 { | ||
None | ||
} else { | ||
reader.read_name() | ||
}; | ||
|
||
match tag_type { | ||
0 => Tag::End, | ||
1 => { | ||
let value = reader.read_i8(); | ||
Tag::Byte { name, value } | ||
} | ||
2 => { | ||
let value = reader.read_i16(); | ||
Tag::Short { name, value } | ||
} | ||
3 => { | ||
let value = reader.read_i32(); | ||
Tag::Int { name, value } | ||
} | ||
4 => { | ||
let value = reader.read_i64(); | ||
Tag::Long { name, value } | ||
} | ||
5 => { | ||
let value = reader.read_f32(); | ||
Tag::Float { name, value } | ||
} | ||
6 => { | ||
let value = reader.read_f64(); | ||
Tag::Double { name, value } | ||
} | ||
7 => { | ||
let value = reader.read_byte_array(); | ||
Tag::ByteArray { name, value } | ||
} | ||
8 => { | ||
let value = reader.read_string().unwrap_or_default(); | ||
Tag::String { name, value } | ||
} | ||
9 => { | ||
let (tag_type, value) = parse_list_tag(reader); | ||
Tag::List { | ||
name, | ||
value, | ||
tag_type, | ||
} | ||
} | ||
10 => { | ||
let value = parse_compound_tag(reader); | ||
Tag::Compound { name, value } | ||
} | ||
11 => { | ||
let value = reader.read_int_array(); | ||
Tag::IntArray { name, value } | ||
} | ||
12 => { | ||
let value = reader.read_long_array(); | ||
Tag::LongArray { name, value } | ||
} | ||
_ => panic!("Unsupported tag type {tag_type}"), | ||
} | ||
} |
Oops, something went wrong.