Skip to content

Commit

Permalink
Replace 4 unsafe transmutes with a safe abstraction.
Browse files Browse the repository at this point in the history
  • Loading branch information
caibear committed Mar 13, 2024
1 parent d53b292 commit 26960e8
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 16 deletions.
14 changes: 14 additions & 0 deletions src/fast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,20 @@ impl<'borrowed, T> CowSlice<'borrowed, T> {
self.slice = slice.into();
ret
}

/// Casts `&mut CowSlice<T>` to `&mut CowSlice<B>`.
#[inline]
pub fn cast_mut<B>(&mut self) -> &mut CowSlice<'borrowed, B>
where
T: bytemuck::Pod,
B: bytemuck::Pod,
{
use std::mem::*;
assert_eq!(size_of::<T>(), size_of::<B>());
assert_eq!(align_of::<T>(), align_of::<B>());
// Safety: size/align are equal and both are bytemuck::Pod.
unsafe { transmute(self) }
}
}

pub struct SetOwned<'a, 'borrowed, T>(&'a mut CowSlice<'borrowed, T>);
Expand Down
9 changes: 5 additions & 4 deletions src/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ pub struct IntEncoder<T>(VecImpl<T>);
impl<T: Int, P: NoUninit> Encoder<P> for IntEncoder<T> {
#[inline(always)]
fn as_primitive(&mut self) -> Option<&mut VecImpl<P>> {
assert_eq!(std::mem::size_of::<T>(), std::mem::size_of::<P>());
// Safety: T and P are the same size, T is Pod, and we aren't reading P.
let vec: &mut VecImpl<P> = unsafe { std::mem::transmute(&mut self.0) };
Some(vec)
use std::mem::*;
assert_eq!(align_of::<T>(), align_of::<P>());
assert_eq!(size_of::<T>(), size_of::<P>());
// Safety: size/align are equal, T: Int implies Pod, and caller isn't reading P which may be NonZero.
unsafe { Some(transmute(&mut self.0)) }
}

#[inline(always)]
Expand Down
4 changes: 1 addition & 3 deletions src/pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,7 @@ pub fn unpack_bytes<'a, T: Byte>(
length: usize,
out: &mut CowSlice<'a, T>,
) -> Result<()> {
// Safety: T is u8 or i8 which have same size/align and are Copy.
let out: &mut CowSlice<'a, u8> = unsafe { std::mem::transmute(out) };
unpack_bytes_unsigned(input, length, out)
unpack_bytes_unsigned(input, length, out.cast_mut())
}

/// [`unpack_bytes`] but after i8s have been cast to u8s.
Expand Down
17 changes: 8 additions & 9 deletions src/pack_ints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ macro_rules! impl_usize_and_isize {
}
fn with_output<'a>(out: &mut CowSlice<'a, Self::Une>, length: usize, f: impl FnOnce(&mut CowSlice<'a, <Self::Int as Int>::Une>) -> Result<()>) -> Result<()> {
if cfg!(target_pointer_width = "64") {
// Safety: isize::Une == i64::Une on 64 bit.
f(unsafe { std::mem::transmute(out) })
f(out.cast_mut())
} else {
// i64 to 32 bit isize on requires checked conversion. TODO reuse allocations.
let mut out_i64 = CowSlice::default();
Expand Down Expand Up @@ -311,10 +310,12 @@ impl SizedUInt for u8 {
fn pack8(v: &mut [Self], out: &mut Vec<u8>) {
pack_bytes(v, out);
}
fn unpack8(input: &mut &[u8], length: usize, out: &mut CowSlice<[u8; 1]>) -> Result<()> {
// Safety: [u8; 1] and u8 are the same from the perspective of CowSlice.
let out: &mut CowSlice<u8> = unsafe { std::mem::transmute(out) };
unpack_bytes(input, length, out)
fn unpack8<'a>(
input: &mut &'a [u8],
length: usize,
out: &mut CowSlice<'a, [u8; 1]>,
) -> Result<()> {
unpack_bytes(input, length, out.cast_mut::<u8>())
}
}

Expand Down Expand Up @@ -438,9 +439,7 @@ fn unpack_ints_sized<'a, T: SizedInt>(
length: usize,
out: &mut CowSlice<'a, T::Une>,
) -> Result<()> {
// Safety: T::Une and T::Unsigned::Une are the same type.
let out: &mut CowSlice<'a, _> = unsafe { std::mem::transmute(out) };
unpack_ints_sized_unsigned::<T::Unsigned>(input, length, out)
unpack_ints_sized_unsigned::<T::Unsigned>(input, length, out.cast_mut())
}

/// [`unpack_ints_sized`] but after signed integers have been cast to unsigned.
Expand Down

0 comments on commit 26960e8

Please sign in to comment.