use core::ffi::c_void;
use core::marker::PhantomData;
use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64};
use core::ptr::NonNull;
pub unsafe trait Shape {
const SHAPE: u64;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ShapeHasher(u64);
impl ShapeHasher {
const PRIME: u64 = 0x0000_0100_0000_01b3;
#[must_use]
pub const fn new(kind: &str) -> Self {
Self(0xcbf2_9ce4_8422_2325).text(kind)
}
#[must_use]
pub const fn text(self, text: &str) -> Self {
let mut hash = self.number(text.len() as u64).0;
let bytes = text.as_bytes();
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(Self::PRIME);
i += 1;
}
Self(hash)
}
#[must_use]
pub const fn number(self, number: u64) -> Self {
let mut hash = self.0;
let bytes = number.to_le_bytes();
let mut i = 0;
while i < bytes.len() {
hash ^= bytes[i] as u64;
hash = hash.wrapping_mul(Self::PRIME);
i += 1;
}
Self(hash)
}
#[must_use]
pub const fn layout<T>(self) -> Self {
self.number(size_of::<T>() as u64)
.number(align_of::<T>() as u64)
}
#[must_use]
pub const fn offset(self, offset: usize) -> Self {
self.number(offset as u64)
}
#[must_use]
pub const fn value(self, value: i128) -> Self {
let bits = value.to_le_bytes();
let mut low = [0u8; 8];
let mut high = [0u8; 8];
let mut i = 0;
while i < 8 {
low[i] = bits[i];
high[i] = bits[i + 8];
i += 1;
}
self.number(u64::from_le_bytes(low))
.number(u64::from_le_bytes(high))
}
#[must_use]
pub const fn finish(self) -> u64 {
self.0
}
}
macro_rules! leaf {
($($ty:ty),* $(,)?) => {$(
unsafe impl Shape for $ty {
const SHAPE: u64 = ShapeHasher::new(stringify!($ty)).finish();
}
)*};
}
leaf!(
(),
u8,
u16,
u32,
u64,
u128,
usize,
i8,
i16,
i32,
i64,
i128,
isize,
f32,
f64,
char,
str,
c_void,
NonZeroU8,
NonZeroU16,
NonZeroU32,
NonZeroU64,
);
unsafe impl Shape for bool {
const SHAPE: u64 = ShapeHasher::new("bool")
.text("false")
.number(0)
.text("true")
.number(1)
.finish();
}
unsafe impl<T: Shape, const N: usize> Shape for [T; N] {
const SHAPE: u64 = ShapeHasher::new("array")
.number(T::SHAPE)
.number(N as u64)
.finish();
}
unsafe impl<T: Shape> Shape for [T] {
const SHAPE: u64 = ShapeHasher::new("slice").number(T::SHAPE).finish();
}
unsafe impl<T: Shape> Shape for Option<T> {
const SHAPE: u64 = ShapeHasher::new("Option").number(T::SHAPE).finish();
}
unsafe impl<T: ?Sized> Shape for PhantomData<T> {
const SHAPE: u64 = ShapeHasher::new("PhantomData").finish();
}
macro_rules! pointer {
($($kind:literal $ty:ty),* $(,)?) => {$(
unsafe impl<T: Shape + ?Sized> Shape for $ty {
const SHAPE: u64 = ShapeHasher::new($kind).number(T::SHAPE).finish();
}
)*};
}
pointer!(
"*const" *const T,
"*mut" *mut T,
"&" &T,
"&mut" &mut T,
"NonNull" NonNull<T>,
);
macro_rules! function {
($($arg:ident)*) => {
function!(@abi "Rust" fn($($arg),*) -> R; $($arg)*);
function!(@abi "C" extern "C" fn($($arg),*) -> R; $($arg)*);
function!(@abi "system" extern "system" fn($($arg),*) -> R; $($arg)*);
function!(@abi "unsafe Rust" unsafe fn($($arg),*) -> R; $($arg)*);
function!(@abi "unsafe C" unsafe extern "C" fn($($arg),*) -> R; $($arg)*);
function!(@abi "unsafe system" unsafe extern "system" fn($($arg),*) -> R; $($arg)*);
};
(@abi $abi:literal $ty:ty; $($arg:ident)*) => {
unsafe impl<R: Shape, $($arg: Shape),*> Shape for $ty {
const SHAPE: u64 = ShapeHasher::new("fn")
.text($abi)
.number(R::SHAPE)
$(.number($arg::SHAPE))*
.finish();
}
};
}
function!();
function!(A);
function!(A B);
function!(A B C);
function!(A B C D);
function!(A B C D E);
function!(A B C D E F);
function!(A B C D E F G);
function!(A B C D E F G H);
#[cfg(test)]
mod tests {
use super::Shape;
#[test]
fn primitives_differ() {
let shapes = [
u8::SHAPE,
i8::SHAPE,
bool::SHAPE,
u32::SHAPE,
f32::SHAPE,
<[u8; 4]>::SHAPE,
<[u8; 2]>::SHAPE,
<*const u32>::SHAPE,
<*mut u32>::SHAPE,
<extern "system" fn(u32) -> u32>::SHAPE,
<extern "C" fn(u32) -> u32>::SHAPE,
<extern "system" fn(u64) -> u32>::SHAPE,
<extern "system" fn(u32, u32) -> u32>::SHAPE,
];
for (i, a) in shapes.iter().enumerate() {
for b in &shapes[i + 1..] {
assert_ne!(a, b);
}
}
}
}