aboutsummaryrefslogtreecommitdiff
path: root/azalea-buf/src
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2026-01-13 10:51:45 -0600
committerGitHub <noreply@github.com>2026-01-13 10:51:45 -0600
commitb21ac946cafaacc9ee2478ea48ed9e72554f79ed (patch)
tree4d05744b9801e94f5da6563d8fabddfb20d1c7b7 /azalea-buf/src
parentd5fa5e32b37754b3b5c136e58821e48cd3b7c2ff (diff)
downloadazalea-drasl-b21ac946cafaacc9ee2478ea48ed9e72554f79ed.tar.xz
Merge AzaleaRead and AzaleaWrite (#305)
Diffstat (limited to 'azalea-buf/src')
-rw-r--r--azalea-buf/src/impls/extra.rs309
-rw-r--r--azalea-buf/src/impls/mod.rs158
-rw-r--r--azalea-buf/src/impls/primitives.rs213
-rw-r--r--azalea-buf/src/lib.rs8
-rw-r--r--azalea-buf/src/read.rs465
-rw-r--r--azalea-buf/src/serializable_uuid.rs7
-rw-r--r--azalea-buf/src/write.rs341
7 files changed, 685 insertions, 816 deletions
diff --git a/azalea-buf/src/impls/extra.rs b/azalea-buf/src/impls/extra.rs
new file mode 100644
index 00000000..f22ddb1a
--- /dev/null
+++ b/azalea-buf/src/impls/extra.rs
@@ -0,0 +1,309 @@
+use std::{
+ collections::HashMap,
+ hash::Hash,
+ io::{self, Cursor, Write},
+ sync::Arc,
+};
+
+use indexmap::IndexMap;
+
+use crate::{
+ AzBuf, AzBufLimited, AzBufVar, BufReadError, MAX_STRING_LENGTH, UnsizedByteArray, read_bytes,
+ read_utf_with_len, write_utf_with_len,
+};
+
+impl AzBuf for UnsizedByteArray {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ // read to end of the buffer
+ let data = buf.get_ref()[buf.position() as usize..].to_vec();
+ buf.set_position((buf.position()) + data.len() as u64);
+ Ok(UnsizedByteArray(data))
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_all(self)
+ }
+}
+
+macro_rules! impl_for_map_type {
+ ($ty: ident) => {
+ impl<K: AzBuf + Eq + Hash, V: AzBuf> AzBuf for $ty<K, V> {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let length = i32::azalea_read_var(buf)? as usize;
+ let mut contents = Self::with_capacity(usize::min(length, 65536));
+ for _ in 0..length {
+ contents.insert(K::azalea_read(buf)?, V::azalea_read(buf)?);
+ }
+ Ok(contents)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ u32::azalea_write_var(&(self.len() as u32), buf)?;
+ for (key, value) in self {
+ key.azalea_write(buf)?;
+ value.azalea_write(buf)?;
+ }
+
+ Ok(())
+ }
+ }
+ impl<K: AzBuf + Eq + Hash, V: AzBufVar> AzBufVar for $ty<K, V> {
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let length = i32::azalea_read_var(buf)? as usize;
+ let mut contents = Self::with_capacity(usize::min(length, 65536));
+ for _ in 0..length {
+ contents.insert(K::azalea_read(buf)?, V::azalea_read_var(buf)?);
+ }
+ Ok(contents)
+ }
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ u32::azalea_write_var(&(self.len() as u32), buf)?;
+ for (key, value) in self {
+ key.azalea_write(buf)?;
+ value.azalea_write_var(buf)?;
+ }
+
+ Ok(())
+ }
+ }
+ };
+}
+
+impl_for_map_type!(HashMap);
+impl_for_map_type!(IndexMap);
+
+macro_rules! impl_for_list_type {
+ ($ty: ty) => {
+ impl<T: AzBuf> AzBuf for $ty {
+ default fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let length = u32::azalea_read_var(buf)? as usize;
+ // we limit the capacity to not get exploited into allocating a bunch
+ let mut contents = Vec::with_capacity(usize::min(length, 65536));
+ for _ in 0..length {
+ contents.push(T::azalea_read(buf)?);
+ }
+ Ok(contents.into())
+ }
+ default fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ (self.len() as u32).azalea_write_var(buf)?;
+ for item in self {
+ T::azalea_write(item, buf)?;
+ }
+ Ok(())
+ }
+ }
+ impl<T: AzBufVar> AzBufVar for $ty {
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let length = i32::azalea_read_var(buf)? as usize;
+ let mut contents = Vec::with_capacity(usize::min(length, 65536));
+ for _ in 0..length {
+ contents.push(T::azalea_read_var(buf)?);
+ }
+ Ok(contents.into())
+ }
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ (self.len() as u32).azalea_write_var(buf)?;
+ for item in self {
+ T::azalea_write_var(item, buf)?;
+ }
+ Ok(())
+ }
+ }
+ impl<T: AzBuf> AzBufLimited for $ty {
+ fn azalea_read_limited(
+ buf: &mut Cursor<&[u8]>,
+ limit: u32,
+ ) -> Result<Self, BufReadError> {
+ let length = u32::azalea_read_var(buf)?;
+ if length > limit {
+ return Err(BufReadError::VecLengthTooLong {
+ length: length as u32,
+ max_length: limit as u32,
+ });
+ }
+
+ let mut contents = Vec::with_capacity(u32::min(length, 65536) as usize);
+ for _ in 0..length {
+ contents.push(T::azalea_read(buf)?);
+ }
+ Ok(contents.into())
+ }
+ }
+ };
+}
+
+impl_for_list_type!(Vec<T>);
+impl_for_list_type!(Box<[T]>);
+
+impl AzBuf for Vec<u8> {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let length = i32::azalea_read_var(buf)? as usize;
+ read_bytes(buf, length).map(|b| b.to_vec())
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ (self.len() as u32).azalea_write_var(buf)?;
+ buf.write_all(self)
+ }
+}
+
+impl AzBuf for String {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ read_utf_with_len(buf, MAX_STRING_LENGTH).map(Into::into)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ write_utf_with_len(buf, self, MAX_STRING_LENGTH)
+ }
+}
+impl AzBufLimited for String {
+ fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: u32) -> Result<Self, BufReadError> {
+ read_utf_with_len(buf, limit).map(Into::into)
+ }
+}
+
+impl AzBuf for Box<str> {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ read_utf_with_len(buf, MAX_STRING_LENGTH).map(Into::into)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ write_utf_with_len(buf, self, MAX_STRING_LENGTH)
+ }
+}
+
+impl AzBufLimited for Box<str> {
+ fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: u32) -> Result<Self, BufReadError> {
+ String::azalea_read_limited(buf, limit).map(Into::into)
+ }
+}
+
+impl<T: AzBuf> AzBuf for Option<T> {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let present = bool::azalea_read(buf)?;
+ Ok(if present {
+ Some(T::azalea_read(buf)?)
+ } else {
+ None
+ })
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ if let Some(s) = self {
+ true.azalea_write(buf)?;
+ s.azalea_write(buf)?;
+ } else {
+ false.azalea_write(buf)?;
+ };
+ Ok(())
+ }
+}
+
+impl<T: AzBufVar> AzBufVar for Option<T> {
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let present = bool::azalea_read(buf)?;
+ Ok(if present {
+ Some(T::azalea_read_var(buf)?)
+ } else {
+ None
+ })
+ }
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ if let Some(s) = self {
+ true.azalea_write(buf)?;
+ s.azalea_write_var(buf)?;
+ } else {
+ false.azalea_write(buf)?;
+ };
+ Ok(())
+ }
+}
+impl<T: AzBufLimited> AzBufLimited for Option<T> {
+ fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: u32) -> Result<Self, BufReadError> {
+ let present = bool::azalea_read(buf)?;
+ Ok(if present {
+ Some(T::azalea_read_limited(buf, limit)?)
+ } else {
+ None
+ })
+ }
+}
+
+impl<T: AzBuf, const N: usize> AzBuf for [T; N] {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let mut contents = Vec::with_capacity(N);
+ for _ in 0..N {
+ contents.push(T::azalea_read(buf)?);
+ }
+ Ok(contents
+ .try_into()
+ .unwrap_or_else(|_| unreachable!("The vec is the same size as the array")))
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ for i in self {
+ i.azalea_write(buf)?;
+ }
+ Ok(())
+ }
+}
+
+impl AzBuf for simdnbt::owned::NbtTag {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(simdnbt::owned::read_tag(buf).map_err(simdnbt::Error::from)?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ let mut data = Vec::new();
+ self.write(&mut data);
+ buf.write_all(&data)
+ }
+}
+
+impl AzBuf for simdnbt::owned::NbtCompound {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ match simdnbt::owned::read_tag(buf).map_err(simdnbt::Error::from)? {
+ simdnbt::owned::NbtTag::Compound(compound) => Ok(compound),
+ _ => Err(BufReadError::Custom("Expected compound tag".to_owned())),
+ }
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ let mut data = Vec::new();
+ simdnbt::owned::NbtTag::Compound(self.clone()).write(&mut data);
+ buf.write_all(&data)
+ }
+}
+
+impl AzBuf for simdnbt::owned::Nbt {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(simdnbt::owned::read_unnamed(buf)?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ let mut data = Vec::new();
+ self.write_unnamed(&mut data);
+ buf.write_all(&data)
+ }
+}
+
+impl<T> AzBuf for Box<T>
+where
+ T: AzBuf,
+{
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ T::azalea_read(buf).map(Box::new)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ T::azalea_write(&**self, buf)
+ }
+}
+
+impl<A: AzBuf, B: AzBuf> AzBuf for (A, B) {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok((A::azalea_read(buf)?, B::azalea_read(buf)?))
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ self.0.azalea_write(buf)?;
+ self.1.azalea_write(buf)
+ }
+}
+
+impl<T: AzBuf> AzBuf for Arc<T> {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(Arc::new(T::azalea_read(buf)?))
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ T::azalea_write(&**self, buf)
+ }
+}
diff --git a/azalea-buf/src/impls/mod.rs b/azalea-buf/src/impls/mod.rs
new file mode 100644
index 00000000..37f53624
--- /dev/null
+++ b/azalea-buf/src/impls/mod.rs
@@ -0,0 +1,158 @@
+mod extra;
+mod primitives;
+
+use std::{
+ backtrace::Backtrace,
+ io::{self, Cursor, Write},
+};
+
+use thiserror::Error;
+
+/// A trait that's implemented on types that are used by the Minecraft protocol.
+pub trait AzBuf
+where
+ Self: Sized,
+{
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError>;
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()>;
+}
+
+/// Used for types that have an alternative variable-length encoding.
+///
+/// This mostly exists for varints.
+pub trait AzBufVar
+where
+ Self: Sized,
+{
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError>;
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()>;
+}
+
+/// Used for types that have some configurable limit.
+///
+/// For example, the implementation of this on `String` limits the maximum
+/// length of the string.
+///
+/// This exists partially as an anti-abuse mechanism in Minecraft, so there is
+/// no limited write function.
+pub trait AzBufLimited
+where
+ Self: Sized,
+{
+ fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: u32) -> Result<Self, BufReadError>;
+}
+
+#[derive(Debug, Error)]
+pub enum BufReadError {
+ #[error("Invalid VarInt")]
+ InvalidVarInt,
+ #[error("Invalid VarLong")]
+ InvalidVarLong,
+ #[error("Error reading bytes")]
+ CouldNotReadBytes,
+ #[error(
+ "The received encoded string buffer length is longer than maximum allowed ({length} > {max_length})"
+ )]
+ StringLengthTooLong { length: u32, max_length: u32 },
+ #[error("The received Vec length is longer than maximum allowed ({length} > {max_length})")]
+ VecLengthTooLong { length: u32, max_length: u32 },
+ #[error("{source}")]
+ Io {
+ #[from]
+ #[backtrace]
+ source: io::Error,
+ },
+ #[error("Invalid UTF-8: {bytes:?} (lossy: {lossy:?})")]
+ InvalidUtf8 {
+ bytes: Vec<u8>,
+ lossy: String,
+ // backtrace: Backtrace,
+ },
+ #[error("Unexpected enum variant {id}")]
+ UnexpectedEnumVariant { id: i32 },
+ #[error("Unexpected enum variant {id}")]
+ UnexpectedStringEnumVariant { id: String },
+ #[error("Tried to read {attempted_read} bytes but there were only {actual_read}")]
+ UnexpectedEof {
+ attempted_read: usize,
+ actual_read: usize,
+ backtrace: Backtrace,
+ },
+ #[error("{0}")]
+ Custom(String),
+ #[cfg(feature = "serde_json")]
+ #[error("{source}")]
+ Deserialization {
+ #[from]
+ #[backtrace]
+ source: serde_json::Error,
+ },
+ #[error("{source}")]
+ Nbt {
+ #[from]
+ #[backtrace]
+ source: simdnbt::Error,
+ },
+ #[error("{source}")]
+ DeserializeNbt {
+ #[from]
+ #[backtrace]
+ source: simdnbt::DeserializeError,
+ },
+}
+
+pub(crate) fn read_bytes<'a>(
+ buf: &'a mut Cursor<&[u8]>,
+ length: usize,
+) -> Result<&'a [u8], BufReadError> {
+ if length > (buf.get_ref().len() - buf.position() as usize) {
+ return Err(BufReadError::UnexpectedEof {
+ attempted_read: length,
+ actual_read: buf.get_ref().len() - buf.position() as usize,
+ backtrace: Backtrace::capture(),
+ });
+ }
+ let initial_position = buf.position() as usize;
+ buf.set_position(buf.position() + length as u64);
+ let data = &buf.get_ref()[initial_position..initial_position + length];
+ Ok(data)
+}
+
+pub(crate) fn read_utf_with_len<'a>(
+ buf: &'a mut Cursor<&[u8]>,
+ max_length: u32,
+) -> Result<&'a str, BufReadError> {
+ let length = u32::azalea_read_var(buf)?;
+ // i don't know why it's multiplied by 4 but it's like that in mojang's code so
+ if length > max_length * 4 {
+ return Err(BufReadError::StringLengthTooLong {
+ length,
+ max_length: max_length * 4,
+ });
+ }
+
+ let buffer = read_bytes(buf, length as usize)?;
+ let string = std::str::from_utf8(buffer).map_err(|_| BufReadError::InvalidUtf8 {
+ bytes: buffer.to_vec(),
+ lossy: String::from_utf8_lossy(buffer).to_string(),
+ // backtrace: Backtrace::capture(),
+ })?;
+ if string.len() > length as usize {
+ return Err(BufReadError::StringLengthTooLong { length, max_length });
+ }
+
+ Ok(string)
+}
+
+pub(crate) fn write_utf_with_len(
+ buf: &mut impl Write,
+ string: &str,
+ max_len: u32,
+) -> io::Result<()> {
+ let actual_len = string.len();
+ if actual_len > max_len as usize {
+ panic!("String too big (was {actual_len} bytes encoded, max {max_len})");
+ }
+ string.as_bytes().to_vec().azalea_write(buf)?;
+ Ok(())
+}
diff --git a/azalea-buf/src/impls/primitives.rs b/azalea-buf/src/impls/primitives.rs
new file mode 100644
index 00000000..023086bb
--- /dev/null
+++ b/azalea-buf/src/impls/primitives.rs
@@ -0,0 +1,213 @@
+use std::io::{self, Cursor, Read, Write};
+
+use byteorder::{BE, ReadBytesExt, WriteBytesExt};
+use tracing::warn;
+
+use crate::{AzBuf, AzBufVar, BufReadError};
+
+impl AzBuf for () {
+ fn azalea_read(_buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(())
+ }
+ fn azalea_write(&self, _buf: &mut impl Write) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl AzBuf for i32 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(buf.read_i32::<BE>()?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_i32::<BE>(*self)
+ }
+}
+
+impl AzBufVar for i32 {
+ /// Read a single varint from the reader and return the value
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ // fast varint impl based on https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67
+ let mut buffer = [0];
+ let mut ans = 0;
+ for i in 0..5 {
+ buf.read_exact(&mut buffer)?;
+ ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i);
+ if buffer[0] & 0b1000_0000 == 0 {
+ break;
+ }
+ }
+ Ok(ans)
+ }
+
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ let mut buffer = [0];
+ let mut value = *self;
+ if value == 0 {
+ buf.write_all(&buffer)?;
+ }
+ while value != 0 {
+ buffer[0] = (value & 0b0111_1111) as u8;
+ value = (value >> 7) & (i32::MAX >> 6);
+ if value != 0 {
+ buffer[0] |= 0b1000_0000;
+ }
+ buf.write_all(&buffer)?;
+ }
+ Ok(())
+ }
+}
+
+impl AzBufVar for i64 {
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let mut buffer = [0];
+ let mut ans = 0;
+ for i in 0..10 {
+ buf.read_exact(&mut buffer)
+ .map_err(|_| BufReadError::InvalidVarLong)?;
+ ans |= ((buffer[0] & 0b0111_1111) as i64) << (7 * i);
+ if buffer[0] & 0b1000_0000 == 0 {
+ break;
+ }
+ }
+ Ok(ans)
+ }
+
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ let mut buffer = [0];
+ let mut value = *self;
+ if value == 0 {
+ buf.write_all(&buffer).unwrap();
+ }
+ while value != 0 {
+ buffer[0] = (value & 0b0111_1111) as u8;
+ value = (value >> 7) & (i64::MAX >> 6);
+ if value != 0 {
+ buffer[0] |= 0b1000_0000;
+ }
+ buf.write_all(&buffer)?;
+ }
+ Ok(())
+ }
+}
+
+impl AzBufVar for u64 {
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ i64::azalea_read_var(buf).map(|i| i as u64)
+ }
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ i64::azalea_write_var(&(*self as i64), buf)
+ }
+}
+
+impl AzBuf for u32 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(i32::azalea_read(buf)? as u32)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ i32::azalea_write(&(*self as i32), buf)
+ }
+}
+
+impl AzBufVar for u32 {
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(i32::azalea_read_var(buf)? as u32)
+ }
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ i32::azalea_write_var(&(*self as i32), buf)
+ }
+}
+
+impl AzBuf for u16 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ i16::azalea_read(buf).map(|i| i as u16)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ i16::azalea_write(&(*self as i16), buf)
+ }
+}
+
+impl AzBuf for i16 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(buf.read_i16::<BE>()?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_i16::<BE>(*self)
+ }
+}
+
+impl AzBufVar for u16 {
+ fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(i32::azalea_read_var(buf)? as u16)
+ }
+ fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
+ i32::azalea_write_var(&(*self as i32), buf)
+ }
+}
+
+impl AzBuf for i64 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(buf.read_i64::<BE>()?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_i64::<BE>(*self)
+ }
+}
+
+impl AzBuf for u64 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ i64::azalea_read(buf).map(|i| i as u64)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_u64::<BE>(*self)
+ }
+}
+
+impl AzBuf for bool {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let byte = u8::azalea_read(buf)?;
+ if byte > 1 {
+ warn!("Boolean value was not 0 or 1, but {byte}");
+ }
+ Ok(byte != 0)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ let byte = u8::from(*self);
+ byte.azalea_write(buf)
+ }
+}
+
+impl AzBuf for u8 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(buf.read_u8()?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_u8(*self)
+ }
+}
+
+impl AzBuf for i8 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ u8::azalea_read(buf).map(|i| i as i8)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ (*self as u8).azalea_write(buf)
+ }
+}
+
+impl AzBuf for f32 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(buf.read_f32::<BE>()?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_f32::<BE>(*self)
+ }
+}
+
+impl AzBuf for f64 {
+ fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ Ok(buf.read_f64::<BE>()?)
+ }
+ fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
+ buf.write_f64::<BE>(*self)
+ }
+}
diff --git a/azalea-buf/src/lib.rs b/azalea-buf/src/lib.rs
index 966b1372..d6eb9299 100644
--- a/azalea-buf/src/lib.rs
+++ b/azalea-buf/src/lib.rs
@@ -4,18 +4,16 @@
#![feature(error_generic_member_access)]
mod definitions;
-mod read;
+pub mod impls;
mod serializable_uuid;
-mod write;
pub use azalea_buf_macros::*;
pub use definitions::*;
-pub use read::{AzaleaRead, AzaleaReadLimited, AzaleaReadVar, BufReadError};
+pub use impls::*;
pub use serializable_uuid::*;
-pub use write::{AzaleaWrite, AzaleaWriteVar};
// const DEFAULT_NBT_QUOTA: u32 = 2097152;
-const MAX_STRING_LENGTH: u16 = 32767;
+const MAX_STRING_LENGTH: u32 = 32767;
// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144;
#[cfg(test)]
diff --git a/azalea-buf/src/read.rs b/azalea-buf/src/read.rs
deleted file mode 100644
index f4d6a951..00000000
--- a/azalea-buf/src/read.rs
+++ /dev/null
@@ -1,465 +0,0 @@
-use std::{
- backtrace::Backtrace,
- collections::HashMap,
- hash::Hash,
- io::{self, Cursor, Read},
- sync::Arc,
-};
-
-use byteorder::{BE, ReadBytesExt};
-use indexmap::IndexMap;
-use thiserror::Error;
-use tracing::warn;
-
-use super::{MAX_STRING_LENGTH, UnsizedByteArray};
-
-#[derive(Debug, Error)]
-pub enum BufReadError {
- #[error("Invalid VarInt")]
- InvalidVarInt,
- #[error("Invalid VarLong")]
- InvalidVarLong,
- #[error("Error reading bytes")]
- CouldNotReadBytes,
- #[error(
- "The received encoded string buffer length is longer than maximum allowed ({length} > {max_length})"
- )]
- StringLengthTooLong { length: u32, max_length: u32 },
- #[error("The received Vec length is longer than maximum allowed ({length} > {max_length})")]
- VecLengthTooLong { length: u32, max_length: u32 },
- #[error("{source}")]
- Io {
- #[from]
- #[backtrace]
- source: io::Error,
- },
- #[error("Invalid UTF-8: {bytes:?} (lossy: {lossy:?})")]
- InvalidUtf8 {
- bytes: Vec<u8>,
- lossy: String,
- // backtrace: Backtrace,
- },
- #[error("Unexpected enum variant {id}")]
- UnexpectedEnumVariant { id: i32 },
- #[error("Unexpected enum variant {id}")]
- UnexpectedStringEnumVariant { id: String },
- #[error("Tried to read {attempted_read} bytes but there were only {actual_read}")]
- UnexpectedEof {
- attempted_read: usize,
- actual_read: usize,
- backtrace: Backtrace,
- },
- #[error("{0}")]
- Custom(String),
- #[cfg(feature = "serde_json")]
- #[error("{source}")]
- Deserialization {
- #[from]
- #[backtrace]
- source: serde_json::Error,
- },
- #[error("{source}")]
- Nbt {
- #[from]
- #[backtrace]
- source: simdnbt::Error,
- },
- #[error("{source}")]
- DeserializeNbt {
- #[from]
- #[backtrace]
- source: simdnbt::DeserializeError,
- },
-}
-
-fn read_bytes<'a>(buf: &'a mut Cursor<&[u8]>, length: usize) -> Result<&'a [u8], BufReadError> {
- if length > (buf.get_ref().len() - buf.position() as usize) {
- return Err(BufReadError::UnexpectedEof {
- attempted_read: length,
- actual_read: buf.get_ref().len() - buf.position() as usize,
- backtrace: Backtrace::capture(),
- });
- }
- let initial_position = buf.position() as usize;
- buf.set_position(buf.position() + length as u64);
- let data = &buf.get_ref()[initial_position..initial_position + length];
- Ok(data)
-}
-
-fn read_utf_with_len(buf: &mut Cursor<&[u8]>, max_length: u32) -> Result<String, BufReadError> {
- let length = u32::azalea_read_var(buf)?;
- // i don't know why it's multiplied by 4 but it's like that in mojang's code so
- if length > max_length * 4 {
- return Err(BufReadError::StringLengthTooLong {
- length,
- max_length: max_length * 4,
- });
- }
-
- let buffer = read_bytes(buf, length as usize)?;
- let string = std::str::from_utf8(buffer)
- .map_err(|_| BufReadError::InvalidUtf8 {
- bytes: buffer.to_vec(),
- lossy: String::from_utf8_lossy(buffer).to_string(),
- // backtrace: Backtrace::capture(),
- })?
- .to_owned();
- if string.len() > length as usize {
- return Err(BufReadError::StringLengthTooLong { length, max_length });
- }
-
- Ok(string)
-}
-
-pub trait AzaleaRead
-where
- Self: Sized,
-{
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError>;
-}
-
-pub trait AzaleaReadVar
-where
- Self: Sized,
-{
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError>;
-}
-
-// note that there's no Write equivalent for this trait since we don't really
-// care if we're writing over the limit (and maybe we already know that the
-// server implementation accepts it)
-pub trait AzaleaReadLimited
-where
- Self: Sized,
-{
- fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: usize) -> Result<Self, BufReadError>;
-}
-
-impl AzaleaRead for () {
- fn azalea_read(_buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(())
- }
-}
-
-impl AzaleaRead for i32 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(buf.read_i32::<BE>()?)
- }
-}
-
-impl AzaleaReadVar for i32 {
- /// Read a single varint from the reader and return the value
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- // fast varint impl based on https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67
- let mut buffer = [0];
- let mut ans = 0;
- for i in 0..5 {
- buf.read_exact(&mut buffer)?;
- ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i);
- if buffer[0] & 0b1000_0000 == 0 {
- break;
- }
- }
- Ok(ans)
- }
-}
-
-impl AzaleaReadVar for i64 {
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let mut buffer = [0];
- let mut ans = 0;
- for i in 0..10 {
- buf.read_exact(&mut buffer)
- .map_err(|_| BufReadError::InvalidVarLong)?;
- ans |= ((buffer[0] & 0b0111_1111) as i64) << (7 * i);
- if buffer[0] & 0b1000_0000 == 0 {
- break;
- }
- }
- Ok(ans)
- }
-}
-impl AzaleaReadVar for u64 {
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- i64::azalea_read_var(buf).map(|i| i as u64)
- }
-}
-
-impl AzaleaRead for UnsizedByteArray {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- // read to end of the buffer
- let data = buf.get_ref()[buf.position() as usize..].to_vec();
- buf.set_position((buf.position()) + data.len() as u64);
- Ok(UnsizedByteArray(data))
- }
-}
-
-macro_rules! impl_for_map_type {
- ($ty: ident) => {
- impl<K: AzaleaRead + Send + Eq + Hash, V: AzaleaRead + Send> AzaleaRead for $ty<K, V> {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let length = i32::azalea_read_var(buf)? as usize;
- let mut contents = Self::with_capacity(usize::min(length, 65536));
- for _ in 0..length {
- contents.insert(K::azalea_read(buf)?, V::azalea_read(buf)?);
- }
- Ok(contents)
- }
- }
- impl<K: AzaleaRead + Send + Eq + Hash, V: AzaleaReadVar + Send> AzaleaReadVar
- for $ty<K, V>
- {
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let length = i32::azalea_read_var(buf)? as usize;
- let mut contents = Self::with_capacity(usize::min(length, 65536));
- for _ in 0..length {
- contents.insert(K::azalea_read(buf)?, V::azalea_read_var(buf)?);
- }
- Ok(contents)
- }
- }
- };
-}
-
-impl_for_map_type!(HashMap);
-impl_for_map_type!(IndexMap);
-
-macro_rules! impl_for_list_type {
- ($ty: ty) => {
- impl<T: AzaleaRead> AzaleaRead for $ty {
- default fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let length = u32::azalea_read_var(buf)? as usize;
- // we limit the capacity to not get exploited into allocating a bunch
- let mut contents = Vec::with_capacity(usize::min(length, 65536));
- for _ in 0..length {
- contents.push(T::azalea_read(buf)?);
- }
- Ok(contents.into())
- }
- }
- impl<T: AzaleaReadVar> AzaleaReadVar for $ty {
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let length = i32::azalea_read_var(buf)? as usize;
- let mut contents = Vec::with_capacity(usize::min(length, 65536));
- for _ in 0..length {
- contents.push(T::azalea_read_var(buf)?);
- }
- Ok(contents.into())
- }
- }
- impl<T: AzaleaRead> AzaleaReadLimited for $ty {
- fn azalea_read_limited(
- buf: &mut Cursor<&[u8]>,
- limit: usize,
- ) -> Result<Self, BufReadError> {
- let length = u32::azalea_read_var(buf)? as usize;
- if length > limit {
- return Err(BufReadError::VecLengthTooLong {
- length: length as u32,
- max_length: limit as u32,
- });
- }
-
- let mut contents = Vec::with_capacity(usize::min(length, 65536));
- for _ in 0..length {
- contents.push(T::azalea_read(buf)?);
- }
- Ok(contents.into())
- }
- }
- };
-}
-
-impl_for_list_type!(Vec<T>);
-impl_for_list_type!(Box<[T]>);
-
-impl AzaleaRead for Vec<u8> {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let length = i32::azalea_read_var(buf)? as usize;
- read_bytes(buf, length).map(|b| b.to_vec())
- }
-}
-
-impl AzaleaRead for String {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- read_utf_with_len(buf, MAX_STRING_LENGTH.into())
- }
-}
-impl AzaleaRead for Box<str> {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- String::azalea_read(buf).map(Into::into)
- }
-}
-impl AzaleaReadLimited for String {
- fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: usize) -> Result<Self, BufReadError> {
- read_utf_with_len(buf, limit as u32)
- }
-}
-impl AzaleaReadLimited for Box<str> {
- fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: usize) -> Result<Self, BufReadError> {
- String::azalea_read_limited(buf, limit).map(Into::into)
- }
-}
-
-impl AzaleaRead for u32 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(i32::azalea_read(buf)? as u32)
- }
-}
-
-impl AzaleaReadVar for u32 {
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(i32::azalea_read_var(buf)? as u32)
- }
-}
-
-impl AzaleaRead for u16 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- i16::azalea_read(buf).map(|i| i as u16)
- }
-}
-
-impl AzaleaRead for i16 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(buf.read_i16::<BE>()?)
- }
-}
-
-impl AzaleaReadVar for u16 {
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(i32::azalea_read_var(buf)? as u16)
- }
-}
-
-impl AzaleaRead for i64 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(buf.read_i64::<BE>()?)
- }
-}
-
-impl AzaleaRead for u64 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- i64::azalea_read(buf).map(|i| i as u64)
- }
-}
-
-impl AzaleaRead for bool {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let byte = u8::azalea_read(buf)?;
- if byte > 1 {
- warn!("Boolean value was not 0 or 1, but {}", byte);
- }
- Ok(byte != 0)
- }
-}
-
-impl AzaleaRead for u8 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(buf.read_u8()?)
- }
-}
-
-impl AzaleaRead for i8 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- u8::azalea_read(buf).map(|i| i as i8)
- }
-}
-
-impl AzaleaRead for f32 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(buf.read_f32::<BE>()?)
- }
-}
-
-impl AzaleaRead for f64 {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(buf.read_f64::<BE>()?)
- }
-}
-
-impl<T: AzaleaRead> AzaleaRead for Option<T> {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let present = bool::azalea_read(buf)?;
- Ok(if present {
- Some(T::azalea_read(buf)?)
- } else {
- None
- })
- }
-}
-
-impl<T: AzaleaReadVar> AzaleaReadVar for Option<T> {
- fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let present = bool::azalea_read(buf)?;
- Ok(if present {
- Some(T::azalea_read_var(buf)?)
- } else {
- None
- })
- }
-}
-impl<T: AzaleaReadLimited> AzaleaReadLimited for Option<T> {
- fn azalea_read_limited(buf: &mut Cursor<&[u8]>, limit: usize) -> Result<Self, BufReadError> {
- let present = bool::azalea_read(buf)?;
- Ok(if present {
- Some(T::azalea_read_limited(buf, limit)?)
- } else {
- None
- })
- }
-}
-
-// [String; 4]
-impl<T: AzaleaRead, const N: usize> AzaleaRead for [T; N] {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let mut contents = Vec::with_capacity(N);
- for _ in 0..N {
- contents.push(T::azalea_read(buf)?);
- }
- contents.try_into().map_err(|_| {
- unreachable!("Panic is not possible since the Vec is the same size as the array")
- })
- }
-}
-
-impl AzaleaRead for simdnbt::owned::NbtTag {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(simdnbt::owned::read_tag(buf).map_err(simdnbt::Error::from)?)
- }
-}
-
-impl AzaleaRead for simdnbt::owned::NbtCompound {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- match simdnbt::owned::read_tag(buf).map_err(simdnbt::Error::from)? {
- simdnbt::owned::NbtTag::Compound(compound) => Ok(compound),
- _ => Err(BufReadError::Custom("Expected compound tag".to_owned())),
- }
- }
-}
-
-impl AzaleaRead for simdnbt::owned::Nbt {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(simdnbt::owned::read_unnamed(buf)?)
- }
-}
-
-impl<T> AzaleaRead for Box<T>
-where
- T: AzaleaRead,
-{
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(Box::new(T::azalea_read(buf)?))
- }
-}
-
-impl<A: AzaleaRead, B: AzaleaRead> AzaleaRead for (A, B) {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok((A::azalea_read(buf)?, B::azalea_read(buf)?))
- }
-}
-
-impl<T: AzaleaRead> AzaleaRead for Arc<T> {
- fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- Ok(Arc::new(T::azalea_read(buf)?))
- }
-}
diff --git a/azalea-buf/src/serializable_uuid.rs b/azalea-buf/src/serializable_uuid.rs
index 76737034..a455a13a 100644
--- a/azalea-buf/src/serializable_uuid.rs
+++ b/azalea-buf/src/serializable_uuid.rs
@@ -2,7 +2,7 @@ use std::io::{self, Cursor, Write};
use uuid::Uuid;
-use crate::{AzaleaRead, AzaleaWrite, read::BufReadError};
+use crate::{AzBuf, BufReadError};
pub trait SerializableUuid {
fn to_int_array(&self) -> [u32; 4];
@@ -34,7 +34,7 @@ impl SerializableUuid for Uuid {
}
}
-impl AzaleaRead for Uuid {
+impl AzBuf for Uuid {
fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
Ok(Uuid::from_int_array([
u32::azalea_read(buf)?,
@@ -43,9 +43,6 @@ impl AzaleaRead for Uuid {
u32::azalea_read(buf)?,
]))
}
-}
-
-impl AzaleaWrite for Uuid {
fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
let [a, b, c, d] = self.to_int_array();
a.azalea_write(buf)?;
diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs
deleted file mode 100644
index 4fcce7a0..00000000
--- a/azalea-buf/src/write.rs
+++ /dev/null
@@ -1,341 +0,0 @@
-use std::{
- collections::HashMap,
- io::{self, Write},
- sync::Arc,
-};
-
-use byteorder::{BigEndian, WriteBytesExt};
-use indexmap::IndexMap;
-
-use super::{MAX_STRING_LENGTH, UnsizedByteArray};
-
-fn write_utf_with_len(buf: &mut impl Write, string: &str, len: usize) -> io::Result<()> {
- if string.len() > len {
- panic!(
- "String too big (was {} bytes encoded, max {})",
- string.len(),
- len
- );
- }
- string.as_bytes().to_vec().azalea_write(buf)?;
- Ok(())
-}
-
-pub trait AzaleaWrite {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()>;
-}
-
-pub trait AzaleaWriteVar {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()>;
-}
-
-impl AzaleaWrite for () {
- fn azalea_write(&self, _buf: &mut impl Write) -> io::Result<()> {
- Ok(())
- }
-}
-
-impl AzaleaWrite for i32 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- WriteBytesExt::write_i32::<BigEndian>(buf, *self)
- }
-}
-
-impl AzaleaWriteVar for i32 {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- let mut buffer = [0];
- let mut value = *self;
- if value == 0 {
- buf.write_all(&buffer)?;
- }
- while value != 0 {
- buffer[0] = (value & 0b0111_1111) as u8;
- value = (value >> 7) & (i32::MAX >> 6);
- if value != 0 {
- buffer[0] |= 0b1000_0000;
- }
- buf.write_all(&buffer)?;
- }
- Ok(())
- }
-}
-
-impl AzaleaWrite for UnsizedByteArray {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- buf.write_all(self)
- }
-}
-
-impl<T: AzaleaWrite> AzaleaWrite for Vec<T> {
- default fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- self[..].azalea_write(buf)
- }
-}
-impl<T: AzaleaWrite> AzaleaWrite for Box<[T]> {
- default fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- self[..].azalea_write(buf)
- }
-}
-
-impl<T: AzaleaWrite> AzaleaWrite for [T] {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- (self.len() as u32).azalea_write_var(buf)?;
- for item in self {
- T::azalea_write(item, buf)?;
- }
- Ok(())
- }
-}
-
-macro_rules! impl_for_map_type {
- ($ty: ident) => {
- impl<K: AzaleaWrite, V: AzaleaWrite> AzaleaWrite for $ty<K, V> {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- u32::azalea_write_var(&(self.len() as u32), buf)?;
- for (key, value) in self {
- key.azalea_write(buf)?;
- value.azalea_write(buf)?;
- }
-
- Ok(())
- }
- }
- impl<K: AzaleaWrite, V: AzaleaWriteVar> AzaleaWriteVar for $ty<K, V> {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- u32::azalea_write_var(&(self.len() as u32), buf)?;
- for (key, value) in self {
- key.azalea_write(buf)?;
- value.azalea_write_var(buf)?;
- }
-
- Ok(())
- }
- }
- };
-}
-
-impl_for_map_type!(HashMap);
-impl_for_map_type!(IndexMap);
-
-impl AzaleaWrite for Vec<u8> {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- (self.len() as u32).azalea_write_var(buf)?;
- buf.write_all(self)
- }
-}
-
-impl AzaleaWrite for String {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- self.as_str().azalea_write(buf)
- }
-}
-
-impl AzaleaWrite for Box<str> {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- (&**self).azalea_write(buf)
- }
-}
-
-impl AzaleaWrite for &str {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- write_utf_with_len(buf, self, MAX_STRING_LENGTH.into())
- }
-}
-
-impl AzaleaWrite for u32 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- i32::azalea_write(&(*self as i32), buf)
- }
-}
-
-impl AzaleaWriteVar for u32 {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- i32::azalea_write_var(&(*self as i32), buf)
- }
-}
-
-impl AzaleaWriteVar for i64 {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- let mut buffer = [0];
- let mut value = *self;
- if value == 0 {
- buf.write_all(&buffer).unwrap();
- }
- while value != 0 {
- buffer[0] = (value & 0b0111_1111) as u8;
- value = (value >> 7) & (i64::MAX >> 6);
- if value != 0 {
- buffer[0] |= 0b1000_0000;
- }
- buf.write_all(&buffer)?;
- }
- Ok(())
- }
-}
-
-impl AzaleaWriteVar for u64 {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- i64::azalea_write_var(&(*self as i64), buf)
- }
-}
-
-impl AzaleaWrite for u16 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- i16::azalea_write(&(*self as i16), buf)
- }
-}
-
-impl AzaleaWriteVar for u16 {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- i32::azalea_write_var(&(*self as i32), buf)
- }
-}
-
-impl<T: AzaleaWriteVar> AzaleaWriteVar for [T] {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- u32::azalea_write_var(&(self.len() as u32), buf)?;
- for i in self {
- i.azalea_write_var(buf)?;
- }
- Ok(())
- }
-}
-impl<T: AzaleaWriteVar> AzaleaWriteVar for Vec<T> {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- self[..].azalea_write_var(buf)
- }
-}
-impl<T: AzaleaWriteVar> AzaleaWriteVar for Box<[T]> {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- self[..].azalea_write_var(buf)
- }
-}
-
-impl AzaleaWrite for u8 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- WriteBytesExt::write_u8(buf, *self)
- }
-}
-
-impl AzaleaWrite for i16 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- WriteBytesExt::write_i16::<BigEndian>(buf, *self)
- }
-}
-
-impl AzaleaWrite for i64 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- WriteBytesExt::write_i64::<BigEndian>(buf, *self)
- }
-}
-
-impl AzaleaWrite for u64 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- i64::azalea_write(&(*self as i64), buf)
- }
-}
-
-impl AzaleaWrite for bool {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- let byte = u8::from(*self);
- byte.azalea_write(buf)
- }
-}
-
-impl AzaleaWrite for i8 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- (*self as u8).azalea_write(buf)
- }
-}
-
-impl AzaleaWrite for f32 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- WriteBytesExt::write_f32::<BigEndian>(buf, *self)
- }
-}
-
-impl AzaleaWrite for f64 {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- WriteBytesExt::write_f64::<BigEndian>(buf, *self)
- }
-}
-
-impl<T: AzaleaWrite> AzaleaWrite for Option<T> {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- if let Some(s) = self {
- true.azalea_write(buf)?;
- s.azalea_write(buf)?;
- } else {
- false.azalea_write(buf)?;
- };
- Ok(())
- }
-}
-
-impl<T: AzaleaWriteVar> AzaleaWriteVar for Option<T> {
- fn azalea_write_var(&self, buf: &mut impl Write) -> io::Result<()> {
- if let Some(s) = self {
- true.azalea_write(buf)?;
- s.azalea_write_var(buf)?;
- } else {
- false.azalea_write(buf)?;
- };
- Ok(())
- }
-}
-
-// [T; N]
-impl<T: AzaleaWrite, const N: usize> AzaleaWrite for [T; N] {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- for i in self {
- i.azalea_write(buf)?;
- }
- Ok(())
- }
-}
-
-impl AzaleaWrite for simdnbt::owned::NbtTag {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- let mut data = Vec::new();
- self.write(&mut data);
- buf.write_all(&data)
- }
-}
-
-impl AzaleaWrite for simdnbt::owned::NbtCompound {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- let mut data = Vec::new();
- simdnbt::owned::NbtTag::Compound(self.clone()).write(&mut data);
- buf.write_all(&data)
- }
-}
-
-impl AzaleaWrite for simdnbt::owned::Nbt {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- let mut data = Vec::new();
- self.write_unnamed(&mut data);
- buf.write_all(&data)
- }
-}
-
-impl<T> AzaleaWrite for Box<T>
-where
- T: AzaleaWrite,
-{
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- T::azalea_write(&**self, buf)
- }
-}
-
-impl<A: AzaleaWrite, B: AzaleaWrite> AzaleaWrite for (A, B) {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- self.0.azalea_write(buf)?;
- self.1.azalea_write(buf)
- }
-}
-
-impl<T: AzaleaWrite> AzaleaWrite for Arc<T> {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- T::azalea_write(&**self, buf)
- }
-}