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}