#![doc = include_str!("../example.md")]
#![deny(
future_incompatible,
missing_docs,
nonstandard_style,
unsafe_op_in_unsafe_fn,
unused,
warnings,
clippy::all,
clippy::missing_safety_doc,
clippy::undocumented_unsafe_blocks,
rustdoc::broken_intra_doc_links,
rustdoc::missing_crate_level_docs
)]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg))]
#![cfg_attr(miri, allow(internal_features), feature(core_intrinsics))]
mod impls;
use core::{
ffi::CStr,
fmt,
hash::{Hash, Hasher},
};
#[cfg(feature = "derive")]
pub use ptr_meta_derive::{pointee, Pointee};
pub unsafe trait Pointee {
type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
}
unsafe impl<T> Pointee for T {
type Metadata = ();
}
unsafe impl<T> Pointee for [T] {
type Metadata = usize;
}
unsafe impl Pointee for str {
type Metadata = usize;
}
unsafe impl Pointee for CStr {
type Metadata = usize;
}
#[cfg(feature = "std")]
unsafe impl Pointee for std::ffi::OsStr {
type Metadata = usize;
}
#[inline]
pub const fn metadata<T: Pointee + ?Sized>(
ptr: *const T,
) -> <T as Pointee>::Metadata {
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
}
#[inline]
pub const fn to_raw_parts<T: Pointee + ?Sized>(
ptr: *const T,
) -> (*const (), <T as Pointee>::Metadata) {
(ptr as *const (), metadata(ptr))
}
#[inline]
pub const fn to_raw_parts_mut<T: Pointee + ?Sized>(
ptr: *mut T,
) -> (*mut (), <T as Pointee>::Metadata) {
(ptr as *mut (), metadata(ptr))
}
#[inline]
pub const fn from_raw_parts<T: Pointee + ?Sized>(
data_address: *const (),
metadata: <T as Pointee>::Metadata,
) -> *const T {
unsafe {
PtrRepr {
components: PtrComponents {
data_address,
metadata,
},
}
.const_ptr
}
}
#[inline]
pub const fn from_raw_parts_mut<T: Pointee + ?Sized>(
data_address: *mut (),
metadata: <T as Pointee>::Metadata,
) -> *mut T {
unsafe {
PtrRepr {
components: PtrComponents {
data_address,
metadata,
},
}
.mut_ptr
}
}
#[repr(C)]
union PtrRepr<T: Pointee + ?Sized> {
const_ptr: *const T,
mut_ptr: *mut T,
components: PtrComponents<T>,
}
#[repr(C)]
struct PtrComponents<T: Pointee + ?Sized> {
data_address: *const (),
metadata: <T as Pointee>::Metadata,
}
impl<T: Pointee + ?Sized> Copy for PtrComponents<T> {}
impl<T: Pointee + ?Sized> Clone for PtrComponents<T> {
fn clone(&self) -> Self {
*self
}
}
pub struct DynMetadata<Dyn: ?Sized> {
vtable_ptr: &'static VTable,
phantom: core::marker::PhantomData<Dyn>,
}
struct VTable;
impl<Dyn: ?Sized> DynMetadata<Dyn> {
#[inline]
pub fn size_of(self) -> usize {
#[cfg(miri)]
{
return unsafe {
core::intrinsics::vtable_size(
self.vtable_ptr as *const VTable as *const (),
)
};
}
#[cfg(not(miri))]
{
unsafe {
(self.vtable_ptr as *const VTable as *const usize)
.add(1)
.read()
}
}
}
#[inline]
pub fn align_of(self) -> usize {
#[cfg(miri)]
{
return unsafe {
core::intrinsics::vtable_align(
self.vtable_ptr as *const VTable as *const (),
)
};
}
#[cfg(not(miri))]
{
unsafe {
(self.vtable_ptr as *const VTable as *const usize)
.add(2)
.read()
}
}
}
#[inline]
pub fn layout(self) -> core::alloc::Layout {
unsafe {
core::alloc::Layout::from_size_align_unchecked(
self.size_of(),
self.align_of(),
)
}
}
}
unsafe impl<Dyn: ?Sized> Send for DynMetadata<Dyn> {}
unsafe impl<Dyn: ?Sized> Sync for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> fmt::Debug for DynMetadata<Dyn> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DynMetadata")
.field(&(self.vtable_ptr as *const VTable))
.finish()
}
}
impl<Dyn: ?Sized> Unpin for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Copy for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> Clone for DynMetadata<Dyn> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<Dyn: ?Sized> Eq for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> PartialEq for DynMetadata<Dyn> {
#[inline]
fn eq(&self, other: &Self) -> bool {
core::ptr::eq::<VTable>(self.vtable_ptr, other.vtable_ptr)
}
}
impl<Dyn: ?Sized> Ord for DynMetadata<Dyn> {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
(self.vtable_ptr as *const VTable)
.cmp(&(other.vtable_ptr as *const VTable))
}
}
impl<Dyn: ?Sized> PartialOrd for DynMetadata<Dyn> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<Dyn: ?Sized> Hash for DynMetadata<Dyn> {
#[inline]
fn hash<H: Hasher>(&self, hasher: &mut H) {
core::ptr::hash::<VTable, _>(self.vtable_ptr, hasher)
}
}
#[cfg(test)]
fn test_pointee<T: Pointee + ?Sized>(value: &T) {
let ptr = value as *const T;
let (raw, meta) = to_raw_parts(ptr);
let re_ptr = from_raw_parts::<T>(raw, meta);
assert_eq!(ptr, re_ptr);
}
#[cfg(test)]
mod tests {
use super::test_pointee;
#[test]
fn sized_types() {
test_pointee(&());
test_pointee(&42);
test_pointee(&true);
test_pointee(&[1, 2, 3, 4]);
struct TestUnit;
test_pointee(&TestUnit);
#[allow(dead_code)]
struct TestStruct {
a: (),
b: i32,
c: bool,
}
test_pointee(&TestStruct {
a: (),
b: 42,
c: true,
});
#[allow(dead_code)]
struct TestTuple((), i32, bool);
test_pointee(&TestTuple((), 42, true));
struct TestGeneric<T>(T);
test_pointee(&TestGeneric(42));
}
#[test]
fn unsized_types() {
test_pointee("hello world");
test_pointee(&[1, 2, 3, 4] as &[i32]);
}
}
#[cfg(all(test, feature = "derive"))]
mod derive_tests {
use core::any::Any;
use super::{test_pointee, Pointee};
#[test]
fn trait_objects() {
#[crate::pointee(crate)]
trait TestTrait {
#[allow(dead_code)]
fn foo(&self);
}
struct A;
impl TestTrait for A {
fn foo(&self) {}
}
let trait_object = &A as &dyn TestTrait;
test_pointee(trait_object);
#[allow(dead_code)]
struct B(i32);
impl TestTrait for B {
fn foo(&self) {}
}
let b = B(42);
let trait_object = &b as &dyn TestTrait;
test_pointee(trait_object);
}
#[test]
fn last_field_dst() {
#[allow(dead_code)]
#[derive(Pointee)]
#[ptr_meta(crate)]
struct Test<H, T> {
head: H,
tail: [T],
}
#[allow(dead_code)]
#[derive(Pointee)]
#[ptr_meta(crate)]
struct TestDyn {
tail: dyn Any,
}
#[crate::pointee(crate)]
trait TestTrait {}
#[allow(dead_code)]
#[derive(Pointee)]
#[ptr_meta(crate)]
struct TestCustomDyn {
tail: dyn TestTrait,
}
}
#[test]
fn generic_trait() {
#[allow(dead_code)]
#[crate::pointee(crate)]
trait TestTrait<T: ?Sized> {}
impl<T: ?Sized> TestTrait<T> for () {}
test_pointee(&() as &dyn TestTrait<u32>);
}
}