Compare commits
4 Commits
1d8a7400eb
...
ec6600e123
Author | SHA1 | Date | |
---|---|---|---|
ec6600e123 | |||
04b4cf3047 | |||
90ab2135ed | |||
395a26bd41 |
12
README.md
12
README.md
@ -1,4 +1,4 @@
|
|||||||
# nbt-rust
|
# shen-nbt5
|
||||||
|
|
||||||
一个 "全功能" 的 "快速" NBT 解析器
|
一个 "全功能" 的 "快速" NBT 解析器
|
||||||
|
|
||||||
@ -64,9 +64,11 @@ writen in rust!
|
|||||||
|
|
||||||
## 感谢
|
## 感谢
|
||||||
|
|
||||||
感谢 @langyo 和 @InfyniteHeap
|
感谢 [@langyo](https://github.com/langyo) 和 [@InfyniteHeap](https://github.com/InfyniteHeap)
|
||||||
在编写过程中的帮助(
|
在编写过程中的帮助(
|
||||||
|
|
||||||
|
感谢 [mat](https://github.com/mat-1) 的 simd-nbt 中 [`mutf8.rs`](https://github.com/azalea-rs/simdnbt/blob/master/simdnbt/benches/mutf8.rs) 的实现
|
||||||
|
|
||||||
感谢 [wiki.vg](https://wiki.vg/NBT) 存储的 NBT 格式的详细信息
|
感谢 [wiki.vg](https://wiki.vg/NBT) 存储的 NBT 格式的详细信息
|
||||||
|
|
||||||
## 概况
|
## 概况
|
||||||
@ -137,3 +139,9 @@ speed: 2483288579.985664 (bytes/s)
|
|||||||
2368.2485389572753 (MB/s)
|
2368.2485389572753 (MB/s)
|
||||||
2.312742713825464 (GB/s)
|
2.312742713825464 (GB/s)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
shen-nbt5 通过了作者电脑上 所有 .nbt 格式的文件的读取测试
|
||||||
|
|
||||||
|
```text
|
||||||
|
total: 6063, open failed: 25, parse failed: 0, gzip parse: 6013, normal parse: 25
|
||||||
|
```
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "shen-nbt5"
|
name = "shen-nbt5"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Just A FASSST NBT parser/writer"
|
description = "Just A FASSST NBT parser/writer"
|
||||||
homepage = "https://github.com/shenjackyuanjie/nbt-rust"
|
homepage = "https://github.com/shenjackyuanjie/nbt-rust"
|
||||||
@ -20,3 +20,9 @@ serde = { version = "1.0", features = ["derive"], optional = true }
|
|||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
serde = ["dep:serde"]
|
serde = ["dep:serde"]
|
||||||
|
test = []
|
||||||
|
|
||||||
|
# test dep
|
||||||
|
[dev-dependencies]
|
||||||
|
# gzip
|
||||||
|
flate2 = "1.0"
|
||||||
|
@ -237,7 +237,7 @@ impl std::fmt::Display for NbtError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 核心 Value
|
/// 核心 Value
|
||||||
///
|
///
|
||||||
/// 暂时不支持 `from_value` 和 `to_value`
|
/// 暂时不支持 `from_value` 和 `to_value`
|
||||||
/// https://github.com/shenjackyuanjie/nbt-rust/issues/1
|
/// https://github.com/shenjackyuanjie/nbt-rust/issues/1
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -721,6 +721,26 @@ impl NbtReader<'_> {
|
|||||||
self.cursor += len;
|
self.cursor += len;
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
/// 读取指定长度的 i16 数组
|
||||||
|
///
|
||||||
|
/// # 安全性
|
||||||
|
///
|
||||||
|
/// 长度溢出会导致 UB
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn read_be_i16_array_unsafe(&mut self, len: usize) -> Vec<i16> {
|
||||||
|
let mut value: Vec<i16> = Vec::with_capacity(len);
|
||||||
|
std::ptr::copy_nonoverlapping(
|
||||||
|
self.data[self.cursor..].as_ptr() as *const u8,
|
||||||
|
value.as_ptr() as *mut u8,
|
||||||
|
len * 2,
|
||||||
|
);
|
||||||
|
value.set_len(len);
|
||||||
|
for n in &mut value {
|
||||||
|
*n = n.to_be();
|
||||||
|
}
|
||||||
|
self.cursor += len * 2;
|
||||||
|
value
|
||||||
|
}
|
||||||
/// 读取指定长度的 i32 数组
|
/// 读取指定长度的 i32 数组
|
||||||
///
|
///
|
||||||
/// # 安全性
|
/// # 安全性
|
||||||
@ -728,15 +748,39 @@ impl NbtReader<'_> {
|
|||||||
/// 长度溢出会导致 UB
|
/// 长度溢出会导致 UB
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn read_be_i32_array_unsafe(&mut self, len: usize) -> Vec<i32> {
|
pub unsafe fn read_be_i32_array_unsafe(&mut self, len: usize) -> Vec<i32> {
|
||||||
let value =
|
let mut value: Vec<i32> = Vec::with_capacity(len);
|
||||||
std::slice::from_raw_parts(self.data[self.cursor..].as_ptr() as *const i32, len);
|
std::ptr::copy_nonoverlapping(
|
||||||
let mut value = value.to_vec();
|
self.data[self.cursor..].as_ptr() as *const u8,
|
||||||
|
value.as_ptr() as *mut u8,
|
||||||
|
len * 4,
|
||||||
|
);
|
||||||
|
value.set_len(len);
|
||||||
for n in &mut value {
|
for n in &mut value {
|
||||||
*n = n.to_be();
|
*n = n.to_be();
|
||||||
}
|
}
|
||||||
self.cursor += len * 4;
|
self.cursor += len * 4;
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
/// 读取指定长度的 i64 数组
|
||||||
|
///
|
||||||
|
/// # 安全性
|
||||||
|
///
|
||||||
|
/// 长度溢出会导致 UB
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn read_be_i64_array_unsafe(&mut self, len: usize) -> Vec<i64> {
|
||||||
|
let mut value: Vec<i64> = Vec::with_capacity(len);
|
||||||
|
std::ptr::copy_nonoverlapping(
|
||||||
|
self.data[self.cursor..].as_ptr() as *const u8,
|
||||||
|
value.as_ptr() as *mut u8,
|
||||||
|
len * 8,
|
||||||
|
);
|
||||||
|
value.set_len(len);
|
||||||
|
for n in &mut value {
|
||||||
|
*n = n.to_be();
|
||||||
|
}
|
||||||
|
self.cursor += len * 8;
|
||||||
|
value
|
||||||
|
}
|
||||||
/// 读取指定长度的 i32 数组
|
/// 读取指定长度的 i32 数组
|
||||||
///
|
///
|
||||||
/// # 安全性
|
/// # 安全性
|
||||||
@ -755,22 +799,6 @@ impl NbtReader<'_> {
|
|||||||
///
|
///
|
||||||
/// # 安全性
|
/// # 安全性
|
||||||
///
|
///
|
||||||
/// 长度溢出会导致 UB
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn read_be_i64_array_unsafe(&mut self, len: usize) -> Vec<i64> {
|
|
||||||
let value =
|
|
||||||
std::slice::from_raw_parts(self.data[self.cursor..].as_ptr() as *const i64, len);
|
|
||||||
let mut value = value.to_vec();
|
|
||||||
for n in &mut value {
|
|
||||||
*n = n.to_be();
|
|
||||||
}
|
|
||||||
self.cursor += len * 8;
|
|
||||||
value
|
|
||||||
}
|
|
||||||
/// 读取指定长度的 i64 数组
|
|
||||||
///
|
|
||||||
/// # 安全性
|
|
||||||
///
|
|
||||||
/// 长度溢出会导致 panic
|
/// 长度溢出会导致 panic
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_be_i64_array(&mut self, len: usize) -> Vec<i64> {
|
pub fn read_be_i64_array(&mut self, len: usize) -> Vec<i64> {
|
||||||
|
@ -280,9 +280,51 @@ mod unsafe_test {
|
|||||||
assert_eq!(reader.cursor, 100 * 8);
|
assert_eq!(reader.cursor, 100 * 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 未对齐的地址
|
||||||
|
#[test]
|
||||||
|
fn unaligned_read_u16_array() {
|
||||||
|
let mut value = vec![0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04];
|
||||||
|
let mut reader = NbtReader::new(&mut value);
|
||||||
|
let value = reader.read_u8();
|
||||||
|
assert_eq!(value, 0x01);
|
||||||
|
assert_eq!(reader.cursor, 1);
|
||||||
|
unsafe {
|
||||||
|
// 读取 u16 数组
|
||||||
|
let array = reader.read_be_i16_array_unsafe(3);
|
||||||
|
assert_eq!(array, vec![0x0203, 0x0401, 0x0203]);
|
||||||
|
assert_eq!(reader.cursor, 7);
|
||||||
|
let value = reader.read_u8();
|
||||||
|
assert_eq!(value, 0x04);
|
||||||
|
assert_eq!(reader.cursor, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 依然是未对齐
|
||||||
|
/// 只不过是 u32/i32
|
||||||
|
#[test]
|
||||||
|
fn unaligned_read_x32_array() {
|
||||||
|
let mut value = gen_datas(202);
|
||||||
|
let mut reader = NbtReader::new(&mut value);
|
||||||
|
let value = reader.read_u8();
|
||||||
|
assert_eq!(value, 0x00);
|
||||||
|
assert_eq!(reader.cursor, 1);
|
||||||
|
unsafe {
|
||||||
|
let array = reader.read_be_i32_array_unsafe(50);
|
||||||
|
reader.roll_back(50 * 4);
|
||||||
|
let safe_array = reader.read_be_i32_array(50);
|
||||||
|
assert_eq!(array, safe_array);
|
||||||
|
assert_eq!(reader.cursor, 201);
|
||||||
|
}
|
||||||
|
let value = reader.read_u8();
|
||||||
|
assert_eq!(value, 201);
|
||||||
|
assert_eq!(reader.cursor, 202);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod nbt {
|
mod nbt {
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -471,4 +513,65 @@ mod nbt {
|
|||||||
assert!(value.is_ok());
|
assert!(value.is_ok());
|
||||||
// 其他版本
|
// 其他版本
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "test")]
|
||||||
|
fn file_sys_test() {
|
||||||
|
// 测试所有能直接找到的 .nbt 文件
|
||||||
|
// es -r .*\.nbt
|
||||||
|
// command
|
||||||
|
// 总计数, 文件打开失败计数, 解析失败计数, gzip 解析计数, 普通解析计数
|
||||||
|
let mut counter: Vec<i32> = vec![0; 5];
|
||||||
|
|
||||||
|
let find_paths = std::process::Command::new("es")
|
||||||
|
.arg("-r")
|
||||||
|
.arg(r".*\.nbt$")
|
||||||
|
.output()
|
||||||
|
.expect("failed to execute process");
|
||||||
|
let find_paths = String::from_utf8_lossy(find_paths.stdout.as_slice());
|
||||||
|
let find_paths = find_paths.split("\n").collect::<Vec<&str>>();
|
||||||
|
for path in find_paths {
|
||||||
|
if path.len() == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
counter[0] += 1;
|
||||||
|
let path = path.trim();
|
||||||
|
// println!("path: {}", path);
|
||||||
|
let file = std::fs::File::open(path);
|
||||||
|
if file.is_err() {
|
||||||
|
println!("open file failed: {}", path);
|
||||||
|
counter[1] += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let file = file.unwrap();
|
||||||
|
let mut data = file.bytes().collect::<Result<Vec<u8>, _>>().unwrap();
|
||||||
|
|
||||||
|
// 检查一下是否是 gzip
|
||||||
|
if data[0] == 0x1F && data[1] == 0x8B {
|
||||||
|
let mut decoder = flate2::read::GzDecoder::new(data.as_slice());
|
||||||
|
let mut data = Vec::with_capacity(data.len());
|
||||||
|
decoder.read_to_end(&mut data).unwrap();
|
||||||
|
let value = NbtValue::from_binary::<nbt_version::Java>(&mut data);
|
||||||
|
if !value.is_ok() {
|
||||||
|
counter[2] += 1;
|
||||||
|
println!("failed: {} {:?}", path, value.as_ref());
|
||||||
|
assert!(value.is_ok());
|
||||||
|
}
|
||||||
|
counter[3] += 1;
|
||||||
|
} else {
|
||||||
|
let value = NbtValue::from_binary::<nbt_version::Java>(&mut data);
|
||||||
|
if !value.is_ok() {
|
||||||
|
counter[2] += 1;
|
||||||
|
println!("failed: {} {:?}", path, value.as_ref());
|
||||||
|
assert!(value.is_ok());
|
||||||
|
}
|
||||||
|
counter[4] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 输出统计结果
|
||||||
|
println!(
|
||||||
|
"total: {}, open failed: {}, parse failed: {}, gzip parse: {}, normal parse: {}",
|
||||||
|
counter[0], counter[1], counter[2], counter[3], counter[4]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ impl NbtWriteTrait for JavaNetAfter1_20_2 {
|
|||||||
buff.push(0);
|
buff.push(0);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
x => return Err(NbtError::WrongRootType(x.tag())),
|
x => Err(NbtError::WrongRootType(x.tag())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
Loading…
Reference in New Issue
Block a user