#![doc = include_str!("../README.md")]
#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub trait TypeSignature {
const SIGNATURE: TypeSignatureHasher;
const CONST_HASH: u64 = Self::SIGNATURE.const_hash();
}
pub use type_signature_derive::TypeSignature;
#[derive(Debug, Hash)]
pub struct TypeSignatureHasher {
#[doc(hidden)]
pub ty_name: &'static str,
#[doc(hidden)]
pub ty_generics: &'static [&'static TypeSignatureHasher],
#[doc(hidden)]
pub const_generic_hashes: &'static [u64],
#[doc(hidden)]
pub variants: &'static [(
&'static str,
&'static [(&'static str, &'static TypeSignatureHasher)],
)],
}
impl TypeSignatureHasher {
#[must_use]
pub const fn const_hash(&self) -> u64 {
let mut accumulator = 0x1b61_42dc_8803_64ed;
__macro_export::mix_values(&mut accumulator, __macro_export::hash_str(self.ty_name));
{
__macro_export::mix_values(&mut accumulator, self.ty_generics.len() as u64);
let mut generic_idx = 0;
while generic_idx < self.ty_generics.len() {
__macro_export::mix_values(
&mut accumulator,
self.ty_generics[generic_idx].const_hash(),
);
generic_idx += 1;
}
}
{
__macro_export::mix_values(&mut accumulator, self.const_generic_hashes.len() as u64);
let mut const_generic_idx = 0;
while const_generic_idx < self.const_generic_hashes.len() {
__macro_export::mix_values(
&mut accumulator,
self.const_generic_hashes[const_generic_idx],
);
const_generic_idx += 1;
}
}
{
__macro_export::mix_values(&mut accumulator, self.variants.len() as u64);
let mut variant_idx = 0;
while variant_idx < self.variants.len() {
let (variant_name, variant_fields) = self.variants[variant_idx];
__macro_export::mix_values(
&mut accumulator,
__macro_export::hash_str(variant_name),
);
__macro_export::mix_values(&mut accumulator, variant_fields.len() as u64);
let mut field_idx = 0;
while field_idx < variant_fields.len() {
let (field_name, field_hasher) = variant_fields[field_idx];
__macro_export::mix_values(
&mut accumulator,
__macro_export::hash_str(field_name),
);
__macro_export::mix_values(&mut accumulator, field_hasher.const_hash());
field_idx += 1;
}
variant_idx += 1;
}
}
accumulator
}
}
macro_rules! impl_for_stdlib_ty {
($(
$stdty:ty $(where < $( $generic:ident $( : $generic_cond:tt )? ),* > )?
),+ $(,)?) => {$(
impl$( < $( $generic $( : $generic_cond )? ),* > )? $crate::TypeSignature for $stdty {
const SIGNATURE: $crate::TypeSignatureHasher = $crate::TypeSignatureHasher {
ty_name: concat!(stringify!($crate::TypeSignature), " impl for ", stringify!($stdty)),
ty_generics: &[
$( $( &<$generic as $crate::TypeSignature>::SIGNATURE, )* )?
],
const_generic_hashes: &[],
variants: &[],
};
}
)+};
}
impl_for_stdlib_ty!(
u8, u16, u32, usize, u64, u128,
i8, i16, i32, isize, i64, i128,
bool,
f32,
f64,
char,
str,
(),
&T where <T: TypeSignature>,
&mut T where <T: TypeSignature>,
*const T where <T: TypeSignature>,
*mut T where <T: TypeSignature>,
[T] where <T: TypeSignature>,
Option<T> where <T: TypeSignature> ,
Result<T, E> where <T: TypeSignature, E: TypeSignature>,
core::marker::PhantomData<T> where <T: TypeSignature>,
core::mem::MaybeUninit<T> where <T: TypeSignature>,
core::mem::ManuallyDrop<T> where <T: TypeSignature>,
core::net::IpAddr, core::net::Ipv4Addr, core::net::Ipv6Addr,
core::net::SocketAddr, core::net::SocketAddrV4, core::net::SocketAddrV6,
core::num::NonZeroU8, core::num::NonZeroU16, core::num::NonZeroU32, core::num::NonZeroU64, core::num::NonZeroUsize, core::num::NonZeroU128,
core::num::NonZeroI8, core::num::NonZeroI16, core::num::NonZeroI32, core::num::NonZeroI64, core::num::NonZeroIsize, core::num::NonZeroI128,
core::num::Saturating<T> where <T: TypeSignature>,
core::num::Wrapping<T> where <T: TypeSignature>,
core::ops::Range<T> where <T: TypeSignature>,
core::ops::RangeFrom<T> where <T: TypeSignature>,
core::ops::RangeFull,
core::ops::RangeInclusive<T> where <T: TypeSignature>,
core::ops::RangeTo<T> where <T: TypeSignature>,
core::ops::RangeToInclusive<T> where <T: TypeSignature>,
core::pin::Pin<T> where <T: TypeSignature>,
core::ptr::NonNull<T> where <T: TypeSignature>,
core::cmp::Ordering,
core::convert::Infallible,
core::time::Duration,
);
#[cfg(target_has_atomic = "8")]
impl_for_stdlib_ty!(
core::sync::atomic::AtomicBool,
core::sync::atomic::AtomicI8,
core::sync::atomic::AtomicU8,
);
#[cfg(target_has_atomic = "16")]
impl_for_stdlib_ty!(core::sync::atomic::AtomicI16, core::sync::atomic::AtomicU16);
#[cfg(target_has_atomic = "32")]
impl_for_stdlib_ty!(core::sync::atomic::AtomicI32, core::sync::atomic::AtomicU32);
#[cfg(target_has_atomic = "64")]
impl_for_stdlib_ty!(core::sync::atomic::AtomicI64, core::sync::atomic::AtomicU64);
#[cfg(target_has_atomic = "ptr")]
impl_for_stdlib_ty!(
core::sync::atomic::AtomicIsize,
core::sync::atomic::AtomicUsize,
core::sync::atomic::AtomicPtr<T> where <T: TypeSignature>,
);
impl<const N: usize, T: TypeSignature> TypeSignature for [T; N] {
const SIGNATURE: TypeSignatureHasher = TypeSignatureHasher {
ty_name: "TypeSignature impl for [T; N]",
ty_generics: &[&T::SIGNATURE],
const_generic_hashes: &[__macro_export::hash_const_usize(N)],
variants: &[],
};
}
macro_rules! impl_for_tuple {
($(
( $( $elem_ty:ident, )* $(,)? )
),+ $(,)? ) => {$(
impl< $( $elem_ty ),* > TypeSignature for ($($elem_ty,)*)
where $( $elem_ty : TypeSignature ),*
{
const SIGNATURE: TypeSignatureHasher = TypeSignatureHasher {
ty_name: concat!(stringify!($crate::TypeSignature), " impl for (", $( stringify!($elem_ty), "," ),* ),
ty_generics: &[
$( &<$elem_ty as $crate::TypeSignature>::SIGNATURE, )*
],
const_generic_hashes: &[],
variants: &[],
};
}
)+};
}
impl_for_tuple!(
(T0,),
(T0, T1,),
(T0, T1, T2,),
(T0, T1, T2, T3,),
(T0, T1, T2, T3, T4,),
(T0, T1, T2, T3, T4, T5,),
(T0, T1, T2, T3, T4, T5, T6,),
(T0, T1, T2, T3, T4, T5, T6, T7,),
(T0, T1, T2, T3, T4, T5, T6, T7, T8,),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9,),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10,),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11,),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12,),
(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13,),
(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14,
),
(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15,
),
);
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
mod std_impl {
extern crate std;
use crate::TypeSignature;
impl_for_stdlib_ty!(
std::collections::HashMap<K, V> where <K: TypeSignature, V: TypeSignature>,
std::collections::HashSet<T> where <T: TypeSignature>,
std::ffi::OsStr,
std::ffi::OsString,
std::path::Path,
std::path::PathBuf,
std::sync::Mutex<T> where <T: TypeSignature>,
std::sync::RwLock<T> where <T: TypeSignature>,
std::sync::Once,
std::time::Instant,
std::time::SystemTime,
);
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
mod alloc_impl {
extern crate alloc;
use crate::TypeSignature;
impl_for_stdlib_ty!(
alloc::boxed::Box<T> where <T: TypeSignature>,
alloc::collections::BinaryHeap<T> where <T: TypeSignature>,
alloc::collections::BTreeMap<K, V> where <K: TypeSignature, V: TypeSignature>,
alloc::collections::BTreeSet<T> where <T: TypeSignature>,
alloc::collections::LinkedList<T> where <T: TypeSignature>,
alloc::collections::VecDeque<T> where <T: TypeSignature>,
alloc::ffi::CString,
alloc::rc::Rc<T> where <T: TypeSignature>,
alloc::rc::Weak<T> where <T: TypeSignature>,
alloc::string::String,
alloc::vec::Vec<T> where <T: TypeSignature>,
);
#[cfg(target_has_atomic = "ptr")]
impl_for_stdlib_ty!(
alloc::sync::Arc<T> where <T: TypeSignature>,
alloc::sync::Weak<T> where <T: TypeSignature>,
);
impl<'a, B: TypeSignature + alloc::borrow::ToOwned + ?Sized + 'a> TypeSignature
for alloc::borrow::Cow<'a, B>
{
const SIGNATURE: crate::TypeSignatureHasher = crate::TypeSignatureHasher {
ty_name: "TypeSignature impl for Cow<'a, B>",
ty_generics: &[&<B as TypeSignature>::SIGNATURE],
const_generic_hashes: &[],
variants: &[],
};
}
}
#[macro_export]
macro_rules! impl_type_signature_as {
($target:ident as struct { $( $fields:tt )* }) => {
$crate::_impl_ts_as_helper! { $target as $target in struct $target { $( $fields )* } }
};
($target:ident as enum { $( $variants:tt )* }) => {
$crate::_impl_ts_as_helper! { $target as $target in enum $target { $( $variants )* } }
};
}
#[doc(hidden)]
pub mod __macro_export {
#[must_use]
pub const fn hash_const_usize(param_val: usize) -> u64 {
let mut accumulator = hash_str("usize");
mix_values(&mut accumulator, param_val as u64);
if size_of::<usize>() == 16 {
mix_values(&mut accumulator, ((param_val as u128) >> 64) as u64);
}
accumulator
}
#[must_use]
pub const fn hash_const_bool(param_val: bool) -> u64 {
let mut accumulator = hash_str("bool");
mix_values(
&mut accumulator,
if param_val {
0x7907_e475_126f_2049
} else {
0xa656_face_e66f_d217
},
);
accumulator
}
pub const fn mix_values(accumulator: &mut u64, value: u64) {
*accumulator = accumulator
.wrapping_mul(0x35ce_5fac_9b48_99b5)
.wrapping_add(0x1e5d_49b9_70ea_d075)
^ value
.wrapping_mul(0x13fd_608d_551c_c1d1)
.wrapping_add(0x87b5_2407_45ca_ca0f);
}
#[must_use]
pub const fn hash_str(s: &str) -> u64 {
let mut accumulator = 0x1124_262e_5999_d5bb;
mix_values(&mut accumulator, s.len() as u64);
let mut byte_idx = 0;
while byte_idx < s.len() {
mix_values(&mut accumulator, s.as_bytes()[byte_idx] as u64);
byte_idx += 1;
}
accumulator
}
#[doc(hidden)]
#[macro_export]
macro_rules! _impl_ts_as_helper {
($target:ty as $ident:ident in $item:item) => {
impl $crate::TypeSignature for $target {
const SIGNATURE: $crate::TypeSignatureHasher = {
#[derive($crate::TypeSignature)]
#[type_signature(crate = $crate)]
#[allow(dead_code)]
$item
$ident::SIGNATURE
};
}
};
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(TypeSignature)]
#[type_signature(crate = super)]
#[allow(dead_code, reason = "Schema depends on it")]
struct Foo {
bar: u32,
}
#[test]
fn test_derive_with_custom_crate_name() {
assert_eq!(Foo::CONST_HASH, 0x9cb2_d1de_e1dc_8ae3);
}
}