|
120 | 120 | ))]
|
121 | 121 | #![cfg_attr(not(test), no_std)]
|
122 | 122 | #![cfg_attr(feature = "simd-nightly", feature(stdsimd))]
|
| 123 | +#![cfg_attr(feature = "ptr-metadata", feature(ptr_metadata, layout_for_ptr, const_size_of_val_raw))] |
123 | 124 |
|
124 | 125 | pub mod byteorder;
|
125 | 126 | #[doc(hidden)]
|
@@ -151,6 +152,9 @@ use {
|
151 | 152 | core::{alloc::Layout, ptr::NonNull},
|
152 | 153 | };
|
153 | 154 |
|
| 155 | +#[cfg(feature = "ptr-metadata")] |
| 156 | +use core::ptr::Pointee; |
| 157 | + |
154 | 158 | // This is a hack to allow derives of `FromBytes`, `AsBytes`, and `Unaligned` to
|
155 | 159 | // work in this crate. They assume that zerocopy is linked as an extern crate,
|
156 | 160 | // so they access items from it as `zerocopy::Xxx`. This makes that still work.
|
@@ -2453,6 +2457,60 @@ mod alloc_support {
|
2453 | 2457 | #[doc(inline)]
|
2454 | 2458 | pub use alloc_support::*;
|
2455 | 2459 |
|
| 2460 | +/// The type of [`Pointee::Metadata`] for types which can be constructed by this |
| 2461 | +/// crate. |
| 2462 | +/// |
| 2463 | +/// This is implemented for `()` (the metadata used by [`Sized`] types) and |
| 2464 | +/// `usize` (the metadata used by unsized types whose last field is a slice |
| 2465 | +/// type). |
| 2466 | +#[cfg(feature = "ptr-metadata")] |
| 2467 | +pub unsafe trait PtrMetadata { |
| 2468 | + /// The value passed to [`core::ptr::from_raw_parts_mut`] in order to |
| 2469 | + /// construct a pointer to the minimum-sized instance of `Self`. |
| 2470 | + /// |
| 2471 | + /// See [`prefix_size_of`]. |
| 2472 | + #[doc(hidden)] |
| 2473 | + const ZERO: Self; |
| 2474 | +} |
| 2475 | + |
| 2476 | +#[cfg(feature = "ptr-metadata")] |
| 2477 | +unsafe impl PtrMetadata for () { |
| 2478 | + const ZERO: () = (); |
| 2479 | +} |
| 2480 | + |
| 2481 | +#[cfg(feature = "ptr-metadata")] |
| 2482 | +unsafe impl PtrMetadata for usize { |
| 2483 | + const ZERO: usize = 0; |
| 2484 | +} |
| 2485 | + |
| 2486 | +/// Gets the size of `T` or of the fixed-sized prefix if `T` is a |
| 2487 | +/// dynamically-sized type. |
| 2488 | +#[cfg(feature = "ptr-metadata")] |
| 2489 | +const fn prefix_size_of<T: ?Sized>() -> usize |
| 2490 | +where |
| 2491 | + <T as Pointee>::Metadata: PtrMetadata, |
| 2492 | +{ |
| 2493 | + let smallest_ptr = |
| 2494 | + ptr::from_raw_parts_mut::<T>(ptr::null_mut(), <T as Pointee>::Metadata::ZERO); |
| 2495 | + // SAFETY: Since `T::Metadata: Metadata`, we know that `T` is either a sized |
| 2496 | + // type (with `T::Metadata = ()`) or an unsized type whose tail is a slice |
| 2497 | + // (with `T::Metadata = usize`). Per the `size_of_val_raw` docs, if `T` is |
| 2498 | + // sized: |
| 2499 | + // |
| 2500 | + // [T]his function is always safe to call. |
| 2501 | + // |
| 2502 | + // ...and if `T` is unsized and `T`'s unsized tail is a slice: |
| 2503 | + // |
| 2504 | + // [T]he length of the slice tail must be an initialized integer, and the |
| 2505 | + // size of the entire value (dynamic tail length + statically sized |
| 2506 | + // prefix) must fit in isize. |
| 2507 | + // |
| 2508 | + // We have initialized the tail length to 0. |
| 2509 | + // |
| 2510 | + // TODO: Can the static prefix have a size which overflows isize? |
| 2511 | + unsafe { mem::size_of_val_raw(smallest_ptr) } |
| 2512 | +} |
| 2513 | + |
2456 | 2514 | #[cfg(test)]
|
2457 | 2515 | mod tests {
|
2458 | 2516 | #![allow(clippy::unreadable_literal)]
|
@@ -3269,4 +3327,33 @@ mod tests {
|
3269 | 3327 | assert_impls_frombytes::<Foo<u32>>();
|
3270 | 3328 | assert_impls_unaligned::<Foo<u8>>();
|
3271 | 3329 | }
|
| 3330 | + |
| 3331 | + #[test] |
| 3332 | + #[cfg(feature = "ptr-metadata")] |
| 3333 | + fn test_prefix_size_of() { |
| 3334 | + macro_rules! test { |
| 3335 | + (($($fields:ty),*), $size:expr) => {{ |
| 3336 | + #[repr(C)] |
| 3337 | + struct Foo($($fields),*); |
| 3338 | + assert_eq!(prefix_size_of::<Foo>(), $size); |
| 3339 | + }}; |
| 3340 | + } |
| 3341 | + |
| 3342 | + assert_eq!(prefix_size_of::<()>(), 0); |
| 3343 | + test!((u8), 1); |
| 3344 | + test!((u8, u8), 2); |
| 3345 | + // Since `AU64`'s alignment requirement is 8, 7 bytes of padding are |
| 3346 | + // added after the `u8` field. |
| 3347 | + test!((u8, AU64), 16); |
| 3348 | + |
| 3349 | + assert_eq!(prefix_size_of::<[u8]>(), 0); |
| 3350 | + test!(([u8]), 0); |
| 3351 | + test!((u8, [u8]), 1); |
| 3352 | + test!((u32, [u8]), 4); |
| 3353 | + test!((u32, [u32]), 4); |
| 3354 | + // Since `[AU64]`'s alignment requirement is 8, 4 bytes of padding are |
| 3355 | + // added after the `u32` field. |
| 3356 | + test!((u32, [AU64]), 8); |
| 3357 | + test!((u32, u32, [AU64]), 8); |
| 3358 | + } |
3272 | 3359 | }
|
0 commit comments