1#![allow(clippy::redundant_field_names, reason = "Triggered by `Error` derive")]
2
3mod extra;
4mod primitives;
5
6use std::{
7 backtrace::Backtrace,
8 io::{self, Cursor, Write},
9};
10
11use thiserror::Error;
12
13pub trait AzBuf
15where
16 Self: Sized,
17{
18 fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError>;
19 fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()>;
20}
21
22pub trait AzBufVar
26where
27 Self: Sized,
28{
29 fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError>;
30 fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()>;
31}
32
33pub trait AzBufLimited
41where
42 Self: Sized,
43{
44 fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: u32) -> Result<Self, BufReadError>;
45}
46
47#[derive(Debug, Error)]
48pub enum BufReadError {
49 #[error("Invalid VarInt")]
50 InvalidVarInt,
51 #[error("Invalid VarLong")]
52 InvalidVarLong,
53 #[error("Error reading bytes")]
54 CouldNotReadBytes,
55 #[error(
56 "The received encoded string buffer length is longer than maximum allowed ({length} > {max_length})"
57 )]
58 StringLengthTooLong { length: u32, max_length: u32 },
59 #[error("The received Vec length is longer than maximum allowed ({length} > {max_length})")]
60 VecLengthTooLong { length: u32, max_length: u32 },
61 #[error("{source}")]
62 Io {
63 #[from]
64 #[backtrace]
65 source: io::Error,
66 },
67 #[error("Invalid UTF-8: {bytes:?} (lossy: {lossy:?})")]
68 InvalidUtf8 {
69 bytes: Vec<u8>,
70 lossy: String,
71 },
73 #[error("Unexpected enum variant {id}")]
74 UnexpectedEnumVariant { id: i32 },
75 #[error("Unexpected enum variant {id}")]
76 UnexpectedStringEnumVariant { id: String },
77 #[error("Tried to read {attempted_read} bytes but there were only {actual_read}")]
78 UnexpectedEof {
79 attempted_read: usize,
80 actual_read: usize,
81 backtrace: Backtrace,
82 },
83 #[error("{0}")]
84 Custom(String),
85 #[cfg(feature = "serde_json")]
86 #[error("{source}")]
87 Deserialization {
88 #[from]
89 #[backtrace]
90 source: serde_json::Error,
91 },
92 #[error("{source}")]
93 Nbt {
94 #[from]
95 #[backtrace]
96 source: simdnbt::Error,
97 },
98 #[error("{source}")]
99 DeserializeNbt {
100 #[from]
101 #[backtrace]
102 source: simdnbt::DeserializeError,
103 },
104}
105
106pub(crate) fn read_bytes<'a>(
107 buf: &'a mut Cursor<&[u8]>,
108 length: usize,
109) -> Result<&'a [u8], BufReadError> {
110 if length > (buf.get_ref().len() - buf.position() as usize) {
111 return Err(BufReadError::UnexpectedEof {
112 attempted_read: length,
113 actual_read: buf.get_ref().len() - buf.position() as usize,
114 backtrace: Backtrace::capture(),
115 });
116 }
117 let initial_position = buf.position() as usize;
118 buf.set_position(buf.position() + length as u64);
119 let data = &buf.get_ref()[initial_position..initial_position + length];
120 Ok(data)
121}
122
123pub(crate) fn read_utf_with_len<'a>(
124 buf: &'a mut Cursor<&[u8]>,
125 max_length: u32,
126) -> Result<&'a str, BufReadError> {
127 let length = u32::azalea_read_var(buf)?;
128 if length > max_length * 4 {
130 return Err(BufReadError::StringLengthTooLong {
131 length,
132 max_length: max_length * 4,
133 });
134 }
135
136 let buffer = read_bytes(buf, length as usize)?;
137 let string = std::str::from_utf8(buffer).map_err(|_| BufReadError::InvalidUtf8 {
138 bytes: buffer.to_vec(),
139 lossy: String::from_utf8_lossy(buffer).to_string(),
140 })?;
142 if string.len() > length as usize {
143 return Err(BufReadError::StringLengthTooLong { length, max_length });
144 }
145
146 Ok(string)
147}
148
149pub(crate) fn write_utf_with_len(
150 buf: &mut impl Write,
151 string: &str,
152 max_len: u32,
153) -> io::Result<()> {
154 let actual_len = string.len();
155 if actual_len > max_len as usize {
156 panic!("String too big (was {actual_len} bytes encoded, max {max_len})");
157 }
158 string.as_bytes().to_vec().azalea_write(buf)?;
159 Ok(())
160}