use core::marker::PhantomData;
use crate::EpeeError;
pub trait BytesLike<'encoding>: Sized {
type ExternallyTrackedLength: Sized + Copy;
fn len(&self, len: Self::ExternallyTrackedLength) -> usize;
fn read_bytes(
&mut self,
bytes: usize,
) -> Result<(Self::ExternallyTrackedLength, Self), EpeeError>;
fn read_into_slice(&mut self, slice: &mut [u8]) -> Result<(), EpeeError>;
#[inline(always)]
fn read_byte(&mut self) -> Result<u8, EpeeError> {
let mut buf = [0; 1];
self.read_into_slice(&mut buf)?;
Ok(buf[0])
}
#[inline(always)]
fn advance<const N: usize>(&mut self) -> Result<(), EpeeError> {
self.read_bytes(N).map(|_| ())
}
}
impl<'encoding> BytesLike<'encoding> for &'encoding [u8] {
type ExternallyTrackedLength = ();
#[inline(always)]
fn len(&self, (): ()) -> usize {
<[u8]>::len(self)
}
#[inline(always)]
fn read_bytes(
&mut self,
bytes: usize,
) -> Result<(Self::ExternallyTrackedLength, Self), EpeeError> {
if self.len() < bytes {
Err(EpeeError::Short(bytes))?;
}
let res = &self[.. bytes];
*self = &self[bytes ..];
Ok(((), res))
}
#[inline(always)]
fn read_into_slice(&mut self, slice: &mut [u8]) -> Result<(), EpeeError> {
slice.copy_from_slice(self.read_bytes(slice.len())?.1);
Ok(())
}
}
pub(crate) fn read_varint<'encoding>(
reader: &mut impl BytesLike<'encoding>,
) -> Result<u64, EpeeError> {
let vi_start = reader.read_byte()?;
let len = match vi_start & 0b11 {
0 => 1,
1 => 2,
2 => 4,
3 => 8,
_ => unreachable!(),
};
let mut vi = u64::from(vi_start);
for i in 1 .. len {
vi |= u64::from(reader.read_byte()?) << (i * 8);
}
vi >>= 2;
Ok(vi)
}
pub struct String<'encoding, B: BytesLike<'encoding>> {
pub(crate) len: B::ExternallyTrackedLength,
pub(crate) bytes: B,
pub(crate) _encoding: PhantomData<&'encoding ()>,
}
impl<'encoding, B: BytesLike<'encoding>> String<'encoding, B> {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.bytes.len(self.len) == 0
}
#[inline(always)]
pub fn len(&self) -> usize {
self.bytes.len(self.len)
}
#[inline(always)]
pub fn consume(self) -> B {
self.bytes
}
}
#[inline(always)]
pub(crate) fn read_str<'encoding, B: BytesLike<'encoding>>(
reader: &mut B,
) -> Result<String<'encoding, B>, EpeeError> {
let len = usize::try_from(read_varint(reader)?).map_err(|_| EpeeError::Short(usize::MAX))?;
let (len, bytes) = reader.read_bytes(len)?;
Ok(String { len, bytes, _encoding: PhantomData })
}