Skip to main content

musli_zerocopy/phf/
factory.rs

1#![allow(clippy::type_complexity)]
2
3use core::hash::Hash;
4
5use alloc::vec;
6
7use crate::Ref;
8use crate::ZeroCopy;
9use crate::buf::{StoreBuf, Visit};
10use crate::error::Error;
11use crate::phf::hashing::HashKey;
12use crate::phf::{Entry, MapRef, SetRef};
13
14/// Store a map based on a perfect hash function into a buffer.
15///
16/// This will utilize a perfect hash functions derived from the [`phf` crate] to
17/// construct a persistent hash set.
18///
19/// This returns a [`MapRef`] which can be bound into a [`Map`] through the
20/// [`bind()`] method for convenience.
21///
22/// [`phf` crate]: https://crates.io/crates/phf
23/// [`Map`]: crate::phf::Map
24/// [`bind()`]: crate::buf::Buf::bind
25///
26/// # Examples
27///
28/// ```
29/// use musli_zerocopy::OwnedBuf;
30/// use musli_zerocopy::phf;
31///
32/// let mut buf = OwnedBuf::new();
33///
34/// let first = buf.store_unsized("first")?;
35/// let second = buf.store_unsized("second")?;
36///
37/// let map = phf::store_map(&mut buf, [(first, 1u32), (second, 2u32)])?;
38/// let map = buf.bind(map)?;
39///
40/// assert_eq!(map.get("first")?, Some(&1));
41/// assert_eq!(map.get(&first)?, Some(&1));
42/// assert_eq!(map.get("second")?, Some(&2));
43/// assert_eq!(map.get(&second)?, Some(&2));
44/// assert_eq!(map.get("third")?, None);
45/// # Ok::<_, musli_zerocopy::Error>(())
46/// ```
47///
48/// Using non-references as keys:
49///
50/// ```
51/// use musli_zerocopy::OwnedBuf;
52/// use musli_zerocopy::phf;
53///
54/// let mut buf = OwnedBuf::new();
55///
56/// let map = phf::store_map(&mut buf, [(10u64, 1), (20u64, 2)])?;
57/// let map = buf.bind(map)?;
58///
59/// assert_eq!(map.get(&10u64)?, Some(&1));
60/// assert_eq!(map.get(&20u64)?, Some(&2));
61/// assert_eq!(map.get(&30u64)?, None);
62/// # Ok::<_, musli_zerocopy::Error>(())
63/// ```
64pub fn store_map<K, V, S, I>(
65    buf: &mut S,
66    entries: I,
67) -> Result<MapRef<K, V, S::ByteOrder, S::Size>, Error>
68where
69    K: Visit + ZeroCopy,
70    V: ZeroCopy,
71    K::Target: Hash,
72    S: ?Sized + StoreBuf,
73    I: IntoIterator<Item = (K, V)>,
74    I::IntoIter: ExactSizeIterator,
75{
76    let entries = entries.into_iter().map(|(k, v)| Entry::new(k, v));
77    let (key, entries, displacements) = store_raw(buf, entries, |entry| &entry.key)?;
78    Ok(MapRef::new(key, entries, displacements))
79}
80
81/// Store a set based on a perfect hash function into a buffer.
82///
83/// This will utilize a perfect hash functions derived from the [`phf` crate] to
84/// construct a persistent hash map.
85///
86/// This returns a [`SetRef`] which can be bound into a [`Set`] through the
87/// [`bind()`] method for convenience.
88///
89/// [`phf` crate]: https://crates.io/crates/phf
90/// [`Set`]: crate::phf::Set
91/// [`bind()`]: crate::buf::Buf::bind
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 first = buf.store_unsized("first")?;
102/// let second = buf.store_unsized("second")?;
103/// let third = buf.store_unsized("third")?;
104///
105/// let set = phf::store_set(&mut buf, [first, second])?;
106/// let set = buf.bind(set)?;
107///
108/// assert!(set.contains("first")?);
109/// assert!(set.contains(&first)?);
110/// assert!(set.contains("second")?);
111/// assert!(set.contains(&second)?);
112/// assert!(!set.contains("third")?);
113/// assert!(!set.contains(&third)?);
114/// # Ok::<_, musli_zerocopy::Error>(())
115/// ```
116///
117/// Using non-references as keys:
118///
119/// ```
120/// use musli_zerocopy::OwnedBuf;
121/// use musli_zerocopy::phf;
122///
123/// let mut buf = OwnedBuf::new();
124///
125/// let set = phf::store_set(&mut buf, [1, 2])?;
126/// let set = buf.bind(set)?;
127///
128/// assert!(set.contains(&1)?);
129/// assert!(set.contains(&2)?);
130/// assert!(!set.contains(&3)?);
131/// # Ok::<_, musli_zerocopy::Error>(())
132/// ```
133pub fn store_set<S, I>(
134    buf: &mut S,
135    entries: I,
136) -> Result<SetRef<I::Item, S::ByteOrder, S::Size>, Error>
137where
138    S: ?Sized + StoreBuf,
139    I: IntoIterator<Item: Visit<Target: Hash> + ZeroCopy, IntoIter: ExactSizeIterator>,
140{
141    let (key, entries, displacements) = store_raw(buf, entries, |entry| entry)?;
142    Ok(SetRef::new(key, entries, displacements))
143}
144
145fn store_raw<K, I, S, F>(
146    buf: &mut S,
147    entries: I,
148    access: F,
149) -> Result<
150    (
151        HashKey,
152        Ref<[I::Item], S::ByteOrder, S::Size>,
153        Ref<[Entry<u32, u32>], S::ByteOrder, S::Size>,
154    ),
155    Error,
156>
157where
158    K: Visit<Target: Hash> + ZeroCopy,
159    I: IntoIterator<Item: ZeroCopy, IntoIter: ExactSizeIterator>,
160    S: ?Sized + StoreBuf,
161    F: Fn(&I::Item) -> &K,
162{
163    let entries = build_slice(buf, entries)?;
164    let len = crate::phf::generator::displacements_len(entries.len());
165    let displacements = build_slice(buf, (0..len).map(|_| Entry::new(0, 0)))?;
166
167    let len = buf.len();
168
169    let map = build_slice(buf, (0..entries.len()).map(|_| usize::MAX))?;
170
171    let hash_state = {
172        buf.align_in_place()?;
173
174        // SAFETY: The internal structures are all padded to avoid uninitialized
175        // data.
176        let buf = unsafe { buf.as_mut_buf() };
177
178        crate::phf::generator::generate_hash(buf, &entries, &displacements, &map, access)?
179    };
180
181    // `map[slot]` is the index of the entry which belongs at `slot`. Invert it
182    // into `permutation[entry] = slot`, since the cycle sort below moves each
183    // entry to the position named by the permutation it is given.
184    let mut permutation = vec![0usize; entries.len()];
185
186    for (slot, a) in map.iter().enumerate() {
187        let entry = *buf.as_buf().load(a)?;
188        permutation[entry] = slot;
189    }
190
191    for from in 0..permutation.len() {
192        loop {
193            let to = permutation[from];
194
195            if from == to {
196                break;
197            }
198
199            buf.swap(entries.at(from), entries.at(to))?;
200            permutation.swap(from, to);
201        }
202    }
203
204    // Free up temporary memory we needed to build the map.
205    buf.truncate(len);
206    Ok((hash_state.key, entries, displacements))
207}
208
209fn build_slice<S, I>(
210    buf: &mut S,
211    entries: I,
212) -> Result<Ref<[I::Item], S::ByteOrder, S::Size>, Error>
213where
214    S: ?Sized + StoreBuf,
215    I: IntoIterator<Item: ZeroCopy, IntoIter: ExactSizeIterator>,
216{
217    let offset = buf.next_offset::<I::Item>()?;
218    let iter = entries.into_iter();
219    let len = iter.len();
220
221    for value in iter {
222        buf.store(&value)?;
223    }
224
225    // SAFETY: Rust language requirements ensures that layouts are not violated.
226    Ok(unsafe { Ref::try_with_metadata_unchecked(offset, len)? })
227}