// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use crate::ule::AsULE; use crate::ZeroSlice; use core::cmp::Ordering; use core::fmt; pub use super::kv::ZeroMapKV; pub use super::vecs::{MutableZeroVecLike, ZeroVecLike}; /// A borrowed-only version of [`ZeroMap`](super::ZeroMap) /// /// This is useful for fully-zero-copy deserialization from non-human-readable /// serialization formats. It also has the advantage that it can return references that live for /// the lifetime of the backing buffer as opposed to that of the [`ZeroMapBorrowed`] instance. /// /// # Examples /// /// ``` /// use zerovec::maps::ZeroMapBorrowed; /// /// // Example byte buffer representing the map { 1: "one" } /// let BINCODE_BYTES: &[u8; 29] = &[ /// 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, /// 0, 0, 111, 110, 101, /// ]; /// /// // Deserializing to ZeroMap requires no heap allocations. /// let zero_map: ZeroMapBorrowed = /// bincode::deserialize(BINCODE_BYTES) /// .expect("Should deserialize successfully"); /// assert_eq!(zero_map.get(&1), Some("one")); /// ``` /// /// This can be obtained from a [`ZeroMap`](super::ZeroMap) via [`ZeroMap::as_borrowed`](super::ZeroMap::as_borrowed) pub struct ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a>, V: ZeroMapKV<'a>, K: ?Sized, V: ?Sized, { pub(crate) keys: &'a >::Slice, pub(crate) values: &'a >::Slice, } impl<'a, K, V> Copy for ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a>, V: ZeroMapKV<'a>, K: ?Sized, V: ?Sized, { } impl<'a, K, V> Clone for ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a>, V: ZeroMapKV<'a>, K: ?Sized, V: ?Sized, { fn clone(&self) -> Self { ZeroMapBorrowed { keys: self.keys, values: self.values, } } } impl<'a, K, V> Default for ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a>, V: ZeroMapKV<'a>, K::Slice: 'static, V::Slice: 'static, K: ?Sized, V: ?Sized, { fn default() -> Self { Self::new() } } impl<'a, K, V> ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a>, V: ZeroMapKV<'a>, K::Slice: 'static, V::Slice: 'static, K: ?Sized, V: ?Sized, { /// Creates a new, empty `ZeroMapBorrowed`. /// /// Note: Since [`ZeroMapBorrowed`] is not mutable, the return value will be a stub unless /// converted into a [`ZeroMap`](super::ZeroMap). /// /// # Examples /// /// ``` /// use zerovec::maps::ZeroMapBorrowed; /// /// let zm: ZeroMapBorrowed = ZeroMapBorrowed::new(); /// assert!(zm.is_empty()); /// ``` pub fn new() -> Self { Self { keys: K::Container::zvl_new_borrowed(), values: V::Container::zvl_new_borrowed(), } } } impl<'a, K, V> ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a>, V: ZeroMapKV<'a>, K: ?Sized, V: ?Sized, { #[doc(hidden)] // databake internal pub const unsafe fn from_parts_unchecked( keys: &'a >::Slice, values: &'a >::Slice, ) -> Self { Self { keys, values } } /// The number of elements in the [`ZeroMapBorrowed`] pub fn len(&self) -> usize { self.values.zvl_len() } /// Whether the [`ZeroMapBorrowed`] is empty pub fn is_empty(&self) -> bool { self.values.zvl_len() == 0 } } impl<'a, K, V> ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a> + Ord, V: ZeroMapKV<'a>, K: ?Sized, V: ?Sized, { /// Get the value associated with `key`, if it exists. /// /// This is able to return values that live longer than the map itself /// since they borrow directly from the backing buffer. This is the /// primary advantage of using [`ZeroMapBorrowed`](super::ZeroMapBorrowed) over [`ZeroMap`](super::ZeroMap). /// /// ```rust /// use zerovec::maps::ZeroMapBorrowed; /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// let borrowed = map.as_borrowed(); /// assert_eq!(borrowed.get(&1), Some("one")); /// assert_eq!(borrowed.get(&3), None); /// ``` pub fn get(&self, key: &K) -> Option<&'a V::GetType> { let index = self.keys.zvl_binary_search(key).ok()?; self.values.zvl_get(index) } /// Binary search the map with `predicate` to find a key, returning the value. /// /// This is able to return values that live longer than the map itself /// since they borrow directly from the backing buffer. This is the /// primary advantage of using [`ZeroMapBorrowed`](super::ZeroMapBorrowed) over [`ZeroMap`](super::ZeroMap). /// /// ```rust /// use zerovec::maps::ZeroMapBorrowed; /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// let borrowed = map.as_borrowed(); /// assert_eq!(borrowed.get_by(|probe| probe.cmp(&1)), Some("one")); /// assert_eq!(borrowed.get_by(|probe| probe.cmp(&3)), None); /// ``` pub fn get_by(&self, predicate: impl FnMut(&K) -> Ordering) -> Option<&'a V::GetType> { let index = self.keys.zvl_binary_search_by(predicate).ok()?; self.values.zvl_get(index) } /// Returns whether `key` is contained in this map /// /// ```rust /// use zerovec::maps::ZeroMapBorrowed; /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// let borrowed = map.as_borrowed(); /// assert!(borrowed.contains_key(&1)); /// assert!(!borrowed.contains_key(&3)); /// ``` pub fn contains_key(&self, key: &K) -> bool { self.keys.zvl_binary_search(key).is_ok() } } impl<'a, K, V> ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a> + ?Sized, V: ZeroMapKV<'a> + ?Sized, { /// Produce an ordered iterator over key-value pairs pub fn iter<'b>( &'b self, ) -> impl Iterator< Item = ( &'a >::GetType, &'a >::GetType, ), > + 'b { self.iter_keys().zip(self.iter_values()) } /// Produce an ordered iterator over keys pub fn iter_keys<'b>(&'b self) -> impl Iterator>::GetType> + 'b { #[allow(clippy::unwrap_used)] // idx in 0..keys.zvl_len() (0..self.keys.zvl_len()).map(move |idx| self.keys.zvl_get(idx).unwrap()) } /// Produce an iterator over values, ordered by keys pub fn iter_values<'b>( &'b self, ) -> impl Iterator>::GetType> + 'b { #[allow(clippy::unwrap_used)] // idx in 0..keys.zvl_len() == values.zvl_len() (0..self.values.zvl_len()).map(move |idx| self.values.zvl_get(idx).unwrap()) } } impl<'a, K, V> ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a> + Ord + ?Sized, V: ZeroMapKV<'a, Slice = ZeroSlice> + AsULE + Copy + 'static, { /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE` pub fn get_copied(&self, key: &K) -> Option { let index = self.keys.zvl_binary_search(key).ok()?; self.values.get(index) } /// Similar to [`Self::iter()`] except it returns a direct copy of the values instead of references /// to `V::ULE`, in cases when `V` is fixed-size pub fn iter_copied_values<'b>( &'b self, ) -> impl Iterator>::GetType, V)> { (0..self.keys.zvl_len()).map(move |idx| { ( #[allow(clippy::unwrap_used)] // idx in 0..keys.zvl_len() self.keys.zvl_get(idx).unwrap(), #[allow(clippy::unwrap_used)] // idx in 0..keys.zvl_len() = values.zvl_len() self.values.get(idx).unwrap(), ) }) } } impl<'a, K, V> ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a, Slice = ZeroSlice> + AsULE + Copy + Ord + 'static, V: ZeroMapKV<'a, Slice = ZeroSlice> + AsULE + Copy + 'static, { /// Similar to [`Self::iter()`] except it returns a direct copy of the keys values instead of references /// to `K::ULE` and `V::ULE`, in cases when `K` and `V` are fixed-size #[allow(clippy::needless_lifetimes)] // Lifetime is necessary in impl Trait pub fn iter_copied<'b: 'a>(&'b self) -> impl Iterator + 'b { let keys = &*self.keys; let values = &*self.values; let len = self.keys.zvl_len(); (0..len).map(move |idx| { ( #[allow(clippy::unwrap_used)] // idx in 0..keys.zvl_len() ZeroSlice::get(keys, idx).unwrap(), #[allow(clippy::unwrap_used)] // idx in 0..keys.zvl_len() = values.zvl_len() ZeroSlice::get(values, idx).unwrap(), ) }) } } // We can't use the default PartialEq because ZeroMap is invariant // so otherwise rustc will not automatically allow you to compare ZeroMaps // with different lifetimes impl<'a, 'b, K, V> PartialEq> for ZeroMapBorrowed<'a, K, V> where K: for<'c> ZeroMapKV<'c> + ?Sized, V: for<'c> ZeroMapKV<'c> + ?Sized, >::Slice: PartialEq<>::Slice>, >::Slice: PartialEq<>::Slice>, { fn eq(&self, other: &ZeroMapBorrowed<'b, K, V>) -> bool { self.keys.eq(other.keys) && self.values.eq(other.values) } } impl<'a, K, V> fmt::Debug for ZeroMapBorrowed<'a, K, V> where K: ZeroMapKV<'a> + ?Sized, V: ZeroMapKV<'a> + ?Sized, K::Slice: fmt::Debug, V::Slice: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.debug_struct("ZeroMapBorrowed") .field("keys", &self.keys) .field("values", &self.values) .finish() } }