blob: 7b6ffac8f82ee89aeb2d9b185199425ffdd78e1f [file] [log] [blame]
//! A radioactive stabilization of the [`ptr_meta` RFC][rfc].
//!
//! [rfc]: https://rust-lang.github.io/rfcs/2580-ptr-meta.html
//!
//! # Usage
//!
//! ## Sized types
//!
//! All `Sized` types have `Pointee` implemented for them with a blanket implementation. You do not
//! need to derive `Pointee` for these types.
//!
//! ## `slice`s and `str`s
//!
//! These core types have implementations built in.
//!
//!## `dyn Any`
//!
//! The trait object for this standard library type comes with an implementation built in.
//!
//! ## Structs with a DST as its last field
//!
//! You can derive `Pointee` for structs with a trailing DST:
//!
//! ```
//! use ptr_meta::Pointee;
//!
//! #[derive(Pointee)]
//! struct Block<H, T> {
//! header: H,
//! elements: [T],
//! }
//! ```
//!
//! Note that this will only work when the last field is guaranteed to be a DST. Structs with a
//! generic last field may have a conflicting blanket impl since the generic type may be `Sized`. In
//! these cases, a collection of specific implementations may be required with the generic parameter
//! set to a slice, `str`, or specific trait object.
//!
//! ## Trait objects
//!
//! You can generate a `Pointee` implementation for trait objects:
//!
//! ```
//! use ptr_meta::pointee;
//!
//! // Generates Pointee for dyn Stringy
//! #[pointee]
//! trait Stringy {
//! fn as_string(&self) -> String;
//! }
//! ```
#![cfg_attr(not(feature = "std"), no_std)]
mod impls;
use core::{alloc::Layout, cmp, fmt, hash, marker::PhantomData, ptr};
pub use ptr_meta_derive::{pointee, Pointee};
/// Provides the pointer metadata type of any pointed-to type.
///
/// # Pointer metadata
///
/// Raw pointer types and reference types in Rust can be thought of as made of two parts: a data
/// pointer that contains the memory address of the value, and some additional metadata.
///
/// For statically-sized types (that implement `Sized`) as well as `extern` types, pointers are said
/// to be "thin". That is, their metadata is zero-sized and its type is `()`.
///
/// Pointers to [dynamically-sized types][dst] are said to be "wide" or "fat", and they have
/// metadata that is not zero-sized:
///
/// * For structs with a DST as the last field, the metadata of the struct is the metadata of the
/// last field.
/// * For the `str` type, the metadata is the length in bytes as a `usize`.
/// * For slice types like `[T]`, the metadata is the length in items as a `usize`.
/// * For trait objects like `dyn SomeTrait`, the metadata is [`DynMetadata<Self>`][DynMetadata]
/// (e.g. `DynMetadata<dyn SomeTrait>`) which contains a pointer to the trait object's vtable.
///
/// In the future, the Rust language may gain new kinds of types that have different pointer
/// metadata.
///
/// [dst]: https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts
///
///
/// # The `Pointee` trait
///
/// The point of this trait is its associated `Metadata` type, which may be `()`, `usize`, or
/// `DynMetadata<_>` as described above. It is automatically implemented for `Sized` types, slices,
/// and `str`s. An implementation can be generated for structs with a trailing DST and trait objects
/// using the [derive macro] and [attribute macro] respectively.
///
/// [derive macro]: ptr_meta_derive::Pointee
/// [attribute macro]: ptr_meta_derive::pointee
///
/// # Usage
///
/// Raw pointers can be decomposed into the data address and metadata components with their
/// [`to_raw_parts`] methods.
///
/// Alternatively, metadata alone can be extracted with the [`metadata`] function. A reference can
/// be passed to [`metadata`] and be implicitly coerced to a pointer.
///
/// A (possibly wide) pointer can be put back together from its address and metadata with
/// [`from_raw_parts`] or [`from_raw_parts_mut`].
///
/// [`to_raw_parts`]: PtrExt::to_raw_parts
pub trait Pointee {
/// The type for metadata in pointers and references to `Self`.
type Metadata: Copy + Send + Sync + Ord + hash::Hash + Unpin;
}
impl<T> Pointee for T {
type Metadata = ();
}
impl<T> Pointee for [T] {
type Metadata = usize;
}
impl Pointee for str {
type Metadata = usize;
}
#[cfg(feature = "std")]
impl Pointee for ::std::ffi::CStr {
type Metadata = usize;
}
#[cfg(feature = "std")]
impl Pointee for ::std::ffi::OsStr {
type Metadata = usize;
}
#[repr(C)]
pub(crate) union PtrRepr<T: Pointee + ?Sized> {
pub(crate) const_ptr: *const T,
pub(crate) mut_ptr: *mut T,
pub(crate) components: PtrComponents<T>,
}
#[repr(C)]
pub(crate) struct PtrComponents<T: Pointee + ?Sized> {
pub(crate) data_address: *const (),
pub(crate) metadata: <T as Pointee>::Metadata,
}
impl<T: Pointee + ?Sized> Clone for PtrComponents<T> {
fn clone(&self) -> Self {
Self {
data_address: self.data_address.clone(),
metadata: self.metadata.clone(),
}
}
}
impl<T: Pointee + ?Sized> Copy for PtrComponents<T> {}
/// Returns the metadata component of a pointer.
///
/// Values of type `*mut T`, `&T`, or `&mut T` can be passed directly to this function as they
/// implicitly coerce to `*const T`.
///
/// # Example
///
/// ```
/// use ptr_meta::metadata;
///
/// assert_eq!(metadata("foo"), 3_usize);
/// ```
pub fn metadata<T: Pointee + ?Sized>(ptr: *const T) -> <T as Pointee>::Metadata {
unsafe { PtrRepr { const_ptr: ptr }.components.metadata }
}
/// Forms a (possibly wide) raw pointer from a data address and metadata.
///
/// This function is safe to call, but the returned pointer is not necessarily safe to dereference.
/// For slices, see the documentation of [`slice::from_raw_parts`] for safety requirements. For
/// trait objects, the metadata must come from a pointer to the same underlying erased type.
///
/// [`slice::from_raw_parts`]: core::slice::from_raw_parts
pub 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
}
}
/// Performs the same functionality as [`from_raw_parts`], except that a raw `*mut` pointer is
/// returned, as opposed to a raw `*const` pointer.
///
/// See the documentation of [`from_raw_parts`] for more details.
pub 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
}
}
/// Extension methods for [`NonNull`](core::ptr::NonNull).
pub trait NonNullExt<T: Pointee + ?Sized> {
/// Creates a new non-null pointer from its raw parts.
fn from_raw_parts(raw: ptr::NonNull<()>, meta: <T as Pointee>::Metadata) -> Self;
/// Converts a non-null pointer to its raw parts.
fn to_raw_parts(self) -> (ptr::NonNull<()>, <T as Pointee>::Metadata);
}
impl<T: Pointee + ?Sized> NonNullExt<T> for ptr::NonNull<T> {
fn from_raw_parts(raw: ptr::NonNull<()>, meta: <T as Pointee>::Metadata) -> Self {
unsafe { Self::new_unchecked(from_raw_parts_mut(raw.as_ptr(), meta)) }
}
fn to_raw_parts(self) -> (ptr::NonNull<()>, <T as Pointee>::Metadata) {
let (raw, meta) = PtrExt::to_raw_parts(self.as_ptr());
unsafe { (ptr::NonNull::new_unchecked(raw), meta) }
}
}
/// Extension methods for pointers.
pub trait PtrExt<T: Pointee + ?Sized> {
/// The type's raw pointer (`*const ()` or `*mut ()`).
type Raw;
/// Decompose a (possibly wide) pointer into its address and metadata components.
///
/// The pointer can be later reconstructed with [`from_raw_parts`].
fn to_raw_parts(self) -> (Self::Raw, <T as Pointee>::Metadata);
}
impl<T: Pointee + ?Sized> PtrExt<T> for *const T {
type Raw = *const ();
fn to_raw_parts(self) -> (Self::Raw, <T as Pointee>::Metadata) {
unsafe {
(&self as *const Self)
.cast::<(Self::Raw, <T as Pointee>::Metadata)>()
.read()
}
}
}
impl<T: Pointee + ?Sized> PtrExt<T> for *mut T {
type Raw = *mut ();
fn to_raw_parts(self) -> (Self::Raw, <T as Pointee>::Metadata) {
unsafe {
(&self as *const Self)
.cast::<(Self::Raw, <T as Pointee>::Metadata)>()
.read()
}
}
}
/// The metadata for a `Dyn = dyn SomeTrait` trait object type.
///
/// It is a pointer to a vtable (virtual call table) that represents all the necessary information
/// to manipulate the concrete type stored inside a trait object. The vtable notably contains:
///
/// * Type size
/// * Type alignment
/// * A pointer to the type’s `drop_in_place` impl (may be a no-op for plain-old-data)
/// * Pointers to all the methods for the type’s implementation of the trait
///
/// Note that the first three are special because they’re necessary to allocate, drop, and
/// deallocate any trait object.
///
/// It is possible to name this struct with a type parameter that is not a `dyn` trait object (for
/// example `DynMetadata<u64>`), but not to obtain a meaningful value of that struct.
#[repr(transparent)]
pub struct DynMetadata<Dyn: ?Sized> {
vtable_ptr: &'static VTable,
phantom: PhantomData<Dyn>,
}
#[repr(C)]
struct VTable {
drop_in_place: fn(*mut ()),
size_of: usize,
align_of: usize,
}
impl<Dyn: ?Sized> DynMetadata<Dyn> {
/// Returns the size of the type associated with this vtable.
pub fn size_of(self) -> usize {
self.vtable_ptr.size_of
}
/// Returns the alignment of the type associated with this vtable.
pub fn align_of(self) -> usize {
self.vtable_ptr.align_of
}
/// Returns the size and alignment together as a `Layout`.
pub fn layout(self) -> Layout {
unsafe { 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> cmp::Eq for DynMetadata<Dyn> {}
impl<Dyn: ?Sized> cmp::PartialEq for DynMetadata<Dyn> {
#[inline]
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.vtable_ptr, other.vtable_ptr)
}
}
impl<Dyn: ?Sized> cmp::Ord for DynMetadata<Dyn> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
(self.vtable_ptr as *const VTable).cmp(&(other.vtable_ptr as *const VTable))
}
}
impl<Dyn: ?Sized> cmp::PartialOrd for DynMetadata<Dyn> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<Dyn: ?Sized> hash::Hash for DynMetadata<Dyn> {
fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
ptr::hash(self.vtable_ptr, hasher)
}
}
#[cfg(test)]
mod tests {
use super::{from_raw_parts, pointee, Pointee, PtrExt};
use crate as ptr_meta;
fn test_pointee<T: Pointee + ?Sized>(value: &T) {
let ptr = value as *const T;
let (raw, meta) = PtrExt::to_raw_parts(ptr);
let re_ptr = from_raw_parts::<T>(raw, meta);
assert_eq!(ptr, re_ptr);
}
#[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,
});
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]);
}
#[test]
fn trait_objects() {
#[pointee]
trait TestTrait {
fn foo(&self);
}
struct A;
impl TestTrait for A {
fn foo(&self) {}
}
let trait_object = &A as &dyn TestTrait;
test_pointee(trait_object);
let (_, meta) = PtrExt::to_raw_parts(trait_object as *const dyn TestTrait);
assert_eq!(meta.size_of(), 0);
assert_eq!(meta.align_of(), 1);
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);
let (_, meta) = PtrExt::to_raw_parts(trait_object as *const dyn TestTrait);
assert_eq!(meta.size_of(), 4);
assert_eq!(meta.align_of(), 4);
}
#[test]
fn last_field_dst() {
#[allow(dead_code)]
#[derive(Pointee)]
struct Test<H, T> {
head: H,
tail: [T],
}
#[allow(dead_code)]
#[derive(Pointee)]
struct TestDyn {
tail: dyn core::any::Any,
}
#[pointee]
trait TestTrait {}
#[allow(dead_code)]
#[derive(Pointee)]
struct TestCustomDyn {
tail: dyn TestTrait,
}
}
}