Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions fyrox-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -413,13 +413,34 @@ pub fn array_as_u8_slice_mut<T: Sized + Pod>(v: &mut [T]) -> &'_ mut [u8] {
}

/// "Transmutes" array of any sized type to a slice of some other type.
pub fn transmute_slice<T: Sized, U: Sized>(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::<U>()`
/// - The pointer to `v` is not properly aligned for type `U`
pub fn transmute_slice<T: Sized + Pod, U: Sized + Pod>(v: &[T]) -> &'_ [U] {
let byte_len = std::mem::size_of_val(v);
let target_size = std::mem::size_of::<U>();
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::<U>(),
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::<U>(),
)
std::slice::from_raw_parts(ptr, byte_len / target_size)
}
}

Expand Down
44 changes: 32 additions & 12 deletions fyrox-texture/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1292,24 +1292,44 @@ pub enum CompressionOptions {
uuid_provider!(CompressionOptions = "fbdcc081-d0b8-4b62-9925-2de6c013fbf5");

fn transmute_slice<T>(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::<T>();
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::<T>(),
);
// 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::<T>(),
)
std::slice::from_raw_parts(ptr, bytes.len() / target_size)
}
}

fn transmute_slice_mut<T>(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::<T>();
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::<T>(),
);
// 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::<T>(),
)
std::slice::from_raw_parts_mut(ptr, bytes.len() / target_size)
}
}

Expand Down