musli_zerocopy/swiss/
factory.rs

1use core::hash::{Hash, Hasher};
2use core::mem::size_of;
3
4use crate::ZeroCopy;
5use crate::buf::{Buf, OwnedBuf, StoreBuf, Visit};
6use crate::endian::ByteOrder;
7use crate::error::Error;
8use crate::pointer::{Ref, Size};
9use crate::sip::SipHasher13;
10use crate::swiss::constructor::Constructor;
11use crate::swiss::map::RawTableRef;
12use crate::swiss::raw::{self};
13use crate::swiss::{Entry, MapRef, SetRef};
14
15const FIXED_SEED: u64 = 1234567890;
16
17/// Store a [SwissTable] map into an [`OwnedBuf`].
18///
19/// This returns a [`MapRef`] which can be bound into a [`Map`] through the
20/// [`bind()`] method for convenience.
21///
22/// See the [module level documentation] for more information.
23///
24/// [`bind()`]: crate::buf::Buf::bind
25/// [`Map`]: crate::swiss::Map
26/// [SwissTable]: https://abseil.io/about/design/swisstables
27/// [module level documentation]: crate::swiss
28///
29/// # Duplicates
30///
31/// The caller is responsible for ensuring that no duplicate keys are provided
32/// to the constructor, since the input will be used to size the table.
33///
34/// In the face of duplicate keys, the default accessor will return a random
35/// element and the table will waste space.
36///
37/// Technically the elements are stored so the length is affected, and a future
38/// update might make them available.
39///
40/// ```
41/// use musli_zerocopy::OwnedBuf;
42/// use musli_zerocopy::swiss;
43///
44/// let mut buf = OwnedBuf::new();
45///
46/// let map = swiss::store_map(&mut buf, [(10, 1u32), (10, 2u32)])?;
47/// let map = buf.bind(map)?;
48///
49/// assert!(matches!(map.get(&10)?, Some(&1) | Some(&2)));
50/// assert_eq!(map.len(), 2);
51/// # Ok::<_, musli_zerocopy::Error>(())
52/// ```
53///
54/// # Examples
55///
56/// ```
57/// use musli_zerocopy::OwnedBuf;
58/// use musli_zerocopy::swiss;
59///
60/// let mut buf = OwnedBuf::new();
61///
62/// let pairs = [
63///     (buf.store_unsized("first"), 1u32),
64///     (buf.store_unsized("second"), 2u32),
65/// ];
66///
67/// let map = swiss::store_map(&mut buf, pairs)?;
68/// let map = buf.bind(map)?;
69///
70/// assert_eq!(map.get("first")?, Some(&1));
71/// assert_eq!(map.get("second")?, Some(&2));
72/// assert_eq!(map.get("third")?, None);
73/// # Ok::<_, musli_zerocopy::Error>(())
74/// ```
75///
76/// Using non-references as keys:
77///
78/// ```
79/// use musli_zerocopy::OwnedBuf;
80/// use musli_zerocopy::swiss;
81///
82/// let mut buf = OwnedBuf::new();
83///
84/// let map = swiss::store_map(&mut buf, [(10u64, 1u32), (20u64, 2u32)])?;
85/// let map = buf.bind(map)?;
86///
87/// assert_eq!(map.get(&10u64)?, Some(&1));
88/// assert_eq!(map.get(&20u64)?, Some(&2));
89/// assert_eq!(map.get(&30u64)?, None);
90/// # Ok::<_, musli_zerocopy::Error>(())
91/// ```
92///
93/// Storing a zero-sized type:
94///
95/// ```
96/// use musli_zerocopy::OwnedBuf;
97/// use musli_zerocopy::swiss;
98///
99/// let mut buf = OwnedBuf::new();
100///
101/// let map = swiss::store_map(&mut buf, [((), ()), ((), ())])?;
102/// let map = buf.bind(map)?;
103///
104/// assert_eq!(map.get(&())?, Some(&()));
105/// # Ok::<_, musli_zerocopy::Error>(())
106/// ```
107pub fn store_map<K, V, I, E, O>(
108    buf: &mut OwnedBuf<E, O>,
109    entries: I,
110) -> Result<MapRef<K, V, E, O>, Error>
111where
112    K: Visit + ZeroCopy,
113    V: ZeroCopy,
114    K::Target: Hash,
115    I: IntoIterator<Item = (K, V)>,
116    I::IntoIter: ExactSizeIterator,
117    E: ByteOrder,
118    O: Size,
119{
120    let (key, ctrl, buckets, bucket_mask, len) = store_raw(entries, buf, |buf, (k, v), hasher| {
121        k.visit(buf, |key| key.hash(hasher))?;
122        Ok(Entry::new(k, v))
123    })?;
124
125    Ok(MapRef::new(
126        key,
127        RawTableRef::new(ctrl, buckets, bucket_mask, len),
128    ))
129}
130
131/// Store a [SwissTable] set into an [`OwnedBuf`].
132///
133/// This returns a [`SetRef`] which can be bound into a [`Set`] through the
134/// [`bind()`] method for convenience.
135///
136/// See the [module level documentation] for more information.
137///
138/// [`bind()`]: crate::buf::Buf::bind
139/// [`Set`]: crate::swiss::Set
140/// [SwissTable]: https://abseil.io/about/design/swisstables
141/// [module level documentation]: crate::swiss
142///
143/// # Duplicates
144///
145/// The caller is responsible for ensuring that no duplicate values are provided
146/// to the constructor, since the input will be used to size the table.
147///
148/// In the face of duplicate values, one of the entries will be preserved at
149/// random and the table will waste space.
150///
151/// # Examples
152///
153/// ```
154/// use musli_zerocopy::OwnedBuf;
155/// use musli_zerocopy::swiss;
156///
157/// let mut buf = OwnedBuf::new();
158///
159/// let first = buf.store_unsized("first");
160/// let second = buf.store_unsized("second");
161/// let third = buf.store_unsized("third");
162///
163/// let set = swiss::store_set(&mut buf, [first, second])?;
164/// let set = buf.bind(set)?;
165///
166/// assert!(set.contains("first")?);
167/// assert!(set.contains(&first)?);
168/// assert!(set.contains("second")?);
169/// assert!(set.contains(&second)?);
170/// assert!(!set.contains("third")?);
171/// assert!(!set.contains(&third)?);
172/// # Ok::<_, musli_zerocopy::Error>(())
173/// ```
174///
175/// Using non-references as keys:
176///
177/// ```
178/// use musli_zerocopy::OwnedBuf;
179/// use musli_zerocopy::swiss;
180///
181/// let mut buf = OwnedBuf::new();
182///
183/// let set = swiss::store_set(&mut buf, [1, 2])?;
184/// let set = buf.bind(set)?;
185///
186/// assert!(set.contains(&1)?);
187/// assert!(set.contains(&2)?);
188/// assert!(!set.contains(&3)?);
189/// # Ok::<_, musli_zerocopy::Error>(())
190/// ```
191///
192/// Storing a zero-sized type:
193///
194/// ```
195/// use musli_zerocopy::OwnedBuf;
196/// use musli_zerocopy::swiss;
197///
198/// let mut buf = OwnedBuf::new();
199///
200/// let set = swiss::store_set(&mut buf, [(), ()])?;
201/// let set = buf.bind(set)?;
202///
203/// assert!(set.contains(&())?);
204/// # Ok::<_, musli_zerocopy::Error>(())
205/// ```
206pub fn store_set<T, I, S>(
207    buf: &mut S,
208    entries: I,
209) -> Result<SetRef<T, S::ByteOrder, S::Size>, Error>
210where
211    T: Visit + ZeroCopy,
212    T::Target: Hash,
213    I: IntoIterator<Item = T>,
214    I::IntoIter: ExactSizeIterator,
215    S: ?Sized + StoreBuf,
216{
217    let (key, ctrl, buckets, bucket_mask, len) = store_raw(entries, buf, |buf, v, hasher| {
218        v.visit(buf, |key| key.hash(hasher))?;
219        Ok(v)
220    })?;
221
222    Ok(SetRef::new(
223        key,
224        RawTableRef::new(ctrl, buckets, bucket_mask, len),
225    ))
226}
227
228// Output from storing raw values.
229type Raw<U, E, O> = (u64, Ref<[u8], E, O>, Ref<[U], E, O>, usize, usize);
230
231// Raw store function which is capable of storing any value using a hashing
232// adapter.
233fn store_raw<T, U, I, S>(
234    entries: I,
235    buf: &mut S,
236    hash: fn(&Buf, T, &mut SipHasher13) -> Result<U, Error>,
237) -> Result<Raw<U, S::ByteOrder, S::Size>, Error>
238where
239    U: ZeroCopy,
240    I: IntoIterator<Item = T>,
241    I::IntoIter: ExactSizeIterator,
242    S: ?Sized + StoreBuf,
243{
244    let entries = entries.into_iter();
245    let key = FIXED_SEED;
246
247    let Some(buckets) = raw::capacity_to_buckets(entries.len()) else {
248        panic!("Capacity overflow");
249    };
250
251    let ctrl_len = buckets + size_of::<raw::Group>();
252    let ctrl_align = raw::Group::WIDTH;
253
254    debug_assert!(ctrl_align.is_power_of_two());
255
256    buf.next_offset_with_and_reserve(ctrl_align, ctrl_len);
257    let ctrl_ptr = buf.len();
258
259    // All ones indicates that the table is empty, since the ctrl byte for empty
260    // buckets is 1111_1111.
261    buf.fill(raw::EMPTY, ctrl_len + size_of::<raw::Group>());
262
263    let base_ptr = buf.next_offset::<U>();
264    buf.fill(0, size_of::<T>().wrapping_mul(buckets));
265
266    let (bucket_mask, len) = {
267        buf.align_in_place();
268        let mut table = Constructor::<U, _>::with_buf(buf, ctrl_ptr, base_ptr, buckets);
269
270        for v in entries {
271            let mut hasher = SipHasher13::new_with_keys(0, key);
272            let v = hash(table.buf(), v, &mut hasher)?;
273            let hash = hasher.finish();
274            table.insert(hash, &v)?;
275        }
276
277        (table.bucket_mask(), table.len())
278    };
279
280    let ctrl = Ref::with_metadata(ctrl_ptr, ctrl_len);
281    let buckets = Ref::with_metadata(base_ptr, buckets);
282    Ok((key, ctrl, buckets, bucket_mask, len))
283}