oct 0.29.2

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

//! The [`IntoOcts`] trait.

mod test;

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

use core::mem::MaybeUninit;
use core::slice;

/// Denotes a type representable as octets (bytes).
///
/// This trait is implemented for any, possible type
/// -- which is safe, given Rust's octonary memory
/// model. The real crème de la crème, however,
/// comes when the implementing type additionally
/// implements [`Init`], as this allows for
/// conversions into readable octets (see
/// [`as_octs`] and [`as_octs_mut`]).
///
/// [`as_octs`]: Self::as_octs
/// [`as_octs_mut`]: Self::as_octs_mut
///
/// # Safety
///
/// This trait is safe to implemement as-is for any
/// type (as all types are, by definition,
/// represented by octets). Provided defaults,
/// however, may not be overriden.
pub unsafe trait IntoOcts {
	/// Reinterprets the object as a slice of octets.
	#[inline(always)]
	#[must_use]
	fn as_octs(&self) -> &[u8]
	where
		Self: Init + Immutable,
	{
		let len  = size_of_val(self);
		let data = (&raw const *self).cast::<u8>();

		// SAFETY: `Init` guarantees initialised data.
		// `size_of_val` guarantees the exact size of the
		// referenced object. Additionally, `Immutable`
		// guarantees interiour-immutability.
		unsafe { slice::from_raw_parts(data, len) }
	}

	/// Reinterprets the object as a mutable slice of
	/// octets.
	///
	/// This method requires [`FromOcts`] as we cannot
	/// alone guarantee whether any write to the
	/// returned slice is valid.
	#[inline(always)]
	#[must_use]
	fn as_octs_mut(&mut self) -> &mut [u8]
	where
		Self: Init + FromOcts,
	{
		let len  = size_of_val(self);
		let data = (&raw mut *self).cast::<u8>();

		// SAFETY: `Init` guarantees initialised data.
		// `size_of_val` guarantees the exact size of the
		// referenced object. Lastly, `FromOcts` guarantees
		// that any write to the returned slice is valid.
		unsafe { slice::from_raw_parts_mut(data, len) }
	}

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

		// SAFETY: `size_of_val` guarantees the exact size
		// of the referenced object. Additionally,
		// `Immutable` guarantees that no write can be made
		// to the returned slice.
		unsafe { slice::from_raw_parts(data, len) }
	}

	/// Reverses the order of the octets.
	///
	/// This operation preserves the initialisation
	/// state of each octet.
	///
	/// Note that this method requires [`Init`] as this
	/// operation could otherwise ilegally uninitialise
	/// subfields.
	#[inline]
	fn swap_octs(&mut self)
	where
		Self: Init + FromOcts,
	{
		let octs = self.as_octs_mut();

		// SAFETY: Implementers guarantee that any permuta-
		// tion of octets is a valid representation.
		octs.reverse();
	}
}

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