Skip to content

Commit 87c2b56

Browse files
committed
[WIP] KnownLayout
TODO: Tests, especially for behavior of methods
1 parent 673a809 commit 87c2b56

File tree

1 file changed

+303
-13
lines changed

1 file changed

+303
-13
lines changed

src/lib.rs

Lines changed: 303 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -179,17 +179,14 @@ use core::{
179179
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
180180
},
181181
ops::{Deref, DerefMut},
182-
ptr, slice,
182+
ptr::{self, NonNull},
183+
slice,
183184
};
184185

185186
#[cfg(feature = "alloc")]
186187
extern crate alloc;
187188
#[cfg(feature = "alloc")]
188-
use {
189-
alloc::boxed::Box,
190-
alloc::vec::Vec,
191-
core::{alloc::Layout, ptr::NonNull},
192-
};
189+
use {alloc::boxed::Box, alloc::vec::Vec, core::alloc::Layout};
193190

194191
// This is a hack to allow zerocopy-derive derives to work in this crate. They
195192
// assume that zerocopy is linked as an extern crate, so they access items from
@@ -199,6 +196,237 @@ mod zerocopy {
199196
pub(crate) use crate::*;
200197
}
201198

199+
/// When performing a byte-slice-to-type cast, is the type taken from the prefix
200+
/// of the byte slice or from the suffix of the byte slice?
201+
#[doc(hidden)]
202+
#[allow(missing_debug_implementations, missing_copy_implementations)]
203+
pub enum CastType {
204+
Prefix,
205+
Suffix,
206+
}
207+
208+
/// A trait which carries information about a type's layout that is used by the
209+
/// internals of this crate.
210+
///
211+
/// This trait is not meant for consumption by code outsie of this crate. While
212+
/// the normal semver stability guarantees apply with respect to which types
213+
/// implement this trait and which trait implementations are implied by this
214+
/// trait, no semver stability guarantees are made regarding its internals; they
215+
/// may change at any time, and code which makes use of them may break.
216+
///
217+
/// # Safety
218+
///
219+
/// This trait does not convey any safety guarantees to code outside this crate.
220+
pub unsafe trait KnownLayout: sealed::KnownLayoutSealed {
221+
#[doc(hidden)]
222+
const FIXED_PREFIX_SIZE: usize;
223+
#[doc(hidden)]
224+
const ALIGN: NonZeroUsize;
225+
#[doc(hidden)]
226+
const TRAILING_SLICE_ELEM_SIZE: Option<usize>;
227+
228+
/// Validates that the memory region at `addr` of length `bytes_len`
229+
/// satisfies `Self`'s size and alignment requirements, returning `(elems,
230+
/// split_at, prefix_suffix_bytes)`.
231+
///
232+
/// In particular, `validate_size_align` validates that:
233+
/// - `bytes_len` is large enough to hold an instance of `Self`
234+
/// - If `cast_type` is `Prefix`, `addr` satisfies `Self`'s alignment
235+
/// requirements
236+
/// - If `cast_type` is `Suffix`, `addr + split_at` satisfies `Self`'s
237+
/// alignment requirements
238+
///
239+
/// For DSTs, `elems` is the maximum number of trailing slice elements such
240+
/// that a `Self` with that number of trailing slice elements can fit in the
241+
/// provided space. For sized types, `elems` is always 0.
242+
///
243+
/// `split_at` indicates the point at which to split the memory region in
244+
/// order to split it into the `Self` and the prefix or suffix. If
245+
/// `cast_type` is `Prefix`, `split_at` is the address of the first byte of
246+
/// the suffix. If `cast_type` is `Suffix`, `split_at` is the address of the
247+
/// first byte of the `Self`.
248+
///
249+
/// # Panics
250+
///
251+
/// Panics if called on a DST whose trailing slice element type is a
252+
/// zero-sized type.
253+
#[doc(hidden)]
254+
#[inline(always)]
255+
fn validate_size_align<A: AsAddress>(
256+
addr: A,
257+
bytes_len: usize,
258+
cast_type: CastType,
259+
) -> Option<(usize, usize, usize)> {
260+
let trailing_slice_bytes = bytes_len.checked_sub(Self::FIXED_PREFIX_SIZE)?;
261+
let (elems, self_bytes) = if let Some(elem_size) = Self::TRAILING_SLICE_ELEM_SIZE {
262+
let elem_size = NonZeroUsize::new(elem_size)
263+
.expect("attempted to cast to slice type with zero-sized element");
264+
#[allow(clippy::arithmetic_side_effects)]
265+
let elems = trailing_slice_bytes / elem_size;
266+
#[allow(clippy::arithmetic_side_effects)]
267+
let self_bytes = Self::FIXED_PREFIX_SIZE + (elems * elem_size.get());
268+
(elems, self_bytes)
269+
} else {
270+
(0, Self::FIXED_PREFIX_SIZE)
271+
};
272+
273+
// `self_addr` indicates where in the given byte range the `Self` will
274+
// start. If we're doing a prefix cast, it starts at the beginning. If
275+
// we're doing a suffix cast, it starts after whatever bytes are
276+
// remaining.
277+
#[allow(clippy::arithmetic_side_effects)]
278+
let (self_addr, split_at) = match cast_type {
279+
CastType::Prefix => (addr.addr(), self_bytes),
280+
CastType::Suffix => {
281+
let split_at = bytes_len - self_bytes;
282+
(addr.addr() + split_at, split_at)
283+
}
284+
};
285+
286+
#[allow(clippy::arithmetic_side_effects)]
287+
if self_addr % Self::ALIGN != 0 {
288+
return None;
289+
}
290+
291+
#[allow(clippy::arithmetic_side_effects)]
292+
let ret = Some((elems, split_at, bytes_len - self_bytes));
293+
ret
294+
}
295+
296+
/// SAFETY: The returned pointer has the same address and provenance as
297+
/// `bytes`. If `Self` is a DST, the returned pointer's referent has `elems`
298+
/// elements in its trailing slice.
299+
#[doc(hidden)]
300+
fn raw_from_ptr_len(bytes: NonNull<u8>, elems: usize) -> NonNull<Self>;
301+
}
302+
303+
impl<T: KnownLayout> sealed::KnownLayoutSealed for [T] {}
304+
// SAFETY: See inline comments.
305+
unsafe impl<T: KnownLayout> KnownLayout for [T] {
306+
// `[T]` is a slice type; it has no fields before the trailing slice.
307+
const FIXED_PREFIX_SIZE: usize = 0;
308+
// Slices have the same layout as the array they slice. [1] Arrays `[T; _]`
309+
// have the same alignment as `T`. [2]
310+
//
311+
// [1] https://doc.rust-lang.org/reference/type-layout.html#slice-layout
312+
// [2] https://doc.rust-lang.org/reference/type-layout.html#array-layout
313+
const ALIGN: NonZeroUsize = if let Some(align) = NonZeroUsize::new(mem::align_of::<T>()) {
314+
align
315+
} else {
316+
unreachable!()
317+
};
318+
const TRAILING_SLICE_ELEM_SIZE: Option<usize> = Some(mem::size_of::<T>());
319+
320+
// SAFETY: `.cast` preserves address and provenance. The returned pointer
321+
// refers to an object with `elems` elements by construction.
322+
#[inline(always)]
323+
fn raw_from_ptr_len(data: NonNull<u8>, elems: usize) -> NonNull<Self> {
324+
// TODO(#67): Remove this allow. See NonNullExt for more details.
325+
#[allow(unstable_name_collisions)]
326+
NonNull::slice_from_raw_parts(data.cast::<T>(), elems)
327+
}
328+
}
329+
330+
/// Implements `KnownLayout` for a sized type.
331+
macro_rules! impl_known_layout {
332+
(const $constvar:ident : $constty:ty, $tyvar:ident $(: ?$optbound:ident)? => $ty:ty) => {
333+
impl_known_layout!(@inner const $constvar: $constty, $tyvar $(: ?$optbound)? => $ty);
334+
};
335+
($tyvar:ident $(: ?$optbound:ident)? => $ty:ty) => {
336+
impl_known_layout!(@inner , $tyvar $(: ?$optbound)? => $ty);
337+
};
338+
($ty:ty) => {
339+
impl_known_layout!(@inner , => $ty);
340+
};
341+
($($tyvar:ident $(: ?$optbound:ident)? => $ty:ty),*) => {
342+
$(
343+
impl_known_layout!(@inner , $tyvar $(: ?$optbound)? => $ty);
344+
)*
345+
};
346+
($($ty:ty),*) => {
347+
$(
348+
impl_known_layout!(@inner , => $ty);
349+
)*
350+
};
351+
(@inner $(const $constvar:ident : $constty:ty)? , $($tyvar:ident $(: ?$optbound:ident)?)? => $ty:ty) => {
352+
impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> sealed::KnownLayoutSealed for $ty {}
353+
// SAFETY: See inline comments.
354+
unsafe impl<$(const $constvar : $constty,)? $($tyvar $(: ?$optbound)?)?> KnownLayout for $ty {
355+
const FIXED_PREFIX_SIZE: usize = mem::size_of::<$ty>();
356+
const ALIGN: NonZeroUsize = if let Some(align) = NonZeroUsize::new(mem::align_of::<$ty>()) {
357+
align
358+
} else {
359+
unreachable!()
360+
};
361+
// `T` is sized so it has no trailing slice.
362+
const TRAILING_SLICE_ELEM_SIZE: Option<usize> = None;
363+
364+
// SAFETY: `.cast` preserves address and provenance.
365+
#[inline(always)]
366+
fn raw_from_ptr_len(bytes: NonNull<u8>, _elems: usize) -> NonNull<Self> {
367+
bytes.cast::<Self>()
368+
}
369+
}
370+
};
371+
}
372+
373+
/// Implements `KnownLayout` for a type in terms of the implementation of
374+
/// another type with the same representation.
375+
///
376+
/// # Safety
377+
///
378+
/// - `$ty` and `$repr` must have the same:
379+
/// - Fixed prefix size
380+
/// - Alignment
381+
/// - (For DSTs) trailing slice element size
382+
/// - It must be valid to perform an `as` cast from `*mut $repr` to `*mut $ty`,
383+
/// and this operation must preserve referent size (ie, `size_of_val_raw`).
384+
macro_rules! unsafe_impl_known_layout {
385+
($($tyvar:ident: ?Sized + KnownLayout =>)? #[repr($repr:ty)] $ty:ty) => {
386+
impl<$($tyvar: ?Sized + KnownLayout)?> sealed::KnownLayoutSealed for $ty {}
387+
unsafe impl<$($tyvar: ?Sized + KnownLayout)?> KnownLayout for $ty {
388+
// SAFETY: Caller has promised that these values are the same for
389+
// `$ty` and `$repr`.
390+
const FIXED_PREFIX_SIZE: usize = <$repr as KnownLayout>::FIXED_PREFIX_SIZE;
391+
const ALIGN: NonZeroUsize = <$repr as KnownLayout>::ALIGN;
392+
const TRAILING_SLICE_ELEM_SIZE: Option<usize> = <$repr as KnownLayout>::TRAILING_SLICE_ELEM_SIZE;
393+
394+
// SAFETY: All operations preserve address and provenance. Caller
395+
// has promised that the `as` cast preserves size.
396+
#[inline(always)]
397+
fn raw_from_ptr_len(bytes: NonNull<u8>, elems: usize) -> NonNull<Self> {
398+
#[allow(clippy::as_conversions)]
399+
let ptr = <$repr>::raw_from_ptr_len(bytes, elems).as_ptr() as *mut Self;
400+
// SAFETY: `ptr` was converted from `bytes`, which is non-null.
401+
unsafe { NonNull::new_unchecked(ptr) }
402+
}
403+
}
404+
};
405+
}
406+
407+
#[rustfmt::skip]
408+
impl_known_layout!(
409+
(),
410+
u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64,
411+
bool, char,
412+
NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32,
413+
NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize
414+
);
415+
impl_known_layout!(T => Option<T>);
416+
impl_known_layout!(T: ?Sized => PhantomData<T>);
417+
impl_known_layout!(T => Wrapping<T>);
418+
impl_known_layout!(T => MaybeUninit<T>);
419+
impl_known_layout!(const N: usize, T => [T; N]);
420+
421+
safety_comment! {
422+
/// SAFETY:
423+
/// `str` and `ManuallyDrop<[T]>` have the same representations as `[u8]`
424+
/// and `[T]` repsectively. `str` has different bit validity than `[u8]`,
425+
/// but that doesn't affect the soundness of this impl.
426+
unsafe_impl_known_layout!(#[repr([u8])] str);
427+
unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ManuallyDrop<T>);
428+
}
429+
202430
/// Types for which a sequence of bytes all set to zero represents a valid
203431
/// instance of the type.
204432
///
@@ -1153,6 +1381,7 @@ mod simd {
11531381
use core::arch::$arch::{$($typ),*};
11541382

11551383
use crate::*;
1384+
impl_known_layout!($($typ),*);
11561385
safety_comment! {
11571386
/// SAFETY:
11581387
/// See comment on module definition for justification.
@@ -2345,7 +2574,8 @@ where
23452574
}
23462575
}
23472576

