musli_zerocopy/phf/map.rs
1//! A map which implements a hash-map like interface, where values can be looked
2//! up by keys.
3//!
4//! This map are implemented using a perfect hash functions, and are inserted
5//! into a buffering using [`phf::store_map`].
6//!
7//! There's two types provided by this module:
8//! * [`Map<K, V>`] which is a *bound* reference to a map, providing a
9//! convenient map-like access.
10//! * [`MapRef<K, V>`] which is the *pointer* of the map. This is what you store
11//! in [`ZeroCopy`] types and is what is returned by [`phf::store_map`].
12//!
13//! [`phf::store_map`]: crate::phf::store_map
14
15use core::borrow::Borrow;
16use core::hash::Hash;
17
18use crate::buf::{Bindable, Buf, Visit};
19use crate::endian::{ByteOrder, Native};
20use crate::error::Error;
21use crate::phf::Entry;
22use crate::phf::hashing::HashKey;
23use crate::pointer::{DefaultSize, Ref, Size};
24use crate::{Endian, ZeroCopy};
25
26/// A map bound to a [`Buf`] through [`Buf::bind`] for convenience.
27///
28/// ## Examples
29///
30/// ```
31/// use musli_zerocopy::OwnedBuf;
32/// use musli_zerocopy::phf;
33///
34/// let mut buf = OwnedBuf::new();
35///
36/// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
37/// let map = buf.bind(map)?;
38///
39/// assert_eq!(map.get(&1)?, Some(&2));
40/// assert_eq!(map.get(&2)?, Some(&3));
41/// assert_eq!(map.get(&3)?, None);
42///
43/// assert!(map.contains_key(&1)?);
44/// assert!(!map.contains_key(&3)?);
45/// # Ok::<_, musli_zerocopy::Error>(())
46/// ```
47pub struct Map<'a, K, V> {
48 key: HashKey,
49 entries: &'a [Entry<K, V>],
50 displacements: &'a [Entry<u32, u32>],
51 buf: &'a Buf,
52}
53
54impl<K, V> Map<'_, K, V>
55where
56 K: ZeroCopy,
57 V: ZeroCopy,
58{
59 /// Get a value from the map.
60 ///
61 /// ## Examples
62 ///
63 /// ```
64 /// use musli_zerocopy::OwnedBuf;
65 /// use musli_zerocopy::phf;
66 ///
67 /// let mut buf = OwnedBuf::new();
68 ///
69 /// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
70 /// let map = buf.bind(map)?;
71 ///
72 /// assert_eq!(map.get(&1)?, Some(&2));
73 /// assert_eq!(map.get(&2)?, Some(&3));
74 /// assert_eq!(map.get(&3)?, None);
75 /// # Ok::<_, musli_zerocopy::Error>(())
76 /// ```
77 pub fn get<T>(&self, key: &T) -> Result<Option<&V>, Error>
78 where
79 T: ?Sized + Visit,
80 T::Target: Eq + Hash,
81 K: Visit,
82 K::Target: Borrow<T::Target>,
83 {
84 let Some(entry) = self.get_entry(key)? else {
85 return Ok(None);
86 };
87
88 Ok(Some(entry.1))
89 }
90
91 /// Test if the map contains the given `key`.
92 ///
93 /// ## Examples
94 ///
95 /// ```
96 /// use musli_zerocopy::OwnedBuf;
97 /// use musli_zerocopy::phf;
98 ///
99 /// let mut buf = OwnedBuf::new();
100 ///
101 /// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
102 /// let map = buf.bind(map)?;
103 ///
104 /// assert!(map.contains_key(&1)?);
105 /// assert!(map.contains_key(&2)?);
106 /// assert!(!map.contains_key(&3)?);
107 /// # Ok::<_, musli_zerocopy::Error>(())
108 /// ```
109 pub fn contains_key<T>(&self, key: &T) -> Result<bool, Error>
110 where
111 T: ?Sized + Visit,
112 T::Target: Eq + Hash,
113 K: Visit,
114 K::Target: Borrow<T::Target>,
115 {
116 Ok(self.get_entry(key)?.is_some())
117 }
118
119 /// Get an entry from the map.
120 ///
121 /// ## Examples
122 ///
123 /// ```
124 /// use musli_zerocopy::OwnedBuf;
125 /// use musli_zerocopy::phf;
126 ///
127 /// let mut buf = OwnedBuf::new();
128 ///
129 /// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
130 /// let map = buf.bind(map)?;
131 ///
132 /// assert_eq!(map.get_entry(&1)?, Some((&1, &2)));
133 /// assert_eq!(map.get_entry(&2)?, Some((&2, &3)));
134 /// assert_eq!(map.get_entry(&3)?, None);
135 /// # Ok::<_, musli_zerocopy::Error>(())
136 /// ```
137 pub fn get_entry<T>(&self, key: &T) -> Result<Option<(&K, &V)>, Error>
138 where
139 T: ?Sized + Visit,
140 T::Target: Eq + Hash,
141 K: Visit,
142 K::Target: Borrow<T::Target>,
143 {
144 if self.displacements.is_empty() {
145 return Ok(None);
146 }
147
148 let hashes = crate::phf::hashing::hash(self.buf, key, &self.key)?;
149 let index =
150 crate::phf::hashing::get_index(&hashes, self.displacements, self.entries.len())?;
151
152 let Some(e) = self.entries.get(index) else {
153 return Ok(None);
154 };
155
156 if key.visit(self.buf, |b| e.key.visit(self.buf, |a| a.borrow() == b))?? {
157 Ok(Some((&e.key, &e.value)))
158 } else {
159 Ok(None)
160 }
161 }
162}
163
164/// Bind a [`MapRef`] into a [`Map`].
165impl<K, V, E, O> Bindable for MapRef<K, V, E, O>
166where
167 K: ZeroCopy,
168 V: ZeroCopy,
169 E: ByteOrder,
170 O: Size,
171{
172 type Bound<'a>
173 = Map<'a, K, V>
174 where
175 Self: 'a;
176
177 #[inline]
178 fn bind(self, buf: &Buf) -> Result<Self::Bound<'_>, Error> {
179 Ok(Map {
180 key: self.key.to_ne(),
181 entries: buf.load(self.entries)?,
182 displacements: buf.load(self.displacements)?,
183 buf,
184 })
185 }
186}
187
188/// A stored reference to a map.
189///
190/// Note that operating over the methods provided in [`MapRef`] does not demand
191/// that the entire contents of the set is validated as would be the case when
192/// [`bind()`] is used and might result in better performance if the data is
193/// infrequently accessed.
194///
195/// Constructed through [`phf::store_map`].
196///
197/// [`phf::store_map`]: crate::phf::store_map
198/// [`bind()`]: crate::buf::Buf::bind
199///
200/// ## Examples
201///
202/// ```
203/// use musli_zerocopy::OwnedBuf;
204/// use musli_zerocopy::phf;
205///
206/// let mut buf = OwnedBuf::new();
207///
208/// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
209///
210/// assert_eq!(map.get(&buf, &1)?, Some(&2));
211/// assert_eq!(map.get(&buf, &2)?, Some(&3));
212/// assert_eq!(map.get(&buf, &3)?, None);
213///
214/// assert!(map.contains_key(&buf, &1)?);
215/// assert!(!map.contains_key(&buf, &3)?);
216/// # Ok::<_, musli_zerocopy::Error>(())
217/// ```
218#[derive(Debug, ZeroCopy)]
219#[repr(C)]
220#[zero_copy(crate)]
221pub struct MapRef<K, V, E = Native, O = DefaultSize>
222where
223 K: ZeroCopy,
224 V: ZeroCopy,
225 E: ByteOrder,
226 O: Size,
227{
228 key: Endian<HashKey, E>,
229 entries: Ref<[Entry<K, V>], E, O>,
230 displacements: Ref<[Entry<u32, u32>], E, O>,
231}
232
233impl<K, V, E, O> MapRef<K, V, E, O>
234where
235 K: ZeroCopy,
236 V: ZeroCopy,
237 E: ByteOrder,
238 O: Size,
239{
240 #[cfg(feature = "alloc")]
241 pub(crate) fn new(
242 key: HashKey,
243 entries: Ref<[Entry<K, V>], E, O>,
244 displacements: Ref<[Entry<u32, u32>], E, O>,
245 ) -> Self {
246 Self {
247 key: Endian::new(key),
248 entries,
249 displacements,
250 }
251 }
252}
253
254impl<K, V, E, O> MapRef<K, V, E, O>
255where
256 K: ZeroCopy,
257 V: ZeroCopy,
258 E: ByteOrder,
259 O: Size,
260{
261 /// Get a value from the map.
262 ///
263 /// ## Examples
264 ///
265 /// ```
266 /// use musli_zerocopy::OwnedBuf;
267 /// use musli_zerocopy::phf;
268 ///
269 /// let mut buf = OwnedBuf::new();
270 ///
271 /// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
272 ///
273 /// assert_eq!(map.get(&buf, &1)?, Some(&2));
274 /// assert_eq!(map.get(&buf, &2)?, Some(&3));
275 /// assert_eq!(map.get(&buf, &3)?, None);
276 /// # Ok::<_, musli_zerocopy::Error>(())
277 /// ```
278 pub fn get<'a, T>(&self, buf: &'a Buf, key: &T) -> Result<Option<&'a V>, Error>
279 where
280 T: ?Sized + Visit,
281 T::Target: Eq + Hash,
282 K: 'a + Visit,
283 K::Target: Borrow<T::Target>,
284 {
285 let Some(entry) = self.get_entry(buf, key)? else {
286 return Ok(None);
287 };
288
289 Ok(Some(entry.1))
290 }
291
292 /// Test if the map contains the given `key`.
293 ///
294 /// ## Examples
295 ///
296 /// ```
297 /// use musli_zerocopy::OwnedBuf;
298 /// use musli_zerocopy::phf;
299 ///
300 /// let mut buf = OwnedBuf::new();
301 ///
302 /// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
303 ///
304 /// assert!(map.contains_key(&buf, &1)?);
305 /// assert!(map.contains_key(&buf, &2)?);
306 /// assert!(!map.contains_key(&buf, &3)?);
307 /// # Ok::<_, musli_zerocopy::Error>(())
308 /// ```
309 pub fn contains_key<T>(&self, buf: &Buf, key: &T) -> Result<bool, Error>
310 where
311 T: ?Sized + Visit,
312 T::Target: Eq + Hash,
313 K: Visit,
314 K::Target: Borrow<T::Target>,
315 {
316 Ok(self.get_entry(buf, key)?.is_some())
317 }
318
319 /// Get an entry from the map.
320 ///
321 /// ## Examples
322 ///
323 /// ```
324 /// use musli_zerocopy::OwnedBuf;
325 /// use musli_zerocopy::phf;
326 ///
327 /// let mut buf = OwnedBuf::new();
328 ///
329 /// let map = phf::store_map(&mut buf, [(1, 2), (2, 3)])?;
330 ///
331 /// assert_eq!(map.get_entry(&buf, &1)?, Some((&1, &2)));
332 /// assert_eq!(map.get_entry(&buf, &2)?, Some((&2, &3)));
333 /// assert_eq!(map.get_entry(&buf, &3)?, None);
334 /// # Ok::<_, musli_zerocopy::Error>(())
335 /// ```
336 pub fn get_entry<'a, T>(&self, buf: &'a Buf, key: &T) -> Result<Option<(&'a K, &'a V)>, Error>
337 where
338 T: ?Sized + Visit,
339 T::Target: Eq + Hash,
340 K: 'a + Visit,
341 K::Target: Borrow<T::Target>,
342 {
343 if self.displacements.is_empty() {
344 return Ok(None);
345 }
346
347 let hashes = crate::phf::hashing::hash(buf, key, &self.key.to_ne())?;
348
349 let displacements = |index| match self.displacements.get(index) {
350 Some(entry) => Ok(Some(buf.load(entry)?)),
351 None => Ok(None),
352 };
353
354 let index = crate::phf::hashing::get_custom_index(
355 &hashes,
356 displacements,
357 self.displacements.len(),
358 self.entries.len(),
359 )?;
360
361 let Some(e) = self.entries.get(index) else {
362 return Ok(None);
363 };
364
365 let e = buf.load(e)?;
366
367 if key.visit(buf, |b| e.key.visit(buf, |a| a.borrow() == b))?? {
368 Ok(Some((&e.key, &e.value)))
369 } else {
370 Ok(None)
371 }
372 }
373}
374
375impl<K, V, E, O> Clone for MapRef<K, V, E, O>
376where
377 K: ZeroCopy,
378 V: ZeroCopy,
379 E: ByteOrder,
380 O: Size,
381{
382 fn clone(&self) -> Self {
383 *self
384 }
385}
386
387impl<K, V, E, O> Copy for MapRef<K, V, E, O>
388where
389 K: ZeroCopy,
390 V: ZeroCopy,
391 E: ByteOrder,
392 O: Size,
393{
394}