voidmc_codec/primitives/
nbt.rs1use crate::{Decode, DecodeError, Encode};
2use ussr_nbt::owned::Nbt;
3
4struct NbtReader<'a> {
7 read_bytes: usize,
8 inner: &'a [u8],
9}
10
11impl<'a> NbtReader<'a> {
12 fn new(inner: &'a [u8]) -> Self {
13 Self {
14 read_bytes: 0,
15 inner,
16 }
17 }
18}
19
20impl<'a> std::io::Read for NbtReader<'a> {
21 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
22 let len = buf.len();
23 if len == 0 {
24 return Ok(0);
25 }
26
27 if self.read_bytes == 0 {
29 buf[0] = 0x0A; self.read_bytes = 1;
31 return Ok(1);
32 }
33 if self.read_bytes == 1 {
34 buf[0] = 0x00; self.read_bytes = 2;
36 return Ok(1);
37 }
38 if self.read_bytes == 2 {
39 buf[0] = 0x00; self.read_bytes = 3;
41 let _ = self.inner.read(&mut [0])?;
43 return Ok(1);
44 }
45
46 match self.inner.read(buf) {
48 Ok(n) => {
49 self.read_bytes += n;
50 Ok(n)
51 }
52 Err(e) => Err(e),
53 }
54 }
55}
56
57impl Encode for Nbt {
58 fn encode(&self, buf: &mut Vec<u8>) {
59 let mut temp_buf = Vec::new();
60 if self.write(&mut temp_buf).is_ok() {
61 buf.push(0x0A);
63 if temp_buf.len() > 3 {
66 buf.extend_from_slice(&temp_buf[3..]);
67 }
68 }
69 }
70}
71
72impl Decode for Nbt {
73 fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
74 let mut reader = NbtReader::new(buf);
76
77 match Nbt::read(&mut reader) {
78 Ok(nbt) => {
79 let consumed = reader.read_bytes - 3; *buf = &buf[consumed..];
83 Ok(nbt)
84 }
85 Err(_) => Err(DecodeError::InvalidLength),
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_nbt_encode_produces_output() {
96 let nbt_data = vec![
99 0x0A, 0x00, 0x00, 0x00, ];
103
104 let mut slice = nbt_data.as_slice();
105 let nbt = Nbt::read(&mut slice).expect("Failed to read NBT");
106
107 let mut buf = Vec::new();
109 nbt.encode(&mut buf);
110
111 assert_eq!(buf.len(), 2); assert_eq!(buf[0], 0x0A); assert_eq!(buf[1], 0x00); }
118
119 #[test]
120 fn test_nbt_decode_with_reader() {
121 let nbt_data = vec![
124 0x00, ];
126
127 let mut slice = nbt_data.as_slice();
128 let result = Nbt::decode(&mut slice);
129
130 let _ = result;
132 }
133
134 #[test]
135 fn test_nbt_truncated_data() {
136 let mut slice = &[][..];
137 let result = Nbt::decode(&mut slice);
138
139 assert!(result.is_err());
141 }
142
143 #[test]
144 fn test_nbt_empty_buffer() {
145 let buf = Vec::new();
146 let mut slice = buf.as_slice();
147
148 let result = Nbt::decode(&mut slice);
149 assert!(result.is_err());
150 }
151
152 #[test]
153 fn test_nbt_roundtrip_simple() {
154 use ussr_nbt::owned::Tag;
155
156 let original_nbt = Nbt {
158 name: "".into(),
159 compound: vec![("field".into(), Tag::String("value".into()))].into(),
160 };
161
162 let mut encoded = Vec::new();
164 original_nbt.encode(&mut encoded);
165
166 assert!(!encoded.is_empty());
167 assert_eq!(encoded[0], 0x0A, "First byte should be compound tag");
168
169 let mut buf_slice = encoded.as_slice();
171 let decoded_nbt = Nbt::decode(&mut buf_slice).expect("Failed to decode NBT");
172
173 assert_eq!(decoded_nbt.name, original_nbt.name);
175 assert_eq!(
176 decoded_nbt.compound.tags.len(),
177 original_nbt.compound.tags.len(),
178 "Should have same number of fields"
179 );
180 }
181
182 #[test]
183 fn test_nbt_roundtrip_multiple_tags() {
184 use ussr_nbt::owned::Tag;
185
186 let original_nbt = Nbt {
188 name: "".into(),
189 compound: vec![
190 (
191 "wild_texture".into(),
192 Tag::String("minecraft:entity/wolf/wolf_ashen".into()),
193 ),
194 ("flag".into(), Tag::Byte(1)),
195 ]
196 .into(),
197 };
198
199 let mut encoded = Vec::new();
201 original_nbt.encode(&mut encoded);
202
203 assert_eq!(encoded[0], 0x0A);
204
205 let mut buf_slice = encoded.as_slice();
207 let decoded = Nbt::decode(&mut buf_slice).expect("Failed to decode multi-tag NBT");
208
209 assert_eq!(decoded.name, original_nbt.name);
211 assert_eq!(
212 decoded.compound.tags.len(),
213 original_nbt.compound.tags.len()
214 );
215 }
216}