1use std::{
7 collections::HashMap,
8 error::Error,
9 fmt,
10 io::{self, Read, Write},
11};
12
13use fastnbt::{
14 SerOpts, Tag,
15 stream::{Parser, Value as StreamValue},
16};
17use mcproto_codec::{
18 error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
19 io::write_all_counted,
20};
21
22use crate::{TypeCodec, component::NbtComponent};
23
24pub use fastnbt::Value as NbtValue;
26
27#[derive(Debug, Clone, PartialEq)]
36pub struct TextComponent(
37 pub NbtComponent,
39);
40
41impl TextComponent {
42 pub const MAX_DECODE_BYTES: usize = 2 * 1024 * 1024;
44 pub const MAX_DECODE_NODES: usize = 262_144;
46 const MAX_COMPONENT_DEPTH: usize = 512;
47
48 pub fn text(value: impl Into<String>) -> Self {
50 Self(NbtComponent::text(value))
51 }
52}
53
54impl Default for TextComponent {
55 fn default() -> Self {
56 Self::text("")
57 }
58}
59
60impl From<NbtComponent> for TextComponent {
61 fn from(value: NbtComponent) -> Self {
62 Self(value)
63 }
64}
65
66impl From<String> for TextComponent {
67 fn from(value: String) -> Self {
68 Self::text(value)
69 }
70}
71
72impl From<&str> for TextComponent {
73 fn from(value: &str) -> Self {
74 Self::text(value)
75 }
76}
77
78impl TypeCodec for TextComponent {
79 fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
80 self.0
81 .validate_depth(Self::MAX_COMPONENT_DEPTH)
82 .map_err(|source| invalid_nbt(CodecOperation::Write, 0, source))?;
83 self.0
84 .validate_dynamic_depth(Self::MAX_COMPONENT_DEPTH)
85 .map_err(|source| invalid_nbt(CodecOperation::Write, 0, source))?;
86 let normalized = self.0.normalized_root_for_nbt();
87 let value = fastnbt::to_value(&normalized)
88 .map_err(|source| invalid_nbt(CodecOperation::Write, 0, source))?;
89 validate_nbt_strings(&value, CodecOperation::Write, 0)?;
90 encode_root(&value, writer)
91 }
92
93 fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
94 let (value, bytes_processed) = decode_root(reader)?;
95 let component: NbtComponent = fastnbt::from_value(&value)
96 .map_err(|source| invalid_nbt(CodecOperation::Read, bytes_processed, source))?;
97 component
98 .validate_depth(Self::MAX_COMPONENT_DEPTH)
99 .map_err(|source| invalid_nbt(CodecOperation::Read, bytes_processed, source))?;
100 component
101 .validate_dynamic_depth(Self::MAX_COMPONENT_DEPTH)
102 .map_err(|source| invalid_nbt(CodecOperation::Read, bytes_processed, source))?;
103 Ok(Self(component.normalized_root_for_nbt()))
104 }
105}
106
107fn encode_root(value: &NbtValue, writer: &mut impl Write) -> Result<(), CodecError> {
108 let wrapper = HashMap::from([("", value)]);
109 let encoded = fastnbt::to_bytes_with_opts(&wrapper, SerOpts::network_nbt())
110 .map_err(|source| invalid_nbt(CodecOperation::Write, 0, source))?;
111
112 if encoded.len() < 5
113 || encoded[0] != Tag::Compound as u8
114 || encoded[2..4] != [0, 0]
115 || encoded.last() != Some(&(Tag::End as u8))
116 || !matches!(encoded[1], 8 | 10)
117 {
118 return Err(CodecError::invalid_encoding_for_operation(
119 CodecKind::TextComponent,
120 CodecOperation::Write,
121 0,
122 InvalidEncodingReason::InvalidNbt,
123 ));
124 }
125
126 write_all_counted(writer, &encoded[1..2], CodecKind::TextComponent, 0)?;
127 write_all_counted(
128 writer,
129 &encoded[4..encoded.len() - 1],
130 CodecKind::TextComponent,
131 1,
132 )
133}
134
135fn decode_root(reader: &mut impl Read) -> Result<(NbtValue, usize), CodecError> {
136 let mut reader = CountedReader::new(reader, TextComponent::MAX_DECODE_BYTES);
137 let mut root_tag = [0_u8; 1];
138 reader.read_exact(&mut root_tag).map_err(|source| {
139 CodecError::from_read_error(CodecKind::TextComponent, reader.processed, source)
140 })?;
141 if !matches!(root_tag[0], 8 | 10) {
142 return Err(CodecError::invalid_encoding(
143 CodecKind::TextComponent,
144 reader.processed,
145 InvalidEncodingReason::InvalidTextComponentRootTag { tag: root_tag[0] },
146 ));
147 }
148
149 let mut prefixed = PrefixReader {
151 prefix: &[root_tag[0], 0, 0],
152 inner: &mut reader,
153 };
154 let result = (|| {
155 let mut parser = Parser::new(&mut prefixed);
156 let first = parser.next().map_err(NbtTreeError::Parser)?;
157 let mut remaining_nodes = TextComponent::MAX_DECODE_NODES;
158 parse_stream_value(&mut parser, first, 1, &mut remaining_nodes)
159 })();
160
161 match result {
162 Ok(value) => Ok((value, prefixed.inner.processed)),
163 Err(error) => {
164 let processed = prefixed.inner.processed;
165 if prefixed.inner.limit_exceeded {
166 return Err(CodecError::invalid_encoding(
167 CodecKind::TextComponent,
168 processed,
169 InvalidEncodingReason::TooLong {
170 max_bytes: TextComponent::MAX_DECODE_BYTES,
171 },
172 ));
173 }
174 match prefixed.inner.failure.take() {
175 Some(source) => Err(CodecError::from_read_error(
176 CodecKind::TextComponent,
177 processed,
178 source,
179 )),
180 None => Err(invalid_nbt(CodecOperation::Read, processed, error)),
181 }
182 }
183 }
184}
185
186fn parse_stream_value<R: Read>(
187 parser: &mut Parser<R>,
188 value: StreamValue,
189 depth: usize,
190 remaining_nodes: &mut usize,
191) -> Result<NbtValue, NbtTreeError> {
192 *remaining_nodes = remaining_nodes
193 .checked_sub(1)
194 .ok_or(NbtTreeError::TooManyNodes)?;
195 if depth > TextComponent::MAX_COMPONENT_DEPTH {
196 return Err(NbtTreeError::TooDeep);
197 }
198 match value {
199 StreamValue::Byte(_, value) => Ok(NbtValue::Byte(value)),
200 StreamValue::Short(_, value) => Ok(NbtValue::Short(value)),
201 StreamValue::Int(_, value) => Ok(NbtValue::Int(value)),
202 StreamValue::Long(_, value) => Ok(NbtValue::Long(value)),
203 StreamValue::Float(_, value) => Ok(NbtValue::Float(value)),
204 StreamValue::Double(_, value) => Ok(NbtValue::Double(value)),
205 StreamValue::ByteArray(_, value) => Ok(NbtValue::ByteArray(fastnbt::ByteArray::new(value))),
206 StreamValue::String(_, value) => Ok(NbtValue::String(value)),
207 StreamValue::IntArray(_, value) => Ok(NbtValue::IntArray(fastnbt::IntArray::new(value))),
208 StreamValue::LongArray(_, value) => Ok(NbtValue::LongArray(fastnbt::LongArray::new(value))),
209 StreamValue::List(_, _, length) => {
210 let length = usize::try_from(length).map_err(|_| NbtTreeError::InvalidStructure)?;
211 let mut values = Vec::with_capacity(length.min(1024));
213 for _ in 0..length {
214 let value = parser.next().map_err(NbtTreeError::Parser)?;
215 values.push(parse_stream_value(
216 parser,
217 value,
218 depth + 1,
219 remaining_nodes,
220 )?);
221 }
222 if !matches!(parser.next(), Ok(StreamValue::ListEnd)) {
223 return Err(NbtTreeError::InvalidStructure);
224 }
225 Ok(NbtValue::List(values))
226 }
227 StreamValue::Compound(_) => {
228 let mut values = HashMap::new();
229 loop {
230 let value = parser.next().map_err(NbtTreeError::Parser)?;
231 if matches!(value, StreamValue::CompoundEnd) {
232 break;
233 }
234 let name = stream_name(&value).ok_or(NbtTreeError::InvalidStructure)?;
235 values.insert(
236 name.to_owned(),
237 parse_stream_value(parser, value, depth + 1, remaining_nodes)?,
238 );
239 }
240 Ok(NbtValue::Compound(values))
241 }
242 StreamValue::ListEnd | StreamValue::CompoundEnd => Err(NbtTreeError::InvalidStructure),
243 }
244}
245
246fn stream_name(value: &StreamValue) -> Option<&str> {
247 match value {
248 StreamValue::Byte(name, _)
249 | StreamValue::Short(name, _)
250 | StreamValue::Int(name, _)
251 | StreamValue::Long(name, _)
252 | StreamValue::Float(name, _)
253 | StreamValue::Double(name, _)
254 | StreamValue::ByteArray(name, _)
255 | StreamValue::String(name, _)
256 | StreamValue::List(name, _, _)
257 | StreamValue::Compound(name)
258 | StreamValue::IntArray(name, _)
259 | StreamValue::LongArray(name, _) => name.as_deref(),
260 StreamValue::ListEnd | StreamValue::CompoundEnd => None,
261 }
262}
263
264#[derive(Debug)]
265enum NbtTreeError {
266 Parser(fastnbt::stream::Error),
267 InvalidStructure,
268 TooDeep,
269 TooManyNodes,
270}
271
272impl fmt::Display for NbtTreeError {
273 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
274 match self {
275 Self::Parser(source) => source.fmt(formatter),
276 Self::InvalidStructure => formatter.write_str("invalid NBT tree structure"),
277 Self::TooDeep => formatter.write_str("NBT tree is nested too deeply"),
278 Self::TooManyNodes => formatter.write_str("NBT tree contains too many nodes"),
279 }
280 }
281}
282
283impl Error for NbtTreeError {
284 fn source(&self) -> Option<&(dyn Error + 'static)> {
285 match self {
286 Self::Parser(source) => Some(source),
287 _ => None,
288 }
289 }
290}
291
292fn invalid_nbt(
293 operation: CodecOperation,
294 bytes_processed: usize,
295 source: impl Error + Send + Sync + 'static,
296) -> CodecError {
297 CodecError::invalid_encoding_for_operation_with_source(
298 CodecKind::TextComponent,
299 operation,
300 bytes_processed,
301 InvalidEncodingReason::InvalidNbt,
302 source,
303 )
304}
305
306fn validate_nbt_strings(
307 root: &NbtValue,
308 operation: CodecOperation,
309 bytes_processed: usize,
310) -> Result<(), CodecError> {
311 let mut pending = vec![root];
312 while let Some(value) = pending.pop() {
313 match value {
314 NbtValue::String(value) => validate_nbt_string(value, operation, bytes_processed)?,
315 NbtValue::List(values) => pending.extend(values),
316 NbtValue::Compound(values) => {
317 for (name, value) in values {
318 validate_nbt_string(name, operation, bytes_processed)?;
319 pending.push(value);
320 }
321 }
322 _ => {}
323 }
324 }
325 Ok(())
326}
327
328fn validate_nbt_string(
329 value: &str,
330 operation: CodecOperation,
331 bytes_processed: usize,
332) -> Result<(), CodecError> {
333 let encoded_len = value.chars().try_fold(0_usize, |length, character| {
334 let width = match character as u32 {
335 0 => 2,
336 1..=0x7f => 1,
337 0x80..=0x7ff => 2,
338 0x800..=0xffff => 3,
339 _ => 6,
340 };
341 length.checked_add(width)
342 });
343 if !matches!(encoded_len, Some(length) if length <= u16::MAX as usize) {
344 return Err(CodecError::invalid_encoding_for_operation(
345 CodecKind::TextComponent,
346 operation,
347 bytes_processed,
348 InvalidEncodingReason::StringTooLong {
349 max_bytes: u16::MAX as usize,
350 },
351 ));
352 }
353 Ok(())
354}
355
356struct CountedReader<'a, R: ?Sized> {
363 inner: &'a mut R,
364 processed: usize,
365 byte_limit: usize,
366 limit_exceeded: bool,
367 failure: Option<io::Error>,
368}
369
370impl<'a, R: ?Sized> CountedReader<'a, R> {
371 fn new(inner: &'a mut R, byte_limit: usize) -> Self {
372 Self {
373 inner,
374 processed: 0,
375 byte_limit,
376 limit_exceeded: false,
377 failure: None,
378 }
379 }
380}
381
382impl<R: Read + ?Sized> Read for CountedReader<'_, R> {
383 fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
384 loop {
385 if buffer.is_empty() {
386 return Ok(0);
387 }
388 let remaining = self.byte_limit.saturating_sub(self.processed);
389 if remaining == 0 {
390 self.limit_exceeded = true;
391 return Err(io::Error::other("NBT byte limit exceeded"));
392 }
393 let buffer_len = buffer.len().min(remaining);
394 match self.inner.read(&mut buffer[..buffer_len]) {
395 Ok(0) => {
396 let error =
397 io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected end of NBT");
398 self.failure = Some(error);
399 return Err(io::Error::new(
400 io::ErrorKind::UnexpectedEof,
401 "unexpected end of NBT",
402 ));
403 }
404 Ok(read) => {
405 self.processed += read;
406 return Ok(read);
407 }
408 Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
409 Err(error) => {
410 let returned = clone_io_error(&error);
411 self.failure = Some(error);
412 return Err(returned);
413 }
414 }
415 }
416 }
417}
418
419struct PrefixReader<'a, 'b, R: ?Sized> {
420 prefix: &'a [u8],
421 inner: &'a mut CountedReader<'b, R>,
422}
423
424impl<R: Read + ?Sized> Read for PrefixReader<'_, '_, R> {
425 fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
426 if buffer.is_empty() {
427 return Ok(0);
428 }
429 if !self.prefix.is_empty() {
430 let read = buffer.len().min(self.prefix.len());
431 buffer[..read].copy_from_slice(&self.prefix[..read]);
432 self.prefix = &self.prefix[read..];
433 return Ok(read);
434 }
435 self.inner.read(buffer)
436 }
437}
438
439fn clone_io_error(error: &io::Error) -> io::Error {
440 match error.raw_os_error() {
441 Some(code) => io::Error::from_raw_os_error(code),
442 None => io::Error::new(error.kind(), error.to_string()),
443 }
444}