diff --git a/fyrox-core/src/lib.rs b/fyrox-core/src/lib.rs index 5a8b0c080..5e07bbed9 100644 --- a/fyrox-core/src/lib.rs +++ b/fyrox-core/src/lib.rs @@ -413,13 +413,34 @@ pub fn array_as_u8_slice_mut(v: &mut [T]) -> &'_ mut [u8] { } /// "Transmutes" array of any sized type to a slice of some other type. -pub fn transmute_slice(v: &[T]) -> &'_ [U] { - // SAFETY: It is safe to reinterpret data to read it. +/// +/// Both `T` and `U` must be [`Pod`] types (no padding, no invalid bit patterns). +/// +/// # Panics +/// +/// Panics if: +/// - `U` is a zero-sized type +/// - The byte length of `v` is not a multiple of `size_of::()` +/// - The pointer to `v` is not properly aligned for type `U` +pub fn transmute_slice(v: &[T]) -> &'_ [U] { + let byte_len = std::mem::size_of_val(v); + let target_size = std::mem::size_of::(); + assert!(target_size != 0, "transmute_slice: target type must not be zero-sized"); + assert!( + byte_len % target_size == 0, + "transmute_slice: source byte length ({byte_len}) is not a multiple of target type size ({target_size})", + ); + let ptr = v.as_ptr() as *const U; + assert!( + ptr.is_aligned(), + "transmute_slice: source pointer is not aligned for target type (required {} byte alignment, got pointer {:?})", + std::mem::align_of::(), + ptr, + ); + // SAFETY: T and U are Pod (no uninit bytes, no invalid bit patterns). Alignment and + // divisibility are checked above. unsafe { - std::slice::from_raw_parts( - v.as_ptr() as *const U, - std::mem::size_of_val(v) / std::mem::size_of::(), - ) + std::slice::from_raw_parts(ptr, byte_len / target_size) } } diff --git a/fyrox-texture/src/lib.rs b/fyrox-texture/src/lib.rs index 3df73fe20..0571fa04f 100644 --- a/fyrox-texture/src/lib.rs +++ b/fyrox-texture/src/lib.rs @@ -1292,24 +1292,44 @@ pub enum CompressionOptions { uuid_provider!(CompressionOptions = "fbdcc081-d0b8-4b62-9925-2de6c013fbf5"); fn transmute_slice(bytes: &[u8]) -> &'_ [T] { - // SAFETY: This is absolutely safe because `image` crate's Rgb8/Rgba8/etc. and `tbc`s Rgb8/Rgba8/etc. - // have exactly the same memory layout. + let target_size = std::mem::size_of::(); + assert!(target_size != 0, "transmute_slice: target type must not be zero-sized"); + assert!( + bytes.len() % target_size == 0, + "transmute_slice: byte length ({}) is not a multiple of target type size ({target_size})", + bytes.len(), + ); + let ptr = bytes.as_ptr() as *const T; + assert!( + ptr.is_aligned(), + "transmute_slice: source pointer is not aligned for target type (required {} byte alignment)", + std::mem::align_of::(), + ); + // SAFETY: Alignment and divisibility are checked above. The image crate's Rgb8/Rgba8/etc. + // and tbc's Rgb8/Rgba8/etc. have exactly the same memory layout. unsafe { - std::slice::from_raw_parts( - bytes.as_ptr() as *const T, - bytes.len() / std::mem::size_of::(), - ) + std::slice::from_raw_parts(ptr, bytes.len() / target_size) } } fn transmute_slice_mut(bytes: &mut [u8]) -> &'_ mut [T] { - // SAFETY: This is absolutely safe because `image` crate's Rgb8/Rgba8/etc. and `tbc`s Rgb8/Rgba8/etc. - // have exactly the same memory layout. + let target_size = std::mem::size_of::(); + assert!(target_size != 0, "transmute_slice_mut: target type must not be zero-sized"); + assert!( + bytes.len() % target_size == 0, + "transmute_slice_mut: byte length ({}) is not a multiple of target type size ({target_size})", + bytes.len(), + ); + let ptr = bytes.as_mut_ptr() as *mut T; + assert!( + ptr.is_aligned(), + "transmute_slice_mut: source pointer is not aligned for target type (required {} byte alignment)", + std::mem::align_of::(), + ); + // SAFETY: Alignment and divisibility are checked above. The image crate's Rgb8/Rgba8/etc. + // and tbc's Rgb8/Rgba8/etc. have exactly the same memory layout. unsafe { - std::slice::from_raw_parts_mut( - bytes.as_ptr() as *mut T, - bytes.len() / std::mem::size_of::(), - ) + std::slice::from_raw_parts_mut(ptr, bytes.len() / target_size) } }