oct 0.36.2

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

//! The [`IntoOctets`] trait.

mod test;

use core::mem::MaybeUninit;
use core::slice;
use oct::{
	FromOctets,
	Immutable,
	Init,
	transmute_mut,
	transmute_ref,
};

/// Denotes a type representable as octets (bytes).
///
/// A type that implements this trait is a type that
/// has an underlying octonary/bytewise
/// representation.
///
/// As this represention is required by Rust's
/// memory model, this trait is actually implemented
/// for any given type. However, due to some types
/// having unitialised fields (e.g. padding bytes),
/// types must additionally implement [`Init`] for
/// conversions directly into [`u8`] objects to be
/// sound.
///
/// # Safety
///
/// Implementations may not override provided items.
#[doc(alias = "IntoBytes")]
pub unsafe trait IntoOctets {
	/// Reinterprets the object as a slice of octets.
	///
	/// The returned slice covers the entire
	/// representation of the referenced object.
	///
	/// See also [`as_octets_mut`].
	///
	/// [`as_octets_mut`]: Self::as_octets_mut
	#[doc(alias = "as_bytes")]
	#[inline]
	#[must_use]
	fn as_octets(&self) -> &[u8]
	where
		Self: Init + Immutable,
	{
		transmute_ref::<Self, [u8]>(self)
	}

	/// Reinterprets the object as a mutable slice of
	/// octets.
	///
	/// The returned slice covers the entire
	/// representation of the referenced object.
	///
	/// See also [`as_octets`].
	///
	/// [`as_octets`]: Self::as_octets
	#[doc(alias = "as_bytes_mut")]
	#[doc(alias = "as_mut_bytes")]
	#[doc(alias = "as_mut_octets")]
	#[inline]
	#[must_use]
	fn as_octets_mut(&mut self) -> &mut [u8]
	where
		Self: Init + FromOctets,
	{
		transmute_mut::<Self, [u8]>(self)
	}

	/// Reinterprets the object as a slice of
	/// uninitialised octets.
	///
	/// This operation is valid for any virtually any
	/// type. Do note, however, that the returned octets
	/// can rarely be used for anything other than pure
	/// copy operations.
	#[inline]
	#[must_use]
	fn as_uninit_octets(&self) -> &[MaybeUninit<u8>]
	where
		Self: Immutable,
	{
		// The length of the object, in count of octets.
		let len = size_of_val(self);

		// The raw data representing the object.
		let data = (&raw const *self).cast::<MaybeUninit<u8>>();

		// NOTE: Should be a no-op.
		debug_assert!(data.is_aligned());

		// SAFETY:
		// * `data` derives from a reference and can there-
		//   fore neither be null nor reference multiple
		//   allocations. As the destination type is `u8`,
		//   we can guarantee that the address is aligned
		//   as all pointers must be byte-aligned (i.e.
		//   `u8`-aligned). The slice also inherits the
		//   original reference's (guaranteed) read
		//   provenance.
		//
		// * `size_of_val` returns the exact size of the
		//   destination object. `MaybeUninit<u8>` itself
		//   has no invariants, so representation is unimpor-
		//   tant.
		//
		// * The destination object(s) cannot be modified,
		//   thanks to the `Immutable` bound.
		//
		// * The total size cannot overflow `isize` (see
		//   note #1.)
		//
		// For the record, the returned slice cannot deini-
		// tialise the destination object
		//
		// Note #1: This assumption is deduced from,
		// `core::slice::from_ref`, as that would otherwise
		// be unsound.
		unsafe { slice::from_raw_parts(data, len) }
	}
}

unsafe impl<T: ?Sized> IntoOctets for T {}