2348-
trait AsAddress {
2577+
#[doc(hidden)]
2578+
pub trait AsAddress {
23492579
fn addr(self) -> usize;
23502580
}
23512581

@@ -2659,7 +2889,8 @@ where
26592889
}
26602890

26612891
mod sealed {
2662-
pub trait Sealed {}
2892+
pub trait ByteSliceSealed {}
2893+
pub trait KnownLayoutSealed {}
26632894
}
26642895

26652896
// ByteSlice and ByteSliceMut abstract over [u8] references (&[u8], &mut [u8],
@@ -2685,7 +2916,9 @@ mod sealed {
26852916
///
26862917
/// [`Vec<u8>`]: alloc::vec::Vec
26872918
/// [`split_at`]: crate::ByteSlice::split_at
2688-
pub unsafe trait ByteSlice: Deref<Target = [u8]> + Sized + self::sealed::Sealed {
2919+
pub unsafe trait ByteSlice:
2920+
Deref<Target = [u8]> + Sized + self::sealed::ByteSliceSealed
2921+
{
26892922
/// Gets a raw pointer to the first byte in the slice.
26902923
#[inline]
26912924
fn as_ptr(&self) -> *const u8 {
@@ -2716,7 +2949,7 @@ pub unsafe trait ByteSliceMut: ByteSlice + DerefMut {
27162949
}
27172950
}
27182951

2719-
impl<'a> sealed::Sealed for &'a [u8] {}
2952+
impl<'a> sealed::ByteSliceSealed for &'a [u8] {}
27202953
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
27212954
#[allow(clippy::undocumented_unsafe_blocks)]
27222955
unsafe impl<'a> ByteSlice for &'a [u8] {
@@ -2726,7 +2959,7 @@ unsafe impl<'a> ByteSlice for &'a [u8] {
27262959
}
27272960
}
27282961

2729-
impl<'a> sealed::Sealed for &'a mut [u8] {}
2962+
impl<'a> sealed::ByteSliceSealed for &'a mut [u8] {}
27302963
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
27312964
#[allow(clippy::undocumented_unsafe_blocks)]
27322965
unsafe impl<'a> ByteSlice for &'a mut [u8] {
@@ -2736,7 +2969,7 @@ unsafe impl<'a> ByteSlice for &'a mut [u8] {
27362969
}
27372970
}
27382971

2739-
impl<'a> sealed::Sealed for cell::Ref<'a, [u8]> {}
2972+
impl<'a> sealed::ByteSliceSealed for cell::Ref<'a, [u8]> {}
27402973
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
27412974
#[allow(clippy::undocumented_unsafe_blocks)]
27422975
unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
@@ -2746,7 +2979,7 @@ unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
27462979
}
27472980
}
27482981

2749-
impl<'a> sealed::Sealed for RefMut<'a, [u8]> {}
2982+
impl<'a> sealed::ByteSliceSealed for RefMut<'a, [u8]> {}
27502983
// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
27512984
#[allow(clippy::undocumented_unsafe_blocks)]
27522985
unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> {
@@ -2764,6 +2997,63 @@ unsafe impl<'a> ByteSliceMut for &'a mut [u8] {}
27642997
#[allow(clippy::undocumented_unsafe_blocks)]
27652998
unsafe impl<'a> ByteSliceMut for RefMut<'a, [u8]> {}
27662999

3000+
// A polyfill for `<*const _>::cast_mut` that we can use before our MSRV is
3001+
// 1.65, when that method was stabilized.
3002+
3003+
// TODO(#67): Once our MSRV is 1.65, remove this.
3004+
trait RawPtrExt {
3005+
type Mut;
3006+
fn cast_mut(self) -> Self::Mut;
3007+
}
3008+
3009+
impl<T: ?Sized> RawPtrExt for *const T {
3010+
type Mut = *mut T;
3011+
#[allow(clippy::as_conversions)]
3012+
#[inline(always)]
3013+
fn cast_mut(self) -> *mut T {
3014+
self as *mut T
3015+
}
3016+
}
3017+
3018+
// A polyfill for `<*mut _>::cast_const` that we can use before our MSRV is
3019+
// 1.65, when that method was stabilized.
3020+
//
3021+
// TODO(#67): Once our MSRV is 1.65, remove this.
3022+
trait RawMutPtrExt {
3023+
type Const;
3024+
fn cast_const(self) -> Self::Const;
3025+
}
3026+
3027+
impl<T: ?Sized> RawMutPtrExt for *mut T {
3028+
type Const = *const T;
3029+
#[allow(clippy::as_conversions)]
3030+
#[inline(always)]
3031+
fn cast_const(self) -> *const T {
3032+
self as *const T
3033+
}
3034+
}
3035+
3036+
// A polyfill for `NonNull::slice_from_raw_parts` that we can use before our
3037+
// MSRV is 1.70, when that function was stabilized.
3038+
//
3039+
// TODO(#67): Once our MSRV is 1.70, remove this.
3040+
trait NonNullExt {
3041+
type SliceOfSelf;
3042+
3043+
fn slice_from_raw_parts(data: Self, len: usize) -> Self::SliceOfSelf;
3044+
}
3045+
3046+
impl<T> NonNullExt for NonNull<T> {
3047+
type SliceOfSelf = NonNull<[T]>;
3048+
3049+
#[inline(always)]
3050+
fn slice_from_raw_parts(data: Self, len: usize) -> NonNull<[T]> {
3051+
let ptr = ptr::slice_from_raw_parts_mut(data.as_ptr(), len);
3052+
// SAFETY: `ptr` is converted from `data`, which is non-null.
3053+
unsafe { NonNull::new_unchecked(ptr) }
3054+
}
3055+
}
3056+
27673057
#[cfg(feature = "alloc")]
27683058
mod alloc_support {
27693059
use alloc::vec::Vec;

0 commit comments

Comments
 (0)