#[allow(unused)]
#[cfg(test)]
use similar_asserts::assert_eq;
use std::ptr::NonNull;
pub(crate) unsafe trait TransparentNewtype: Sized {
type Inner;
#[inline]
fn check_basic_layout() {
use std::mem::{align_of, size_of};
#[cold]
fn failed() -> ! {
panic!("Invalid TransparentNewtype impl detected!");
}
if size_of::<Self>() != size_of::<Self::Inner>() {
failed()
}
if align_of::<Self>() != align_of::<Self::Inner>() {
failed()
}
}
}
#[allow(clippy::wrong_self_convention)]
pub(crate) unsafe trait AsNewtype<Newtype: TransparentNewtype> {
type Wrapped;
unsafe fn as_newtype(self) -> Self::Wrapped;
}
unsafe impl<'a, T, NewT: TransparentNewtype<Inner = T> + 'a> AsNewtype<NewT> for &'a T {
type Wrapped = &'a NewT;
unsafe fn as_newtype(self) -> Self::Wrapped {
NewT::check_basic_layout();
let ptr: *const T = self;
unsafe { &*ptr.cast::<NewT>() }
}
}
unsafe impl<'a, T, NewT: TransparentNewtype<Inner = T> + 'a> AsNewtype<NewT> for &'a mut T {
type Wrapped = &'a mut NewT;
unsafe fn as_newtype(self) -> Self::Wrapped {
NewT::check_basic_layout();
let ptr: *mut T = self;
unsafe { &mut *ptr.cast::<NewT>() }
}
}
unsafe impl<T, NewT: TransparentNewtype<Inner = T>> AsNewtype<NewT> for NonNull<T> {
type Wrapped = NonNull<NewT>;
unsafe fn as_newtype(self) -> Self::Wrapped {
NewT::check_basic_layout();
self.cast()
}
}
unsafe impl<T, NewT: TransparentNewtype<Inner = T>> AsNewtype<NewT> for *const T {
type Wrapped = *const NewT;
unsafe fn as_newtype(self) -> Self::Wrapped {
NewT::check_basic_layout();
self.cast()
}
}
unsafe impl<T, NewT: TransparentNewtype<Inner = T>> AsNewtype<NewT> for *mut T {
type Wrapped = *mut NewT;
unsafe fn as_newtype(self) -> Self::Wrapped {
NewT::check_basic_layout();
self.cast()
}
}
#[allow(clippy::wrong_self_convention)]
#[allow(unused)]
pub(crate) unsafe trait AsInner {
type Unwrapped;
fn as_inner(self) -> Self::Unwrapped;
}
unsafe impl<'a, NewT: TransparentNewtype + 'a> AsInner for &'a NewT {
type Unwrapped = &'a NewT::Inner;
fn as_inner(self) -> Self::Unwrapped {
NewT::check_basic_layout();
let ptr: *const NewT = self;
unsafe { &*ptr.cast::<NewT::Inner>() }
}
}
unsafe impl<'a, NewT: TransparentNewtype + 'a> AsInner for &'a mut NewT {
type Unwrapped = &'a mut NewT::Inner;
fn as_inner(self) -> Self::Unwrapped {
NewT::check_basic_layout();
let ptr: *mut NewT = self;
unsafe { &mut *ptr.cast::<NewT::Inner>() }
}
}
unsafe impl<NewT: TransparentNewtype> AsInner for NonNull<NewT> {
type Unwrapped = NonNull<NewT::Inner>;
fn as_inner(self) -> Self::Unwrapped {
NewT::check_basic_layout();
self.cast()
}
}
unsafe impl<NewT: TransparentNewtype> AsInner for *const NewT {
type Unwrapped = *const NewT::Inner;
fn as_inner(self) -> Self::Unwrapped {
NewT::check_basic_layout();
self.cast()
}
}
unsafe impl<NewT: TransparentNewtype> AsInner for *mut NewT {
type Unwrapped = *mut NewT::Inner;
fn as_inner(self) -> Self::Unwrapped {
NewT::check_basic_layout();
self.cast()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::info::TextualInfo;
use hwlocality_sys::hwloc_info_s;
#[allow(unused)]
use similar_asserts::assert_eq;
use std::ptr;
#[test]
fn transparent_newtype() {
let mut info = hwloc_info_s {
name: ptr::null_mut(),
value: ptr::null_mut(),
};
let nonnull_info = NonNull::from(&mut info);
let mut_info = nonnull_info.as_ptr();
let const_info: *const hwloc_info_s = mut_info;
TextualInfo::check_basic_layout();
{
let r = unsafe { <&hwloc_info_s as AsNewtype<TextualInfo>>::as_newtype(&info) };
let p: *const TextualInfo = r;
assert_eq!(p.cast::<hwloc_info_s>(), const_info);
assert!(ptr::eq(<&TextualInfo as AsInner>::as_inner(r), const_info));
}
{
let r = unsafe { <&mut hwloc_info_s as AsNewtype<TextualInfo>>::as_newtype(&mut info) };
let p: *mut TextualInfo = r;
assert_eq!(p.cast::<hwloc_info_s>(), mut_info);
assert!(ptr::eq(
<&mut TextualInfo as AsInner>::as_inner(r),
mut_info
));
}
{
let p: NonNull<TextualInfo> = unsafe {
<NonNull<hwloc_info_s> as AsNewtype<TextualInfo>>::as_newtype(nonnull_info)
};
assert_eq!(p.cast::<hwloc_info_s>(), nonnull_info);
assert_eq!(<NonNull<TextualInfo> as AsInner>::as_inner(p), nonnull_info);
}
{
let p: *const TextualInfo =
unsafe { <*const hwloc_info_s as AsNewtype<TextualInfo>>::as_newtype(const_info) };
assert_eq!(p.cast::<hwloc_info_s>(), const_info);
assert_eq!(<*const TextualInfo as AsInner>::as_inner(p), const_info);
}
{
let p: *mut TextualInfo =
unsafe { <*mut hwloc_info_s as AsNewtype<TextualInfo>>::as_newtype(mut_info) };
assert_eq!(p.cast::<hwloc_info_s>(), mut_info);
assert_eq!(<*mut TextualInfo as AsInner>::as_inner(p), mut_info);
}
}
unsafe impl TransparentNewtype for () {
type Inner = u8;
}
unsafe impl TransparentNewtype for u16 {
type Inner = [u8; 2];
}
#[test]
#[should_panic]
fn bad_transparent_newtype_size() {
<()>::check_basic_layout();
}
#[test]
#[should_panic]
fn bad_transparent_newtype_alignment() {
u16::check_basic_layout();
}
}