diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2026-01-13 10:51:45 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-01-13 10:51:45 -0600 |
| commit | b21ac946cafaacc9ee2478ea48ed9e72554f79ed (patch) | |
| tree | 4d05744b9801e94f5da6563d8fabddfb20d1c7b7 /azalea-buf/src | |
| parent | d5fa5e32b37754b3b5c136e58821e48cd3b7c2ff (diff) | |
| download | azalea-drasl-b21ac946cafaacc9ee2478ea48ed9e72554f79ed.tar.xz | |
Merge AzaleaRead and AzaleaWrite (#305)
Diffstat (limited to 'azalea-buf/src')
| -rw-r--r-- | azalea-buf/src/impls/extra.rs | 309 | ||||
| -rw-r--r-- | azalea-buf/src/impls/mod.rs | 158 | ||||
| -rw-r--r-- | azalea-buf/src/impls/primitives.rs | 213 | ||||
| -rw-r--r-- | azalea-buf/src/lib.rs | 8 | ||||
| -rw-r--r-- | azalea-buf/src/read.rs | 465 | ||||
| -rw-r--r-- | azalea-buf/src/serializable_uuid.rs | 7 | ||||
| -rw-r--r-- | azalea-buf/src/write.rs | 341 |
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) - } -} |
