intern_mint/borrow.rs
1use std::{
2 cmp::Ordering,
3 hash::{Hash, Hasher},
4 ops::Deref,
5};
6
7use crate::{
8 interned::{self, Interned},
9 pool::POOL,
10};
11
12#[derive(Eq)]
13#[repr(transparent)]
14/// &[BorrowedInterned] exists to pass around instead of cloning [Interned] instances when not
15/// needed, and in order to avoid passing &[Interned] which will require double-dereference to
16/// access the data
17///
18/// # Example
19///
20/// &[BorrowedInterned] can be used with hash-maps
21///
22/// Note that the pointer is being used for hashing and comparing
23/// (see [Hash] and [PartialEq] trait implementations)
24///
25/// As opposed to hashing and comparing the actual data - because the pointers are
26/// unique for the same data as long as it "lives" in memory
27///
28/// ```
29/// use intern_mint::{BorrowedInterned, Interned};
30///
31/// let map = std::collections::HashMap::<Interned, u64>::from_iter([(Interned::new(b"key"), 1)]);
32///
33/// let key = Interned::new(b"key");
34/// assert_eq!(map.get(&key), Some(&1));
35///
36/// let borrowed_key: &BorrowedInterned = &key;
37/// assert_eq!(map.get(borrowed_key), Some(&1));
38/// ```
39/// &[BorrowedInterned] can be used with btree-maps
40///
41/// ```
42/// use intern_mint::{BorrowedInterned, Interned};
43///
44/// let map = std::collections::BTreeMap::<Interned, u64>::from_iter([(Interned::new(b"key"), 1)]);
45///
46/// let key = Interned::new(b"key");
47/// assert_eq!(map.get(&key), Some(&1));
48///
49/// let borrowed_key: &BorrowedInterned = &key;
50/// assert_eq!(map.get(borrowed_key), Some(&1));
51/// ```
52pub struct BorrowedInterned([u8]);
53
54impl BorrowedInterned {
55 pub(crate) fn new(value: &[u8]) -> &BorrowedInterned {
56 unsafe { &*(value as *const [u8] as *const BorrowedInterned) }
57 }
58
59 /// Constructs back an [Interned] value from the given &[BorrowedInterned]
60 ///
61 /// Note that using this function has almost the same performance penalty as using
62 /// [Interned::new]
63 pub fn intern(&self) -> Interned {
64 Interned::from_existing(
65 POOL.get_from_existing_ref(self.deref())
66 .expect("borrowed values must already exist in the pool"),
67 )
68 }
69
70 /// The default [Hash] trait implementation for [BorrowedInterned] is to hash the pointer
71 /// instead of the data (for performance gains)
72 ///
73 /// This is allowed because the pointers are unique for the same data as long as it "lives" in
74 /// memory
75 ///
76 /// If for some reason you need the hash of the actual data, this function can be used
77 ///
78 /// # Example
79 ///
80 /// ```
81 /// use std::hash::{BuildHasher, Hasher};
82 ///
83 /// use intern_mint::Interned;
84 ///
85 /// let hash_builder = ahash::RandomState::new();
86 ///
87 /// let hash_data = |data: &Interned| {
88 /// let mut hasher = hash_builder.build_hasher();
89 /// data.hash_data(&mut hasher);
90 /// hasher.finish()
91 /// };
92 ///
93 /// let (ptr_hash_1, data_hash_1) = {
94 /// let interned = Interned::new(b"hello!");
95 /// (hash_builder.hash_one(&interned), hash_data(&interned))
96 /// };
97 ///
98 /// let (ptr_hash_2, data_hash_2) = {
99 /// let _a = Interned::new(b"more allocations");
100 /// let _a = Interned::new(b"to avoid");
101 /// let _a = Interned::new(b"same address");
102 ///
103 /// let interned = Interned::new(b"hello!");
104 /// (hash_builder.hash_one(&interned), hash_data(&interned))
105 /// };
106 ///
107 /// // The hash of the pointers is different, but the hash of the data is the same
108 /// assert_ne!(ptr_hash_1, ptr_hash_2);
109 /// assert_eq!(data_hash_1, data_hash_2);
110 /// ```
111 pub fn hash_data<H: Hasher>(&self, state: &mut H) {
112 self.deref().hash(state);
113 0u8.hash(state);
114 }
115}
116
117impl Default for &BorrowedInterned {
118 fn default() -> Self {
119 interned::DEFAULT.deref().as_ref()
120 }
121}
122
123impl Deref for BorrowedInterned {
124 type Target = [u8];
125
126 fn deref(&self) -> &Self::Target {
127 &self.0
128 }
129}
130
131impl PartialEq for BorrowedInterned {
132 fn eq(&self, other: &Self) -> bool {
133 std::ptr::addr_eq(self.as_ptr(), other.as_ptr())
134 }
135}
136
137impl Hash for BorrowedInterned {
138 fn hash<H: Hasher>(&self, state: &mut H) {
139 self.as_ptr().hash(state);
140 }
141}
142
143impl PartialOrd for BorrowedInterned {
144 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
145 Some(self.cmp(other))
146 }
147}
148
149impl Ord for BorrowedInterned {
150 fn cmp(&self, other: &Self) -> Ordering {
151 self.deref().cmp(other.deref())
152 }
153}
154
155impl ToOwned for BorrowedInterned {
156 type Owned = Interned;
157
158 fn to_owned(&self) -> Self::Owned {
159 self.intern()
160 }
161}