musli_zerocopy/swiss/map.rs
1//! A map which implements a hash-map like interface, where values can be looked
2//! up by keys.
3//!
4//! This map are implemented using a perfect hash functions, and are inserted
5//! into a buffering using [`swiss::store_map`].
6//!
7//! There's two types provided by this module:
8//! * [`Map<K, V>`] which is a *bound* reference to a map, providing a
9//! convenient map-like access.
10//! * [`MapRef<K, V>`] which is the *pointer* of the map. This is what you store
11//! in [`ZeroCopy`] types and is what is returned by [`swiss::store_map`].
12//!
13//! [`swiss::store_map`]: crate::swiss::store_map
14
15use core::borrow::Borrow;
16use core::convert::identity as likely;
17use core::hash::{Hash, Hasher};
18use core::mem::size_of;
19
20use crate::buf::{Bindable, Buf, Visit};
21use crate::endian::{ByteOrder, Native};
22use crate::error::{Error, ErrorKind};
23use crate::pointer::{DefaultSize, Ref, Size};
24use crate::sip::SipHasher13;
25use crate::swiss::Entry;
26use crate::swiss::raw::{Group, h2, probe_seq};
27use crate::{Endian, ZeroCopy};
28
29/// A map bound to a [`Buf`] through [`Buf::bind`] for convenience.
30///
31/// ## Examples
32///
33/// ```
34/// use musli_zerocopy::OwnedBuf;
35/// use musli_zerocopy::swiss;
36///
37/// let mut buf = OwnedBuf::new();
38///
39/// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
40/// let map = buf.bind(map)?;
41///
42/// assert_eq!(map.get(&1)?, Some(&2));
43/// assert_eq!(map.get(&2)?, Some(&3));
44/// assert_eq!(map.get(&3)?, None);
45///
46/// assert!(map.contains_key(&1)?);
47/// assert!(!map.contains_key(&3)?);
48/// # Ok::<_, musli_zerocopy::Error>(())
49/// ```
50pub struct Map<'a, K, V> {
51 key: u64,
52 table: RawTable<'a, Entry<K, V>>,
53 buf: &'a Buf,
54}
55
56impl<K, V> Map<'_, K, V>
57where
58 K: ZeroCopy,
59 V: ZeroCopy,
60{
61 /// Get a value from the map.
62 ///
63 /// ## Examples
64 ///
65 /// ```
66 /// use musli_zerocopy::OwnedBuf;
67 /// use musli_zerocopy::swiss;
68 ///
69 /// let mut buf = OwnedBuf::new();
70 ///
71 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
72 /// let map = buf.bind(map)?;
73 ///
74 /// assert_eq!(map.get(&1)?, Some(&2));
75 /// assert_eq!(map.get(&2)?, Some(&3));
76 /// assert_eq!(map.get(&3)?, None);
77 /// # Ok::<_, musli_zerocopy::Error>(())
78 /// ```
79 pub fn get<Q>(&self, key: &Q) -> Result<Option<&V>, Error>
80 where
81 Q: ?Sized + Visit,
82 Q::Target: Eq + Hash,
83 K: Visit,
84 K::Target: Borrow<Q::Target>,
85 {
86 let hash = key.visit(self.buf, |k| self.hash(k))?;
87
88 let entry = self.table.find(hash, |e| {
89 key.visit(self.buf, |b| e.key.visit(self.buf, |a| a.borrow() == b))?
90 })?;
91
92 if let Some(entry) = entry {
93 Ok(Some(&entry.value))
94 } else {
95 Ok(None)
96 }
97 }
98
99 /// Get the length of the map.
100 ///
101 /// ## Examples
102 ///
103 /// ```
104 /// use musli_zerocopy::OwnedBuf;
105 /// use musli_zerocopy::swiss;
106 ///
107 /// let mut buf = OwnedBuf::new();
108 ///
109 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
110 /// let map = buf.bind(map)?;
111 ///
112 /// assert_eq!(map.len(), 2);
113 /// # Ok::<_, musli_zerocopy::Error>(())
114 /// ```
115 #[inline]
116 pub fn len(&self) -> usize {
117 self.table.len
118 }
119
120 /// Test if the map is empty.
121 ///
122 /// ## Examples
123 ///
124 /// ```
125 /// use musli_zerocopy::OwnedBuf;
126 /// use musli_zerocopy::swiss;
127 ///
128 /// let mut buf = OwnedBuf::new();
129 ///
130 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
131 /// let map = buf.bind(map)?;
132 ///
133 /// assert!(!map.is_empty());
134 /// # Ok::<_, musli_zerocopy::Error>(())
135 /// ```
136 #[inline]
137 pub fn is_empty(&self) -> bool {
138 self.table.len == 0
139 }
140
141 /// Test if the map contains the given `key`.
142 ///
143 /// ## Examples
144 ///
145 /// ```
146 /// use musli_zerocopy::OwnedBuf;
147 /// use musli_zerocopy::swiss;
148 ///
149 /// let mut buf = OwnedBuf::new();
150 ///
151 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
152 /// let map = buf.bind(map)?;
153 ///
154 /// assert!(map.contains_key(&1)?);
155 /// assert!(map.contains_key(&2)?);
156 /// assert!(!map.contains_key(&3)?);
157 /// # Ok::<_, musli_zerocopy::Error>(())
158 /// ```
159 pub fn contains_key<Q>(&self, key: &Q) -> Result<bool, Error>
160 where
161 Q: ?Sized + Visit,
162 Q::Target: Eq + Hash,
163 K: Visit,
164 K::Target: Borrow<Q::Target>,
165 {
166 let hash = key.visit(self.buf, |k| self.hash(k))?;
167
168 let entry = self.table.find(hash, |e| {
169 key.visit(self.buf, |b| e.key.visit(self.buf, |a| a.borrow() == b))?
170 })?;
171
172 Ok(entry.is_some())
173 }
174
175 fn hash<H>(&self, value: &H) -> u64
176 where
177 H: ?Sized + Hash,
178 {
179 let mut hasher = SipHasher13::new_with_keys(0, self.key);
180 value.hash(&mut hasher);
181 hasher.finish()
182 }
183}
184
185/// Bind a [`MapRef`] into a [`Map`].
186impl<K, V, E, O> Bindable for MapRef<K, V, E, O>
187where
188 K: ZeroCopy,
189 V: ZeroCopy,
190 E: ByteOrder,
191 O: Size,
192{
193 type Bound<'a>
194 = Map<'a, K, V>
195 where
196 Self: 'a;
197
198 #[inline]
199 fn bind(self, buf: &Buf) -> Result<Self::Bound<'_>, Error> {
200 Ok(Map {
201 key: self.key.to_ne(),
202 table: self.table.bind(buf)?,
203 buf,
204 })
205 }
206}
207
208/// A stored reference to a map.
209///
210/// Note that operating over the methods provided in [`MapRef`] does not demand
211/// that the entire contents of the set is validated as would be the case when
212/// [`bind()`] is used and might result in better performance if the data is
213/// infrequently accessed.
214///
215/// Constructed through [`swiss::store_map`].
216///
217/// [`swiss::store_map`]: crate::swiss::store_map
218/// [`bind()`]: crate::buf::Buf::bind
219///
220/// ## Examples
221///
222/// ```
223/// use musli_zerocopy::OwnedBuf;
224/// use musli_zerocopy::swiss;
225///
226/// let mut buf = OwnedBuf::new();
227///
228/// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
229///
230/// assert_eq!(map.get(&buf, &1)?, Some(&2));
231/// assert_eq!(map.get(&buf, &2)?, Some(&3));
232/// assert_eq!(map.get(&buf, &3)?, None);
233///
234/// assert!(map.contains_key(&buf, &1)?);
235/// assert!(!map.contains_key(&buf, &3)?);
236/// # Ok::<_, musli_zerocopy::Error>(())
237/// ```
238#[derive(Debug, ZeroCopy)]
239#[repr(C)]
240#[zero_copy(crate)]
241pub struct MapRef<K, V, E = Native, O = DefaultSize>
242where
243 K: ZeroCopy,
244 V: ZeroCopy,
245 E: ByteOrder,
246 O: Size,
247{
248 key: Endian<u64, E>,
249 table: RawTableRef<Entry<K, V>, E, O>,
250}
251
252impl<K, V, E, O> MapRef<K, V, E, O>
253where
254 K: ZeroCopy,
255 V: ZeroCopy,
256 E: ByteOrder,
257 O: Size,
258{
259 #[cfg(feature = "alloc")]
260 pub(crate) fn new(key: u64, table: RawTableRef<Entry<K, V>, E, O>) -> Self {
261 Self {
262 key: Endian::new(key),
263 table,
264 }
265 }
266
267 /// Get a value from the map.
268 ///
269 /// ## Examples
270 ///
271 /// ```
272 /// use musli_zerocopy::OwnedBuf;
273 /// use musli_zerocopy::swiss;
274 ///
275 /// let mut buf = OwnedBuf::new();
276 ///
277 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
278 ///
279 /// assert_eq!(map.get(&buf, &1)?, Some(&2));
280 /// assert_eq!(map.get(&buf, &2)?, Some(&3));
281 /// assert_eq!(map.get(&buf, &3)?, None);
282 /// # Ok::<_, musli_zerocopy::Error>(())
283 /// ```
284 pub fn get<'a, Q>(&self, buf: &'a Buf, key: &Q) -> Result<Option<&'a V>, Error>
285 where
286 Q: ?Sized + Visit,
287 Q::Target: Eq + Hash,
288 K: 'a + Visit,
289 K::Target: Borrow<Q::Target>,
290 {
291 let hash = key.visit(buf, |k| self.hash(k))?;
292
293 let entry = self.table.find(buf, hash, |e| {
294 key.visit(buf, |b| e.key.visit(buf, |a| a.borrow() == b))?
295 })?;
296
297 if let Some(entry) = entry {
298 Ok(Some(&entry.value))
299 } else {
300 Ok(None)
301 }
302 }
303
304 /// Get the length of the map.
305 ///
306 /// ## Examples
307 ///
308 /// ```
309 /// use musli_zerocopy::OwnedBuf;
310 /// use musli_zerocopy::swiss;
311 ///
312 /// let mut buf = OwnedBuf::new();
313 ///
314 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
315 ///
316 /// assert_eq!(map.len(), 2);
317 /// # Ok::<_, musli_zerocopy::Error>(())
318 /// ```
319 #[inline]
320 pub fn len(&self) -> usize {
321 self.table.len.to_ne()
322 }
323
324 /// Test if the map is empty.
325 ///
326 /// ## Examples
327 ///
328 /// ```
329 /// use musli_zerocopy::OwnedBuf;
330 /// use musli_zerocopy::swiss;
331 ///
332 /// let mut buf = OwnedBuf::new();
333 ///
334 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
335 ///
336 /// assert!(!map.is_empty());
337 /// # Ok::<_, musli_zerocopy::Error>(())
338 /// ```
339 #[inline]
340 pub fn is_empty(&self) -> bool {
341 self.table.len.to_ne() == 0
342 }
343
344 /// Test if the map contains the given `key`.
345 ///
346 /// ## Examples
347 ///
348 /// ```
349 /// use musli_zerocopy::OwnedBuf;
350 /// use musli_zerocopy::swiss;
351 ///
352 /// let mut buf = OwnedBuf::new();
353 ///
354 /// let map = swiss::store_map(&mut buf, [(1, 2), (2, 3)])?;
355 ///
356 /// assert!(map.contains_key(&buf, &1)?);
357 /// assert!(map.contains_key(&buf, &2)?);
358 /// assert!(!map.contains_key(&buf, &3)?);
359 /// # Ok::<_, musli_zerocopy::Error>(())
360 /// ```
361 pub fn contains_key<Q>(&self, buf: &Buf, key: &Q) -> Result<bool, Error>
362 where
363 Q: ?Sized + Visit,
364 Q::Target: Eq + Hash,
365 K: Visit,
366 K::Target: Borrow<Q::Target>,
367 {
368 let hash = key.visit(buf, |key| self.hash(key))?;
369
370 let entry = self.table.find(buf, hash, |e| {
371 key.visit(buf, |b| e.key.visit(buf, |a| a.borrow() == b))?
372 })?;
373
374 Ok(entry.is_some())
375 }
376
377 #[inline]
378 fn hash<H>(&self, value: &H) -> u64
379 where
380 H: ?Sized + Hash,
381 {
382 let mut hasher = SipHasher13::new_with_keys(0, self.key.to_ne());
383 value.hash(&mut hasher);
384 hasher.finish()
385 }
386}
387
388impl<K, V, E, O> Clone for MapRef<K, V, E, O>
389where
390 K: ZeroCopy,
391 V: ZeroCopy,
392 E: ByteOrder,
393 O: Size,
394{
395 fn clone(&self) -> Self {
396 *self
397 }
398}
399
400impl<K, V, E, O> Copy for MapRef<K, V, E, O>
401where
402 K: ZeroCopy,
403 V: ZeroCopy,
404 E: ByteOrder,
405 O: Size,
406{
407}
408
409pub(crate) struct RawTable<'a, T> {
410 ctrl: &'a [u8],
411 entries: &'a [T],
412 bucket_mask: usize,
413 len: usize,
414}
415
416impl<'a, T> RawTable<'a, T> {
417 /// Searches for an element in the table.
418 #[inline]
419 pub(crate) fn find(
420 &self,
421 hash: u64,
422 mut eq: impl FnMut(&T) -> Result<bool, Error>,
423 ) -> Result<Option<&'a T>, Error> {
424 let result = self.find_inner(hash, &mut |index| {
425 let entry = self.entry(index)?;
426 eq(entry)
427 })?;
428
429 Ok(match result {
430 Some(index) => Some(self.entry(index)?),
431 None => None,
432 })
433 }
434
435 fn entry(&self, index: usize) -> Result<&'a T, Error> {
436 let Some(entry) = self.entries.get(index) else {
437 return Err(Error::new(ErrorKind::IndexOutOfBounds {
438 index,
439 len: self.entries.len(),
440 }));
441 };
442
443 Ok(entry)
444 }
445
446 /// Searches for an element in a table, returning the `index` of the found
447 /// element.
448 #[inline(always)]
449 fn find_inner(
450 &self,
451 hash: u64,
452 eq: &mut dyn FnMut(usize) -> Result<bool, Error>,
453 ) -> Result<Option<usize>, Error> {
454 let h2_hash = h2(hash);
455 let mut probe_seq = probe_seq(self.bucket_mask, hash);
456
457 loop {
458 let range = probe_seq.pos..probe_seq.pos + size_of::<Group>();
459
460 let Some(bytes) = self.ctrl.get(range.clone()) else {
461 return Err(Error::new(ErrorKind::ControlRangeOutOfBounds {
462 range,
463 len: self.ctrl.len(),
464 }));
465 };
466
467 // SAFETY: We've made sure to provide this load with a buffer of the
468 // appropriate size.
469 let group = unsafe { Group::load(bytes.as_ptr()) };
470
471 for bit in group.match_byte(h2_hash) {
472 // This is the same as `(probe_seq.pos + bit) % self.buckets()` because the number
473 // of buckets is a power of two, and `self.bucket_mask = self.buckets() - 1`.
474 let index = (probe_seq.pos + bit) & self.bucket_mask;
475
476 if likely(eq(index)?) {
477 return Ok(Some(index));
478 }
479 }
480
481 if likely(group.match_empty().any_bit_set()) {
482 return Ok(None);
483 }
484
485 probe_seq.move_next(self.bucket_mask)?;
486 }
487 }
488}
489
490#[derive(Debug, ZeroCopy)]
491#[repr(C)]
492#[zero_copy(crate)]
493pub(crate) struct RawTableRef<T, E = Native, O = DefaultSize>
494where
495 T: ZeroCopy,
496 E: ByteOrder,
497 O: Size,
498{
499 ctrl: Ref<[u8], E, O>,
500 entries: Ref<[T], E, O>,
501 bucket_mask: Endian<usize, E>,
502 len: Endian<usize, E>,
503}
504
505impl<T, E, O> RawTableRef<T, E, O>
506where
507 T: ZeroCopy,
508 E: ByteOrder,
509 O: Size,
510{
511 #[cfg(feature = "alloc")]
512 #[inline]
513 pub(crate) fn new(
514 ctrl: Ref<[u8], E, O>,
515 entries: Ref<[T], E, O>,
516 bucket_mask: usize,
517 len: usize,
518 ) -> Self {
519 Self {
520 ctrl,
521 entries,
522 bucket_mask: Endian::new(bucket_mask),
523 len: Endian::new(len),
524 }
525 }
526
527 #[inline]
528 pub(crate) fn bind<'buf>(&self, buf: &'buf Buf) -> Result<RawTable<'buf, T>, Error> {
529 Ok(RawTable {
530 ctrl: buf.load(self.ctrl)?,
531 entries: buf.load(self.entries)?,
532 bucket_mask: self.bucket_mask.to_ne(),
533 len: self.len.to_ne(),
534 })
535 }
536
537 /// Searches for an element in the table.
538 #[inline]
539 pub(crate) fn find<'buf>(
540 &self,
541 buf: &'buf Buf,
542 hash: u64,
543 mut eq: impl FnMut(&T) -> Result<bool, Error>,
544 ) -> Result<Option<&'buf T>, Error> {
545 let result = self.find_inner(buf, hash, &mut |index| eq(self.entry(index, buf)?))?;
546
547 Ok(match result {
548 Some(index) => Some(self.entry(index, buf)?),
549 None => None,
550 })
551 }
552
553 fn entry<'buf>(&self, index: usize, buf: &'buf Buf) -> Result<&'buf T, Error> {
554 let Some(entry) = self.entries.get(index) else {
555 return Err(Error::new(ErrorKind::IndexOutOfBounds {
556 index,
557 len: self.entries.len(),
558 }));
559 };
560
561 buf.load(entry)
562 }
563
564 /// Searches for an element in a table, returning the `index` of the found
565 /// element.
566 #[inline(always)]
567 fn find_inner(
568 &self,
569 buf: &Buf,
570 hash: u64,
571 eq: &mut dyn FnMut(usize) -> Result<bool, Error>,
572 ) -> Result<Option<usize>, Error> {
573 let h2_hash = h2(hash);
574 let mut probe_seq = probe_seq(self.bucket_mask.to_ne(), hash);
575
576 let ctrl = buf.load(self.ctrl)?;
577
578 loop {
579 let range = probe_seq.pos..probe_seq.pos + size_of::<Group>();
580
581 let Some(bytes) = ctrl.get(range.clone()) else {
582 return Err(Error::new(ErrorKind::ControlRangeOutOfBounds {
583 range,
584 len: self.ctrl.len(),
585 }));
586 };
587
588 let group = unsafe { Group::load(bytes.as_ptr()) };
589
590 for bit in group.match_byte(h2_hash) {
591 // This is the same as `(probe_seq.pos + bit) % self.buckets()` because the number
592 // of buckets is a power of two, and `self.bucket_mask = self.buckets() - 1`.
593 let index = (probe_seq.pos + bit) & self.bucket_mask.to_ne();
594
595 if likely(eq(index)?) {
596 return Ok(Some(index));
597 }
598 }
599
600 if likely(group.match_empty().any_bit_set()) {
601 return Ok(None);
602 }
603
604 probe_seq.move_next(self.bucket_mask.to_ne())?;
605 }
606 }
607}
608
609impl<T, E, O> Clone for RawTableRef<T, E, O>
610where
611 T: ZeroCopy,
612 E: ByteOrder,
613 O: Size,
614{
615 fn clone(&self) -> Self {
616 *self
617 }
618}
619
620impl<T, E, O> Copy for RawTableRef<T, E, O>
621where
622 T: ZeroCopy,
623 E: ByteOrder,
624 O: Size,
625{
626}