use core::any;
use core::cmp::Ordering;
use core::fmt;
use core::hash::Hash;
use core::marker::PhantomData;
use core::mem::size_of;
use crate::ZeroCopy;
use crate::buf::{Padder, Validator};
use crate::endian::{Big, ByteOrder, DefaultEndian, Little, Native};
use crate::error::{CoerceError, CoerceErrorKind, Error};
use crate::mem::PackedMaybeUninit;
use crate::pointer::Coerce;
use crate::pointer::{DefaultSize, Pointee, Size};
#[repr(C)]
pub struct Ref<T, E = DefaultEndian, O = DefaultSize>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
offset: O,
metadata: T::Stored<O>,
_marker: PhantomData<E>,
}
unsafe impl<T, E, O> ZeroCopy for Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
const ANY_BITS: bool = false;
const PADDED: bool = const {
debug_assert!(
size_of::<Self>() == (size_of::<O>() + size_of::<T::Stored<O>>()),
"Size of Ref should equal its fields"
);
debug_assert!(!O::PADDED, "Offset should not be padded");
debug_assert!(!T::Stored::<O>::PADDED, "Metadata should not be padded");
false
};
const CAN_SWAP_BYTES: bool = false;
#[inline]
unsafe fn pad(padder: &mut Padder<'_, Self>) {
unsafe {
padder.pad::<O>();
padder.pad::<T::Stored<O>>();
}
}
#[inline]
unsafe fn validate(validator: &mut Validator<'_, Self>) -> Result<(), Error> {
unsafe {
let offset = *validator.field::<O>()?;
let metadata = *validator.field::<T::Stored<O>>()?;
Self::try_from_parts(offset, metadata)?;
Ok(())
}
}
#[inline]
fn swap_bytes<B: ByteOrder>(self) -> Self {
self
}
}
impl<T, E, O> Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
#[inline]
pub(crate) const unsafe fn new_unchecked(offset: O, metadata: T::Stored<O>) -> Self {
Self {
offset,
metadata,
_marker: PhantomData,
}
}
#[inline]
pub fn to_be(self) -> Ref<T, Big, O> {
self.to_endian()
}
#[inline]
pub fn to_le(self) -> Ref<T, Little, O> {
self.to_endian()
}
#[inline]
pub fn to_ne(self) -> Ref<T, Native, O> {
self.to_endian()
}
#[inline]
pub fn to_endian<U: ByteOrder>(self) -> Ref<T, U, O> {
Ref {
offset: self.offset.swap_bytes::<E>().swap_bytes::<U>(),
metadata: self.metadata.swap_bytes::<E>().swap_bytes::<U>(),
_marker: PhantomData,
}
}
}
impl<T, E, O> Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
#[inline]
fn from_parts(offset: O, metadata: T::Stored<O>) -> Self {
match Self::try_from_parts(offset, metadata) {
Ok(ok) => ok,
Err(error) => panic!("{error}"),
}
}
#[inline]
fn try_from_parts(offset: O, metadata: T::Stored<O>) -> Result<Self, CoerceError> {
T::check_layout(
offset.swap_bytes::<E>().as_usize(),
T::to_metadata(metadata.swap_bytes::<E>()),
)?;
Ok(Self {
offset,
metadata,
_marker: PhantomData,
})
}
#[inline]
pub fn with_metadata<U>(offset: U, metadata: T::Metadata) -> Self
where
U: Size,
{
match Ref::try_with_metadata(offset, metadata) {
Ok(ok) => ok,
Err(error) => panic!("{error}"),
}
}
pub fn try_with_metadata<U>(offset: U, metadata: T::Metadata) -> Result<Self, CoerceError>
where
U: Size,
{
T::check_layout(offset.as_usize(), metadata)?;
unsafe { Self::try_with_metadata_unchecked(offset, metadata) }
}
#[inline(always)]
pub(crate) unsafe fn try_with_metadata_unchecked<U>(
offset: U,
metadata: T::Metadata,
) -> Result<Self, CoerceError>
where
U: Size,
{
Ok(Ref {
offset: O::try_from(offset)?.swap_bytes::<E>(),
metadata: T::try_from_metadata(metadata)?.swap_bytes::<E>(),
_marker: PhantomData,
})
}
}
impl<T, E, O> Ref<[T], E, O>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
{
#[inline]
pub fn len(self) -> usize {
self.metadata.swap_bytes::<E>().as_usize()
}
#[inline]
pub fn is_empty(self) -> bool {
self.metadata.is_zero()
}
#[inline]
pub fn get(self, index: usize) -> Option<Ref<T, E, O>> {
if index >= self.len() {
return None;
}
let offset = self.offset.swap_bytes::<E>().as_usize() + size_of::<T>() * index;
Some(Ref::new(offset))
}
pub fn get_unchecked(self, index: usize) -> Ref<T, E, O> {
let offset = self.offset.swap_bytes::<E>().as_usize() + size_of::<T>() * index;
Ref::new(offset)
}
#[inline]
pub fn split_at(self, at: usize) -> Result<(Self, Self), CoerceError> {
let offset = self.offset();
let len = self.len();
if at > len {
return Err(CoerceError::new(CoerceErrorKind::SplitAt { at, len }));
}
unsafe {
let a = Self::try_with_metadata_unchecked(offset, at)?;
let b = Self::try_with_metadata_unchecked(offset + at * size_of::<T>(), len - at)?;
Ok((a, b))
}
}
#[inline]
#[cfg(feature = "alloc")]
pub(crate) fn at(self, index: usize) -> Ref<T, E, O> {
let Some(r) = self.get(index) else {
panic!("Index {index} out of bounds 0-{}", self.len());
};
r
}
#[inline]
pub fn iter(self) -> Iter<T, E, O> {
let start = self.offset.swap_bytes::<E>();
let end = self
.metadata
.swap_bytes::<E>()
.checked_mul(O::from_usize(size_of::<T>()))
.and_then(|v| v.checked_add(start))
.unwrap_or(start);
Iter {
start,
end,
_marker: PhantomData,
}
}
}
impl<E, O> Ref<str, E, O>
where
E: ByteOrder,
O: Size,
{
#[inline]
pub fn len(self) -> usize {
self.metadata.swap_bytes::<E>().as_usize()
}
#[inline]
pub fn is_empty(self) -> bool {
self.metadata.is_zero()
}
}
pub struct Iter<T, E, O> {
start: O,
end: O,
_marker: PhantomData<(T, E)>,
}
impl<T, E, O> Iterator for Iter<T, E, O>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
{
type Item = Ref<T, E, O>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.start.eq(self.end) {
return None;
}
let start = self.start;
self.start = self.start.checked_add(O::from_usize(size_of::<T>()))?;
Some(Ref::from_parts(start.swap_bytes::<E>(), ()))
}
}
impl<T, E, O> DoubleEndedIterator for Iter<T, E, O>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
if self.start.eq(self.end) {
return None;
}
self.end = self.end.checked_sub(O::from_usize(size_of::<T>()))?;
Some(Ref::from_parts(self.end.swap_bytes::<E>(), ()))
}
}
impl<T, E, O> Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
#[inline]
pub fn metadata(self) -> T::Stored<O> {
self.metadata
}
}
impl<T, E, O> Ref<T, E, O>
where
T: Pointee<Metadata = (), Stored<O> = ()>,
E: ByteOrder,
O: Size,
{
#[inline]
pub fn new<U>(offset: U) -> Self
where
U: Size,
{
const {
assert!(
O::CAN_SWAP_BYTES,
"Offset cannot be byte-ordered since it would not inhabit valid types",
);
}
let Ok(offset) = O::try_from(offset) else {
panic!(
"Offset {} not in the valid range 0-{}",
offset.as_usize(),
O::MAX
);
};
Ref::from_parts(O::swap_bytes::<E>(offset), ())
}
#[inline]
pub const fn zero() -> Self {
Self {
offset: O::ZERO,
metadata: (),
_marker: PhantomData,
}
}
}
impl<T, E, O> Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
#[inline]
pub fn offset(self) -> usize {
self.offset.swap_bytes::<E>().as_usize()
}
pub fn coerce<U>(self) -> Ref<U, E, O>
where
T: Coerce<U>,
U: ?Sized + Pointee,
{
let metadata = T::coerce_metadata(self.metadata.swap_bytes::<E>()).swap_bytes::<E>();
Ref::from_parts(self.offset, metadata)
}
pub fn try_coerce<U>(self) -> Result<Ref<U, E, O>, CoerceError>
where
T: Coerce<U>,
U: ?Sized + Pointee,
{
let metadata = T::try_coerce_metadata(self.metadata.swap_bytes::<E>())?.swap_bytes::<E>();
Ref::try_from_parts(self.offset, metadata)
}
#[cfg(test)]
pub(crate) fn cast<U>(self) -> Ref<U, E, O>
where
U: ?Sized + Pointee<Stored<O> = T::Stored<O>>,
{
Ref::from_parts(self.offset, self.metadata)
}
}
impl<T, const N: usize, E, O> Ref<[T; N], E, O>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
{
#[inline]
pub fn array_into_slice(self) -> Result<Ref<[T], E, O>, CoerceError> {
Ref::try_with_metadata(self.offset, N)
}
}
impl<T, E, O> Ref<PackedMaybeUninit<T>, E, O>
where
T: Pointee,
E: ByteOrder,
O: Size,
{
#[inline]
pub const fn assume_init(self) -> Ref<T, E, O> {
Ref {
offset: self.offset,
metadata: self.metadata,
_marker: PhantomData,
}
}
}
impl<T, E, O> fmt::Debug for Ref<T, E, O>
where
T: ?Sized + Pointee<Stored<O>: fmt::Debug>,
E: ByteOrder,
O: Size + fmt::Debug,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Ref<{}, {}, {}> {{ offset: {:?}, metadata: {:?} }}",
any::type_name::<T>(),
E::NAME,
any::type_name::<O>(),
self.offset,
self.metadata,
)
}
}
impl<T, E, O> Clone for Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T, E, O> Copy for Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
}
impl<T, E, O> PartialEq for Ref<T, E, O>
where
T: ?Sized + Pointee<Stored<O>: PartialEq>,
E: ByteOrder,
O: PartialEq + Size,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset && self.metadata == other.metadata
}
}
impl<T, E, O> Eq for Ref<T, E, O>
where
T: ?Sized + Pointee<Stored<O>: Eq>,
E: ByteOrder,
O: Eq + Size,
{
}
impl<T, E, O> PartialOrd for Ref<T, E, O>
where
T: ?Sized + Pointee<Stored<O>: PartialOrd>,
E: ByteOrder,
O: Ord + Size,
{
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.offset.partial_cmp(&other.offset) {
Some(Ordering::Equal) => {}
ord => return ord,
}
self.metadata.partial_cmp(&other.metadata)
}
}
impl<T, E, O> Ord for Ref<T, E, O>
where
T: ?Sized + Pointee<Stored<O>: Ord>,
E: ByteOrder,
O: Ord + Size,
{
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
match self.offset.cmp(&other.offset) {
Ordering::Equal => {}
ord => return ord,
}
self.metadata.cmp(&other.metadata)
}
}
impl<T, E, O> Hash for Ref<T, E, O>
where
T: ?Sized + Pointee<Stored<O>: Hash>,
E: ByteOrder,
O: Hash + Size,
{
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.offset.hash(state);
self.metadata.hash(state);
}
}