musli_zerocopy/phf/
factory.rs

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