use std::{str, mem, ptr};
use mem::MaybeUninit;
use crate::errors::PacketDecodeError;
#[derive(Copy)]
#[repr(C, packed)]
pub struct BigEndian<T: Copy>(pub T);
impl BigEndian<u16>
{
pub const fn from_native(val: u16) -> Self
{
Self(val.to_be())
}
pub const fn to_native(self) -> u16
{
u16::from_be(self.0)
}
}
impl<T: Copy> Clone for BigEndian<T>
{
fn clone(&self) -> Self
{
Self(self.0)
}
}
pub struct ByteReader<'a>(&'a [u8]);
impl<'a> ByteReader<'a>
{
pub fn new(bytes: &'a [u8]) -> Self
{
Self(bytes)
}
#[allow(dead_code)]
pub fn read_u8(&mut self) -> Result<u8, PacketDecodeError>
{
if self.0.len() < 1 {
return Err(PacketDecodeError::ReachedEndUnexpectedly);
}
let ret = self.0[0];
self.0 = &self.0[1..];
return Ok(ret);
}
pub fn read_u16(&mut self) -> Result<BigEndian<u16>, PacketDecodeError>
{
self.read_trivial::<u16>().map(BigEndian)
}
pub fn read_byte_array(&mut self) -> Result<&'a [u8], PacketDecodeError>
{
let len = u16::from_be(self.read_trivial::<u16>()?) as usize;
if self.0.len() < len {
Err(PacketDecodeError::ReachedEndUnexpectedly)
} else {
let ret = &self.0[..len];
self.0 = &self.0[len..];
Ok(ret)
}
}
pub fn read_utf8(&mut self) -> Result<&'a str, PacketDecodeError>
{
let slice = self.read_byte_array()?;
str::from_utf8(slice).map_err(PacketDecodeError::Utf8Error)
}
pub fn read_trivial<T: Copy>(&mut self) -> Result<T, PacketDecodeError>
{
if self.0.len() < mem::size_of::<T>() {
return Err(PacketDecodeError::ReachedEndUnexpectedly);
}
let ret = unsafe {
ptr::read_unaligned(self.0.as_ptr().cast())
};
self.0 = &self.0[mem::size_of::<T>()..];
return Ok(ret);
}
pub fn remaining(&self) -> usize
{
self.0.len()
}
pub fn read_remaining(&mut self) -> &'a [u8]
{
mem::replace(&mut self.0, &[])
}
}
trait AddOffset
{
fn add_offset(&mut self, offset: usize);
}
impl<T> AddOffset for &mut [T]
{
#[inline]
fn add_offset(&mut self, offset: usize)
{
let old_ref = mem::replace(self, &mut []);
*self = &mut old_ref[offset..];
}
}
pub struct ByteWriter<'a>(&'a mut [MaybeUninit<u8>]);
impl<'a> ByteWriter<'a>
{
pub fn new(dst: &'a mut [MaybeUninit<u8>]) -> Self
{
Self(dst)
}
pub fn write_trivial<T: Copy>(&mut self, data: &T) -> &mut Self
{
let len = mem::size_of::<T>();
assert!(self.0.len() >= len, "not enough space left");
unsafe {
ptr::copy_nonoverlapping((data as *const T).cast(), self.0.as_mut_ptr(), len);
self.0.add_offset(len);
}
self
}
pub fn write_u8(&mut self, byte: u8) -> &mut Self
{
assert!(self.0.len() >= 1, "not enough space left");
self.0[0].write(byte);
self.0.add_offset(1);
self
}
pub fn write_u16(&mut self, data: BigEndian<u16>) -> &mut Self
{
self.write_trivial(&data)
}
pub fn write_bytes<T: AsRef<[u8]>>(&mut self, bytes: T) -> &mut Self
{
let bytes = bytes.as_ref();
let array_len = bytes.len();
let array_len_be = u16::try_from(array_len).expect("array too wide").to_be();
let total_len = mem::size_of::<u16>() + array_len;
assert!(self.0.len() >= total_len, "not enough space left");
unsafe {
let ptr = self.0.as_mut_ptr();
ptr::write_unaligned(ptr.cast(), array_len_be);
ptr::copy_nonoverlapping(bytes.as_ptr().cast(), ptr.add(mem::size_of::<u16>()), array_len);
self.0.add_offset(total_len);
}
self
}
pub fn write_bytes_no_len<T: AsRef<[u8]>>(&mut self, bytes: T) -> &mut Self
{
let bytes = bytes.as_ref();
assert!(self.0.len() >= bytes.len(), "not enough space left");
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr().cast(), self.0.as_mut_ptr(), bytes.len());
self.0.add_offset(bytes.len());
}
self
}
pub fn finish_and_check(self)
{
assert!(self.0.len() <= 0, "uninitialized bytes left");
}
}