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}