use core::cmp::Ordering;
use core::fmt;
use core::hash::Hash;
use core::marker::PhantomData;
use core::mem::size_of;
use crate::endian::{Big, ByteOrder, Little, Native};
use crate::error::{Error, ErrorKind, IntoRepr};
use crate::mem::MaybeUninit;
use crate::pointer::Coerce;
use crate::pointer::{DefaultSize, Pointee, Size};
use crate::ZeroCopy;
#[derive(ZeroCopy)]
#[repr(C)]
#[zero_copy(crate, swap_bytes_self)]
pub struct Ref<T, E = Native, O = DefaultSize>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
offset: O,
metadata: T::Stored<O>,
#[zero_copy(ignore)]
_marker: PhantomData<(E, T)>,
}
impl<T, E, O> Ref<T, E, O>
where
T: ?Sized + Pointee,
E: ByteOrder,
O: Size,
{
#[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]
pub fn with_metadata<U>(offset: U, metadata: T::Metadata) -> Self
where
U: Copy + fmt::Debug,
O: TryFrom<U>,
{
const {
assert!(
O::CAN_SWAP_BYTES,
"Offset cannot be byte-ordered since it would not inhabit valid types"
);
}
let Some(offset) = O::try_from(offset).ok() else {
panic!("Offset {offset:?} not in legal range 0-{}", O::MAX);
};
let Some(metadata) = T::try_from_metadata(metadata) else {
panic!("Metadata {metadata:?} not in legal range 0-{}", O::MAX);
};
Self {
offset: O::swap_bytes::<E>(offset),
metadata: T::Stored::<O>::swap_bytes::<E>(metadata),
_marker: PhantomData,
}
}
pub fn try_with_metadata<U>(offset: U, metadata: T::Metadata) -> Result<Self, Error>
where
U: Copy + IntoRepr + fmt::Debug,
O: TryFrom<U>,
{
const {
assert!(
O::CAN_SWAP_BYTES,
"Offset cannot be byte-ordered since it would not inhabit valid types"
);
assert!(
T::Stored::<O>::CAN_SWAP_BYTES,
"Packed offset cannot be byte-ordered since it would not inhabit valid types"
);
}
let Some(offset) = O::try_from(offset).ok() else {
return Err(Error::new(ErrorKind::InvalidOffsetRange {
offset: U::into_repr(offset),
max: O::into_repr(O::MAX),
}));
};
let Some(metadata) = T::try_from_metadata(metadata) else {
return Err(Error::new(ErrorKind::InvalidMetadataRange {
metadata: T::Metadata::into_repr(metadata),
max: O::into_repr(O::MAX),
}));
};
Ok(Self {
offset: O::swap_bytes::<E>(offset),
metadata: T::Stored::swap_bytes::<E>(metadata),
_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.as_usize::<E>()
}
#[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.as_usize::<E>() + size_of::<T>() * index;
Some(Ref::new(offset))
}
pub fn get_unchecked(self, index: usize) -> Ref<T, E, O> {
let offset = self.offset.as_usize::<E>() + size_of::<T>() * index;
Ref::new(offset)
}
#[inline]
pub fn split_at(self, at: usize) -> (Self, Self) {
let offset = self.offset();
let len = self.len();
assert!(at <= len, "Split point {at} is out of bounds 0..={len}");
let a = Self::with_metadata(offset, at);
let b = Self::with_metadata(offset + at * size_of::<T>(), len - at);
(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.as_usize::<E>();
let end = start + self.metadata.as_usize::<E>() * size_of::<T>();
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.as_usize::<E>()
}
#[inline]
pub fn is_empty(self) -> bool {
self.metadata.is_zero()
}
}
pub struct Iter<T, E, O> {
start: usize,
end: usize,
_marker: PhantomData<(T, E, O)>,
}
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 == self.end {
return None;
}
let start = self.start;
self.start += size_of::<T>();
Some(Ref::new(start))
}
}
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 == self.end {
return None;
}
self.end -= size_of::<T>();
Some(Ref::new(self.end))
}
}
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: Copy + fmt::Debug,
O: TryFrom<U>,
{
const {
assert!(
O::CAN_SWAP_BYTES,
"Offset cannot be byte-ordered since it would not inhabit valid types",
);
}
let Some(offset) = O::try_from(offset).ok() else {
panic!("Offset {offset:?} not in the legal range 0-{}", O::MAX);
};
Self {
offset: O::swap_bytes::<E>(offset),
metadata: (),
_marker: PhantomData,
}
}
#[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.as_usize::<E>()
}
pub fn coerce<U>(self) -> Ref<U, E, O>
where
T: Coerce<U>,
U: ?Sized + Pointee,
{
Ref {
offset: self.offset,
metadata: T::coerce_metadata(self.metadata),
_marker: PhantomData,
}
}
pub fn try_coerce<U>(self) -> Option<Ref<U, E, O>>
where
T: Coerce<U>,
U: ?Sized + Pointee,
{
Some(Ref {
offset: self.offset,
metadata: T::try_coerce_metadata(self.metadata)?,
_marker: PhantomData,
})
}
#[cfg(test)]
pub(crate) fn cast<U>(self) -> Ref<U, E, O>
where
U: ?Sized + Pointee<Stored<O> = T::Stored<O>>,
{
Ref {
offset: self.offset,
metadata: self.metadata,
_marker: PhantomData,
}
}
}
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) -> Ref<[T], E, O> {
Ref::with_metadata(self.offset, N)
}
}
impl<T, E, O> Ref<MaybeUninit<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: {:?} }}",
core::any::type_name::<T>(),
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);
}
}