Skip to main content

treentern/
lib.rs

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}