143 lines
3.4 KiB
Rust
143 lines
3.4 KiB
Rust
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<Self, ReadingError> {
|
|
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 = <u64 as TryFrom<$ty>>::Error;
|
|
|
|
fn try_from(value: $ty) -> Result<Self, Self::Error> {
|
|
Ok(VarULong(value.try_into()?))
|
|
}
|
|
}
|
|
};
|
|
}
|
|
gen_try_from!(i32);
|
|
gen_try_from!(i64);
|
|
gen_try_from!(isize);
|
|
gen_try_from!(usize);
|
|
|
|
impl From<VarULong> for u64 {
|
|
fn from(value: VarULong) -> Self {
|
|
value.0
|
|
}
|
|
}
|
|
|
|
impl AsRef<u64> 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<W: 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<R: Read>(reader: &mut R) -> Result<Self, Error> {
|
|
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"))
|
|
}
|
|
}
|