#![cfg_attr(
feature = "derive",
doc = r##"
## Examples
The layout of types is only defined if they're `#[repr(C)]`. This crate works
on non-`#[repr(C)]` types, but their layout is unpredictable.
```rust
# #![feature(const_type_name)]
# #![feature(offset_of)]
# #![feature(offset_of_enum)]
use const_type_layout::TypeLayout;
#[derive(TypeLayout)]
#[repr(C)]
struct Foo {
a: u8,
b: u32,
}
assert_eq!(
format!("{:#?}", Foo::TYPE_LAYOUT),
r#"TypeLayoutInfo {
name: "rust_out::main::_doctest_main_src_lib_rs_49_0::Foo",
size: 8,
alignment: 4,
structure: Struct {
repr: "C",
fields: [
Field {
name: "a",
offset: Inhabited(
0,
),
ty: "u8",
},
Field {
name: "b",
offset: Inhabited(
4,
),
ty: "u32",
},
],
},
}"#
)
```
Over-aligned types have trailing padding, which can be a source of bugs in some
FFI scenarios:
```rust
# #![feature(const_type_name)]
# #![feature(offset_of)]
# #![feature(offset_of_enum)]
use const_type_layout::TypeLayout;
#[derive(TypeLayout)]
#[repr(C, align(128))]
struct OverAligned {
value: u8,
}
assert_eq!(
format!("{:#?}", OverAligned::TYPE_LAYOUT),
r#"TypeLayoutInfo {
name: "rust_out::main::_doctest_main_src_lib_rs_94_0::OverAligned",
size: 128,
alignment: 128,
structure: Struct {
repr: "C,align(128)",
fields: [
Field {
name: "value",
offset: Inhabited(
0,
),
ty: "u8",
},
],
},
}"#
)
```
"##
)]
#![deny(clippy::complexity)]
#![deny(clippy::correctness)]
#![warn(clippy::nursery)]
#![warn(clippy::pedantic)]
#![deny(clippy::perf)]
#![deny(clippy::style)]
#![deny(clippy::suspicious)]
#![deny(clippy::default_union_representation)]
#![deny(clippy::multiple_unsafe_ops_per_block)]
#![deny(clippy::undocumented_unsafe_blocks)]
#![deny(unused_unsafe)]
#![deny(missing_docs)]
#![no_std]
#![feature(const_type_name)]
#![cfg_attr(not(version("1.83")), feature(const_mut_refs))]
#![feature(cfg_target_has_atomic)]
#![feature(decl_macro)]
#![feature(never_type)]
#![feature(c_variadic)]
#![feature(discriminant_kind)]
#![feature(offset_of_enum)]
#![feature(sync_unsafe_cell)]
#![feature(exclusive_wrapper)]
#![feature(doc_auto_cfg)]
#![feature(cfg_version)]
#![cfg_attr(not(version("1.82")), feature(offset_of_nested))]
#![feature(freeze)]
#![allow(incomplete_features)]
#![feature(generic_const_exprs)]
#![feature(specialization)]
#![cfg_attr(
all(doc, not(docsrs)),
doc(html_root_url = "https://juntyr.github.io/const-type-layout")
)]
#![cfg_attr(feature = "serde", allow(clippy::type_repetition_in_bounds))]
extern crate alloc;
use alloc::fmt;
use core::ops::Deref;
#[cfg(feature = "derive")]
pub use const_type_layout_derive::TypeLayout;
mod impls;
pub mod inhabited;
mod ser;
pub mod typeset;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
#[cfg_attr(feature = "serde", derive(::serde::Deserialize))]
pub enum MaybeUninhabited<T> {
Uninhabited,
Inhabited(T),
}
impl<T: Copy> MaybeUninhabited<T> {
#[must_use]
pub const fn new<U: TypeLayout>(v: T) -> Self {
if <U::Inhabited as Same<inhabited::Inhabited>>::EQ {
Self::Inhabited(v)
} else {
Self::Uninhabited
}
}
}
impl<T: Default> Default for MaybeUninhabited<T> {
fn default() -> Self {
Self::Inhabited(T::default())
}
}
pub unsafe trait TypeLayout: Sized {
type Inhabited: inhabited::OutputMaybeInhabited;
const TYPE_LAYOUT: TypeLayoutInfo<'static>;
}
pub trait TypeGraphLayout: TypeLayout + typeset::ComputeTypeSet {
const TYPE_GRAPH: TypeLayoutGraph<'static>;
}
impl<T: TypeLayout + typeset::ComputeTypeSet> TypeGraphLayout for T {
const TYPE_GRAPH: TypeLayoutGraph<'static> = TypeLayoutGraph::new::<T>();
}
#[must_use]
pub const fn serialised_type_graph_len<T: TypeGraphLayout>() -> usize {
T::TYPE_GRAPH.serialised_len()
}
#[must_use]
pub const fn serialise_type_graph<T: TypeGraphLayout>() -> [u8; serialised_type_graph_len::<T>()] {
let mut bytes = [0_u8; serialised_type_graph_len::<T>()];
T::TYPE_GRAPH.serialise(&mut bytes);
bytes
}
#[must_use]
pub const fn hash_type_graph<T: TypeGraphLayout>(seed: u64) -> u64 {
T::TYPE_GRAPH.hash(seed)
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", allow(clippy::unsafe_derive_deserialize))]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
pub struct TypeLayoutGraph<
'a,
F: Deref<Target = [Field<'a>]> = &'a [Field<'a>],
V: Deref<Target = [Variant<'a, F>]> = &'a [Variant<'a, F>],
I: Deref<Target = TypeLayoutInfo<'a, F, V>> = &'a TypeLayoutInfo<'a, F, V>,
G: Deref<Target = [I]> = &'a [I],
> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub ty: &'a str,
pub tys: G,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
pub struct TypeLayoutInfo<
'a,
F: Deref<Target = [Field<'a>]> = &'a [Field<'a>],
V: Deref<Target = [Variant<'a, F>]> = &'a [Variant<'a, F>],
> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub name: &'a str,
pub size: usize,
pub alignment: usize,
#[cfg_attr(feature = "serde", serde(borrow))]
pub structure: TypeStructure<'a, F, V>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
pub enum TypeStructure<
'a,
F: Deref<Target = [Field<'a>]> = &'a [Field<'a>],
V: Deref<Target = [Variant<'a, F>]> = &'a [Variant<'a, F>],
> {
Primitive,
Struct {
#[cfg_attr(feature = "serde", serde(borrow))]
repr: &'a str,
fields: F,
},
Union {
#[cfg_attr(feature = "serde", serde(borrow))]
repr: &'a str,
fields: F,
},
Enum {
#[cfg_attr(feature = "serde", serde(borrow))]
repr: &'a str,
variants: V,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
pub struct Variant<'a, F: Deref<Target = [Field<'a>]> = &'a [Field<'a>]> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub name: &'a str,
pub discriminant: MaybeUninhabited<Discriminant>,
pub fields: F,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", allow(clippy::unsafe_derive_deserialize))]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
pub enum Discriminant {
I8(i8),
I16(i16),
I32(i32),
I64(i64),
I128(i128),
Isize(isize),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
U128(u128),
Usize(usize),
}
impl Discriminant {
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub const fn new<T: ExtractDiscriminant>(v: T::Discriminant) -> Self {
#[repr(C)]
union Transmute<T: Copy> {
v: T,
i8: i8,
i16: i16,
i32: i32,
i64: i64,
i128: i128,
isize: isize,
u8: u8,
u16: u16,
u32: u32,
u64: u64,
u128: u128,
usize: usize,
}
if <T::Discriminant as Same<i8>>::EQ {
return Self::I8(unsafe { Transmute { v }.i8 });
} else if <T::Discriminant as Same<i16>>::EQ {
return Self::I16(unsafe { Transmute { v }.i16 });
} else if <T::Discriminant as Same<i32>>::EQ {
return Self::I32(unsafe { Transmute { v }.i32 });
} else if <T::Discriminant as Same<i64>>::EQ {
return Self::I64(unsafe { Transmute { v }.i64 });
} else if <T::Discriminant as Same<i128>>::EQ {
return Self::I128(unsafe { Transmute { v }.i128 });
} else if <T::Discriminant as Same<isize>>::EQ {
return Self::Isize(unsafe { Transmute { v }.isize });
} else if <T::Discriminant as Same<u8>>::EQ {
return Self::U8(unsafe { Transmute { v }.u8 });
} else if <T::Discriminant as Same<u16>>::EQ {
return Self::U16(unsafe { Transmute { v }.u16 });
} else if <T::Discriminant as Same<u32>>::EQ {
return Self::U32(unsafe { Transmute { v }.u32 });
} else if <T::Discriminant as Same<u64>>::EQ {
return Self::U64(unsafe { Transmute { v }.u64 });
} else if <T::Discriminant as Same<u128>>::EQ {
return Self::U128(unsafe { Transmute { v }.u128 });
} else if <T::Discriminant as Same<usize>>::EQ {
return Self::Usize(unsafe { Transmute { v }.usize });
}
panic!("bug: unknown discriminant kind")
}
}
trait Same<T> {
const EQ: bool;
}
impl<T, U> Same<U> for T {
default const EQ: bool = false;
}
impl<T> Same<T> for T {
const EQ: bool = true;
}
pub trait ExtractDiscriminant {
type Discriminant: Clone
+ Copy
+ fmt::Debug
+ Eq
+ PartialEq
+ core::hash::Hash
+ Send
+ Sync
+ Unpin
+ TypeGraphLayout;
}
impl<T> ExtractDiscriminant for T {
type Discriminant =
<T as ExtractDiscriminantSpec<<T as core::marker::DiscriminantKind>::Discriminant>>::Ty;
}
#[doc(hidden)]
pub trait ExtractDiscriminantSpec<T> {
type Ty: Clone
+ Copy
+ fmt::Debug
+ Eq
+ PartialEq
+ core::hash::Hash
+ Send
+ Sync
+ Unpin
+ TypeGraphLayout;
}
impl<T> ExtractDiscriminantSpec<<T as core::marker::DiscriminantKind>::Discriminant> for T {
default type Ty = !;
}
macro_rules! impl_extract_discriminant {
($variant:ident($ty:ty)) => {
impl<T: core::marker::DiscriminantKind<Discriminant = $ty>> ExtractDiscriminantSpec<$ty> for T {
type Ty = $ty;
}
};
($($variant:ident($ty:ty)),*) => {
$(impl_extract_discriminant! { $variant($ty) })*
};
}
impl_extract_discriminant! {
I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), Isize(isize),
U8(u8), U16(u16), U32(u32), U64(u64), U128(u128), Usize(usize)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
pub struct Field<'a> {
#[cfg_attr(feature = "serde", serde(borrow))]
pub name: &'a str,
pub offset: MaybeUninhabited<usize>,
#[cfg_attr(feature = "serde", serde(borrow))]
pub ty: &'a str,
}
impl TypeLayoutGraph<'static> {
#[must_use]
pub const fn new<T: TypeLayout + typeset::ComputeTypeSet>() -> Self {
Self {
ty: <T as TypeLayout>::TYPE_LAYOUT.name,
tys: unsafe {
core::slice::from_raw_parts(
core::ptr::from_ref(<typeset::TypeSet<T> as typeset::ComputeSet>::TYS).cast(),
<typeset::TypeSet<T> as typeset::ComputeSet>::LEN,
)
},
}
}
}
impl<
'a,
>
TypeLayoutGraph<
'a,
>
{
#[must_use]
pub const fn serialised_len(&self) -> usize
{
let mut counter = ser::Serialiser::counter(0);
counter.serialise_type_layout_graph(self);
let len = counter.cursor();
let mut last_full_len = len;
let mut counter = ser::Serialiser::counter(len);
counter.serialise_usize(last_full_len);
let mut full_len = counter.cursor();
while full_len != last_full_len {
last_full_len = full_len;
let mut counter = ser::Serialiser::counter(len);
counter.serialise_usize(last_full_len);
full_len = counter.cursor();
}
full_len
}
pub const fn serialise(&self, bytes: &mut [u8])
{
let mut writer = ser::Serialiser::writer(bytes, 0);
writer.serialise_usize(self.serialised_len());
writer.serialise_type_layout_graph(self);
}
#[must_use]
pub const fn hash(&self, seed: u64) -> u64
{
let mut hasher = ser::Serialiser::hasher(seed);
hasher.serialise_usize(self.serialised_len());
hasher.serialise_type_layout_graph(self);
hasher.hash()
}
}
impl<
'a,
F: Deref<Target = [Field<'a>]> + fmt::Debug,
V: Deref<Target = [Variant<'a, F>]> + fmt::Debug,
I: Deref<Target = TypeLayoutInfo<'a, F, V>> + fmt::Debug,
G: Deref<Target = [I]> + fmt::Debug,
> fmt::Debug for TypeLayoutGraph<'a, F, V, I, G>
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_fmt(format_args!("TypeLayoutGraph<{}>({:?})", self.ty, self.tys))
}
}