use core::fmt;
use core::marker::PhantomData;
use core::mem::size_of;
use core::ptr;
use core::slice::from_raw_parts;
use crate::error::{ErrorKind, Result};
use crate::{Error, Frame};
#[cfg(feature = "alloc")]
use super::AlignedBuf;
use super::padding_to;
pub struct Aligned<'a> {
data: ptr::NonNull<u8>,
base: usize,
read: usize,
written: usize,
_marker: PhantomData<&'a [u8]>,
}
impl<'a> Aligned<'a> {
pub(crate) const fn empty() -> Self {
Self::new(ptr::NonNull::<u64>::dangling().cast(), 0)
}
pub(crate) const fn new(data: ptr::NonNull<u8>, written: usize) -> Self {
Self {
data,
base: 0,
read: 0,
written,
_marker: PhantomData,
}
}
#[inline]
fn at(&self) -> usize {
self.base + self.read
}
pub(crate) fn get(&self) -> &'a [u8] {
unsafe {
let at = self.data.as_ptr().add(self.read);
from_raw_parts(at, self.len())
}
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.read == self.written
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.written - self.read
}
pub(crate) fn read_until(&mut self, n: usize) -> Aligned<'a> {
assert!(n <= self.len(), "requested: {n} > length: {}", self.len());
let data = unsafe { ptr::NonNull::new_unchecked(self.data.as_ptr().add(self.read)) };
let base = self.at();
self.read += n;
Aligned {
data,
base,
read: 0,
written: n,
_marker: PhantomData,
}
}
pub(crate) fn load<T>(&mut self) -> Result<T>
where
T: Frame,
{
let padding = padding_to::<T>(self.at());
if self.read + padding + size_of::<T>() > self.written {
return Err(Error::new(ErrorKind::BufferUnderflow));
}
self.read += padding;
let frame = unsafe { ptr::read(self.data.as_ptr().add(self.read).cast::<T>()) };
self.read += size_of::<T>();
Ok(frame)
}
#[cfg(feature = "alloc")]
pub(crate) fn advance(&mut self, n: usize) -> Result<()> {
if n == 0 {
return Ok(());
}
if self.read + n > self.written {
return Err(Error::new(ErrorKind::BufferUnderflow));
}
self.read += n;
Ok(())
}
pub(crate) fn align<T>(&mut self) -> Result<()> {
let padding = padding_to::<T>(self.at());
self.pad(padding)
}
#[cfg(feature = "alloc")]
pub(crate) fn align_to(&mut self, align: usize) -> Result<()> {
assert!(align.is_power_of_two(), "alignment must be a power of two");
let padding = unsafe { super::padding_to_with(align, self.at()) };
self.pad(padding)
}
#[inline]
fn pad(&mut self, padding: usize) -> Result<()> {
if self.read + padding > self.written {
return Err(Error::from(ErrorKind::BufferUnderflow));
}
self.read += padding;
Ok(())
}
pub(crate) fn load_slice(&mut self, len: usize) -> Result<&'a [u8]> {
if self.read + len > self.written {
return Err(Error::from(ErrorKind::BufferUnderflow));
}
let slice = unsafe {
let ptr = self.data.as_ptr().add(self.read);
from_raw_parts(ptr, len)
};
self.read += len;
Ok(slice)
}
pub(crate) fn load_slice_nul(&mut self, len: usize) -> Result<&'a [u8]> {
if self.read + len + 1 > self.written {
return Err(Error::from(ErrorKind::BufferUnderflow));
}
let slice = unsafe {
let ptr = self.data.as_ptr().add(self.read);
if ptr.add(len).read() != 0 {
return Err(Error::new(ErrorKind::NotNullTerminated));
}
from_raw_parts(ptr, len)
};
self.read += len + 1;
Ok(slice)
}
}
unsafe impl Send for Aligned<'_> {}
unsafe impl Sync for Aligned<'_> {}
impl Clone for Aligned<'_> {
#[inline]
fn clone(&self) -> Self {
Self {
data: self.data,
base: self.base,
read: self.read,
written: self.written,
_marker: self._marker,
}
}
}
impl fmt::Debug for Aligned<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Aligned").field("len", &self.len()).finish()
}
}
impl<'a> PartialEq<Aligned<'a>> for Aligned<'_> {
#[inline]
fn eq(&self, other: &Aligned<'a>) -> bool {
self.get() == other.get()
}
}
#[cfg(feature = "alloc")]
impl PartialEq<AlignedBuf> for Aligned<'_> {
#[inline]
fn eq(&self, other: &AlignedBuf) -> bool {
self.get() == other.get()
}
}
impl Eq for Aligned<'_> {}