use core::cmp::Ordering;
use core::fmt;
use core::hash::Hash;
use core::marker::PhantomData;
use core::mem::size_of;
use crate::mem::MaybeUninit;
use crate::pointer::{DefaultSize, Pointee, Size};
use crate::ZeroCopy;
#[derive(ZeroCopy)]
#[repr(C)]
#[zero_copy(crate, bounds = {T::Packed: ZeroCopy})]
pub struct Ref<T: ?Sized, O: Size = DefaultSize>
where
T: Pointee<O>,
{
offset: O,
metadata: T::Packed,
#[zero_copy(ignore)]
_marker: PhantomData<T>,
}
impl<T: ?Sized, O: Size> Ref<T, O>
where
T: Pointee<O>,
{
#[inline]
pub fn with_metadata<U>(offset: U, metadata: T::Metadata) -> Self
where
U: Copy + fmt::Debug,
O: TryFrom<U>,
T::Metadata: fmt::Debug,
T::Packed: TryFrom<T::Metadata>,
{
let Some(offset) = O::try_from(offset).ok() else {
panic!("Offset {offset:?} not in legal range 0-{}", O::MAX);
};
let Some(metadata) = T::Packed::try_from(metadata).ok() else {
panic!("Metadata {metadata:?} not in legal range 0-{}", O::MAX);
};
Self {
offset,
metadata,
_marker: PhantomData,
}
}
}
impl<T, O: Size> Ref<[T], O>
where
T: ZeroCopy,
{
#[inline]
pub fn len(&self) -> usize {
self.metadata.as_usize()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.metadata.is_zero()
}
#[inline]
pub fn get(&self, index: usize) -> Option<Ref<T, O>> {
if index >= self.len() {
return None;
}
let ptr = self
.offset
.as_usize()
.wrapping_add(size_of::<T>().wrapping_mul(index));
Some(Ref::new(ptr))
}
}
impl<T: ?Sized, O: Size> Ref<T, O>
where
T: Pointee<O, Packed = O>,
{
#[inline]
pub fn metadata(&self) -> T::Packed {
self.metadata
}
}
impl<T, O: Size> Ref<T, O>
where
T: Pointee<O, Packed = ()>,
{
#[inline]
pub fn new<U>(offset: U) -> Self
where
U: Copy + fmt::Debug,
O: TryFrom<U>,
{
let Some(offset) = O::try_from(offset).ok() else {
panic!("Offset {offset:?} not in the legal range 0-{}", O::MAX);
};
Self {
offset,
metadata: (),
_marker: PhantomData,
}
}
#[inline]
pub const fn zero() -> Self {
Self {
offset: O::ZERO,
metadata: (),
_marker: PhantomData,
}
}
}
impl<T: ?Sized, O: Size> Ref<T, O>
where
T: Pointee<O>,
{
#[inline]
pub fn offset(&self) -> usize {
self.offset.as_usize()
}
pub fn cast<U: ?Sized>(self) -> Ref<U, O>
where
U: Pointee<O, Packed = T::Packed>,
{
Ref {
offset: self.offset,
metadata: self.metadata,
_marker: PhantomData,
}
}
}
impl<T, const N: usize, O: Size> Ref<[T; N], O>
where
T: ZeroCopy,
{
#[inline]
pub fn array_into_slice(self) -> Ref<[T], O> {
Ref::with_metadata(self.offset, N)
}
}
impl<T, O: Size> Ref<MaybeUninit<T>, O>
where
T: Pointee<O>,
{
#[inline]
pub const fn assume_init(self) -> Ref<T, O> {
Ref {
offset: self.offset,
metadata: self.metadata,
_marker: PhantomData,
}
}
}
impl<T: ?Sized, O: Size> fmt::Debug for Ref<T, O>
where
T: Pointee<O>,
T::Packed: fmt::Debug,
O: 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: ?Sized, O: Size> Clone for Ref<T, O>
where
T: Pointee<O>,
{
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized, O: Size> Copy for Ref<T, O> where T: Pointee<O> {}
impl<T: ?Sized, O: Size> PartialEq for Ref<T, O>
where
T: Pointee<O>,
T::Packed: PartialEq,
O: PartialEq,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
self.offset == other.offset && self.metadata == other.metadata
}
}
impl<T: ?Sized, O: Size> Eq for Ref<T, O>
where
T: Pointee<O>,
T::Packed: Eq,
O: Eq,
{
}
impl<T: ?Sized, O: Size> PartialOrd for Ref<T, O>
where
T: Pointee<O>,
T::Packed: PartialOrd,
O: Ord,
{
#[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: ?Sized, O: Size> Ord for Ref<T, O>
where
T: Pointee<O>,
T::Packed: Ord,
O: Ord,
{
#[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: ?Sized, O: Size> Hash for Ref<T, O>
where
T: Pointee<O>,
T::Packed: Hash,
O: Hash,
{
#[inline]
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.offset.hash(state);
self.metadata.hash(state);
}
}