1#![deny(clippy::pedantic)]
2
3use std::{hash::Hash, marker::Sized};
4
5pub mod arena;
6
7pub trait Intern {
8 type InternedType: ?Sized;
9
10 fn intern(&'static self) -> Interned<Self::InternedType>;
11
12 fn intern_owned(self) -> Interned<Self::InternedType>
13 where
14 Self: Sized + 'static,
15 {
16 Box::leak(Box::new(self)).intern()
17 }
18}
19
20impl<T: ?Sized + Intern> Intern for &'static T {
21 type InternedType = T::InternedType;
22
23 fn intern(&'static self) -> Interned<Self::InternedType> {
24 T::intern(self)
25 }
26}
27
28#[derive(Debug, PartialOrd, Ord)]
29pub struct Interned<T: ?Sized + 'static>(&'static T);
30
31impl<T: ?Sized> PartialEq for Interned<T> {
32 fn eq(&self, other: &Self) -> bool {
33 std::ptr::eq(self.0, other.0)
34 }
35}
36
37impl<T: ?Sized> Eq for Interned<T> {}
38
39impl<T: ?Sized> Hash for Interned<T> {
40 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
41 std::ptr::from_ref(self.0).hash(state);
42 }
43}
44
45macro_rules! basic_impl {
46 ($ty:ty $(, $import:path)?) => {
47 paste::paste! {
48 mod [<$ty:snake _intern_impl>] {
49 use std::sync::LazyLock;
50 use crate::arena::Arena;
51 use crate::Intern;
52 use crate::Interned;
53 $(use $import :: $ty;)?
54
55 static ARENA: LazyLock<Arena<$ty>> = LazyLock::new(Arena::new);
56
57 impl Intern for $ty {
58 type InternedType = Self;
59
60 fn intern(&'static self) -> Interned<Self> {
61 ARENA.insert(self)
62 }
63 }
64 }
65 }
66 };
67}
68
69basic_impl!(String);
70basic_impl!(str);
71basic_impl!(OsString, std::ffi);
72basic_impl!(CString, std::ffi);
73basic_impl!(u64);
74basic_impl!(u128);
75basic_impl!(i64);
76basic_impl!(i128);
77
78#[cfg(test)]
79mod test {
80 use crate::Intern;
81
82 #[test]
83 fn intern_string() {
84 let a = String::from("Hello, World").intern_owned();
85 let b = String::from("Bonjour").intern_owned();
86 let c = String::from("Hello, World").intern_owned();
87
88 assert_ne!(a, b);
89 assert_eq!(a, c);
90
91 assert_eq!(a.0.as_ptr(), c.0.as_ptr());
92 }
93
94 #[test]
95 fn intern_str() {
96 let a = "Hello, World".intern();
97 let b = "Bonjour".intern();
98 let c = "Hello, World".intern();
99
100 assert_ne!(a, b);
101 assert_eq!(a, c);
102
103 assert_eq!(a.0.as_ptr(), c.0.as_ptr());
104 }
105}