use core::alloc::Layout;
use core::fmt;
use core::mem::{align_of, size_of, MaybeUninit};
use core::ops::{Index, IndexMut, Range};
use core::ptr::{read_unaligned, NonNull};
use core::slice::SliceIndex;
#[cfg(feature = "alloc")]
use alloc::borrow::{Cow, ToOwned};
#[cfg(feature = "alloc")]
use crate::buf::OwnedBuf;
use crate::buf::{self, Bindable, Load, LoadMut, Validator};
use crate::endian::ByteOrder;
use crate::error::{Error, ErrorKind};
use crate::pointer::{Ref, Size};
use crate::traits::{UnsizedZeroCopy, ZeroCopy};
#[repr(transparent)]
pub struct Buf {
data: [u8],
}
impl Buf {
#[inline]
pub const fn new(data: &[u8]) -> &Buf {
unsafe { &*(data as *const [u8] as *const Self) }
}
#[inline]
pub fn new_mut(data: &mut [u8]) -> &mut Buf {
unsafe { &mut *(data as *mut [u8] as *mut Self) }
}
#[cfg(feature = "alloc")]
#[inline]
pub fn to_aligned<T>(&self) -> Cow<'_, Buf> {
self.to_aligned_with(align_of::<T>())
}
#[cfg(feature = "alloc")]
#[inline]
pub fn to_aligned_with(&self, align: usize) -> Cow<'_, Buf> {
assert!(align.is_power_of_two(), "Alignment must be power of two");
if unsafe { self.is_aligned_with_unchecked(align) } {
Cow::Borrowed(self)
} else {
let mut buf =
unsafe { OwnedBuf::with_capacity_and_custom_alignment(self.len(), align) };
unsafe {
buf.store_bytes(&self.data);
}
Cow::Owned(buf)
}
}
pub fn alignment(&self) -> usize {
1usize << (self.data.as_ptr() as usize).trailing_zeros().min(29)
}
#[inline]
pub fn is_compatible_with<T>(&self) -> bool
where
T: ZeroCopy,
{
self.is_compatible(Layout::new::<T>())
}
#[inline]
pub fn ensure_compatible_with<T>(&self) -> Result<(), Error>
where
T: ZeroCopy,
{
if !self.is_compatible_with::<T>() {
return Err(Error::new(ErrorKind::LayoutMismatch {
layout: Layout::new::<T>(),
range: self.range(),
}));
}
Ok(())
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn get<I>(&self, index: I) -> Option<&I::Output>
where
I: SliceIndex<[u8]>,
{
self.data.get(index)
}
pub fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
where
I: SliceIndex<[u8]>,
{
self.data.get_mut(index)
}
#[inline]
pub fn load<T>(&self, ptr: T) -> Result<&T::Target, Error>
where
T: Load,
{
ptr.load(self)
}
pub fn load_at<T>(&self, offset: usize) -> Result<&T, Error>
where
T: ZeroCopy,
{
self.load_sized::<T>(offset)
}
pub fn load_at_mut<T>(&mut self, offset: usize) -> Result<&mut T, Error>
where
T: ZeroCopy,
{
self.load_sized_mut::<T>(offset)
}
pub fn load_at_unaligned<T>(&self, offset: usize) -> Result<T, Error>
where
T: ZeroCopy,
{
self.load_sized_unaligned::<T>(offset)
}
#[inline]
pub fn load_mut<T>(&mut self, ptr: T) -> Result<&mut T::Target, Error>
where
T: LoadMut,
{
ptr.load_mut(self)
}
#[inline]
pub fn bind<T>(&self, ptr: T) -> Result<T::Bound<'_>, Error>
where
T: Bindable,
{
ptr.bind(self)
}
#[inline]
pub unsafe fn cast<T>(&self) -> &T {
&*self.data.as_ptr().cast()
}
#[inline]
pub unsafe fn cast_mut<T>(&mut self) -> &mut T {
&mut *self.data.as_mut_ptr().cast()
}
#[inline]
pub fn validate_struct<T>(&self) -> Result<Validator<'_, T>, Error>
where
T: ZeroCopy,
{
self.ensure_compatible_with::<T>()?;
Ok(Validator::from_slice(&self.data))
}
pub(crate) unsafe fn get_range_from(
&self,
start: usize,
align: usize,
) -> Result<(NonNull<u8>, usize), Error> {
if self.data.len() < start {
return Err(Error::new(ErrorKind::OutOfRangeFromBounds {
range: start..,
len: self.data.len(),
}));
};
let ptr = NonNull::new_unchecked(self.data.as_ptr().add(start) as *mut _);
let remaining = self.data.len() - start;
if !buf::is_aligned_with(ptr.as_ptr(), align) {
return Err(Error::new(ErrorKind::AlignmentRangeFromMismatch {
range: start..,
align,
}));
}
Ok((ptr, remaining))
}
pub(crate) unsafe fn get_mut_range_from(
&mut self,
start: usize,
align: usize,
) -> Result<(NonNull<u8>, usize), Error> {
if self.data.len() < start {
return Err(Error::new(ErrorKind::OutOfRangeFromBounds {
range: start..,
len: self.data.len(),
}));
};
let ptr = NonNull::new_unchecked(self.data.as_mut_ptr().add(start));
let remaining = self.data.len() - start;
if !buf::is_aligned_with(ptr.as_ptr(), align) {
return Err(Error::new(ErrorKind::AlignmentRangeFromMismatch {
range: start..,
align,
}));
}
Ok((ptr, remaining))
}
#[inline]
pub(crate) unsafe fn inner_get(
&self,
start: usize,
end: usize,
align: usize,
) -> Result<&[u8], Error> {
let buf = self.inner_get_unaligned(start, end)?;
if !buf::is_aligned_with(buf.as_ptr(), align) {
return Err(Error::new(ErrorKind::AlignmentRangeMismatch {
addr: buf.as_ptr() as usize,
range: start..end,
align,
}));
}
Ok(buf)
}
#[inline]
pub(crate) unsafe fn inner_get_mut(
&mut self,
start: usize,
end: usize,
align: usize,
) -> Result<&mut [u8], Error> {
let buf = self.inner_get_mut_unaligned(start, end)?;
if !buf::is_aligned_with(buf.as_ptr(), align) {
return Err(Error::new(ErrorKind::AlignmentRangeMismatch {
addr: buf.as_ptr() as usize,
range: start..end,
align,
}));
}
Ok(buf)
}
#[inline]
pub(crate) fn inner_get_unaligned(&self, start: usize, end: usize) -> Result<&[u8], Error> {
let Some(data) = self.data.get(start..end) else {
return Err(Error::new(ErrorKind::OutOfRangeBounds {
range: start..end,
len: self.data.len(),
}));
};
Ok(data)
}
#[inline]
pub(crate) fn inner_get_mut_unaligned(
&mut self,
start: usize,
end: usize,
) -> Result<&mut [u8], Error> {
let len = self.data.len();
let Some(data) = self.data.get_mut(start..end) else {
return Err(Error::new(ErrorKind::OutOfRangeBounds {
range: start..end,
len,
}));
};
Ok(data)
}
#[inline]
pub(crate) fn load_unsized<T, O, E>(&self, unsize: Ref<T, E, O>) -> Result<&T, Error>
where
T: ?Sized + UnsizedZeroCopy,
O: Size,
E: ByteOrder,
{
let start = unsize.offset();
let metadata = unsize.metadata();
unsafe {
let (buf, remaining) = self.get_range_from(start, T::ALIGN)?;
let metadata = T::validate_unsized::<E, O>(buf, remaining, metadata)?;
Ok(&*T::with_metadata(buf, metadata))
}
}
#[inline]
pub(crate) fn load_unsized_mut<T, O, E>(
&mut self,
unsize: Ref<T, E, O>,
) -> Result<&mut T, Error>
where
T: ?Sized + UnsizedZeroCopy,
O: Size,
E: ByteOrder,
{
let start = unsize.offset();
let metadata = unsize.metadata();
unsafe {
let (buf, remaining) = self.get_mut_range_from(start, T::ALIGN)?;
let metadata = T::validate_unsized::<E, O>(buf, remaining, metadata)?;
Ok(&mut *T::with_metadata_mut(buf, metadata))
}
}
#[inline]
pub(crate) fn load_sized<T>(&self, offset: usize) -> Result<&T, Error>
where
T: ZeroCopy,
{
unsafe {
let end = offset + size_of::<T>();
let buf = self.inner_get(offset, end, align_of::<T>())?;
if !T::ANY_BITS {
T::validate(&mut Validator::from_slice(buf))?;
}
Ok(&*buf.as_ptr().cast())
}
}
#[inline]
pub fn swap<T, E, O>(&mut self, a: Ref<T, E, O>, b: Ref<T, E, O>) -> Result<(), Error>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
{
let a = a.offset();
let b = b.offset();
if a == b {
return Ok(());
}
let start = a.max(b);
let end = start + size_of::<T>();
if end > self.data.len() {
return Err(Error::new(ErrorKind::OutOfRangeBounds {
range: start..end,
len: self.data.len(),
}));
}
unsafe {
let mut tmp = MaybeUninit::<T>::uninit();
let base = self.data.as_mut_ptr();
let tmp = tmp.as_mut_ptr().cast::<u8>();
let a = base.add(a);
let b = base.add(b);
tmp.copy_from_nonoverlapping(a, size_of::<T>());
a.copy_from(b, size_of::<T>());
b.copy_from_nonoverlapping(tmp, size_of::<T>());
}
Ok(())
}
#[inline]
pub(crate) fn load_sized_mut<T>(&mut self, offset: usize) -> Result<&mut T, Error>
where
T: ZeroCopy,
{
let end = offset + size_of::<T>();
unsafe {
let buf = self.inner_get_mut(offset, end, align_of::<T>())?;
if !T::ANY_BITS {
T::validate(&mut Validator::from_slice(buf))?;
}
Ok(&mut *buf.as_mut_ptr().cast())
}
}
#[inline]
pub(crate) fn load_sized_unaligned<T>(&self, start: usize) -> Result<T, Error>
where
T: ZeroCopy,
{
let end = start + size_of::<T>();
unsafe {
let buf = self.inner_get_unaligned(start, end)?;
if !T::ANY_BITS {
T::validate(&mut Validator::from_slice(buf))?;
}
Ok(read_unaligned(buf.as_ptr().cast()))
}
}
#[inline]
pub(crate) fn as_ptr(&self) -> *const u8 {
self.data.as_ptr()
}
#[inline]
pub(crate) fn range(&self) -> Range<usize> {
let range = self.data.as_ptr_range();
range.start as usize..range.end as usize
}
#[inline]
pub(crate) fn is_compatible(&self, layout: Layout) -> bool {
unsafe {
self.is_aligned_with_unchecked(layout.align()) && self.data.len() >= layout.size()
}
}
#[inline]
pub fn is_aligned<T>(&self) -> bool {
buf::is_aligned_with(self.as_ptr(), align_of::<T>())
}
#[inline]
pub fn is_aligned_with(&self, align: usize) -> bool {
assert!(align.is_power_of_two(), "Alignment is not a power of two");
buf::is_aligned_with(self.as_ptr(), align)
}
#[inline]
pub(crate) unsafe fn is_aligned_with_unchecked(&self, align: usize) -> bool {
buf::is_aligned_with(self.as_ptr(), align)
}
}
impl fmt::Debug for Buf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Buf").field(&self.data.len()).finish()
}
}
#[cfg(feature = "alloc")]
impl ToOwned for Buf {
type Owned = OwnedBuf;
#[inline]
fn to_owned(&self) -> Self::Owned {
let mut buf =
unsafe { OwnedBuf::with_capacity_and_custom_alignment(self.len(), self.alignment()) };
buf.extend_from_slice(&self.data);
buf
}
}
impl AsRef<Buf> for Buf {
#[inline]
fn as_ref(&self) -> &Buf {
self
}
}
impl AsMut<Buf> for Buf {
#[inline]
fn as_mut(&mut self) -> &mut Buf {
self
}
}
impl<I> Index<I> for Buf
where
I: SliceIndex<[u8]>,
{
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &I::Output {
&self.data[index]
}
}
impl<I> IndexMut<I> for Buf
where
I: SliceIndex<[u8]>,
{
#[inline]
fn index_mut(&mut self, index: I) -> &mut I::Output {
&mut self.data[index]
}
}