use std::{ io::{Error, Read, Write}, num::NonZeroUsize, ops::Deref, }; use crate::{ WritingError, ser::{NetworkReadExt, NetworkWriteExt, ReadingError}, serial::{PacketRead, PacketWrite}, }; pub type VarULongType = u64; /** * A variable-length long type used by the Minecraft network protocol. */ #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct VarULong(pub VarULongType); impl VarULong { /// The maximum number of bytes a `VarULong` can occupy. const MAX_SIZE: NonZeroUsize = NonZeroUsize::new(10).unwrap(); /// Returns the exact number of bytes this `VarLong` will write when /// [`Encode::encode`] is called, assuming no error occurs. #[must_use] pub const fn written_size(&self) -> usize { match self.0 { 0 => 1, n => (31 - n.leading_zeros() as usize) / 7 + 1, } } pub fn encode(&self, write: &mut impl Write) -> Result<(), WritingError> { let mut x = self.0; loop { let byte = (x & 0x7F) as u8; x >>= 7; if x == 0 { write.write_u8(byte)?; break; } write.write_u8(byte | 0x80)?; } Ok(()) } // TODO: Validate that the first byte will not overflow a i64 pub fn decode(read: &mut impl Read) -> Result { let mut val = 0; for i in 0..Self::MAX_SIZE.get() { let byte = read.get_u8()?; val |= (u64::from(byte) & 0b0111_1111) << (i * 7); if byte & 0b1000_0000 == 0 { return Ok(Self(val)); } } Err(ReadingError::TooLarge("VarLong".to_string())) } } macro_rules! gen_from { ($ty: ty) => { impl From<$ty> for VarULong { fn from(value: $ty) -> Self { VarULong(value.into()) } } }; } gen_from!(u8); gen_from!(u16); gen_from!(u32); gen_from!(u64); macro_rules! gen_try_from { ($ty: ty) => { impl TryFrom<$ty> for VarULong { type Error = >::Error; fn try_from(value: $ty) -> Result { Ok(VarULong(value.try_into()?)) } } }; } gen_try_from!(i32); gen_try_from!(i64); gen_try_from!(isize); gen_try_from!(usize); impl From for u64 { fn from(value: VarULong) -> Self { value.0 } } impl AsRef for VarULong { fn as_ref(&self) -> &u64 { &self.0 } } impl Deref for VarULong { type Target = u64; fn deref(&self) -> &Self::Target { &self.0 } } impl PacketWrite for VarULong { fn write(&self, writer: &mut W) -> Result<(), Error> { let mut x = self.0; loop { let byte = (x & 0x7F) as u8; x >>= 7; if x == 0 { byte.write(writer)?; break; } (byte | 0x80).write(writer)?; } Ok(()) } } impl PacketRead for VarULong { fn read(reader: &mut R) -> Result { let mut val = 0; for i in 0..Self::MAX_SIZE.get() { let byte = u8::read(reader)?; val |= (u64::from(byte) & 0b0111_1111) << (i * 7); if byte & 0b1000_0000 == 0 { return Ok(Self(val)); } } Err(Error::other("Invalid VarUInt")) } }