oct 0.34.0

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

//! The [`Outlay`] trait.

use crate::Immutable;

use core::fmt::Debug;
use core::hash::Hash;
use core::num::NonZero;
use core::ptr;

/// Denotes an outlayable type.
///
/// Note that this trait, as well as [`Slice`],
/// need to be implemented manually for
/// <code>![Sized]</code> types.
///
/// [`Slice`]: crate::Slice
///
/// # Safety
///
/// Furthermore, all associated items must behave as
/// described.
pub unsafe trait Outlay {
	/// The metadata type used by pointers to this type.
	///
	/// Note that the exact instance of this type alone
	/// does not make any guarantees about the actualy
	/// layout of the implementing type: Instead,
	/// specific layouts should be differentiated via
	/// subtraits (e.g. [`Slice`]).
	///
	/// [`Slice`]: crate::Slice
	type Metadata:
		Debug
		+ Copy
		+ Hash
		+ Immutable
		+ Ord
		+ Send
		+ Sync
		+ Unpin;

	/// The alignement requirement of this type.
	const ALIGN: usize;

	/// Classifies the size of an octet representation.
	///
	/// This function must return a [`Some`] instance if
	/// the provided size is sufficient for an
	/// unspecified `Self` value *and* only corresponds
	/// to one, possible layout; If the size is
	/// ambiguous and has multiple, possible metadatas,
	/// this function must return [`None`].
	#[must_use]
	fn classify_size(size: usize) -> Option<Self::Metadata>;

	/// Computes the size of an object with specific
	/// metadata.
	///
	/// This function must return [`None`] if the
	/// calculated size is unrepresentable by [`usize`].
	#[must_use]
	fn size_with_metadata(len: Self::Metadata) -> Option<usize>;

	/// Constructs a raw, constant pointer from raw
	/// metadata.
	///
	/// The returned pointer inherits the provenance of
	/// the provided one.
	#[must_use]
	fn ptr_from_raw_parts(data: *const u8, meta: Self::Metadata) -> *const Self;

	/// Constructs a raw, mutable pointer from raw
	/// parts.
	///
	/// The returned pointer inherits the provenance of
	/// the provided one.
	#[must_use]
	fn ptr_from_raw_parts_mut(data: *mut u8, meta: Self::Metadata) -> *mut Self;
}

unsafe impl<T> Outlay for T {
	type Metadata = ();

	const ALIGN: usize = align_of::<Self>();

	#[inline]
	fn classify_size(size: usize) -> Option<Self::Metadata> {
		// NOTE: There is only one, possible metadatum, so
		// we just test that the size is sufficient.
		(size >= size_of::<Self>())
			.then_some(())
	}

	#[inline(always)]
	fn size_with_metadata(_meta: Self::Metadata) -> Option<usize> {
		Some(size_of::<Self>())
	}

	#[inline(always)]
	fn ptr_from_raw_parts(data: *const u8, _meta: Self::Metadata) -> *const Self {
		data.cast::<Self>()
	}

	#[inline(always)]
	fn ptr_from_raw_parts_mut(data: *mut u8, _meta: Self::Metadata) -> *mut Self {
		data.cast::<Self>()
	}
}

unsafe impl<T> Outlay for [T] {
	type Metadata = usize;

	const ALIGN: usize = align_of::<T>();

	#[inline]
	fn classify_size(size: usize) -> Option<Self::Metadata> {
		let element_size = const {
			NonZero::new(size_of::<T>())
				.expect("cannot classify size for zst slice")
		};

		size.is_multiple_of(size_of::<T>())
			.then(|| size / element_size)
	}

	#[inline]
	fn size_with_metadata(meta: Self::Metadata) -> Option<usize> {
		size_of::<T>().checked_mul(meta)
	}

	#[inline]
	fn ptr_from_raw_parts(data: *const u8, meta: Self::Metadata) -> *const Self {
		let data = data.cast::<T>();
		ptr::slice_from_raw_parts(data, meta)
	}

	#[inline]
	fn ptr_from_raw_parts_mut(data: *mut u8, meta: Self::Metadata) -> *mut Self {
		let data = data.cast::<T>();
		ptr::slice_from_raw_parts_mut(data, meta)
	}
}

unsafe impl Outlay for str {
	type Metadata = <[u8] as Outlay>::Metadata;

	const ALIGN: usize = <[u8] as Outlay>::ALIGN;

	#[inline(always)]
	fn classify_size(size: usize) -> Option<Self::Metadata> {
		<[u8]>::classify_size(size)
	}

	#[inline(always)]
	fn size_with_metadata(meta: Self::Metadata) -> Option<usize> {
		<[u8]>::size_with_metadata(meta)
	}

	#[inline(always)]
	fn ptr_from_raw_parts(data: *const u8, meta: Self::Metadata) -> *const Self {
		// NOTE: `<*const T>::cast` requires `T: Sized`.
		<[u8]>::ptr_from_raw_parts(data, meta) as *const Self
	}

	#[inline(always)]
	fn ptr_from_raw_parts_mut(data: *mut u8, meta: Self::Metadata) -> *mut Self {
		// NOTE: `<*mut T>::cast` requires `T: Sized`.
		<[u8]>::ptr_from_raw_parts_mut(data, meta) as *mut Self
	}
}