Skip to content

Commit 235aa51

Browse files
committed
[WIP] ptr-metadata
Issue #29
1 parent c29ebb9 commit 235aa51

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pinned-nightly = "nightly-2022-10-17"
2626

2727
[features]
2828
alloc = []
29+
ptr-metadata = []
2930
simd = []
3031
simd-nightly = ["simd"]
3132
# This feature depends on all other features that work on the stable compiler.

src/lib.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@
120120
))]
121121
#![cfg_attr(not(test), no_std)]
122122
#![cfg_attr(feature = "simd-nightly", feature(stdsimd))]
123+
#![cfg_attr(feature = "ptr-metadata", feature(ptr_metadata, layout_for_ptr, const_size_of_val_raw))]
123124

124125
pub mod byteorder;
125126
#[doc(hidden)]
@@ -151,6 +152,9 @@ use {
151152
core::{alloc::Layout, ptr::NonNull},
152153
};
153154

155+
#[cfg(feature = "ptr-metadata")]
156+
use core::ptr::Pointee;
157+
154158
// This is a hack to allow derives of `FromBytes`, `AsBytes`, and `Unaligned` to
155159
// work in this crate. They assume that zerocopy is linked as an extern crate,
156160
// so they access items from it as `zerocopy::Xxx`. This makes that still work.
@@ -2453,6 +2457,60 @@ mod alloc_support {
24532457
#[doc(inline)]
24542458
pub use alloc_support::*;
24552459

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+
24562514
#[cfg(test)]
24572515
mod tests {
24582516
#![allow(clippy::unreadable_literal)]
@@ -3269,4 +3327,33 @@ mod tests {
32693327
assert_impls_frombytes::<Foo<u32>>();
32703328
assert_impls_unaligned::<Foo<u8>>();
32713329
}
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+
}
32723359
}

0 commit comments

Comments
 (0)