Skip to main content

typeuri/
lib.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9//! Named trait for types with globally unique type URIs.
10
11use std::any::TypeId;
12use std::collections::HashMap;
13
14// Re-export cityhasher for use in the derive macro
15pub use cityhasher;
16// Re-export dashmap so that the intern_typename macro can use $crate::dashmap
17pub use dashmap;
18// Re-export the Named derive macro from typeuri_macros
19pub use typeuri_macros::Named;
20
21/// Actor handler port should have its most significant bit set to 1.
22pub static ACTOR_PORT_BIT: u64 = 1 << 63;
23
24/// A [`Named`] type is a type that has a globally unique name.
25pub trait Named: Sized + 'static {
26    /// The globally unique type name for the type.
27    /// This should typically be the fully qualified Rust name of the type.
28    fn typename() -> &'static str;
29
30    /// A globally unique hash for this type.
31    /// TODO: actually enforce perfect hashing
32    fn typehash() -> u64 {
33        // The `Named` macro overrides this implementation with one that
34        // memoizes the hash.
35        cityhasher::hash(Self::typename())
36    }
37
38    /// The TypeId for this type. TypeIds are unique only within a binary,
39    /// and should not be used for global identification.
40    fn typeid() -> TypeId {
41        TypeId::of::<Self>()
42    }
43
44    /// The globally unique port for this type. Typed ports are in the range
45    /// of 1<<63..1<<64-1.
46    fn port() -> u64 {
47        Self::typehash() | ACTOR_PORT_BIT
48    }
49
50    /// If the named type is an enum, this returns the name of the arm
51    /// of the value self.
52    fn arm(&self) -> Option<&'static str> {
53        None
54    }
55
56    /// An unsafe version of 'arm', accepting a pointer to the value,
57    /// for use in type-erased settings.
58    ///
59    /// # Safety
60    ///
61    /// self_ must be a valid pointer to a Self instance that
62    /// remains alive for the duration of the call.
63    unsafe fn arm_unchecked(self_: *const ()) -> Option<&'static str> {
64        // SAFETY: This isn't safe. We're passing it on.
65        unsafe { &*(self_ as *const Self) }.arm()
66    }
67}
68
69macro_rules! impl_basic {
70    ($t:ty) => {
71        impl Named for $t {
72            fn typename() -> &'static str {
73                stringify!($t)
74            }
75        }
76    };
77}
78
79impl_basic!(());
80impl_basic!(bool);
81impl_basic!(i8);
82impl_basic!(u8);
83impl_basic!(i16);
84impl_basic!(u16);
85impl_basic!(i32);
86impl_basic!(u32);
87impl_basic!(i64);
88impl_basic!(u64);
89impl_basic!(i128);
90impl_basic!(u128);
91impl_basic!(isize);
92impl_basic!(usize);
93impl_basic!(f32);
94impl_basic!(f64);
95impl_basic!(String);
96impl_basic!(std::net::IpAddr);
97impl_basic!(std::net::Ipv4Addr);
98impl_basic!(std::net::Ipv6Addr);
99impl_basic!(std::time::Duration);
100impl_basic!(std::time::SystemTime);
101impl_basic!(bytes::Bytes);
102
103impl Named for &'static str {
104    fn typename() -> &'static str {
105        "&str"
106    }
107}
108
109// A macro that implements type-keyed interning of typenames. This is useful
110// for implementing [`Named`] for generic types.
111#[doc(hidden)] // not part of the public API
112#[macro_export]
113macro_rules! intern_typename {
114    ($key:ty, $format_string:expr, $($args:ty),+) => {
115        {
116            static CACHE: std::sync::LazyLock<$crate::dashmap::DashMap<std::any::TypeId, &'static str>> =
117              std::sync::LazyLock::new($crate::dashmap::DashMap::new);
118
119            // Don't use entry, because typename() might re-enter intern_typename
120            // for nested types like Option<Option<T>>
121            let typeid = std::any::TypeId::of::<$key>();
122            if let Some(value) = CACHE.get(&typeid) {
123                *value
124            } else {
125                let typename = format!($format_string, $(<$args>::typename()),+).leak();
126                CACHE.insert(typeid, typename);
127                typename
128            }
129        }
130    };
131}
132
133macro_rules! tuple_format_string {
134    ($a:ident,) => { "{}" };
135    ($a:ident, $($rest_a:ident,)+) => { concat!("{}, ", tuple_format_string!($($rest_a,)+)) };
136}
137
138macro_rules! impl_tuple_peel {
139    ($name:ident, $($other:ident,)*) => (impl_tuple! { $($other,)* })
140}
141
142macro_rules! impl_tuple {
143    () => ();
144    ( $($name:ident,)+ ) => (
145        impl<$($name:Named + 'static),+> Named for ($($name,)+) {
146            fn typename() -> &'static str {
147                intern_typename!(Self, concat!("(", tuple_format_string!($($name,)+), ")"), $($name),+)
148            }
149        }
150        impl_tuple_peel! { $($name,)+ }
151    )
152}
153
154impl_tuple! { E, D, C, B, A, Z, Y, X, W, V, U, T, }
155
156impl<T: Named + 'static> Named for Option<T> {
157    fn typename() -> &'static str {
158        intern_typename!(Self, "Option<{}>", T)
159    }
160}
161
162impl<T: Named + 'static> Named for Vec<T> {
163    fn typename() -> &'static str {
164        intern_typename!(Self, "Vec<{}>", T)
165    }
166}
167
168impl<K: Named + 'static, V: Named + 'static> Named for HashMap<K, V> {
169    fn typename() -> &'static str {
170        intern_typename!(Self, "HashMap<{}, {}>", K, V)
171    }
172}
173
174impl<T: Named + 'static, E: Named + 'static> Named for Result<T, E> {
175    fn typename() -> &'static str {
176        intern_typename!(Self, "Result<{}, {}>", T, E)
177    }
178}
179
180impl<T: Named + 'static> Named for std::ops::Range<T> {
181    fn typename() -> &'static str {
182        intern_typename!(Self, "std::ops::Range<{}>", T)
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn test_names() {
192        assert_eq!(String::typename(), "String");
193        assert_eq!(Option::<String>::typename(), "Option<String>");
194        assert_eq!(Vec::<String>::typename(), "Vec<String>");
195        assert_eq!(Vec::<Vec::<String>>::typename(), "Vec<Vec<String>>");
196        assert_eq!(
197            Vec::<Vec::<Vec::<String>>>::typename(),
198            "Vec<Vec<Vec<String>>>"
199        );
200        assert_eq!(
201            <(u64, String, Option::<isize>)>::typename(),
202            "(u64, String, Option<isize>)"
203        );
204    }
205
206    #[test]
207    fn test_ports() {
208        assert_eq!(String::typehash(), 3947244799002047352u64);
209        assert_eq!(String::port(), 13170616835856823160u64);
210        assert_ne!(
211            Vec::<Vec::<Vec::<String>>>::typehash(),
212            Vec::<Vec::<Vec::<Vec::<String>>>>::typehash(),
213        );
214    }
215}