musli_zerocopy/swiss/
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 [`swiss::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 [`swiss::store_map`].
12//!
13//! [`swiss::store_map`]: crate::swiss::store_map
14
15use core::borrow::Borrow;
16use core::convert::identity as likely;
17use core::hash::{Hash, Hasher};
18use core::mem::size_of;
19
20use crate::buf::{Bindable, Buf, Visit};
21use crate::endian::{ByteOrder, Native};
22use crate::error::{Error, ErrorKind};
23use crate::pointer::{DefaultSize, Ref, Size};
24use crate::sip::SipHasher13;
25use crate::swiss::Entry;
26use crate::swiss::raw::{Group, h2, probe_seq};
27use crate::{Endian, ZeroCopy};
28
29/// A map bound to a [`Buf`] through [`Buf::bind`] for convenience.
30///
31/// ## Examples
32///
33/// ```
34/// use musli_zerocopy::OwnedBuf;
35/// use musli_zerocopy::swiss;
36///
37/// let mut buf = OwnedBuf::new();
38///
39/// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
40/// let map = buf.bind(map)?;
41///
42/// assert_eq!(map.get(&1)?, Some(&2));
43/// assert_eq!(map.get(&2)?, Some(&3));
44/// assert_eq!(map.get(&3)?, None);
45///
46/// assert!(map.contains_key(&1)?);
47/// assert!(!map.contains_key(&3)?);
48/// # Ok::<_, musli_zerocopy::Error>(())
49/// ```
50pub struct Map<'a, K, V> {
51    key: u64,
52    table: RawTable<'a, Entry<K, V>>,
53    buf: &'a Buf,
54}
55
56impl<K, V> Map<'_, K, V>
57where
58    K: ZeroCopy,
59    V: ZeroCopy,
60{
61    /// Get a value from the map.
62    ///
63    /// ## Examples
64    ///
65    /// ```
66    /// use musli_zerocopy::OwnedBuf;
67    /// use musli_zerocopy::swiss;
68    ///
69    /// let mut buf = OwnedBuf::new();
70    ///
71    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
72    /// let map = buf.bind(map)?;
73    ///
74    /// assert_eq!(map.get(&1)?, Some(&2));
75    /// assert_eq!(map.get(&2)?, Some(&3));
76    /// assert_eq!(map.get(&3)?, None);
77    /// # Ok::<_, musli_zerocopy::Error>(())
78    /// ```
79    pub fn get<Q>(&self, key: &Q) -> Result<Option<&V>, Error>
80    where
81        Q: ?Sized + Visit,
82        Q::Target: Eq + Hash,
83        K: Visit,
84        K::Target: Borrow<Q::Target>,
85    {
86        let hash = key.visit(self.buf, |k| self.hash(k))?;
87
88        let entry = self.table.find(hash, |e| {
89            key.visit(self.buf, |b| e.key.visit(self.buf, |a| a.borrow() == b))?
90        })?;
91
92        if let Some(entry) = entry {
93            Ok(Some(&entry.value))
94        } else {
95            Ok(None)
96        }
97    }
98
99    /// Get the length of the map.
100    ///
101    /// ## Examples
102    ///
103    /// ```
104    /// use musli_zerocopy::OwnedBuf;
105    /// use musli_zerocopy::swiss;
106    ///
107    /// let mut buf = OwnedBuf::new();
108    ///
109    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
110    /// let map = buf.bind(map)?;
111    ///
112    /// assert_eq!(map.len(), 2);
113    /// # Ok::<_, musli_zerocopy::Error>(())
114    /// ```
115    #[inline]
116    pub fn len(&self) -> usize {
117        self.table.len
118    }
119
120    /// Test if the map is empty.
121    ///
122    /// ## Examples
123    ///
124    /// ```
125    /// use musli_zerocopy::OwnedBuf;
126    /// use musli_zerocopy::swiss;
127    ///
128    /// let mut buf = OwnedBuf::new();
129    ///
130    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
131    /// let map = buf.bind(map)?;
132    ///
133    /// assert!(!map.is_empty());
134    /// # Ok::<_, musli_zerocopy::Error>(())
135    /// ```
136    #[inline]
137    pub fn is_empty(&self) -> bool {
138        self.table.len == 0
139    }
140
141    /// Test if the map contains the given `key`.
142    ///
143    /// ## Examples
144    ///
145    /// ```
146    /// use musli_zerocopy::OwnedBuf;
147    /// use musli_zerocopy::swiss;
148    ///
149    /// let mut buf = OwnedBuf::new();
150    ///
151    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
152    /// let map = buf.bind(map)?;
153    ///
154    /// assert!(map.contains_key(&1)?);
155    /// assert!(map.contains_key(&2)?);
156    /// assert!(!map.contains_key(&3)?);
157    /// # Ok::<_, musli_zerocopy::Error>(())
158    /// ```
159    pub fn contains_key<Q>(&self, key: &Q) -> Result<bool, Error>
160    where
161        Q: ?Sized + Visit,
162        Q::Target: Eq + Hash,
163        K: Visit,
164        K::Target: Borrow<Q::Target>,
165    {
166        let hash = key.visit(self.buf, |k| self.hash(k))?;
167
168        let entry = self.table.find(hash, |e| {
169            key.visit(self.buf, |b| e.key.visit(self.buf, |a| a.borrow() == b))?
170        })?;
171
172        Ok(entry.is_some())
173    }
174
175    fn hash<H>(&self, value: &H) -> u64
176    where
177        H: ?Sized + Hash,
178    {
179        let mut hasher = SipHasher13::new_with_keys(0, self.key);
180        value.hash(&mut hasher);
181        hasher.finish()
182    }
183}
184
185/// Bind a [`MapRef`] into a [`Map`].
186impl<K, V, E, O> Bindable for MapRef<K, V, E, O>
187where
188    K: ZeroCopy,
189    V: ZeroCopy,
190    E: ByteOrder,
191    O: Size,
192{
193    type Bound<'a>
194        = Map<'a, K, V>
195    where
196        Self: 'a;
197
198    #[inline]
199    fn bind(self, buf: &Buf) -> Result<Self::Bound<'_>, Error> {
200        Ok(Map {
201            key: self.key.to_ne(),
202            table: self.table.bind(buf)?,
203            buf,
204        })
205    }
206}
207
208/// A stored reference to a map.
209///
210/// Note that operating over the methods provided in [`MapRef`] does not demand
211/// that the entire contents of the set is validated as would be the case when
212/// [`bind()`] is used and might result in better performance if the data is
213/// infrequently accessed.
214///
215/// Constructed through [`swiss::store_map`].
216///
217/// [`swiss::store_map`]: crate::swiss::store_map
218/// [`bind()`]: crate::buf::Buf::bind
219///
220/// ## Examples
221///
222/// ```
223/// use musli_zerocopy::OwnedBuf;
224/// use musli_zerocopy::swiss;
225///
226/// let mut buf = OwnedBuf::new();
227///
228/// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
229///
230/// assert_eq!(map.get(&buf, &1)?, Some(&2));
231/// assert_eq!(map.get(&buf, &2)?, Some(&3));
232/// assert_eq!(map.get(&buf, &3)?, None);
233///
234/// assert!(map.contains_key(&buf, &1)?);
235/// assert!(!map.contains_key(&buf, &3)?);
236/// # Ok::<_, musli_zerocopy::Error>(())
237/// ```
238#[derive(Debug, ZeroCopy)]
239#[repr(C)]
240#[zero_copy(crate)]
241pub struct MapRef<K, V, E = Native, O = DefaultSize>
242where
243    K: ZeroCopy,
244    V: ZeroCopy,
245    E: ByteOrder,
246    O: Size,
247{
248    key: Endian<u64, E>,
249    table: RawTableRef<Entry<K, V>, E, O>,
250}
251
252impl<K, V, E, O> MapRef<K, V, E, O>
253where
254    K: ZeroCopy,
255    V: ZeroCopy,
256    E: ByteOrder,
257    O: Size,
258{
259    #[cfg(feature = "alloc")]
260    pub(crate) fn new(key: u64, table: RawTableRef<Entry<K, V>, E, O>) -> Self {
261        Self {
262            key: Endian::new(key),
263            table,
264        }
265    }
266
267    /// Get a value from the map.
268    ///
269    /// ## Examples
270    ///
271    /// ```
272    /// use musli_zerocopy::OwnedBuf;
273    /// use musli_zerocopy::swiss;
274    ///
275    /// let mut buf = OwnedBuf::new();
276    ///
277    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
278    ///
279    /// assert_eq!(map.get(&buf, &1)?, Some(&2));
280    /// assert_eq!(map.get(&buf, &2)?, Some(&3));
281    /// assert_eq!(map.get(&buf, &3)?, None);
282    /// # Ok::<_, musli_zerocopy::Error>(())
283    /// ```
284    pub fn get<'a, Q>(&self, buf: &'a Buf, key: &Q) -> Result<Option<&'a V>, Error>
285    where
286        Q: ?Sized + Visit,
287        Q::Target: Eq + Hash,
288        K: 'a + Visit,
289        K::Target: Borrow<Q::Target>,
290    {
291        let hash = key.visit(buf, |k| self.hash(k))?;
292
293        let entry = self.table.find(buf, hash, |e| {
294            key.visit(buf, |b| e.key.visit(buf, |a| a.borrow() == b))?
295        })?;
296
297        if let Some(entry) = entry {
298            Ok(Some(&entry.value))
299        } else {
300            Ok(None)
301        }
302    }
303
304    /// Get the length of the map.
305    ///
306    /// ## Examples
307    ///
308    /// ```
309    /// use musli_zerocopy::OwnedBuf;
310    /// use musli_zerocopy::swiss;
311    ///
312    /// let mut buf = OwnedBuf::new();
313    ///
314    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
315    ///
316    /// assert_eq!(map.len(), 2);
317    /// # Ok::<_, musli_zerocopy::Error>(())
318    /// ```
319    #[inline]
320    pub fn len(&self) -> usize {
321        self.table.len.to_ne()
322    }
323
324    /// Test if the map is empty.
325    ///
326    /// ## Examples
327    ///
328    /// ```
329    /// use musli_zerocopy::OwnedBuf;
330    /// use musli_zerocopy::swiss;
331    ///
332    /// let mut buf = OwnedBuf::new();
333    ///
334    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
335    ///
336    /// assert!(!map.is_empty());
337    /// # Ok::<_, musli_zerocopy::Error>(())
338    /// ```
339    #[inline]
340    pub fn is_empty(&self) -> bool {
341        self.table.len.to_ne() == 0
342    }
343
344    /// Test if the map contains the given `key`.
345    ///
346    /// ## Examples
347    ///
348    /// ```
349    /// use musli_zerocopy::OwnedBuf;
350    /// use musli_zerocopy::swiss;
351    ///
352    /// let mut buf = OwnedBuf::new();
353    ///
354    /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
355    ///
356    /// assert!(map.contains_key(&buf, &1)?);
357    /// assert!(map.contains_key(&buf, &2)?);
358    /// assert!(!map.contains_key(&buf, &3)?);
359    /// # Ok::<_, musli_zerocopy::Error>(())
360    /// ```
361    pub fn contains_key<Q>(&self, buf: &Buf, key: &Q) -> Result<bool, Error>
362    where
363        Q: ?Sized + Visit,
364        Q::Target: Eq + Hash,
365        K: Visit,
366        K::Target: Borrow<Q::Target>,
367    {
368        let hash = key.visit(buf, |key| self.hash(key))?;
369
370        let entry = self.table.find(buf, hash, |e| {
371            key.visit(buf, |b| e.key.visit(buf, |a| a.borrow() == b))?
372        })?;
373
374        Ok(entry.is_some())
375    }
376
377    #[inline]
378    fn hash<H>(&self, value: &H) -> u64
379    where
380        H: ?Sized + Hash,
381    {
382        let mut hasher = SipHasher13::new_with_keys(0, self.key.to_ne());
383        value.hash(&mut hasher);
384        hasher.finish()
385    }
386}
387
388impl<K, V, E, O> Clone for MapRef<K, V, E, O>
389where
390    K: ZeroCopy,
391    V: ZeroCopy,
392    E: ByteOrder,
393    O: Size,
394{
395    fn clone(&self) -> Self {
396        *self
397    }
398}
399
400impl<K, V, E, O> Copy for MapRef<K, V, E, O>
401where
402    K: ZeroCopy,
403    V: ZeroCopy,
404    E: ByteOrder,
405    O: Size,
406{
407}
408
409pub(crate) struct RawTable<'a, T> {
410    ctrl: &'a [u8],
411    entries: &'a [T],
412    bucket_mask: usize,
413    len: usize,
414}
415
416impl<'a, T> RawTable<'a, T> {
417    /// Searches for an element in the table.
418    #[inline]
419    pub(crate) fn find(
420        &self,
421        hash: u64,
422        mut eq: impl FnMut(&T) -> Result<bool, Error>,
423    ) -> Result<Option<&'a T>, Error> {
424        let result = self.find_inner(hash, &mut |index| {
425            let entry = self.entry(index)?;
426            eq(entry)
427        })?;
428
429        Ok(match result {
430            Some(index) => Some(self.entry(index)?),
431            None => None,
432        })
433    }
434
435    fn entry(&self, index: usize) -> Result<&'a T, Error> {
436        let Some(entry) = self.entries.get(index) else {
437            return Err(Error::new(ErrorKind::IndexOutOfBounds {
438                index,
439                len: self.entries.len(),
440            }));
441        };
442
443        Ok(entry)
444    }
445
446    /// Searches for an element in a table, returning the `index` of the found
447    /// element.
448    #[inline(always)]
449    fn find_inner(
450        &self,
451        hash: u64,
452        eq: &mut dyn FnMut(usize) -> Result<bool, Error>,
453    ) -> Result<Option<usize>, Error> {
454        let h2_hash = h2(hash);
455        let mut probe_seq = probe_seq(self.bucket_mask, hash);
456
457        loop {
458            let range = probe_seq.pos..probe_seq.pos + size_of::<Group>();
459
460            let Some(bytes) = self.ctrl.get(range.clone()) else {
461                return Err(Error::new(ErrorKind::ControlRangeOutOfBounds {
462                    range,
463                    len: self.ctrl.len(),
464                }));
465            };
466
467            // SAFETY: We've made sure to provide this load with a buffer of the
468            // appropriate size.
469            let group = unsafe { Group::load(bytes.as_ptr()) };
470
471            for bit in group.match_byte(h2_hash) {
472                // This is the same as `(probe_seq.pos + bit) % self.buckets()` because the number
473                // of buckets is a power of two, and `self.bucket_mask = self.buckets() - 1`.
474                let index = (probe_seq.pos + bit) & self.bucket_mask;
475
476                if likely(eq(index)?) {
477                    return Ok(Some(index));
478                }
479            }
480
481            if likely(group.match_empty().any_bit_set()) {
482                return Ok(None);
483            }
484
485            probe_seq.move_next(self.bucket_mask)?;
486        }
487    }
488}
489
490#[derive(Debug, ZeroCopy)]
491#[repr(C)]
492#[zero_copy(crate)]
493pub(crate) struct RawTableRef<T, E = Native, O = DefaultSize>
494where
495    T: ZeroCopy,
496    E: ByteOrder,
497    O: Size,
498{
499    ctrl: Ref<[u8], E, O>,
500    entries: Ref<[T], E, O>,
501    bucket_mask: Endian<usize, E>,
502    len: Endian<usize, E>,
503}
504
505impl<T, E, O> RawTableRef<T, E, O>
506where
507    T: ZeroCopy,
508    E: ByteOrder,
509    O: Size,
510{
511    #[cfg(feature = "alloc")]
512    #[inline]
513    pub(crate) fn new(
514        ctrl: Ref<[u8], E, O>,
515        entries: Ref<[T], E, O>,
516        bucket_mask: usize,
517        len: usize,
518    ) -> Self {
519        Self {
520            ctrl,
521            entries,
522            bucket_mask: Endian::new(bucket_mask),
523            len: Endian::new(len),
524        }
525    }
526
527    #[inline]
528    pub(crate) fn bind<'buf>(&self, buf: &'buf Buf) -> Result<RawTable<'buf, T>, Error> {
529        Ok(RawTable {
530            ctrl: buf.load(self.ctrl)?,
531            entries: buf.load(self.entries)?,
532            bucket_mask: self.bucket_mask.to_ne(),
533            len: self.len.to_ne(),
534        })
535    }
536
537    /// Searches for an element in the table.
538    #[inline]
539    pub(crate) fn find<'buf>(
540        &self,
541        buf: &'buf Buf,
542        hash: u64,
543        mut eq: impl FnMut(&T) -> Result<bool, Error>,
544    ) -> Result<Option<&'buf T>, Error> {
545        let result = self.find_inner(buf, hash, &mut |index| eq(self.entry(index, buf)?))?;
546
547        Ok(match result {
548            Some(index) => Some(self.entry(index, buf)?),
549            None => None,
550        })
551    }
552
553    fn entry<'buf>(&self, index: usize, buf: &'buf Buf) -> Result<&'buf T, Error> {
554        let Some(entry) = self.entries.get(index) else {
555            return Err(Error::new(ErrorKind::IndexOutOfBounds {
556                index,
557                len: self.entries.len(),
558            }));
559        };
560
561        buf.load(entry)
562    }
563
564    /// Searches for an element in a table, returning the `index` of the found
565    /// element.
566    #[inline(always)]
567    fn find_inner(
568        &self,
569        buf: &Buf,
570        hash: u64,
571        eq: &mut dyn FnMut(usize) -> Result<bool, Error>,
572    ) -> Result<Option<usize>, Error> {
573        let h2_hash = h2(hash);
574        let mut probe_seq = probe_seq(self.bucket_mask.to_ne(), hash);
575
576        let ctrl = buf.load(self.ctrl)?;
577
578        loop {
579            let range = probe_seq.pos..probe_seq.pos + size_of::<Group>();
580
581            let Some(bytes) = ctrl.get(range.clone()) else {
582                return Err(Error::new(ErrorKind::ControlRangeOutOfBounds {
583                    range,
584                    len: self.ctrl.len(),
585                }));
586            };
587
588            let group = unsafe { Group::load(bytes.as_ptr()) };
589
590            for bit in group.match_byte(h2_hash) {
591                // This is the same as `(probe_seq.pos + bit) % self.buckets()` because the number
592                // of buckets is a power of two, and `self.bucket_mask = self.buckets() - 1`.
593                let index = (probe_seq.pos + bit) & self.bucket_mask.to_ne();
594
595                if likely(eq(index)?) {
596                    return Ok(Some(index));
597                }
598            }
599
600            if likely(group.match_empty().any_bit_set()) {
601                return Ok(None);
602            }
603
604            probe_seq.move_next(self.bucket_mask.to_ne())?;
605        }
606    }
607}
608
609impl<T, E, O> Clone for RawTableRef<T, E, O>
610where
611    T: ZeroCopy,
612    E: ByteOrder,
613    O: Size,
614{
615    fn clone(&self) -> Self {
616        *self
617    }
618}
619
620impl<T, E, O> Copy for RawTableRef<T, E, O>
621where
622    T: ZeroCopy,
623    E: ByteOrder,
624    O: Size,
625{
626}