Compare commits
No commits in common. "ec6600e123a7e954b8d7e6d213e52427c1c58ccd" and "1d8a7400eb9a9f089ca7314bf5079be1baee975e" have entirely different histories.
ec6600e123
...
1d8a7400eb
12
README.md
12
README.md
@ -1,4 +1,4 @@
|
|||||||
# shen-nbt5
|
# nbt-rust
|
||||||
|
|
||||||
一个 "全功能" 的 "快速" NBT 解析器
|
一个 "全功能" 的 "快速" NBT 解析器
|
||||||
|
|
||||||
@ -64,11 +64,9 @@ writen in rust!
|
|||||||
|
|
||||||
## 感谢
|
## 感谢
|
||||||
|
|
||||||
感谢 [@langyo](https://github.com/langyo) 和 [@InfyniteHeap](https://github.com/InfyniteHeap)
|
感谢 @langyo 和 @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 格式的详细信息
|
||||||
|
|
||||||
## 概况
|
## 概况
|
||||||
@ -139,9 +137,3 @@ 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.4"
|
version = "0.4.3"
|
||||||
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,9 +20,3 @@ 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"
|
|
||||||
|
@ -721,26 +721,6 @@ 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 数组
|
||||||
///
|
///
|
||||||
/// # 安全性
|
/// # 安全性
|
||||||
@ -748,39 +728,15 @@ 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 mut value: Vec<i32> = Vec::with_capacity(len);
|
let value =
|
||||||
std::ptr::copy_nonoverlapping(
|
std::slice::from_raw_parts(self.data[self.cursor..].as_ptr() as *const i32, len);
|
||||||
self.data[self.cursor..].as_ptr() as *const u8,
|
let mut value = value.to_vec();
|
||||||
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 数组
|
||||||
///
|
///
|
||||||
/// # 安全性
|
/// # 安全性
|
||||||
@ -799,6 +755,22 @@ 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,51 +280,9 @@ 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]
|
||||||
@ -513,65 +471,4 @@ 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 => Err(NbtError::WrongRootType(x.tag())),
|
x => return Err(NbtError::WrongRootType(x.tag())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
|
Loading…
Reference in New Issue
Block a user