use bytes::{Buf, BufMut};
use crate::types::ProtocolError;
const MAX_VARINT_BYTES: usize = 5;
const VAR_INT_LENGTHS: [u8; 33] = [
5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
1,
];
#[must_use]
#[allow(clippy::cast_sign_loss)]
pub const fn var_int_bytes(value: i32) -> usize {
let value = value as u32;
VAR_INT_LENGTHS[value.leading_zeros() as usize] as usize
}
pub fn read_var_int(buf: &mut impl Buf) -> Result<i32, ProtocolError> {
let mut value: i32 = 0;
let mut position: u32 = 0;
loop {
if !buf.has_remaining() {
return Err(ProtocolError::UnexpectedEof);
}
let byte = buf.get_u8();
value |= i32::from(byte & 0x7F) << position;
if byte & 0x80 == 0 {
return Ok(value);
}
position += 7;
if position >= 32 {
return Err(ProtocolError::VarIntTooLong);
}
}
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub fn write_var_int(buf: &mut impl BufMut, value: i32) {
let mut unsigned = value as u32;
if unsigned < 0x80 {
buf.put_u8(unsigned as u8);
return;
}
if unsigned < 0x4000 {
let encoded = ((unsigned & 0x7F | 0x80) << 8) | (unsigned >> 7);
buf.put_u16(encoded as u16);
return;
}
loop {
#[allow(clippy::cast_possible_truncation)]
if unsigned & !0x7F == 0 {
buf.put_u8(unsigned as u8);
return;
}
buf.put_u8((unsigned & 0x7F | 0x80) as u8);
unsigned >>= 7;
}
}
pub fn peek_var_int(data: &[u8]) -> Result<Option<(i32, usize)>, ProtocolError> {
let mut value: i32 = 0;
let mut position: u32 = 0;
for (i, &byte) in data.iter().enumerate() {
if i >= MAX_VARINT_BYTES {
return Err(ProtocolError::VarIntTooLong);
}
value |= i32::from(byte & 0x7F) << position;
if byte & 0x80 == 0 {
return Ok(Some((value, i + 1)));
}
position += 7;
}
Ok(None)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_roundtrip_zero() {
let mut buf = Vec::new();
write_var_int(&mut buf, 0);
assert_eq!(buf, vec![0x00]);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 0);
}
#[test]
fn test_roundtrip_one() {
let mut buf = Vec::new();
write_var_int(&mut buf, 1);
assert_eq!(buf, vec![0x01]);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 1);
}
#[test]
fn test_roundtrip_127() {
let mut buf = Vec::new();
write_var_int(&mut buf, 127);
assert_eq!(buf, vec![0x7F]);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 127);
}
#[test]
fn test_roundtrip_128() {
let mut buf = Vec::new();
write_var_int(&mut buf, 128);
assert_eq!(buf, vec![0x80, 0x01]);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 128);
}
#[test]
fn test_roundtrip_255() {
let mut buf = Vec::new();
write_var_int(&mut buf, 255);
assert_eq!(buf, vec![0xFF, 0x01]);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 255);
}
#[test]
fn test_roundtrip_25565() {
let mut buf = Vec::new();
write_var_int(&mut buf, 25565);
assert_eq!(buf, vec![0xDD, 0xC7, 0x01]);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 25565);
}
#[test]
fn test_roundtrip_max() {
let mut buf = Vec::new();
write_var_int(&mut buf, i32::MAX);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), i32::MAX);
}
#[test]
fn test_roundtrip_negative_one() {
let mut buf = Vec::new();
write_var_int(&mut buf, -1);
assert_eq!(buf, vec![0xFF, 0xFF, 0xFF, 0xFF, 0x0F]);
assert_eq!(read_var_int(&mut &buf[..]).unwrap(), -1);
}
#[test]
fn test_var_int_bytes() {
assert_eq!(var_int_bytes(0), 1);
assert_eq!(var_int_bytes(1), 1);
assert_eq!(var_int_bytes(127), 1);
assert_eq!(var_int_bytes(128), 2);
assert_eq!(var_int_bytes(16383), 2);
assert_eq!(var_int_bytes(16384), 3);
assert_eq!(var_int_bytes(-1), 5);
}
#[test]
fn test_peek_var_int() {
let data = [0xDD, 0xC7, 0x01, 0xFF];
let result = peek_var_int(&data).unwrap().unwrap();
assert_eq!(result, (25565, 3));
let data = [0x80];
assert!(peek_var_int(&data).unwrap().is_none());
assert!(peek_var_int(&[]).unwrap().is_none());
}
}