use core::mem;
pub struct LayoutInfo<T> {
_marker: core::marker::PhantomData<T>,
}
impl<T> LayoutInfo<T> {
pub const fn new() -> Self {
LayoutInfo {
_marker: core::marker::PhantomData,
}
}
pub const fn size() -> usize {
mem::size_of::<T>()
}
pub const fn align() -> usize {
mem::align_of::<T>()
}
pub const fn is_zst() -> bool {
mem::size_of::<T>() == 0
}
pub const fn fits_in_register() -> bool {
mem::size_of::<T>() <= mem::size_of::<usize>()
}
pub const fn fits_in_two_registers() -> bool {
mem::size_of::<T>() <= 2 * mem::size_of::<usize>()
}
pub const fn is_pointer_sized() -> bool {
mem::size_of::<T>() == mem::size_of::<usize>()
}
pub const fn padding_needed(current_offset: usize) -> usize {
let align = mem::align_of::<T>();
let misalign = current_offset % align;
if misalign == 0 { 0 } else { align - misalign }
}
}
impl<T> Default for LayoutInfo<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeCategory {
Zero,
Tiny,
Small,
Medium,
Large,
}
impl SizeCategory {
pub const fn from_size(size: usize) -> Self {
if size == 0 {
SizeCategory::Zero
} else if size <= 8 {
SizeCategory::Tiny
} else if size <= 32 {
SizeCategory::Small
} else if size <= 256 {
SizeCategory::Medium
} else {
SizeCategory::Large
}
}
pub const fn of<T>() -> Self {
Self::from_size(mem::size_of::<T>())
}
#[inline]
pub const fn pass_by_value(self) -> bool {
matches!(
self,
SizeCategory::Zero | SizeCategory::Tiny | SizeCategory::Small
)
}
#[inline]
pub const fn inline_beneficial(self) -> bool {
matches!(self, SizeCategory::Zero | SizeCategory::Tiny)
}
}
pub struct OptimizationHints<T> {
_marker: core::marker::PhantomData<T>,
}
impl<T> OptimizationHints<T> {
pub const fn new() -> Self {
OptimizationHints {
_marker: core::marker::PhantomData,
}
}
#[inline]
pub const fn pass_by_value() -> bool {
SizeCategory::of::<T>().pass_by_value()
}
#[inline]
pub const fn should_inline() -> bool {
SizeCategory::of::<T>().inline_beneficial()
}
#[inline]
pub const fn should_box() -> bool {
matches!(SizeCategory::of::<T>(), SizeCategory::Large)
}
#[inline]
pub const fn use_arena() -> bool {
matches!(
SizeCategory::of::<T>(),
SizeCategory::Small | SizeCategory::Medium
)
}
}
impl<T> Default for OptimizationHints<T> {
fn default() -> Self {
Self::new()
}
}
pub const CACHE_LINE_SIZE: usize = 64;
pub const fn fits_in_cache_line<T>() -> bool {
mem::size_of::<T>() <= CACHE_LINE_SIZE
}
pub const fn instances_per_cache_line<T>() -> usize {
if mem::size_of::<T>() == 0 {
usize::MAX
} else {
CACHE_LINE_SIZE / mem::size_of::<T>()
}
}
#[repr(align(64))]
#[derive(Debug, Clone, Copy)]
pub struct CacheAligned<T>(pub T);
impl<T> CacheAligned<T> {
pub const fn new(value: T) -> Self {
CacheAligned(value)
}
}
impl<T: Default> Default for CacheAligned<T> {
fn default() -> Self {
CacheAligned(T::default())
}
}
#[macro_export]
macro_rules! assert_size {
($t:ty, $size:expr) => {
const _: () = {
if core::mem::size_of::<$t>() != $size {
panic!("Size assertion failed");
}
};
};
}
#[macro_export]
macro_rules! assert_align {
($t:ty, $align:expr) => {
const _: () = {
if core::mem::align_of::<$t>() != $align {
panic!("Alignment assertion failed");
}
};
};
}
#[macro_export]
macro_rules! assert_zst {
($t:ty) => {
const _: () = {
if core::mem::size_of::<$t>() != 0 {
panic!("ZST assertion failed");
}
};
};
}
pub trait CRepr {}
pub trait Packed {}
pub trait Transparent {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_layout_info() {
assert_eq!(LayoutInfo::<u32>::size(), 4);
assert_eq!(LayoutInfo::<u64>::size(), 8);
assert!(LayoutInfo::<()>::is_zst());
assert!(LayoutInfo::<u64>::fits_in_register());
}
#[test]
fn test_size_category() {
assert_eq!(SizeCategory::of::<()>(), SizeCategory::Zero);
assert_eq!(SizeCategory::of::<u8>(), SizeCategory::Tiny);
assert_eq!(SizeCategory::of::<u64>(), SizeCategory::Tiny);
assert_eq!(SizeCategory::of::<[u64; 3]>(), SizeCategory::Small);
assert_eq!(SizeCategory::of::<[u64; 10]>(), SizeCategory::Medium);
assert_eq!(SizeCategory::of::<[u64; 100]>(), SizeCategory::Large);
}
#[test]
fn test_optimization_hints() {
assert!(OptimizationHints::<u32>::pass_by_value());
assert!(OptimizationHints::<u8>::should_inline());
assert!(OptimizationHints::<[u8; 1000]>::should_box());
}
#[test]
fn test_cache_optimization() {
assert!(fits_in_cache_line::<u64>());
assert!(fits_in_cache_line::<[u8; 64]>());
assert!(!fits_in_cache_line::<[u8; 65]>());
assert_eq!(instances_per_cache_line::<u64>(), 8);
assert_eq!(instances_per_cache_line::<u8>(), 64);
}
#[test]
fn test_cache_aligned() {
let aligned = CacheAligned::new(42u32);
assert_eq!(aligned.0, 42);
assert!(core::mem::align_of::<CacheAligned<u32>>() >= 64);
}
assert_size!(u32, 4);
assert_size!(u64, 8);
assert_align!(u64, 8);
assert_zst!(());
}