oct 0.29.2

Octonary transcodings.
Documentation
// Copyright 2024-2026 Gabriel Bjørnager Jensen.
//
// SPDX: MIT OR Apache-2.0

//! The [`transmute`] and [`transmute_unchecked`]
//! functions.

mod test;

use crate::{FromOcts, Immutable, Init, IntoOcts};

use core::mem::ManuallyDrop;
use core::ptr;

/// Transmutes an object to another type.
///
/// The raw octet representation of the object is
/// reused. Note, however, that this isn't necessar-
/// ily equivalent to the new value being equal to
/// the old one (as per [`PartialEq::eq`]).
///
/// This function requires that the source type be
/// [`Init`] as this operation could otherwise
/// rerepresent uninitialised data as being ini-
/// tialised.
///
/// # Panics
///
/// This function will panic at translation time if
/// `T` and `U` aren't of the same size.
#[inline]
#[must_use]
#[track_caller]
pub const fn transmute<T, U>(value: T) -> U
where
	T: IntoOcts + Init,
	U: FromOcts,
{
	const {
		assert!(
			size_of::<U>() == size_of::<T>(),
			"cannot transmute types of different sizes",
		);
	}

	// SAFETY: We have asserted that both types are of
	// equal size. `U` bounds guarantee lack of type
	// invariants.
	unsafe { transmute_unchecked(value) }
}

/// Transmutes a constant reference to another
/// reference type.
///
/// This function requires that the source type be
/// [`Init`] as this operation could otherwise
/// rerepresent uninitialised data as being ini-
/// tialised. It additionally requires [`Immutable`]
/// for both types so as to prevent writes.
///
/// # Panics
///
/// This function will panic at translation time if
/// `T` and `U` aren't of the same size, or if the
/// latter has a greater alignement requirement than
/// the former.
#[inline]
#[must_use]
#[track_caller]
pub const fn transmute_ref<T, U>(r: &T) -> &U
where
	T: IntoOcts + Init + Immutable,
	U: FromOcts + Immutable,
{
	const {
		assert!(
			size_of::<U>() == size_of::<T>(),
			"cannot transmute types of different sizes",
		);

		assert!(
			align_of::<U>() <= align_of::<T>(),
			"cannot transmute reference to increasing alignement",
		);
	}

	let p = ptr::from_ref(r).cast::<U>();

	// SAFETY: We have asserted that both types are of
	// equal size and that alignement isn't a concern.
	// `U` bounds guarantee lack of type invariants.
	unsafe { &*p }
}

/// Transmutes a mutable reference to another
/// reference type.
///
/// This function requires that both types be
/// [`Init`] as this operation could otherwise
/// represent uninitialised data as initialised,
/// through either a read from or a write to the
/// returned reference.
///
/// # Panics
///
/// This function will panic at translation time if
/// `T` and `U` aren't of the same size, or if the
/// latter has a greater alignement requirement than
/// the former.
#[inline]
#[must_use]
#[track_caller]
pub const fn transmute_mut<T, U>(r: &mut T) -> &mut U
where
	T: IntoOcts + Init,
	U: FromOcts + Init,
{
	const {
		assert!(
			size_of::<U>() == size_of::<T>(),
			"cannot transmute types of different sizes",
		);

		assert!(
			align_of::<U>() <= align_of::<T>(),
			"cannot transmute reference to increasing alignement",
		);
	}

	let p = ptr::from_mut(r).cast::<U>();

	// SAFETY: We have asserted that both types are of
	// equal size and that alignement isn't a concern.
	// `U` bounds guarantee lack of type invariants.
	unsafe { &mut *p }
}

/// Unsafely transmutes an object to another type.
///
/// The raw octet representation of the object is
/// reused. Note, however, that this isn't necessar-
/// ily equivalent to the new value being equal to
/// the old one (as per [`PartialEq::eq`]).
///
/// This function should be used rarely, with
/// [`transmute`] guaranteeing safe transmutations
/// instead. This function may be used instead of
/// [`core::mem::transmute`] when transmuting
/// dependently-sized types (e.g. [arrays](array)).
///
/// # Safety
///
/// The sizes of `T` and `U` must be exactly equal.
/// Furthermore, callers guarantee that the exact
/// representation used by the provided value is
/// valid for objects of the destination type.
#[inline(always)]
#[must_use]
#[track_caller]
pub const unsafe fn transmute_unchecked<T, U>(value: T) -> U {
	debug_assert!(
		size_of::<U>() == size_of::<T>(),
		"cannot transmute types of different sizes",
	);

	#[repr(C)]
	union Transmute<Src, Dst> {
		src: ManuallyDrop<Src>,
		dst: ManuallyDrop<Dst>,
	}

	let transmute = Transmute { src: ManuallyDrop::new(value) };

	// SAFETY: Caller guarantees correct representa-
	// tion.
	unsafe { ManuallyDrop::into_inner(transmute.dst) }
}