diff --git a/utils/zerovec/src/flexzerovec/mod.rs b/utils/zerovec/src/flexzerovec/mod.rs index c1081f21659..b9cc7cb870a 100644 --- a/utils/zerovec/src/flexzerovec/mod.rs +++ b/utils/zerovec/src/flexzerovec/mod.rs @@ -9,5 +9,6 @@ pub(crate) mod slice; pub(crate) mod vec; pub use owned::FlexZeroVecOwned; +pub(crate) use slice::chunk_to_usize; pub use slice::FlexZeroSlice; pub use vec::FlexZeroVec; diff --git a/utils/zerovec/src/flexzerovec/owned.rs b/utils/zerovec/src/flexzerovec/owned.rs index 34521f770f9..d9310cbc87b 100644 --- a/utils/zerovec/src/flexzerovec/owned.rs +++ b/utils/zerovec/src/flexzerovec/owned.rs @@ -52,6 +52,12 @@ impl FlexZeroVecOwned { FlexZeroVec::Owned(self) } + /// Clears all values out of this `FlexZeroVecOwned`. + #[inline] + pub fn clear(&mut self) { + *self = Self::new_empty() + } + /// Appends an item to the end of the vector. /// /// # Panics diff --git a/utils/zerovec/src/flexzerovec/slice.rs b/utils/zerovec/src/flexzerovec/slice.rs index 0f76f9fd07c..9efccff285a 100644 --- a/utils/zerovec/src/flexzerovec/slice.rs +++ b/utils/zerovec/src/flexzerovec/slice.rs @@ -5,8 +5,10 @@ use super::FlexZeroVec; use crate::ZeroVecError; use alloc::vec::Vec; +use core::cmp::Ordering; use core::fmt; use core::mem; +use core::ops::Range; const USIZE_WIDTH: usize = mem::size_of::(); @@ -25,7 +27,7 @@ pub struct FlexZeroSlice { /// into a `usize`. We cannot call `usize::from_le_bytes` directly because that function /// requires the high bits to be set to 0. #[inline] -fn chunk_to_usize(chunk: &[u8], width: usize) -> usize { +pub(crate) fn chunk_to_usize(chunk: &[u8], width: usize) -> usize { debug_assert_eq!(chunk.len(), width); let mut bytes = [0; USIZE_WIDTH]; #[allow(clippy::indexing_slicing)] // protected by debug_assert above @@ -194,9 +196,14 @@ impl FlexZeroSlice { #[inline] pub fn get(&self, index: usize) -> Option { let w = self.get_width(); - self.data - .get(index * w..index * w + w) - .map(|chunk| chunk_to_usize(chunk, w)) + self.get_chunk(index).map(|chunk| chunk_to_usize(chunk, w)) + } + + /// Gets the element at `index` as a chunk of bytes, or `None` if `index >= self.len()`. + #[inline] + pub(crate) fn get_chunk(&self, index: usize) -> Option<&[u8]> { + let w = self.get_width(); + self.data.get(index * w..index * w + w) } /// Gets the element at `index` without checking bounds. @@ -244,7 +251,7 @@ impl FlexZeroSlice { /// Creates a `Vec` from a [`FlexZeroSlice`] (or `FlexZeroVec`). /// - /// # Example + /// # Examples /// /// ``` /// use zerovec::vecs::FlexZeroVec; @@ -261,20 +268,117 @@ impl FlexZeroSlice { } /// Binary searches a sorted `FlexZeroSlice` for the given `usize` value. + /// + /// # Examples + /// + /// ``` + /// use zerovec::vecs::FlexZeroVec; + /// + /// let nums: &[usize] = &[211, 281, 421, 461]; + /// let fzv: FlexZeroVec = nums.iter().copied().collect(); + /// + /// assert_eq!(fzv.binary_search(0), Err(0)); + /// assert_eq!(fzv.binary_search(211), Ok(0)); + /// assert_eq!(fzv.binary_search(250), Err(1)); + /// assert_eq!(fzv.binary_search(281), Ok(1)); + /// assert_eq!(fzv.binary_search(300), Err(2)); + /// assert_eq!(fzv.binary_search(421), Ok(2)); + /// assert_eq!(fzv.binary_search(450), Err(3)); + /// assert_eq!(fzv.binary_search(461), Ok(3)); + /// assert_eq!(fzv.binary_search(462), Err(4)); + /// ``` + #[inline] pub fn binary_search(&self, needle: usize) -> Result { - // See comments in components.rs regarding the following code. + self.binary_search_by(|probe| probe.cmp(&needle)) + } - let zero_index = self.data.as_ptr() as *const _ as usize; + /// Binary searches a sorted range of a `FlexZeroSlice` for the given `usize` value. + /// + /// Indices are returned relative to the start of the range. + /// + /// # Examples + /// + /// ``` + /// use zerovec::vecs::FlexZeroVec; + /// + /// // Make a FlexZeroVec with two sorted ranges: 0..3 and 3..5 + /// let nums: &[usize] = &[111, 222, 444, 333, 555]; + /// let fzv: FlexZeroVec = nums.iter().copied().collect(); + /// + /// // Search in the first range: + /// assert_eq!(fzv.binary_search_in_range(0, 0..3), Some(Err(0))); + /// assert_eq!(fzv.binary_search_in_range(111, 0..3), Some(Ok(0))); + /// assert_eq!(fzv.binary_search_in_range(199, 0..3), Some(Err(1))); + /// assert_eq!(fzv.binary_search_in_range(222, 0..3), Some(Ok(1))); + /// assert_eq!(fzv.binary_search_in_range(399, 0..3), Some(Err(2))); + /// assert_eq!(fzv.binary_search_in_range(444, 0..3), Some(Ok(2))); + /// assert_eq!(fzv.binary_search_in_range(999, 0..3), Some(Err(3))); + /// + /// // Search in the second range: + /// assert_eq!(fzv.binary_search_in_range(0, 3..5), Some(Err(0))); + /// assert_eq!(fzv.binary_search_in_range(333, 3..5), Some(Ok(0))); + /// assert_eq!(fzv.binary_search_in_range(399, 3..5), Some(Err(1))); + /// assert_eq!(fzv.binary_search_in_range(555, 3..5), Some(Ok(1))); + /// assert_eq!(fzv.binary_search_in_range(999, 3..5), Some(Err(2))); + /// + /// // Out-of-bounds range: + /// assert_eq!(fzv.binary_search_in_range(0, 4..6), None); + /// ``` + #[inline] + pub fn binary_search_in_range( + &self, + needle: usize, + range: Range, + ) -> Option> { + self.binary_search_in_range_by(|probe| probe.cmp(&needle), range) + } + + /// Binary searches a sorted `FlexZeroSlice` according to a predicate function. + #[inline] + pub fn binary_search_by( + &self, + predicate: impl FnMut(usize) -> Ordering, + ) -> Result { debug_assert!(self.len() <= self.data.len()); // Safety: self.len() <= self.data.len() let scaled_slice = unsafe { self.data.get_unchecked(0..self.len()) }; + self.binary_search_impl(predicate, scaled_slice) + } + /// Binary searches a sorted range of a `FlexZeroSlice` according to a predicate function. + /// + /// Indices are returned relative to the start of the range. + #[inline] + pub fn binary_search_in_range_by( + &self, + predicate: impl FnMut(usize) -> Ordering, + range: Range, + ) -> Option> { + // Note: We need to check bounds separately, since `self.data.get(range)` does not return + // bounds errors, since it is indexing directly into the upscaled data array + if range.start >= self.len() || range.end > self.len() { + return None; + } + let scaled_slice = self.data.get(range)?; + Some(self.binary_search_impl(predicate, scaled_slice)) + } + + /// # Safety + /// + /// `scaled_slice` must be a subslice of `self.data` + fn binary_search_impl( + &self, + mut predicate: impl FnMut(usize) -> Ordering, + scaled_slice: &[u8], + ) -> Result { + // See comments in components.rs regarding the following code. + let zero_index = self.data.as_ptr() as *const _ as usize; scaled_slice.binary_search_by(|probe: &_| { // Note: `scaled_slice` is a slice of u8 let index = probe as *const _ as usize - zero_index; // Safety: we know this is in bounds let actual_probe = unsafe { self.get_unchecked(index) }; - ::cmp(&actual_probe, &needle) + predicate(actual_probe) }) } } diff --git a/utils/zerovec/src/flexzerovec/vec.rs b/utils/zerovec/src/flexzerovec/vec.rs index d7d54ad29b9..784b764d19f 100644 --- a/utils/zerovec/src/flexzerovec/vec.rs +++ b/utils/zerovec/src/flexzerovec/vec.rs @@ -17,13 +17,14 @@ use core::ops::Deref; /// /// The maximum value that can be stored in `FlexZeroVec` is `usize::MAX` on the current platform. /// -/// `FlexZeroVec` will be the data structure for storing `usize` in a `ZeroMap`; see -/// . +/// `FlexZeroVec` is the data structure for storing `usize` in a `ZeroMap`. /// /// `FlexZeroVec` derefs to [`FlexZeroSlice`], which contains most of the methods. /// /// # Examples /// +/// Storing a vec of `usize`s in a zero-copy way: +/// /// ``` /// use zerovec::vecs::FlexZeroVec; /// @@ -44,6 +45,32 @@ use core::ops::Deref; /// assert_eq!(7, bytes.len()); /// assert!(matches!(zv2, FlexZeroVec::Borrowed(_))); /// ``` +/// +/// Storing a map of `usize` to `usize` in a zero-copy way: +/// +/// ``` +/// use zerovec::ZeroMap; +/// +/// // Append some values to the ZeroMap +/// let mut zm = ZeroMap::::new(); +/// assert!(zm.try_append(&29, &92).is_none()); +/// assert!(zm.try_append(&38, &83).is_none()); +/// assert!(zm.try_append(&56, &65).is_none()); +/// assert_eq!(zm.len(), 3); +/// +/// // Insert another value into the middle +/// assert!(zm.try_append(&47, &74).is_some()); +/// assert!(zm.insert(&47, &74).is_none()); +/// assert_eq!(zm.len(), 4); +/// +/// // Verify that the values are correct +/// assert_eq!(zm.get_copied(&0), None); +/// assert_eq!(zm.get_copied(&29), Some(92)); +/// assert_eq!(zm.get_copied(&38), Some(83)); +/// assert_eq!(zm.get_copied(&47), Some(74)); +/// assert_eq!(zm.get_copied(&56), Some(65)); +/// assert_eq!(zm.get_copied(&usize::MAX), None); +/// ``` #[derive(Debug)] pub enum FlexZeroVec<'a> { Owned(FlexZeroVecOwned), @@ -215,3 +242,33 @@ impl FromIterator for FlexZeroVec<'_> { FlexZeroVecOwned::from_iter(iter).into_flexzerovec() } } + +#[test] +fn test_zeromap_usize() { + use crate::ZeroMap; + + let mut zm = ZeroMap::::new(); + assert!(zm.try_append(&29, &92).is_none()); + assert!(zm.try_append(&38, &83).is_none()); + assert!(zm.try_append(&47, &74).is_none()); + assert!(zm.try_append(&56, &65).is_none()); + + assert_eq!(zm.keys.get_width(), 1); + assert_eq!(zm.values.get_width(), 1); + + assert_eq!(zm.insert(&47, &744), Some(74)); + assert_eq!(zm.values.get_width(), 2); + assert_eq!(zm.insert(&47, &774), Some(744)); + assert_eq!(zm.values.get_width(), 2); + assert!(zm.try_append(&1100, &1).is_none()); + assert_eq!(zm.keys.get_width(), 2); + assert_eq!(zm.remove(&1100), Some(1)); + assert_eq!(zm.keys.get_width(), 1); + + assert_eq!(zm.get_copied(&0), None); + assert_eq!(zm.get_copied(&29), Some(92)); + assert_eq!(zm.get_copied(&38), Some(83)); + assert_eq!(zm.get_copied(&47), Some(774)); + assert_eq!(zm.get_copied(&56), Some(65)); + assert_eq!(zm.get_copied(&usize::MAX), None); +} diff --git a/utils/zerovec/src/lib.rs b/utils/zerovec/src/lib.rs index 3aa6a11b468..21c3f617f9e 100644 --- a/utils/zerovec/src/lib.rs +++ b/utils/zerovec/src/lib.rs @@ -232,6 +232,8 @@ pub use crate::map2d::map::ZeroMap2d; pub use crate::varzerovec::{slice::VarZeroSlice, vec::VarZeroVec}; pub use crate::zerovec::{ZeroSlice, ZeroVec}; +pub(crate) use flexzerovec::chunk_to_usize; + #[doc(hidden)] pub mod __zerovec_internal_reexport { pub use zerofrom::ZeroFrom; diff --git a/utils/zerovec/src/map/kv.rs b/utils/zerovec/src/map/kv.rs index 916b115d0a6..b3e650fc765 100644 --- a/utils/zerovec/src/map/kv.rs +++ b/utils/zerovec/src/map/kv.rs @@ -4,7 +4,8 @@ use super::vecs::{MutableZeroVecLike, ZeroVecLike}; use crate::ule::*; -use crate::varzerovec::{VarZeroSlice, VarZeroVec}; +use crate::vecs::{FlexZeroSlice, FlexZeroVec}; +use crate::vecs::{VarZeroSlice, VarZeroVec}; use crate::zerovec::{ZeroSlice, ZeroVec}; use alloc::boxed::Box; @@ -63,6 +64,13 @@ impl_sized_kv!(char); impl_sized_kv!(f32); impl_sized_kv!(f64); +impl<'a> ZeroMapKV<'a> for usize { + type Container = FlexZeroVec<'a>; + type Slice = FlexZeroSlice; + type GetType = [u8]; + type OwnedType = usize; +} + impl<'a, T> ZeroMapKV<'a> for Option where T: AsULE + 'static, diff --git a/utils/zerovec/src/map/map.rs b/utils/zerovec/src/map/map.rs index a15f1347b1f..915fa4b48df 100644 --- a/utils/zerovec/src/map/map.rs +++ b/utils/zerovec/src/map/map.rs @@ -377,8 +377,8 @@ where impl<'a, K, V> ZeroMap<'a, K, V> where K: ZeroMapKV<'a> + ?Sized + Ord, - V: ZeroMapKV<'a, Container = ZeroVec<'a, V>> + ?Sized, - V: AsULE + Copy, + V: ZeroMapKV<'a> + ?Sized, + V: Copy, { /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE`. /// @@ -392,9 +392,10 @@ where /// map.insert(&2, &'b'); /// assert_eq!(map.get_copied(&1), Some('a')); /// assert_eq!(map.get_copied(&3), None); + #[inline] pub fn get_copied(&self, key: &K) -> Option { let index = self.keys.zvl_binary_search(key).ok()?; - ZeroSlice::get(&*self.values, index) + self.get_copied_at(index) } /// Binary search the map with `predicate` to find a key, returning the value. @@ -413,9 +414,18 @@ where /// assert_eq!(map.get_copied_by(|probe| probe.cmp(&1)), Some('a')); /// assert_eq!(map.get_copied_by(|probe| probe.cmp(&3)), None); /// ``` + #[inline] pub fn get_copied_by(&self, predicate: impl FnMut(&K) -> Ordering) -> Option { let index = self.keys.zvl_binary_search_by(predicate).ok()?; - ZeroSlice::get(&*self.values, index) + self.get_copied_at(index) + } + + fn get_copied_at(&self, index: usize) -> Option { + let ule = self.values.zvl_get(index)?; + let mut result = Option::::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()) } } diff --git a/utils/zerovec/src/map/vecs.rs b/utils/zerovec/src/map/vecs.rs index 278a2d2b3d5..7ea15c69115 100644 --- a/utils/zerovec/src/map/vecs.rs +++ b/utils/zerovec/src/map/vecs.rs @@ -4,6 +4,7 @@ use crate::ule::*; use crate::varzerovec::owned::VarZeroVecOwned; +use crate::vecs::{FlexZeroSlice, FlexZeroVec, FlexZeroVecOwned}; use crate::{VarZeroSlice, VarZeroVec}; use crate::{ZeroSlice, ZeroVec}; use alloc::boxed::Box; @@ -67,7 +68,21 @@ pub trait ZeroVecLike { /// Check if this vector is in ascending order according to `T`s `Ord` impl fn zvl_is_ascending(&self) -> bool where - T: Ord; + T: Ord, + { + if let Some(first) = self.zvl_get(0) { + let mut prev = first; + for i in 1..self.zvl_len() { + #[allow(clippy::unwrap_used)] // looping over the valid indices + let curr = self.zvl_get(i).unwrap(); + if Self::get_cmp_get(prev, curr) != Ordering::Less { + return false; + } + prev = curr; + } + } + true + } /// Check if this vector is empty fn zvl_is_empty(&self) -> bool { self.zvl_len() == 0 @@ -107,6 +122,8 @@ pub trait ZeroVecLike { /// /// This uses a callback because it's not possible to return owned-or-borrowed /// types without GATs + /// + /// Impls should guarantee that the callback function is be called exactly once. fn zvl_get_as_t(g: &Self::GetType, f: impl FnOnce(&T) -> R) -> R; } @@ -196,15 +213,6 @@ where fn zvl_len(&self) -> usize { ZeroSlice::len(self) } - fn zvl_is_ascending(&self) -> bool - where - T: Ord, - { - #[allow(clippy::indexing_slicing)] // TODO(#1668) Clippy exceptions need docs or fixing. - self.as_ule_slice() - .windows(2) - .all(|w| T::from_unaligned(w[1]).cmp(&T::from_unaligned(w[0])) == Ordering::Greater) - } fn zvl_as_borrowed(&self) -> &ZeroSlice { &*self @@ -261,15 +269,6 @@ where fn zvl_len(&self) -> usize { ZeroSlice::len(self) } - fn zvl_is_ascending(&self) -> bool - where - T: Ord, - { - #[allow(clippy::indexing_slicing)] // TODO(#1668) Clippy exceptions need docs or fixing. - self.as_ule_slice() - .windows(2) - .all(|w| T::from_unaligned(w[1]).cmp(&T::from_unaligned(w[0])) == Ordering::Greater) - } fn zvl_as_borrowed(&self) -> &ZeroSlice { self @@ -369,21 +368,6 @@ where fn zvl_len(&self) -> usize { self.len() } - fn zvl_is_ascending(&self) -> bool - where - T: Ord, - { - if let Some(first) = self.get(0) { - let mut prev = first; - for element in self.iter().skip(1) { - if element.cmp(prev) != Ordering::Greater { - return false; - } - prev = element; - } - } - true - } fn zvl_as_borrowed(&self) -> &VarZeroSlice { self.as_slice() @@ -434,21 +418,6 @@ where fn zvl_len(&self) -> usize { self.len() } - fn zvl_is_ascending(&self) -> bool - where - T: Ord, - { - if let Some(first) = self.get(0) { - let mut prev = first; - for element in self.iter().skip(1) { - if element.cmp(prev) != Ordering::Greater { - return false; - } - prev = element; - } - } - true - } fn zvl_as_borrowed(&self) -> &VarZeroSlice { self @@ -517,6 +486,145 @@ where } } +impl<'a> ZeroVecLike for FlexZeroVec<'a> { + type GetType = [u8]; + type SliceVariant = FlexZeroSlice; + + fn zvl_new_borrowed() -> &'static Self::SliceVariant { + FlexZeroSlice::new_empty() + } + fn zvl_binary_search(&self, k: &usize) -> Result { + FlexZeroSlice::binary_search(self, *k) + } + fn zvl_binary_search_in_range( + &self, + k: &usize, + range: Range, + ) -> Option> { + FlexZeroSlice::binary_search_in_range(self, *k, range) + } + fn zvl_binary_search_by( + &self, + mut predicate: impl FnMut(&usize) -> Ordering, + ) -> Result { + FlexZeroSlice::binary_search_by(self, |probe| predicate(&probe)) + } + fn zvl_binary_search_in_range_by( + &self, + mut predicate: impl FnMut(&usize) -> Ordering, + range: Range, + ) -> Option> { + FlexZeroSlice::binary_search_in_range_by(self, |probe| predicate(&probe), range) + } + fn zvl_get(&self, index: usize) -> Option<&[u8]> { + self.get_chunk(index) + } + fn zvl_len(&self) -> usize { + FlexZeroSlice::len(self) + } + + fn zvl_as_borrowed(&self) -> &FlexZeroSlice { + &*self + } + + #[inline] + fn zvl_get_as_t(g: &[u8], f: impl FnOnce(&usize) -> R) -> R { + f(&crate::chunk_to_usize(g, g.len())) + } +} + +impl ZeroVecLike for FlexZeroSlice { + type GetType = [u8]; + type SliceVariant = FlexZeroSlice; + + fn zvl_new_borrowed() -> &'static Self::SliceVariant { + FlexZeroSlice::new_empty() + } + fn zvl_binary_search(&self, k: &usize) -> Result { + FlexZeroSlice::binary_search(self, *k) + } + fn zvl_binary_search_in_range( + &self, + k: &usize, + range: Range, + ) -> Option> { + FlexZeroSlice::binary_search_in_range(self, *k, range) + } + fn zvl_binary_search_by( + &self, + mut predicate: impl FnMut(&usize) -> Ordering, + ) -> Result { + FlexZeroSlice::binary_search_by(self, |probe| predicate(&probe)) + } + fn zvl_binary_search_in_range_by( + &self, + mut predicate: impl FnMut(&usize) -> Ordering, + range: Range, + ) -> Option> { + FlexZeroSlice::binary_search_in_range_by(self, |probe| predicate(&probe), range) + } + fn zvl_get(&self, index: usize) -> Option<&[u8]> { + self.get_chunk(index) + } + fn zvl_len(&self) -> usize { + FlexZeroSlice::len(self) + } + + fn zvl_as_borrowed(&self) -> &FlexZeroSlice { + self + } + + #[inline] + fn zvl_get_as_t(g: &Self::GetType, f: impl FnOnce(&usize) -> R) -> R { + f(&crate::chunk_to_usize(g, g.len())) + } +} + +impl<'a> MutableZeroVecLike<'a, usize> for FlexZeroVec<'a> { + type OwnedType = usize; + fn zvl_insert(&mut self, index: usize, value: &usize) { + self.to_mut().insert(index, *value) + } + fn zvl_remove(&mut self, index: usize) -> usize { + self.to_mut().remove(index) + } + fn zvl_replace(&mut self, index: usize, value: &usize) -> usize { + // TODO(#2028): Make this a single operation instead of two operations. + let mutable = self.to_mut(); + let old_value = mutable.remove(index); + mutable.insert(index, *value); + old_value + } + fn zvl_push(&mut self, value: &usize) { + self.to_mut().push(*value) + } + fn zvl_with_capacity(_cap: usize) -> Self { + // There is no `FlexZeroVec::with_capacity()` because it is variable-width + FlexZeroVec::Owned(FlexZeroVecOwned::new_empty()) + } + fn zvl_clear(&mut self) { + self.to_mut().clear() + } + fn zvl_reserve(&mut self, _addl: usize) { + // There is no `FlexZeroVec::reserve()` because it is variable-width + } + + fn owned_as_t(o: &Self::OwnedType) -> &usize { + o + } + + fn zvl_from_borrowed(b: &'a FlexZeroSlice) -> Self { + b.as_flexzerovec() + } + fn zvl_as_borrowed_inner(&self) -> Option<&'a FlexZeroSlice> { + if let FlexZeroVec::Borrowed(b) = *self { + Some(b) + } else { + None + } + } +} + #[cfg(test)] mod test { use super::*; diff --git a/utils/zerovec/src/map2d/cursor.rs b/utils/zerovec/src/map2d/cursor.rs index fa58d515095..aead31ed521 100644 --- a/utils/zerovec/src/map2d/cursor.rs +++ b/utils/zerovec/src/map2d/cursor.rs @@ -2,7 +2,6 @@ // 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::{ZeroMap2d, ZeroSlice}; use core::cmp::Ordering; @@ -206,8 +205,8 @@ impl<'l, 'a, K0, K1, V> ZeroMap2dCursor<'l, 'a, K0, K1, V> where K0: ZeroMapKV<'a>, K1: ZeroMapKV<'a> + Ord, - V: ZeroMapKV<'a, GetType = V::ULE>, - V: AsULE + Copy, + V: ZeroMapKV<'a>, + V: Copy, K0: ?Sized, K1: ?Sized, { @@ -223,11 +222,25 @@ where /// /// assert_eq!(map.get0(&6).unwrap().get1_copied(&7), Some(8)); /// ``` + #[inline] pub fn get1_copied(&self, key1: &K1) -> Option { let key1_index = self.get_key1_index(key1)?; - #[allow(clippy::unwrap_used)] // key1_index is valid - let ule = self.values.zvl_get(key1_index).unwrap(); - Some(V::from_unaligned(*ule)) + self.get1_copied_at(key1_index) + } + + /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE` + #[inline] + pub fn get1_copied_by(&self, predicate: impl FnMut(&K1) -> Ordering) -> Option { + let key1_index = self.get_key1_index_by(predicate)?; + self.get1_copied_at(key1_index) + } + + fn get1_copied_at(&self, index: usize) -> Option { + let ule = self.values.zvl_get(index)?; + let mut result = Option::::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()) } } diff --git a/utils/zerovec/src/map2d/map.rs b/utils/zerovec/src/map2d/map.rs index 8e9385602ab..6872490f2d3 100644 --- a/utils/zerovec/src/map2d/map.rs +++ b/utils/zerovec/src/map2d/map.rs @@ -587,8 +587,8 @@ impl<'a, K0, K1, V> ZeroMap2d<'a, K0, K1, V> where K0: ZeroMapKV<'a> + Ord, K1: ZeroMapKV<'a> + Ord, - V: ZeroMapKV<'a, GetType = V::ULE>, - V: AsULE + Copy + 'static, + V: ZeroMapKV<'a>, + V: Copy, K0: ?Sized, K1: ?Sized, { @@ -605,6 +605,7 @@ where /// /// assert_eq!(map.get_copied(&6, &7), Ok(8)); /// ``` + #[inline] pub fn get_copied(&self, key0: &K0, key1: &K1) -> Result { self.get0(key0) .ok_or(KeyError::K0)? diff --git a/utils/zerovec/src/yoke_impls.rs b/utils/zerovec/src/yoke_impls.rs index 7ca3b6326c8..56484eb6206 100644 --- a/utils/zerovec/src/yoke_impls.rs +++ b/utils/zerovec/src/yoke_impls.rs @@ -5,6 +5,7 @@ // This way we can copy-paste Yokeable impls #![allow(clippy::forget_copy)] +use crate::flexzerovec::FlexZeroVec; use crate::map::ZeroMapBorrowed; use crate::map::ZeroMapKV; use crate::map2d::ZeroMap2dBorrowed; @@ -69,6 +70,34 @@ unsafe impl<'a, T: 'static + VarULE + ?Sized> Yokeable<'a> for VarZeroVec<'stati } } +// This impl is similar to the impl on Cow and is safe for the same reasons +/// This impl can be made available by enabling the optional `yoke` feature of the `zerovec` crate +unsafe impl<'a> Yokeable<'a> for FlexZeroVec<'static> { + type Output = FlexZeroVec<'a>; + #[inline] + fn transform(&'a self) -> &'a Self::Output { + self + } + #[inline] + fn transform_owned(self) -> Self::Output { + self + } + #[inline] + unsafe fn make(from: Self::Output) -> Self { + debug_assert!(mem::size_of::() == mem::size_of::()); + let ptr: *const Self = (&from as *const Self::Output).cast(); + mem::forget(from); + ptr::read(ptr) + } + #[inline] + fn transform_mut(&'a mut self, f: F) + where + F: 'static + for<'b> FnOnce(&'b mut Self::Output), + { + unsafe { f(mem::transmute::<&mut Self, &mut Self::Output>(self)) } + } +} + /// This impl can be made available by enabling the optional `yoke` feature of the `zerovec` crate #[allow(clippy::transmute_ptr_to_ptr)] unsafe impl<'a, K, V> Yokeable<'a> for ZeroMap<'static, K, V> @@ -268,6 +297,11 @@ mod test { _data: ZeroVec<'data, u16>, } + #[derive(yoke::Yokeable, zerofrom::ZeroFrom)] + struct DeriveTest_FlexZeroVec<'data> { + _data: FlexZeroVec<'data>, + } + #[derive(yoke::Yokeable, zerofrom::ZeroFrom)] struct DeriveTest_VarZeroVec<'data> { _data: VarZeroVec<'data, str>, diff --git a/utils/zerovec/src/zerofrom_impls.rs b/utils/zerovec/src/zerofrom_impls.rs index c84d9201326..140e471fd34 100644 --- a/utils/zerovec/src/zerofrom_impls.rs +++ b/utils/zerovec/src/zerofrom_impls.rs @@ -4,6 +4,7 @@ use crate::map::ZeroMapKV; use crate::ule::*; +use crate::vecs::{FlexZeroSlice, FlexZeroVec}; use crate::{VarZeroSlice, VarZeroVec, ZeroMap, ZeroMap2d, ZeroSlice, ZeroVec}; use zerofrom::ZeroFrom; @@ -27,6 +28,20 @@ where } } +impl<'zf> ZeroFrom<'zf, FlexZeroVec<'_>> for FlexZeroVec<'zf> { + #[inline] + fn zero_from(other: &'zf FlexZeroVec<'_>) -> Self { + FlexZeroVec::Borrowed(&*other) + } +} + +impl<'zf> ZeroFrom<'zf, FlexZeroSlice> for FlexZeroVec<'zf> { + #[inline] + fn zero_from(other: &'zf FlexZeroSlice) -> Self { + FlexZeroVec::Borrowed(&*other) + } +} + impl<'zf, T> ZeroFrom<'zf, VarZeroSlice> for VarZeroVec<'zf, T> where T: 'static + VarULE + ?Sized,