From b73044129d41861162bd2154af6222fc264418b7 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Fri, 1 Mar 2024 13:35:10 -0800 Subject: [PATCH] KnownLayout tracks pointer metadata type Makes progress on #29 --- src/lib.rs | 64 ++++++++++++++++++++++++++++++++++++-- src/macros.rs | 10 ++++-- src/pointer/ptr.rs | 2 ++ zerocopy-derive/src/lib.rs | 10 ++++-- 4 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6b8502063b..8671280aca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1053,14 +1053,71 @@ pub unsafe trait KnownLayout { where Self: Sized; + /// `()` for sized types and `usize` for slice DSTs. + #[doc(hidden)] + type PointerMetadata: PointerMetadata; + #[doc(hidden)] const LAYOUT: DstLayout; /// SAFETY: The returned pointer has the same address and provenance as /// `bytes`. If `Self` is a DST, the returned pointer's referent has `elems` - /// elements in its trailing slice. If `Self` is sized, `elems` is ignored. + /// elements in its trailing slice. #[doc(hidden)] - fn raw_from_ptr_len(bytes: NonNull, elems: usize) -> NonNull; + fn raw_from_ptr_len(bytes: NonNull, meta: Self::PointerMetadata) -> NonNull; +} + +/// The metadata associated with a [`KnownLayout`] type. +#[doc(hidden)] +pub trait PointerMetadata { + /// Constructs a `Self` from an element count. + /// + /// If `Self = ()`, this returns `()`. If `Self = usize`, this returns + /// `elems`. No other types are currently supported. + fn from_elem_count(elems: usize) -> Self; + + /// What is the size of the object with the given layout and pointer + /// metadata? + /// + /// # Panics + /// + /// If `Self = ()`, `layout` must describe a sized type. If `Self = usize`, + /// `layout` must describe a slice DST. Otherwise, `size_for_metadata` will + /// panic. + fn size_for_metadata(&self, layout: DstLayout) -> Option; +} + +impl PointerMetadata for () { + #[inline] + #[allow(clippy::unused_unit)] + fn from_elem_count(_elems: usize) -> () {} + + #[inline] + fn size_for_metadata(&self, layout: DstLayout) -> Option { + match layout.size_info { + SizeInfo::Sized { _size } => Some(_size), + SizeInfo::SliceDst(_) => unreachable!(), + } + } +} + +impl PointerMetadata for usize { + #[inline] + fn from_elem_count(elems: usize) -> usize { + elems + } + + #[inline] + fn size_for_metadata(&self, layout: DstLayout) -> Option { + match layout.size_info { + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) => { + let slice_len = _elem_size.checked_mul(*self)?; + let without_padding = _offset.checked_add(slice_len)?; + without_padding.checked_add(util::padding_needed_for(without_padding, layout.align)) + } + SizeInfo::Sized { .. } => unreachable!(), + } + } } // SAFETY: Delegates safety to `DstLayout::for_slice`. @@ -1071,6 +1128,9 @@ unsafe impl KnownLayout for [T] { Self: Sized, { } + + type PointerMetadata = usize; + const LAYOUT: DstLayout = DstLayout::for_slice::(); // SAFETY: `.cast` preserves address and provenance. The returned pointer diff --git a/src/macros.rs b/src/macros.rs index 3bfd00e302..843f19afd5 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -367,6 +367,8 @@ macro_rules! impl_known_layout { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() where Self: Sized {} + type PointerMetadata = (); + #[allow(unused_qualifications)] const LAYOUT: crate::DstLayout = crate::DstLayout::for_type::<$ty>(); @@ -375,7 +377,7 @@ macro_rules! impl_known_layout { // TODO(#429): Add documentation to `.cast` that promises that // it preserves provenance. #[inline(always)] - fn raw_from_ptr_len(bytes: NonNull, _elems: usize) -> NonNull { + fn raw_from_ptr_len(bytes: NonNull, _meta: ()) -> NonNull { bytes.cast::() } } @@ -404,6 +406,8 @@ macro_rules! unsafe_impl_known_layout { #[allow(clippy::missing_inline_in_public_items)] fn only_derive_is_allowed_to_implement_this_trait() {} + type PointerMetadata = <$repr as KnownLayout>::PointerMetadata; + const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT; // SAFETY: All operations preserve address and provenance. @@ -413,9 +417,9 @@ macro_rules! unsafe_impl_known_layout { // that it preserves provenance. #[inline(always)] #[allow(unused_qualifications)] // for `core::ptr::NonNull` - fn raw_from_ptr_len(bytes: NonNull, elems: usize) -> NonNull { + fn raw_from_ptr_len(bytes: NonNull, meta: <$repr as KnownLayout>::PointerMetadata) -> NonNull { #[allow(clippy::as_conversions)] - let ptr = <$repr>::raw_from_ptr_len(bytes, elems).as_ptr() as *mut Self; + let ptr = <$repr>::raw_from_ptr_len(bytes, meta).as_ptr() as *mut Self; // SAFETY: `ptr` was converted from `bytes`, which is non-null. unsafe { NonNull::new_unchecked(ptr) } } diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 5d404a5474..91051d13cb 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -900,6 +900,7 @@ mod _transitions { /// Casts of the referent type. mod _casts { use super::*; + use crate::PointerMetadata; impl<'a, T, I> Ptr<'a, T, I> where @@ -1156,6 +1157,7 @@ mod _casts { // produces a pointer whose address is greater than or equal to that of // `ptr`. Since `ptr` is a `NonNull`, `base` is also non-null. let base = unsafe { NonNull::new_unchecked(base) }; + let elems = ::PointerMetadata::from_elem_count(elems); let ptr = U::raw_from_ptr_len(base, elems); // SAFETY: diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 3b2c0cf1e0..296a781f11 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -161,6 +161,8 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { ( SelfBounds::None, quote!( + type PointerMetadata = <#trailing_field_ty as ::zerocopy::KnownLayout>::PointerMetadata; + // SAFETY: `LAYOUT` accurately describes the layout of `Self`. // The layout of `Self` is reflected using a sequence of // invocations of `DstLayout::{new_zst,extend,pad_to_align}`. @@ -195,10 +197,10 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { #[inline(always)] fn raw_from_ptr_len( bytes: ::zerocopy::macro_util::core_reexport::ptr::NonNull, - elems: usize, + meta: <#trailing_field_ty as ::zerocopy::KnownLayout>::PointerMetadata, ) -> ::zerocopy::macro_util::core_reexport::ptr::NonNull { use ::zerocopy::{KnownLayout}; - let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, elems); + let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta); let slf = trailing.as_ptr() as *mut Self; // SAFETY: Constructed from `trailing`, which is non-null. unsafe { ::zerocopy::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) } @@ -212,6 +214,8 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { ( SelfBounds::SIZED, quote!( + type PointerMetadata = (); + // SAFETY: `LAYOUT` is guaranteed to accurately describe the // layout of `Self`, because that is the documented safety // contract of `DstLayout::for_type`. @@ -224,7 +228,7 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { #[inline(always)] fn raw_from_ptr_len( bytes: ::zerocopy::macro_util::core_reexport::ptr::NonNull, - _elems: usize, + _meta: (), ) -> ::zerocopy::macro_util::core_reexport::ptr::NonNull { bytes.cast::() }