musli_zerocopy/phf/
set.rs

1//! A set which implements a hash-set like interface, where values can be looked
2//! up by keys.
3//!
4//! This set are implemented using a perfect hash functions, and are inserted
5//! into a buffering using [`phf::store_set`].
6//!
7//! There's two types provided by this module:
8//! * [`Set<T>`] which is a *bound* reference to a set, providing a convenient
9//!   set-like access.
10//! * [`SetRef<T>`] which is the *pointer* of the set. This is what you store in
11//!   [`ZeroCopy`] types and is what is returned by [`phf::store_set`].
12//!
13//! [`phf::store_set`]: crate::phf::store_set
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::hashing::HashKey;
22use crate::phf::Entry;
23use crate::pointer::{DefaultSize, Ref, Size};
24use crate::{Endian, ZeroCopy};
25
26/// A set 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 set = phf::store_set(&mut buf, [1, 2])?;
37/// let set = buf.bind(set)?;
38///
39/// assert!(set.contains(&1)?);
40/// assert!(set.contains(&2)?);
41/// assert!(!set.contains(&3)?);
42/// # Ok::<_, musli_zerocopy::Error>(())
43/// ```
44pub struct Set<'a, T> {
45    key: HashKey,
46    entries: &'a [T],
47    displacements: &'a [Entry<u32, u32>],
48    buf: &'a Buf,
49}
50
51impl<T> Set<'_, T>
52where
53    T: ZeroCopy,
54{
55    /// Get a value from the set.
56    ///
57    /// ## Examples
58    ///
59    /// ```
60    /// use musli_zerocopy::OwnedBuf;
61    /// use musli_zerocopy::phf;
62    ///
63    /// let mut buf = OwnedBuf::new();
64    ///
65    /// let set = phf::store_set(&mut buf, [1, 2])?;
66    /// let set = buf.bind(set)?;
67    ///
68    /// assert!(set.contains(&1)?);
69    /// assert!(set.contains(&2)?);
70    /// assert!(!set.contains(&3)?);
71    /// # Ok::<_, musli_zerocopy::Error>(())
72    /// ```
73    pub fn contains<Q>(&self, key: &Q) -> Result<bool, Error>
74    where
75        Q: ?Sized + Visit,
76        Q::Target: Eq + Hash,
77        T: Visit,
78        T::Target: Borrow<Q::Target>,
79    {
80        if self.displacements.is_empty() {
81            return Ok(false);
82        }
83
84        let hashes = crate::phf::hashing::hash(self.buf, key, &self.key)?;
85        let index =
86            crate::phf::hashing::get_index(&hashes, self.displacements, self.entries.len())?;
87
88        let Some(e) = self.entries.get(index) else {
89            return Ok(false);
90        };
91
92        key.visit(self.buf, |b| e.visit(self.buf, |a| a.borrow() == b))?
93    }
94}
95
96/// Bind a [`SetRef`] into a [`Set`].
97impl<T, E, O> Bindable for SetRef<T, E, O>
98where
99    T: ZeroCopy,
100    E: ByteOrder,
101    O: Size,
102{
103    type Bound<'a>
104        = Set<'a, T>
105    where
106        Self: 'a;
107
108    #[inline]
109    fn bind(self, buf: &Buf) -> Result<Self::Bound<'_>, Error> {
110        Ok(Set {
111            key: self.key.to_ne(),
112            entries: buf.load(self.entries)?,
113            displacements: buf.load(self.displacements)?,
114            buf,
115        })
116    }
117}
118
119/// A stored reference to a set.
120///
121/// Note that operating over the methods provided in [`SetRef`] does not demand
122/// that the entire contents of the set is validated as would be the case when
123/// [`bind()`] is used and might result in better performance if the data is
124/// infrequently accessed.
125///
126/// Constructed through [`phf::store_set`].
127///
128/// [`phf::store_set`]: crate::phf::store_set
129/// [`bind()`]: crate::buf::Buf::bind
130///
131/// ## Examples
132///
133/// ```
134/// use musli_zerocopy::OwnedBuf;
135/// use musli_zerocopy::phf;
136///
137/// let mut buf = OwnedBuf::new();
138///
139/// let set = phf::store_set(&mut buf, [1, 2])?;
140///
141/// assert!(set.contains(&buf, &1)?);
142/// assert!(set.contains(&buf, &2)?);
143/// assert!(!set.contains(&buf, &3)?);
144/// # Ok::<_, musli_zerocopy::Error>(())
145/// ```
146#[derive(Debug, ZeroCopy)]
147#[repr(C)]
148#[zero_copy(crate)]
149pub struct SetRef<T, E = Native, O = DefaultSize>
150where
151    T: ZeroCopy,
152    E: ByteOrder,
153    O: Size,
154{
155    key: Endian<HashKey, E>,
156    entries: Ref<[T], E, O>,
157    displacements: Ref<[Entry<u32, u32>], E, O>,
158}
159
160impl<T, E, O> SetRef<T, E, O>
161where
162    T: ZeroCopy,
163    E: ByteOrder,
164    O: Size,
165{
166    #[cfg(feature = "alloc")]
167    #[inline]
168    pub(crate) fn new(
169        key: HashKey,
170        entries: Ref<[T], E, O>,
171        displacements: Ref<[Entry<u32, u32>], E, O>,
172    ) -> Self {
173        Self {
174            key: Endian::new(key),
175            entries,
176            displacements,
177        }
178    }
179}
180
181impl<T, E, O> SetRef<T, E, O>
182where
183    T: ZeroCopy,
184    E: ByteOrder,
185    O: Size,
186{
187    /// Get a value from the set.
188    ///
189    /// ## Examples
190    ///
191    /// ```
192    /// use musli_zerocopy::OwnedBuf;
193    /// use musli_zerocopy::phf;
194    ///
195    /// let mut buf = OwnedBuf::new();
196    ///
197    /// let set = phf::store_set(&mut buf, [1, 2])?;
198    /// let set = buf.bind(set)?;
199    ///
200    /// assert!(set.contains(&1)?);
201    /// assert!(set.contains(&2)?);
202    /// assert!(!set.contains(&3)?);
203    /// # Ok::<_, musli_zerocopy::Error>(())
204    /// ```
205    pub fn contains<Q>(&self, buf: &Buf, key: &Q) -> Result<bool, Error>
206    where
207        Q: ?Sized + Visit,
208        Q::Target: Eq + Hash,
209        T: Visit,
210        T::Target: Borrow<Q::Target>,
211    {
212        if self.displacements.is_empty() {
213            return Ok(false);
214        }
215
216        let hashes = crate::phf::hashing::hash(buf, key, &self.key.to_ne())?;
217
218        let displacements = |index| match self.displacements.get(index) {
219            Some(entry) => Ok(Some(buf.load(entry)?)),
220            None => Ok(None),
221        };
222
223        let index = crate::phf::hashing::get_custom_index(
224            &hashes,
225            displacements,
226            self.displacements.len(),
227            self.entries.len(),
228        )?;
229
230        let Some(e) = self.entries.get(index) else {
231            return Ok(false);
232        };
233
234        let e = buf.load(e)?;
235        key.visit(buf, |b| e.visit(buf, |a| a.borrow() == b))?
236    }
237}