1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*!
An ffi-safe equivalent of ::std::any::TypeId.

No types coming from different dynamic libraries compare equal.
*/

use std::{
    any::TypeId,
    hash::{Hash, Hasher},
    mem,
    sync::atomic::AtomicUsize,
};

use crate::EXECUTABLE_IDENTITY;

///////////////////////////////////////////////////////////////////////////////

/// `extern "C" fn` version of UTypeId::new.
pub extern "C" fn new_utypeid<T>() -> UTypeId
where
    T: 'static,
{
    UTypeId::new::<T>()
}

/// A TypeId that can compare types across dynamic libraries.
///
/// No types coming from different dynamic libraries compare equal.
#[repr(C)]
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Copy, Clone, Hash, StableAbi)]
#[sabi(inside_abi_stable_crate)]
pub struct UTypeId {
    /// A dummy AtomicUsize used as the identity of the dynamic-library/executable
    executable_identity: *const AtomicUsize,
    /// An array containing the butes of a TypeId,
    /// with `MAX_TYPE_ID_SIZE` bytes of space in case it becomes larger.
    type_id_array: [u8; MAX_TYPE_ID_SIZE],
}

impl UTypeId {
    /// Constructs UTypeId from a type that satisfies the `'static` bound.
    #[inline(always)]
    pub fn new<T>() -> Self
    where
        T: 'static,
    {
        Self {
            executable_identity: &EXECUTABLE_IDENTITY,
            type_id_array: get_typeid::<T>(),
        }
    }
}

/////////////////////////////////////////////////////////////////////////////

type TypeIdArray = [u8; mem::size_of::<TypeId>()];

const MAX_TYPE_ID_SIZE: usize = 16;

#[inline(always)]
fn get_typeid<T: 'static>() -> [u8; MAX_TYPE_ID_SIZE] {
    let mut hasher = TypeIdHasher {
        value: [0; MAX_TYPE_ID_SIZE],
        written: 0,
    };
    TypeId::of::<T>().hash(&mut hasher);
    hasher.value
}

#[derive(Default)]
struct TypeIdHasher {
    value: [u8; MAX_TYPE_ID_SIZE],
    written: usize,
}

impl TypeIdHasher {
    #[inline(never)]
    #[cold]
    fn overflow_msg() -> ! {
        eprintln!(
            "TypeId requires writing more than {} bytes to the hasher.",
            MAX_TYPE_ID_SIZE
        );
        ::std::process::abort();
    }
}

impl Hasher for TypeIdHasher {
    #[inline(always)]
    fn write(&mut self, bytes: &[u8]) {
        let _: [u8; MAX_TYPE_ID_SIZE - mem::size_of::<TypeId>()];
        if bytes.len() == mem::size_of::<TypeId>() {
            unsafe {
                let into = (&mut self.value) as *mut _ as *mut TypeIdArray;
                let from = bytes.as_ptr() as *const TypeIdArray;
                *into = *from;
            }
            self.written = mem::size_of::<TypeId>();
            return;
        }
        let old_pos = self.written;
        self.written += bytes.len();
        if self.written <= MAX_TYPE_ID_SIZE {
            self.value[old_pos..self.written].copy_from_slice(bytes);
        } else {
            Self::overflow_msg()
        }
    }

    #[inline(always)]
    fn finish(&self) -> u64 {
        // I'm not gonna call this
        0
    }
}