abi_stable/std_types/
utypeid.rs

1//! An ffi-safe equivalent of `std::any::TypeId`.
2//!
3//! No types coming from different dynamic libraries compare equal.
4
5use std::{
6    any::TypeId,
7    hash::{Hash, Hasher},
8    mem,
9    sync::atomic::AtomicUsize,
10};
11
12use crate::{sabi_types::MaybeCmp, EXECUTABLE_IDENTITY};
13
14///////////////////////////////////////////////////////////////////////////////
15
16/// `extern "C" fn` version of `UTypeId::new`.
17///
18/// # Example
19///
20/// ```
21/// use abi_stable::std_types::utypeid::new_utypeid;
22/// use std::collections::HashMap;
23///
24/// let hashmap_id = new_utypeid::<HashMap<String, String>>();
25/// let vec_id = new_utypeid::<Vec<String>>();
26/// let u32_id = new_utypeid::<u32>();
27///
28/// assert_eq!(hashmap_id, hashmap_id);
29/// assert_eq!(vec_id, vec_id);
30/// assert_eq!(u32_id, u32_id);
31///
32/// assert_ne!(vec_id, hashmap_id);
33/// assert_ne!(u32_id, hashmap_id);
34/// assert_ne!(vec_id, u32_id);
35///
36/// ```
37pub extern "C" fn new_utypeid<T>() -> UTypeId
38where
39    T: 'static,
40{
41    UTypeId::new::<T>()
42}
43
44#[doc(hidden)]
45pub extern "C" fn some_utypeid<T>() -> MaybeCmp<UTypeId>
46where
47    T: 'static,
48{
49    MaybeCmp::Just(UTypeId::new::<T>())
50}
51
52#[doc(hidden)]
53#[allow(clippy::missing_const_for_fn)]
54pub extern "C" fn no_utypeid() -> MaybeCmp<UTypeId> {
55    MaybeCmp::Nothing
56}
57
58/// An ffi-safe equivalent of `std::any::TypeId` that
59/// can compare types across dynamic libraries.
60///
61/// No `UTypeId` constructed in different dynamic libraries compare equal.
62///
63/// # Example
64///
65/// ```
66/// use abi_stable::std_types::UTypeId;
67///
68/// assert_eq!(UTypeId::new::<()>(), UTypeId::new::<()>());
69/// assert_eq!(UTypeId::new::<Box<String>>(), UTypeId::new::<Box<String>>());
70///
71/// assert_ne!(UTypeId::new::<()>(), UTypeId::new::<Vec<()>>());
72/// assert_ne!(UTypeId::new::<Box<String>>(), UTypeId::new::<&str>());
73/// ```
74#[repr(C)]
75#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash, StableAbi)]
76pub struct UTypeId {
77    /// A dummy AtomicUsize used as the identity of the dynamic-library/executable
78    executable_identity: *const AtomicUsize,
79    /// An array containing the butes of a TypeId,
80    /// with `MAX_TYPE_ID_SIZE` bytes of space in case it becomes larger.
81    type_id_array: [u8; MAX_TYPE_ID_SIZE],
82}
83
84unsafe impl Send for UTypeId {}
85unsafe impl Sync for UTypeId {}
86
87impl UTypeId {
88    /// Constructs `UTypeId` from a type that satisfies the `'static` bound.
89    ///
90    /// # Example
91    ///
92    /// ```
93    /// use abi_stable::std_types::UTypeId;
94    /// use std::collections::HashMap;
95    ///
96    /// let id = UTypeId::new::<HashMap<String, String>>();
97    /// # drop(id);
98    /// ```
99    #[inline(always)]
100    pub fn new<T>() -> Self
101    where
102        T: 'static,
103    {
104        Self {
105            executable_identity: &EXECUTABLE_IDENTITY,
106            type_id_array: get_typeid::<T>(),
107        }
108    }
109}
110
111/////////////////////////////////////////////////////////////////////////////
112
113type TypeIdArray = [u8; mem::size_of::<TypeId>()];
114
115const MAX_TYPE_ID_SIZE: usize = 16;
116
117#[inline(always)]
118fn get_typeid<T: 'static>() -> [u8; MAX_TYPE_ID_SIZE] {
119    let mut hasher = TypeIdHasher {
120        value: [0; MAX_TYPE_ID_SIZE],
121        written: 0,
122    };
123    TypeId::of::<T>().hash(&mut hasher);
124    hasher.value
125}
126
127#[derive(Default)]
128struct TypeIdHasher {
129    value: [u8; MAX_TYPE_ID_SIZE],
130    written: usize,
131}
132
133impl TypeIdHasher {
134    #[inline(never)]
135    #[cold]
136    fn overflow_msg() -> ! {
137        eprintln!(
138            "TypeId requires writing more than {} bytes to the hasher.",
139            MAX_TYPE_ID_SIZE
140        );
141        ::std::process::abort();
142    }
143}
144
145impl Hasher for TypeIdHasher {
146    #[inline(always)]
147    fn write(&mut self, bytes: &[u8]) {
148        let _: [u8; MAX_TYPE_ID_SIZE - mem::size_of::<TypeId>()];
149        if bytes.len() == mem::size_of::<TypeId>() {
150            unsafe {
151                let into = (&mut self.value) as *mut _ as *mut TypeIdArray;
152                let from = bytes.as_ptr() as *const TypeIdArray;
153                *into = *from;
154            }
155            self.written = mem::size_of::<TypeId>();
156            return;
157        }
158        let old_pos = self.written;
159        self.written += bytes.len();
160        if self.written <= MAX_TYPE_ID_SIZE {
161            self.value[old_pos..self.written].copy_from_slice(bytes);
162        } else {
163            Self::overflow_msg()
164        }
165    }
166
167    #[inline(always)]
168    fn finish(&self) -> u64 {
169        // I'm not gonna call this
170        0
171    }
172}