use crate::{error::Error, InnerInteger};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io;
pub fn read_var_int<R: io::Read>(read: &mut R) -> io::Result<u64> {
let first_byte = read.read_u8()?;
match first_byte {
0..=0xfc => Ok(first_byte as u64),
0xfd => Ok(read.read_u16::<LittleEndian>()? as u64),
0xfe => Ok(read.read_u32::<LittleEndian>()? as u64),
0xff => Ok(read.read_u64::<LittleEndian>()? as u64),
}
}
pub fn write_var_int<W: io::Write>(write: &mut W, number: u64) -> io::Result<()> {
match number {
0..=0xfc => write.write_u8(number as u8)?,
0xfd..=0xffff => {
write.write_all(b"\xfd")?;
write.write_u16::<LittleEndian>(number as u16)?
}
0x10000..=0xffff_ffff => {
write.write_all(b"\xfe")?;
write.write_u32::<LittleEndian>(number as u32)?
}
_ => {
write.write_all(b"\xff")?;
write.write_u64::<LittleEndian>(number as u64)?
}
}
Ok(())
}
pub fn encode_var_int(number: u64) -> Vec<u8> {
let mut vec = Vec::new();
write_var_int(&mut vec, number).unwrap();
vec
}
pub fn encode_minimally(vec: &mut Vec<u8>) {
if let Some(&last) = vec.last() {
if last & 0x7f != 0 {
return;
}
if vec.len() == 1 {
vec.clear();
return;
}
if vec[vec.len() - 2] & 0x80 != 0 {
return;
}
let mut i = vec.len() - 1;
while i > 0 {
if vec[i - 1] != 0 {
if vec[i - 1] & 0x80 != 0 {
vec[i] = last;
i += 1;
} else {
vec[i - 1] |= last;
}
vec.resize(i, 0u8);
return;
}
i -= 1;
}
vec.resize(i, 0u8);
}
}
pub fn encode_int(int: InnerInteger) -> Vec<u8> {
let mut vec = Vec::new();
vec.write_i32::<LittleEndian>(int.abs()).unwrap();
if int < 0 {
vec.write_u8(0x80).unwrap();
}
encode_minimally(&mut vec);
vec
}
pub fn encode_bool(b: bool) -> Vec<u8> {
if b {
vec![0x01]
} else {
vec![]
}
}
fn try_shl(a: InnerInteger, b: u32, vec: &[u8]) -> Result<InnerInteger, Error> {
a.checked_shl(b)
.ok_or_else(|| Error::Msg(format!("Overflow for {}", hex::encode(vec))))
}
pub fn vec_to_int(vec: &[u8]) -> Result<InnerInteger, Error> {
if vec.is_empty() {
return Ok(0);
}
let mut shift = 0;
let mut int = 0;
let sign_bit = vec[vec.len() - 1] & 0x80;
for (i, value) in vec.iter().enumerate() {
if i == vec.len() - 1 && sign_bit != 0 {
int += try_shl((*value ^ sign_bit) as InnerInteger, shift, vec)?;
int *= -1;
} else {
if *value != 0 {
int += try_shl(*value as InnerInteger, shift, vec)?;
}
shift += 8;
}
}
Ok(int)
}