use core::fmt;
use core::marker::PhantomData;
use core::mem;
use core::ops;
#[cfg(doc)]
use crate::Ptr;
pub trait Offset<T: ?Sized, U: ?Sized> {
fn measure(self) -> Measurement;
}
#[derive(Copy, Clone, Debug)]
pub struct ByteOffset<I>(pub I);
#[derive(Copy, Clone, Debug)]
pub struct Measurement {
pub(crate) offset: isize,
pub(crate) is_element_wise: bool,
pub(crate) inbounds: bool,
}
impl Measurement {
pub const fn by_elements(count: isize) -> Self {
Self {
offset: count,
is_element_wise: true,
inbounds: true,
}
}
pub const fn by_bytes(len: isize) -> Self {
Self {
offset: len,
is_element_wise: false,
inbounds: true,
}
}
pub const fn inbounds(self, enforce: bool) -> Self {
Self {
inbounds: enforce,
..self
}
}
pub const unsafe fn apply<T>(mut self, ptr: *mut T) -> *mut T {
if !self.is_element_wise {
self.is_element_wise = true;
return self.apply(ptr as *mut u8) as *mut T;
}
if cfg!(debug_assertions) {
let byte_offset =
match self.offset.checked_mul(mem::size_of::<T>() as isize) {
Some(offset) => offset,
None => panic!("computed offset overflows `isize`"),
};
let addr = unsafe { mem::transmute::<_, usize>(ptr) };
assert!(
!self.inbounds || addr.checked_add_signed(byte_offset).is_some(),
"offset pointer wrapped around the address space"
);
}
if !self.inbounds {
return ptr.wrapping_offset(self.offset);
}
ptr.offset(self.offset)
}
}
#[derive(Copy, Clone, Debug)]
pub struct NotInBounds<I>(pub I);
impl<T: ?Sized, U: ?Sized, I: Offset<T, U>> Offset<T, U> for NotInBounds<I> {
fn measure(self) -> Measurement {
Measurement {
inbounds: false,
..self.0.measure()
}
}
}
macro_rules! impl_offset {
($($int:ty,)+) => {$(
impl<T: ?Sized> Offset<T, T> for $int {
fn measure(self) -> Measurement {
debug_assert!(
isize::try_from(self).is_ok(),
"{self} is not in bounds for `isize` (0x{:x}..0x{:x})",
isize::MIN, isize::MAX);
Measurement::by_elements(self as isize)
}
}
impl<T: ?Sized> Offset<T, T> for ByteOffset<$int> {
fn measure(self) -> Measurement {
Measurement {
is_element_wise: false,
..Offset::<T, T>::measure(self.0)
}
}
}
)*};
}
impl_offset! {
u8, u16, u32, u64, u128, usize,
i8, i16, i32, i64, i128, isize,
}
pub struct Field<T: ?Sized, U: ?Sized> {
offset: isize,
_ph: PhantomData<for<'a> fn(&T) -> &U>,
}
#[doc(hidden)]
impl<T: ?Sized, U: ?Sized> Field<T, U> {
#[doc(hidden)]
pub fn __new_for_macro(n: isize, _: *const T, _: *const U) -> Self {
Self {
offset: n,
_ph: PhantomData,
}
}
}
impl<T: ?Sized> Field<T, T> {
pub fn this() -> Self {
Self {
offset: 0,
_ph: PhantomData,
}
}
}
impl<T, const N: usize> Field<[T; N], T> {
pub fn index(n: usize) -> Self {
assert!(n < N, "index out of bounds: {n} >= {N}");
Self {
offset: (n * mem::size_of::<T>()) as isize,
_ph: PhantomData,
}
}
}
impl<T: ?Sized, U: ?Sized, V: ?Sized> ops::Add<Field<U, V>> for Field<T, U> {
type Output = Field<T, V>;
fn add(self, that: Field<U, V>) -> Self::Output {
Field {
offset: self.offset + that.offset,
_ph: PhantomData,
}
}
}
impl<T: ?Sized, U: ?Sized> ops::Neg for Field<T, U> {
type Output = Field<U, T>;
fn neg(self) -> Self::Output {
Field {
offset: -self.offset,
_ph: PhantomData,
}
}
}
impl<T: ?Sized, U: ?Sized> Offset<T, U> for Field<T, U> {
fn measure(self) -> Measurement {
Measurement::by_bytes(self.offset)
}
}
impl<T: ?Sized, U: ?Sized> Clone for Field<T, U> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized, U: ?Sized> Copy for Field<T, U> {}
impl<T: ?Sized, U: ?Sized> fmt::Debug for Field<T, U> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use core::any::type_name;
write!(
f,
"Field<{}, {}>({})",
type_name::<T>(),
type_name::<U>(),
self.offset
)
}
}
#[macro_export]
macro_rules! field {
(@impl $base:expr, .$field:tt $($($path:tt)+)?) => {{
let base = $base;
let field = unsafe { core::ptr::addr_of!((*base).$field) };
let offset = unsafe { field.cast::<u8>().offset_from(base.cast::<u8>()) };
$crate::offset::Field::__new_for_macro(offset, base, field)
+ $crate::field!(@impl field, $($($path)+)?)
}};
(@impl $base:expr, [$idx:expr] $($($path:tt)+)?) => {{
let idx: usize = $idx;
let base = $base;
let field = unsafe { core::ptr::addr_of!((*base)[idx]) };
$crate::offset::Field::index(idx)
+ $crate::field!(@impl field, $($($path)+)?)
}};
(@impl $base:expr,) => {{
$crate::offset::Field::this()
}};
(_, $($path:tt)+) => {{
let uninit = core::mem::MaybeUninit::uninit();
$crate::field!(@impl uninit.as_ptr(), $($path)+)
}};
($($ty:tt)::+ $($path:tt)+) => {{
let uninit = core::mem::MaybeUninit::<$($ty)::+>::uninit();
$crate::field!(@impl uninit.as_ptr(), $($path)+)
}};
}