Skip to content

Commit

Permalink
Add {wrap,peel}_box functions to TransparentWrapperAlloc (#119)
Browse files Browse the repository at this point in the history
* Add `{wrap,peel}_box` functions to `TransparentWrapperAlloc`

* Add tests for `{wrap,peel}_box`
  • Loading branch information
WaffleLapkin authored Jul 24, 2022
1 parent a053f1d commit 0fe2b2c
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 8 deletions.
48 changes: 48 additions & 0 deletions src/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,30 @@ pub trait TransparentWrapperAlloc<Inner: ?Sized>: TransparentWrapper<Inner> {
}
}

/// Convert a box to the inner type into a box to the wrapper
/// type.
#[inline]
fn wrap_box(s: Box<Inner>) -> Box<Self> {
assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());

unsafe {
// A pointer cast doesn't work here because rustc can't tell that
// the vtables match (because of the `?Sized` restriction relaxation).
// A `transmute` doesn't work because the sizes are unspecified.
//
// SAFETY:
// * The unsafe contract requires that pointers to Inner and Self
// have identical representations
// * Box is guaranteed to have representation identical to a
// (non-null) pointer
// * The pointer comes from a box (and thus satisfies all safety
// requirements of Box)
let inner_ptr: *mut Inner = Box::into_raw(s);
let wrapper_ptr: *mut Self = transmute!(inner_ptr);
Box::from_raw(wrapper_ptr)
}
}

/// Convert a vec of the wrapper type into a vec of the inner type.
fn peel_vec(s: Vec<Self>) -> Vec<Inner>
where
Expand All @@ -363,5 +387,29 @@ pub trait TransparentWrapperAlloc<Inner: ?Sized>: TransparentWrapper<Inner> {
)
}
}

/// Convert a box to the wrapper type into a box to the inner
/// type.
#[inline]
fn peel_box(s: Box<Self>) -> Box<Inner> {
assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());

unsafe {
// A pointer cast doesn't work here because rustc can't tell that
// the vtables match (because of the `?Sized` restriction relaxation).
// A `transmute` doesn't work because the sizes are unspecified.
//
// SAFETY:
// * The unsafe contract requires that pointers to Inner and Self
// have identical representations
// * Box is guaranteed to have representation identical to a
// (non-null) pointer
// * The pointer comes from a box (and thus satisfies all safety
// requirements of Box)
let wrapper_ptr: *mut Self = Box::into_raw(s);
let inner_ptr: *mut Inner = transmute!(wrapper_ptr);
Box::from_raw(inner_ptr)
}
}
}
impl<I: ?Sized, T: TransparentWrapper<I>> TransparentWrapperAlloc<I> for T {}
27 changes: 19 additions & 8 deletions tests/transparent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
#[test]
fn test_transparent_wrapper() {
// An external type defined in a different crate.
#[derive(Copy, Clone, Default)]
#[derive(Debug, Copy, Clone, Default)]
struct Foreign(u8);

use bytemuck::TransparentWrapper;

#[derive(Copy, Clone)]
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
struct Wrapper(Foreign);

Expand All @@ -20,6 +20,14 @@ fn test_transparent_wrapper() {
unsafe impl bytemuck::Zeroable for Wrapper {}
unsafe impl bytemuck::Pod for Wrapper {}

impl PartialEq<u8> for Foreign {
fn eq(&self, &other: &u8) -> bool { self.0 == other }
}

impl PartialEq<u8> for Wrapper {
fn eq(&self, &other: &u8) -> bool { self.0 == other }
}

let _: u8 = bytemuck::cast(Wrapper::wrap(Foreign::default()));
let _: Foreign = Wrapper::peel(bytemuck::cast(u8::default()));

Expand Down Expand Up @@ -69,13 +77,16 @@ fn test_transparent_wrapper() {
let a: Vec<Foreign> = vec![Foreign::default(); 2];

let b: Vec<Wrapper> = Wrapper::wrap_vec(a);
assert_eq!(
bytemuck::cast_slice::<Wrapper, u8>(b.as_slice()), &[0u8, 0]
);
assert_eq!(b, [0, 0]);

let c: Vec<Foreign> = Wrapper::peel_vec(b);
assert_eq!(
bytemuck::cast_slice::<Wrapper, u8>(Wrapper::wrap_slice(c.as_slice())), &[0, 0]
);
assert_eq!(c, [0, 0]);

let d: Box<Foreign> = Box::new(Foreign::default());

let e: Box<Wrapper> = Wrapper::wrap_box(d);
assert_eq!(&*e, &0);
let f: Box<Foreign> = Wrapper::peel_box(e);
assert_eq!(&*f, &0);
}
}

0 comments on commit 0fe2b2c

Please sign in to comment.