usesuper::*; usecrate::ule::{AsULE, EncodeAsVarULE, VarULE}; usecrate::{VarZeroVec, ZeroSlice, ZeroVec, ZeroVecError}; use alloc::borrow::Borrow; use alloc::boxed::Box; use core::cmp::Ordering; use core::fmt; use core::iter::FromIterator;
/// A zero-copy map datastructure, built on sorted binary-searchable [`ZeroVec`] /// and [`VarZeroVec`]. /// /// This type, like [`ZeroVec`] and [`VarZeroVec`], is able to zero-copy /// deserialize from appropriately formatted byte buffers. It is internally copy-on-write, so it can be mutated /// afterwards as necessary. /// /// Internally, a `ZeroMap` is a zero-copy vector for keys paired with a zero-copy vector for /// values, sorted by the keys. Therefore, all types used in `ZeroMap` need to work with either /// [`ZeroVec`] or [`VarZeroVec`]. /// /// This does mean that for fixed-size data, one must use the regular type (`u32`, `u8`, `char`, etc), /// whereas for variable-size data, `ZeroMap` will use the dynamically sized version (`str` not `String`, /// `ZeroSlice` not `ZeroVec`, `FooULE` not `Foo` for custom types) /// /// # Examples /// /// ``` /// use zerovec::ZeroMap; /// /// #[derive(serde::Serialize, serde::Deserialize)] /// struct Data<'a> { /// #[serde(borrow)] /// map: ZeroMap<'a, u32, str>, /// } /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// map.insert(&4, "four"); /// /// let data = Data { map }; /// /// let bincode_bytes = /// bincode::serialize(&data).expect("Serialization should be successful"); /// /// // Will deserialize without any allocations /// let deserialized: Data = bincode::deserialize(&bincode_bytes) /// .expect("Deserialization should be successful"); /// /// assert_eq!(data.map.get(&1), Some("one")); /// assert_eq!(data.map.get(&2), Some("two")); /// ``` /// /// [`VarZeroVec`]: crate::VarZeroVec // ZeroMap has only one invariant: keys.len() == values.len() // It is also expected that the keys are sorted, but this is not an invariant. See #1433 pubstruct ZeroMap<'a, K, V> where
K: ZeroMapKV<'a> + ?Sized,
V: ZeroMapKV<'a> + ?Sized,
{ pub(crate) keys: K::Container, pub(crate) values: V::Container,
}
/// Construct a new [`ZeroMap`] with a given capacity pubfn with_capacity(capacity: usize) -> Self { Self {
keys: K::Container::zvl_with_capacity(capacity),
values: V::Container::zvl_with_capacity(capacity),
}
}
/// Obtain a borrowed version of this map pubfn as_borrowed(&'a self) -> ZeroMapBorrowed<'a, K, V> {
ZeroMapBorrowed {
keys: self.keys.zvl_as_borrowed(),
values: self.values.zvl_as_borrowed(),
}
}
/// The number of elements in the [`ZeroMap`] pubfn len(&self) -> usize { self.values.zvl_len()
}
/// Whether the [`ZeroMap`] is empty pubfn is_empty(&self) -> bool { self.values.zvl_len() == 0
}
/// Remove all elements from the [`ZeroMap`] pubfn clear(&mutself) { self.keys.zvl_clear(); self.values.zvl_clear();
}
/// Reserve capacity for `additional` more elements to be inserted into /// the [`ZeroMap`] to avoid frequent reallocations. /// /// See [`Vec::reserve()`](alloc::vec::Vec::reserve) for more information. pubfn reserve(&mutself, additional: usize) { self.keys.zvl_reserve(additional); self.values.zvl_reserve(additional);
}
} impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a> + ?Sized + Ord,
V: ZeroMapKV<'a> + ?Sized,
{ /// Get the value associated with `key`, if it exists. /// /// For fixed-size ([`AsULE`]) `V` types, this _will_ return /// their corresponding [`AsULE::ULE`] type. If you wish to work with the `V` /// type directly, [`Self::get_copied()`] exists for convenience. /// /// ```rust /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// assert_eq!(map.get(&1), Some("one")); /// assert_eq!(map.get(&3), None); /// ``` pubfn get(&self, key: &K) -> Option<&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. /// /// ```rust /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// assert_eq!(map.get_by(|probe| probe.cmp(&1)), Some("one")); /// assert_eq!(map.get_by(|probe| probe.cmp(&3)), None); /// ``` pubfn get_by(&self, predicate: impl FnMut(&K) -> Ordering) -> Option<&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::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// assert!(map.contains_key(&1)); /// assert!(!map.contains_key(&3)); /// ``` pubfn contains_key(&self, key: &K) -> bool { self.keys.zvl_binary_search(key).is_ok()
}
/// Insert `value` with `key`, returning the existing value if it exists. /// /// ```rust /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// assert_eq!(map.get(&1), Some("one")); /// assert_eq!(map.get(&3), None); /// ``` pubfn insert(&mutself, key: &K, value: &V) -> Option<V::OwnedType> { matchself.keys.zvl_binary_search(key) {
Ok(index) => Some(self.values.zvl_replace(index, value)),
Err(index) => { self.keys.zvl_insert(index, key); self.values.zvl_insert(index, value);
None
}
}
}
/// Remove the value at `key`, returning it if it exists. /// /// ```rust /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, "one"); /// map.insert(&2, "two"); /// assert_eq!(map.remove(&1), Some("one".to_owned().into_boxed_str())); /// assert_eq!(map.get(&1), None); /// ``` pubfn remove(&mutself, key: &K) -> Option<V::OwnedType> { let idx = self.keys.zvl_binary_search(key).ok()?; self.keys.zvl_remove(idx);
Some(self.values.zvl_remove(idx))
}
/// Appends `value` with `key` to the end of the underlying vector, returning /// `key` and `value` _if it failed_. Useful for extending with an existing /// sorted list. /// ```rust /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// assert!(map.try_append(&1, "uno").is_none()); /// assert!(map.try_append(&3, "tres").is_none()); /// /// let unsuccessful = map.try_append(&3, "tres-updated"); /// assert!(unsuccessful.is_some(), "append duplicate of last key"); /// /// let unsuccessful = map.try_append(&2, "dos"); /// assert!(unsuccessful.is_some(), "append out of order"); /// /// assert_eq!(map.get(&1), Some("uno")); /// /// // contains the original value for the key: 3 /// assert_eq!(map.get(&3), Some("tres")); /// /// // not appended since it wasn't in order /// assert_eq!(map.get(&2), None); /// ``` #[must_use] pubfn try_append<'b>(&mut self, key: &'b K, value: &'b V) -> Option<(&'b K, &'b V)> { ifself.keys.zvl_len() != 0 { iflet Some(last) = self.keys.zvl_get(self.keys.zvl_len() - 1) { if K::Container::t_cmp_get(key, last) != Ordering::Greater { return Some((key, value));
}
}
}
impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a> + ?Sized,
V: ZeroMapKV<'a> + ?Sized,
{ /// Produce an ordered iterator over key-value pairs pubfn iter<'b>(
&'b self,
) -> impl ExactSizeIterator<
Item = (
&'b <K as ZeroMapKV<'a>>::GetType,
&'b <V as ZeroMapKV<'a>>::GetType,
),
> {
(0..self.keys.zvl_len()).map(move |idx| {
( #[allow(clippy::unwrap_used)] // idx is in-range self.keys.zvl_get(idx).unwrap(), #[allow(clippy::unwrap_used)] // idx is in-range self.values.zvl_get(idx).unwrap(),
)
})
}
/// Produce an ordered iterator over keys pubfn iter_keys<'b>(
&'b self,
) -> impl ExactSizeIterator<Item = &'b <K as ZeroMapKV<'a>>::GetType> { #[allow(clippy::unwrap_used)] // idx is in-range
(0..self.keys.zvl_len()).map(move |idx| self.keys.zvl_get(idx).unwrap())
}
/// Produce an iterator over values, ordered by keys pubfn iter_values<'b>(
&'b self,
) -> impl ExactSizeIterator<Item = &'b <V as ZeroMapKV<'a>>::GetType> { #[allow(clippy::unwrap_used)] // idx is in-range
(0..self.values.zvl_len()).map(move |idx| self.values.zvl_get(idx).unwrap())
}
}
impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a, Container = ZeroVec<'a, K>> + ?Sized,
V: ZeroMapKV<'a> + ?Sized,
K: AsULE,
{ /// Cast a `ZeroMap<K, V>` to `ZeroMap<P, V>` where `K` and `P` are [`AsULE`] types /// with the same representation. /// /// # Unchecked Invariants /// /// If `K` and `P` have different ordering semantics, unexpected behavior may occur. pubfn cast_zv_k_unchecked<P>(self) -> ZeroMap<'a, P, V> where
P: AsULE<ULE = K::ULE> + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized,
{
ZeroMap {
keys: self.keys.cast(),
values: self.values,
}
}
/// Convert a `ZeroMap<K, V>` to `ZeroMap<P, V>` where `K` and `P` are [`AsULE`] types /// with the same size. /// /// # Unchecked Invariants /// /// If `K` and `P` have different ordering semantics, unexpected behavior may occur. /// /// # Panics /// /// Panics if `K::ULE` and `P::ULE` are not the same size. pubfn try_convert_zv_k_unchecked<P>(self) -> Result<ZeroMap<'a, P, V>, ZeroVecError> where
P: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized,
{
Ok(ZeroMap {
keys: self.keys.try_into_converted()?,
values: self.values,
})
}
}
impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a> + ?Sized,
V: ZeroMapKV<'a, Container = ZeroVec<'a, V>> + ?Sized,
V: AsULE,
{ /// Cast a `ZeroMap<K, V>` to `ZeroMap<K, P>` where `V` and `P` are [`AsULE`] types /// with the same representation. /// /// # Unchecked Invariants /// /// If `V` and `P` have different ordering semantics, unexpected behavior may occur. pubfn cast_zv_v_unchecked<P>(self) -> ZeroMap<'a, K, P> where
P: AsULE<ULE = V::ULE> + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized,
{
ZeroMap {
keys: self.keys,
values: self.values.cast(),
}
}
/// Convert a `ZeroMap<K, V>` to `ZeroMap<K, P>` where `V` and `P` are [`AsULE`] types /// with the same size. /// /// # Unchecked Invariants /// /// If `V` and `P` have different ordering semantics, unexpected behavior may occur. /// /// # Panics /// /// Panics if `V::ULE` and `P::ULE` are not the same size. pubfn try_convert_zv_v_unchecked<P>(self) -> Result<ZeroMap<'a, K, P>, ZeroVecError> where
P: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized,
{
Ok(ZeroMap {
keys: self.keys,
values: self.values.try_into_converted()?,
})
}
}
impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a> + ?Sized + Ord,
V: ZeroMapKV<'a, Container = VarZeroVec<'a, V>> + ?Sized,
V: VarULE,
{ /// Same as `insert()`, but allows using [EncodeAsVarULE](crate::ule::EncodeAsVarULE) /// types with the value to avoid an extra allocation when dealing with custom ULE types. /// /// ```rust /// use std::borrow::Cow; /// use zerovec::ZeroMap; /// /// #[zerovec::make_varule(PersonULE)] /// #[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] /// struct Person<'a> { /// age: u8, /// name: Cow<'a, str>, /// } /// /// let mut map: ZeroMap<u32, PersonULE> = ZeroMap::new(); /// map.insert_var_v( /// &1, /// &Person { /// age: 20, /// name: "Joseph".into(), /// }, /// ); /// map.insert_var_v( /// &1, /// &Person { /// age: 35, /// name: "Carla".into(), /// }, /// ); /// assert_eq!(&map.get(&1).unwrap().name, "Carla"); /// assert!(map.get(&3).is_none()); /// ``` pubfn insert_var_v<VE: EncodeAsVarULE<V>>(&mutself, key: &K, value: &VE) -> Option<Box<V>> { matchself.keys.zvl_binary_search(key) {
Ok(index) => { #[allow(clippy::unwrap_used)] // binary search let ret = self.values.get(index).unwrap().to_boxed(); self.values.make_mut().replace(index, value);
Some(ret)
}
Err(index) => { self.keys.zvl_insert(index, key); self.values.make_mut().insert(index, value);
None
}
}
}
// insert_var_k, insert_var_kv are not possible since one cannot perform the binary search with EncodeAsVarULE // though we might be able to do it in the future if we add a trait for cross-Ord requirements
}
impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a> + ?Sized + Ord,
V: ZeroMapKV<'a> + ?Sized,
V: Copy,
{ /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE`. /// /// # Examples /// /// ```rust /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, &'a'); /// map.insert(&2, &'b'); /// assert_eq!(map.get_copied(&1), Some('a')); /// assert_eq!(map.get_copied(&3), None); #[inline] pubfn get_copied(&self, key: &K) -> Option<V> { let index = self.keys.zvl_binary_search(key).ok()?; self.get_copied_at(index)
}
/// Binary search the map with `predicate` to find a key, returning the value. /// /// For cases when `V` is fixed-size, use this method to obtain a direct copy of `V` /// instead of `V::ULE`. /// /// # Examples /// /// ```rust /// use zerovec::ZeroMap; /// /// let mut map = ZeroMap::new(); /// map.insert(&1, &'a'); /// map.insert(&2, &'b'); /// assert_eq!(map.get_copied_by(|probe| probe.cmp(&1)), Some('a')); /// assert_eq!(map.get_copied_by(|probe| probe.cmp(&3)), None); /// ``` #[inline] pubfn get_copied_by(&self, predicate: impl FnMut(&K) -> Ordering) -> Option<V> { let index = self.keys.zvl_binary_search_by(predicate).ok()?; self.get_copied_at(index)
}
fn get_copied_at(&self, index: usize) -> Option<V> { let ule = self.values.zvl_get(index)?; letmut result = Option::<V>::None;
V::Container::zvl_get_as_t(ule, |v| result.replace(*v)); #[allow(clippy::unwrap_used)] // `zvl_get_as_t` guarantees that the callback is invoked
Some(result.unwrap())
}
}
impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a> + ?Sized,
V: ZeroMapKV<'a, Container = ZeroVec<'a, V>> + ?Sized,
V: AsULE + Copy,
{ /// 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 pubfn iter_copied_values<'b>(
&'b self,
) -> impl Iterator<Item = (&'b <K as ZeroMapKV<'a>>::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()
ZeroSlice::get(&*self.values, idx).unwrap(),
)
})
}
}
impl<'a, K, V> ZeroMap<'a, K, V> where
K: ZeroMapKV<'a, Container = ZeroVec<'a, K>> + ?Sized,
V: ZeroMapKV<'a, Container = ZeroVec<'a, V>> + ?Sized,
K: AsULE + Copy,
V: AsULE + Copy,
{ /// 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 pubfn iter_copied<'b>(&'b self) -> impl Iterator<Item = (K, V)> + 'b { let keys = &self.keys; let values = &self.values;
(0..keys.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<ZeroMap<'b, K, V>> for ZeroMap<'a, K, V> where
K: for<'c> ZeroMapKV<'c> + ?Sized,
V: for<'c> ZeroMapKV<'c> + ?Sized,
<K as ZeroMapKV<'a>>::Container: PartialEq<<K as ZeroMapKV<'b>>::Container>,
<V as ZeroMapKV<'a>>::Container: PartialEq<<V as ZeroMapKV<'b>>::Container>,
{ fn eq(&self, other: &ZeroMap<'b, K, V>) -> bool { self.keys.eq(&other.keys) && self.values.eq(&other.values)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.