diff --git a/CHANGELOG.md b/CHANGELOG.md index 609470f7e2d..1c991a6185e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - This release has multiple changes that affect the bit representation of various types. Do not update to this release if you wish to retain stable data formats. - Change the `VarZeroVecFormat` values shipped by default to use the same index and length width. This breaks data layout for all `VarZeroVec`s. (https://github.com/unicode-org/icu4x/pull/5594) - Optimize `MultiFieldsULE` to not store a length anymore. This breaks data layout for any `#[make_varule]`-using struct with multiple variable-sized fields. (https://github.com/unicode-org/icu4x/pull/5593) + - Remove `FlexZeroVec` (https://github.com/unicode-org/icu4x/pull/5604) - `writeable` diff --git a/utils/zerotrie/benches/overview.rs b/utils/zerotrie/benches/overview.rs index 832274378fa..2fceba84351 100644 --- a/utils/zerotrie/benches/overview.rs +++ b/utils/zerotrie/benches/overview.rs @@ -57,11 +57,11 @@ fn get_basic_bench(c: &mut Criterion) { #[cfg(feature = "bench")] g.bench_function("ZeroMap/usize", |b| { - let zm: ZeroMap<[u8], usize> = data.iter().copied().collect(); + let zm: ZeroMap<[u8], u32> = data.iter().map(|(a, b)| (*a, *b as u32)).collect(); b.iter(|| { for (key, expected) in black_box(data) { let actual = black_box(&zm).get_copied(key); - assert_eq!(Some(*expected), actual); + assert_eq!(Some(*expected as u32), actual); } }); }); @@ -88,22 +88,6 @@ fn get_basic_bench(c: &mut Criterion) { }); }); - #[cfg(feature = "bench")] - g.bench_function("ZeroHashMap/usize", |b| { - let zhm: ZeroHashMap<[u8], usize> = data - .iter() - .copied() - .collect(); - b.iter(|| { - for (key, expected) in black_box(data) { - let actual = black_box(&zhm).get(key); - // No get_copied on ZHM so we need to do it manually - let actual = actual.map(|x| >::zvl_get_as_t(x, |y| *y)); - assert_eq!(Some(*expected), actual); - } - }); - }); - #[cfg(feature = "bench")] g.bench_function("ZeroHashMap/u8", |b| { let zhm: ZeroHashMap<[u8], u8> = data.iter().map(|(k, v)| (*k, *v as u8)).collect(); @@ -171,11 +155,11 @@ fn get_subtags_bench_helper( #[cfg(feature = "bench")] g.bench_function("ZeroMap/usize", |b| { - let zm: ZeroMap<[u8], usize> = litemap.iter().map(|(a, b)| (*a, b)).collect(); + let zm: ZeroMap<[u8], u32> = litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); b.iter(|| { for (i, key) in black_box(strings).iter().enumerate() { let actual = black_box(&zm).get_copied(key.as_bytes()); - assert_eq!(Some(i), actual); + assert_eq!(Some(i as u32), actual); } }); }); @@ -193,27 +177,11 @@ fn get_subtags_bench_helper( #[cfg(feature = "bench")] g.bench_function("HashMap", |b| { - let hm: HashMap<&[u8], usize> = litemap.iter().map(|(a, b)| (*a, *b)).collect(); + let hm: HashMap<&[u8], u32> = litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); b.iter(|| { for (i, key) in black_box(strings).iter().enumerate() { let actual = black_box(&hm).get(key.as_bytes()); - assert_eq!(Some(&i), actual); - } - }); - }); - - #[cfg(feature = "bench")] - g.bench_function("ZeroHashMap/usize", |b| { - let zhm: ZeroHashMap<[u8], usize> = litemap - .iter() - .map(|(a, b)| (*a, b)) - .collect(); - b.iter(|| { - for (i, key) in black_box(strings).iter().enumerate() { - let actual = black_box(&zhm).get(key.as_bytes()); - // No get_copied on ZHM so we need to do it manually - let actual = actual.map(|x| >::zvl_get_as_t(x, |y| *y)); - assert_eq!(Some(i), actual); + assert_eq!(Some(i as u32), actual.copied()); } }); }); diff --git a/utils/zerotrie/tests/asciitrie_test.rs b/utils/zerotrie/tests/asciitrie_test.rs index ae9dcbcf9e3..c36be3b4cf2 100644 --- a/utils/zerotrie/tests/asciitrie_test.rs +++ b/utils/zerotrie/tests/asciitrie_test.rs @@ -58,7 +58,7 @@ fn test_basic() { } // Compare the size to a postcard ZeroMap - let zm: ZeroMap<[u8], usize> = data_ascii.iter().copied().collect(); + let zm: ZeroMap<[u8], u32> = data_ascii.iter().map(|(a, b)| (*a, *b as u32)).collect(); let mut serializer = postcard::Serializer { output: AllocVec::new(), }; @@ -69,5 +69,5 @@ fn test_basic() { .expect("Failed to finalize serializer output"); assert_eq!(26, bytes_ascii.len()); - assert_eq!(59, zeromap_bytes.len()); + assert_eq!(79, zeromap_bytes.len()); } diff --git a/utils/zerotrie/tests/builder_test.rs b/utils/zerotrie/tests/builder_test.rs index d92ad8520dc..f26e7a1fb3d 100644 --- a/utils/zerotrie/tests/builder_test.rs +++ b/utils/zerotrie/tests/builder_test.rs @@ -648,17 +648,18 @@ fn test_everything() { assert_bytes_eq!(36, trie_phf.as_bytes(), expected_bytes); check_phf_ascii_trie(&litemap, &trie_phf); - let zhm: zerovec::ZeroMap<[u8], usize> = litemap.iter().map(|(a, b)| (*a, b)).collect(); + let zhm: zerovec::ZeroMap<[u8], u32> = litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); - assert_eq!(zhm_buf.len(), 73); + assert_eq!(zhm_buf.len(), 90); let zhm: zerovec::ZeroMap<[u8], u8> = litemap.iter().map(|(a, b)| (*a, *b as u8)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); assert_eq!(zhm_buf.len(), 63); - let zhm: zerovec::ZeroHashMap<[u8], usize> = litemap.iter().map(|(a, b)| (*a, b)).collect(); + let zhm: zerovec::ZeroHashMap<[u8], u32> = + litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); - assert_eq!(zhm_buf.len(), 146); + assert_eq!(zhm_buf.len(), 163); let zhm: zerovec::ZeroHashMap<[u8], u8> = litemap.iter().map(|(a, b)| (*a, *b as u8)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); @@ -804,17 +805,18 @@ fn test_short_subtags_10pct() { assert_eq!(trie_phf.byte_len(), 1100); check_phf_ascii_trie(&litemap, &trie_phf); - let zhm: zerovec::ZeroMap<[u8], usize> = litemap.iter().map(|(a, b)| (*a, b)).collect(); + let zhm: zerovec::ZeroMap<[u8], u32> = litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); - assert_eq!(zhm_buf.len(), 1329); + assert_eq!(zhm_buf.len(), 1892); let zhm: zerovec::ZeroMap<[u8], u8> = litemap.iter().map(|(a, b)| (*a, *b as u8)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); assert_eq!(zhm_buf.len(), 1328); - let zhm: zerovec::ZeroHashMap<[u8], usize> = litemap.iter().map(|(a, b)| (*a, b)).collect(); + let zhm: zerovec::ZeroHashMap<[u8], u32> = + litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); - assert_eq!(zhm_buf.len(), 2835); + assert_eq!(zhm_buf.len(), 3398); let zhm: zerovec::ZeroHashMap<[u8], u8> = litemap.iter().map(|(a, b)| (*a, *b as u8)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); @@ -834,17 +836,18 @@ fn test_short_subtags() { assert_eq!(trie_phf.byte_len(), 9400); check_phf_ascii_trie(&litemap, &trie_phf); - let zm: zerovec::ZeroMap<[u8], usize> = litemap.iter().map(|(a, b)| (*a, b)).collect(); + let zm: zerovec::ZeroMap<[u8], u32> = litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); let zhm_buf = postcard::to_allocvec(&zm).unwrap(); - assert_eq!(zhm_buf.len(), 15180); + assert_eq!(zhm_buf.len(), 18933); let zm: zerovec::ZeroMap<[u8], u8> = litemap.iter().map(|(a, b)| (*a, *b as u8)).collect(); let zhm_buf = postcard::to_allocvec(&zm).unwrap(); assert_eq!(zhm_buf.len(), 13302); - let zhm: zerovec::ZeroHashMap<[u8], usize> = litemap.iter().map(|(a, b)| (*a, b)).collect(); + let zhm: zerovec::ZeroHashMap<[u8], u32> = + litemap.iter().map(|(a, b)| (*a, *b as u32)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); - assert_eq!(zhm_buf.len(), 30198); + assert_eq!(zhm_buf.len(), 33951); let zhm: zerovec::ZeroHashMap<[u8], u8> = litemap.iter().map(|(a, b)| (*a, *b as u8)).collect(); let zhm_buf = postcard::to_allocvec(&zhm).unwrap(); diff --git a/utils/zerovec/src/flexzerovec/databake.rs b/utils/zerovec/src/flexzerovec/databake.rs deleted file mode 100644 index 07551d76094..00000000000 --- a/utils/zerovec/src/flexzerovec/databake.rs +++ /dev/null @@ -1,78 +0,0 @@ -// 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 super::{FlexZeroSlice, FlexZeroVec}; -use databake::*; - -impl Bake for FlexZeroVec<'_> { - fn bake(&self, env: &CrateEnv) -> TokenStream { - env.insert("zerovec"); - if self.is_empty() { - quote! { zerovec::vecs::FlexZeroVec::new() } - } else { - let slice = self.as_ref().bake(env); - quote! { #slice.as_flexzerovec() } - } - } -} - -impl BakeSize for FlexZeroVec<'_> { - fn borrows_size(&self) -> usize { - self.as_ref().borrows_size() - } -} - -impl Bake for &FlexZeroSlice { - fn bake(&self, env: &CrateEnv) -> TokenStream { - env.insert("zerovec"); - if self.is_empty() { - quote! { zerovec::vecs::FlexZeroSlice::new_empty() } - } else { - let bytes = databake::Bake::bake(&self.as_bytes(), env); - quote! { unsafe { zerovec::vecs::FlexZeroSlice::from_byte_slice_unchecked(#bytes) } } - } - } -} - -impl BakeSize for &FlexZeroSlice { - fn borrows_size(&self) -> usize { - if self.is_empty() { - 0 - } else { - self.as_bytes().len() - } - } -} - -#[test] -fn test_baked_vec() { - test_bake!(FlexZeroVec, const, crate::vecs::FlexZeroVec::new(), zerovec); - test_bake!( - FlexZeroVec, - const, - unsafe { - crate::vecs::FlexZeroSlice::from_byte_slice_unchecked(b"\x02\x01\0\x16\0M\x01\x11") - } - .as_flexzerovec(), - zerovec - ); -} - -#[test] -fn test_baked_slice() { - test_bake!( - &FlexZeroSlice, - const, - crate::vecs::FlexZeroSlice::new_empty(), - zerovec - ); - test_bake!( - &FlexZeroSlice, - const, - unsafe { - crate::vecs::FlexZeroSlice::from_byte_slice_unchecked(b"\x02\x01\0\x16\0M\x01\x11") - }, - zerovec - ); -} diff --git a/utils/zerovec/src/flexzerovec/mod.rs b/utils/zerovec/src/flexzerovec/mod.rs deleted file mode 100644 index b6d7e780ac8..00000000000 --- a/utils/zerovec/src/flexzerovec/mod.rs +++ /dev/null @@ -1,20 +0,0 @@ -// 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 ). - -//! See [`FlexZeroVec`](crate::vecs::FlexZeroVec) for details. - -pub(crate) mod owned; -pub(crate) mod slice; -pub(crate) mod vec; - -#[cfg(feature = "databake")] -mod databake; - -#[cfg(feature = "serde")] -mod serde; - -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 deleted file mode 100644 index 7d7bfb33d6d..00000000000 --- a/utils/zerovec/src/flexzerovec/owned.rs +++ /dev/null @@ -1,335 +0,0 @@ -// 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 alloc::vec; -use alloc::vec::Vec; -use core::fmt; -use core::iter::FromIterator; -use core::ops::Deref; - -use super::FlexZeroSlice; -use super::FlexZeroVec; - -/// The fully-owned variant of [`FlexZeroVec`]. Contains all mutation methods. -// Safety invariant: the inner bytes must deref to a valid `FlexZeroSlice` -#[derive(Clone, PartialEq, Eq)] -pub struct FlexZeroVecOwned(Vec); - -impl FlexZeroVecOwned { - /// Creates a new [`FlexZeroVecOwned`] with zero elements. - pub fn new_empty() -> Self { - Self(vec![1]) - } - - /// Creates a [`FlexZeroVecOwned`] from a [`FlexZeroSlice`]. - pub fn from_slice(other: &FlexZeroSlice) -> FlexZeroVecOwned { - // safety: the bytes originate from a valid FlexZeroSlice - Self(other.as_bytes().to_vec()) - } - - /// Obtains this [`FlexZeroVecOwned`] as a [`FlexZeroSlice`]. - pub fn as_slice(&self) -> &FlexZeroSlice { - let slice: &[u8] = &self.0; - unsafe { - // safety: the slice is known to come from a valid parsed FlexZeroSlice - FlexZeroSlice::from_byte_slice_unchecked(slice) - } - } - - /// Mutably obtains this `FlexZeroVecOwned` as a [`FlexZeroSlice`]. - pub(crate) fn as_mut_slice(&mut self) -> &mut FlexZeroSlice { - let slice: &mut [u8] = &mut self.0; - unsafe { - // safety: the slice is known to come from a valid parsed FlexZeroSlice - FlexZeroSlice::from_byte_slice_mut_unchecked(slice) - } - } - - /// Converts this `FlexZeroVecOwned` into a [`FlexZeroVec::Owned`]. - #[inline] - pub fn into_flexzerovec(self) -> FlexZeroVec<'static> { - 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 - /// - /// Panics if inserting the element would require allocating more than `usize::MAX` bytes. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let mut zv: FlexZeroVec = [22, 44, 66].iter().copied().collect(); - /// zv.to_mut().push(33); - /// assert_eq!(zv.to_vec(), vec![22, 44, 66, 33]); - /// ``` - pub fn push(&mut self, item: usize) { - let insert_info = self.get_insert_info(item); - self.0.resize(insert_info.new_bytes_len, 0); - let insert_index = insert_info.new_count - 1; - self.as_mut_slice().insert_impl(insert_info, insert_index); - } - - /// Inserts an element into the middle of the vector. - /// - /// Caution: Both arguments to this function are of type `usize`. Please be careful to pass - /// the index first followed by the value second. - /// - /// # Panics - /// - /// Panics if `index > len`. - /// - /// Panics if inserting the element would require allocating more than `usize::MAX` bytes. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let mut zv: FlexZeroVec = [22, 44, 66].iter().copied().collect(); - /// zv.to_mut().insert(2, 33); - /// assert_eq!(zv.to_vec(), vec![22, 44, 33, 66]); - /// ``` - pub fn insert(&mut self, index: usize, item: usize) { - #[allow(clippy::panic)] // panic is documented in function contract - if index > self.len() { - panic!("index {} out of range {}", index, self.len()); - } - let insert_info = self.get_insert_info(item); - self.0.resize(insert_info.new_bytes_len, 0); - self.as_mut_slice().insert_impl(insert_info, index); - } - - /// Inserts an element into an ascending sorted vector - /// at a position that keeps the vector sorted. - /// - /// # Panics - /// - /// Panics if inserting the element would require allocating more than `usize::MAX` bytes. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVecOwned; - /// - /// let mut fzv = FlexZeroVecOwned::new_empty(); - /// fzv.insert_sorted(10); - /// fzv.insert_sorted(5); - /// fzv.insert_sorted(8); - /// - /// assert!(Iterator::eq(fzv.iter(), [5, 8, 10].iter().copied())); - /// ``` - pub fn insert_sorted(&mut self, item: usize) { - let index = match self.binary_search(item) { - Ok(i) => i, - Err(i) => i, - }; - let insert_info = self.get_insert_info(item); - self.0.resize(insert_info.new_bytes_len, 0); - self.as_mut_slice().insert_impl(insert_info, index); - } - - /// Removes and returns the element at the specified index. - /// - /// # Panics - /// - /// Panics if `index >= len`. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let mut zv: FlexZeroVec = [22, 44, 66].iter().copied().collect(); - /// let removed_item = zv.to_mut().remove(1); - /// assert_eq!(44, removed_item); - /// assert_eq!(zv.to_vec(), vec![22, 66]); - /// ``` - pub fn remove(&mut self, index: usize) -> usize { - #[allow(clippy::panic)] // panic is documented in function contract - if index >= self.len() { - panic!("index {} out of range {}", index, self.len()); - } - let remove_info = self.get_remove_info(index); - // Safety: `remove_index` is a valid index - let item = unsafe { self.get_unchecked(remove_info.remove_index) }; - let new_bytes_len = remove_info.new_bytes_len; - self.as_mut_slice().remove_impl(remove_info); - self.0.truncate(new_bytes_len); - item - } - - /// Removes and returns the last element from an ascending sorted vector. - /// - /// If the vector is not sorted, use [`FlexZeroVecOwned::remove()`] instead. Calling this - /// function would leave the FlexZeroVec in a safe, well-defined state; however, information - /// may be lost and/or the equality invariant might not hold. - /// - /// # Panics - /// - /// Panics if `self.is_empty()`. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let mut zv: FlexZeroVec = [22, 44, 66].iter().copied().collect(); - /// let popped_item = zv.to_mut().pop_sorted(); - /// assert_eq!(66, popped_item); - /// assert_eq!(zv.to_vec(), vec![22, 44]); - /// ``` - /// - /// Calling this function on a non-ascending vector could cause surprising results: - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let mut zv1: FlexZeroVec = [444, 222, 111].iter().copied().collect(); - /// let popped_item = zv1.to_mut().pop_sorted(); - /// assert_eq!(111, popped_item); - /// - /// // Oops! - /// assert_eq!(zv1.to_vec(), vec![188, 222]); - /// ``` - pub fn pop_sorted(&mut self) -> usize { - #[allow(clippy::panic)] // panic is documented in function contract - if self.is_empty() { - panic!("cannot pop from an empty vector"); - } - let remove_info = self.get_sorted_pop_info(); - // Safety: `remove_index` is a valid index - let item = unsafe { self.get_unchecked(remove_info.remove_index) }; - let new_bytes_len = remove_info.new_bytes_len; - self.as_mut_slice().remove_impl(remove_info); - self.0.truncate(new_bytes_len); - item - } -} - -impl Deref for FlexZeroVecOwned { - type Target = FlexZeroSlice; - fn deref(&self) -> &Self::Target { - self.as_slice() - } -} - -impl fmt::Debug for FlexZeroVecOwned { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self.to_vec()) - } -} - -impl From<&FlexZeroSlice> for FlexZeroVecOwned { - fn from(other: &FlexZeroSlice) -> Self { - Self::from_slice(other) - } -} - -impl FromIterator for FlexZeroVecOwned { - /// Creates a [`FlexZeroVecOwned`] from an iterator of `usize`. - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - let mut result = FlexZeroVecOwned::new_empty(); - for item in iter { - result.push(item); - } - result - } -} - -#[cfg(test)] -mod test { - use super::*; - - fn check_contents(fzv: &FlexZeroSlice, expected: &[usize]) { - assert_eq!(fzv.len(), expected.len(), "len: {fzv:?} != {expected:?}"); - assert_eq!( - fzv.is_empty(), - expected.is_empty(), - "is_empty: {fzv:?} != {expected:?}" - ); - assert_eq!( - fzv.first(), - expected.first().copied(), - "first: {fzv:?} != {expected:?}" - ); - assert_eq!( - fzv.last(), - expected.last().copied(), - "last: {fzv:?} != {expected:?}" - ); - for i in 0..(expected.len() + 1) { - assert_eq!( - fzv.get(i), - expected.get(i).copied(), - "@{i}: {fzv:?} != {expected:?}" - ); - } - } - - #[test] - fn test_basic() { - let mut fzv = FlexZeroVecOwned::new_empty(); - assert_eq!(fzv.get_width(), 1); - check_contents(&fzv, &[]); - - fzv.push(42); - assert_eq!(fzv.get_width(), 1); - check_contents(&fzv, &[42]); - - fzv.push(77); - assert_eq!(fzv.get_width(), 1); - check_contents(&fzv, &[42, 77]); - - // Scale up - fzv.push(300); - assert_eq!(fzv.get_width(), 2); - check_contents(&fzv, &[42, 77, 300]); - - // Does not need to be sorted - fzv.insert(1, 325); - assert_eq!(fzv.get_width(), 2); - check_contents(&fzv, &[42, 325, 77, 300]); - - fzv.remove(3); - assert_eq!(fzv.get_width(), 2); - check_contents(&fzv, &[42, 325, 77]); - - // Scale down - fzv.remove(1); - assert_eq!(fzv.get_width(), 1); - check_contents(&fzv, &[42, 77]); - } - - #[test] - fn test_build_sorted() { - let nums: &[usize] = &[0, 50, 0, 77, 831, 29, 89182, 931, 0, 77, 712381]; - let mut fzv = FlexZeroVecOwned::new_empty(); - - for num in nums { - fzv.insert_sorted(*num); - } - assert_eq!(fzv.get_width(), 3); - check_contents(&fzv, &[0, 0, 0, 29, 50, 77, 77, 831, 931, 89182, 712381]); - - for num in nums { - let index = fzv.binary_search(*num).unwrap(); - fzv.remove(index); - } - assert_eq!(fzv.get_width(), 1); - check_contents(&fzv, &[]); - } -} diff --git a/utils/zerovec/src/flexzerovec/serde.rs b/utils/zerovec/src/flexzerovec/serde.rs deleted file mode 100644 index fb7caa7a8b6..00000000000 --- a/utils/zerovec/src/flexzerovec/serde.rs +++ /dev/null @@ -1,175 +0,0 @@ -// 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 super::{FlexZeroSlice, FlexZeroVec}; -use alloc::vec::Vec; -use core::fmt; -use serde::de::{self, Deserialize, Deserializer, SeqAccess, Visitor}; -#[cfg(feature = "serde")] -use serde::ser::{Serialize, SerializeSeq, Serializer}; - -#[derive(Default)] -struct FlexZeroVecVisitor {} - -impl<'de> Visitor<'de> for FlexZeroVecVisitor { - type Value = FlexZeroVec<'de>; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a sequence or borrowed buffer of bytes") - } - - fn visit_borrowed_bytes(self, bytes: &'de [u8]) -> Result - where - E: de::Error, - { - FlexZeroVec::parse_byte_slice(bytes).map_err(de::Error::custom) - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: SeqAccess<'de>, - { - let mut vec: Vec = if let Some(capacity) = seq.size_hint() { - Vec::with_capacity(capacity) - } else { - Vec::new() - }; - while let Some(value) = seq.next_element::()? { - vec.push(value); - } - Ok(vec.into_iter().collect()) - } -} - -/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate -impl<'de, 'a> Deserialize<'de> for FlexZeroVec<'a> -where - 'de: 'a, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let visitor = FlexZeroVecVisitor::default(); - if deserializer.is_human_readable() { - deserializer.deserialize_seq(visitor) - } else { - deserializer.deserialize_bytes(visitor) - } - } -} - -/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate -impl<'de, 'a> Deserialize<'de> for &'a FlexZeroSlice -where - 'de: 'a, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - if deserializer.is_human_readable() { - Err(de::Error::custom( - "&FlexZeroSlice cannot be deserialized from human-readable formats", - )) - } else { - let deserialized: FlexZeroVec<'a> = FlexZeroVec::deserialize(deserializer)?; - let borrowed = if let FlexZeroVec::Borrowed(b) = deserialized { - b - } else { - return Err(de::Error::custom( - "&FlexZeroSlice can only deserialize in zero-copy ways", - )); - }; - Ok(borrowed) - } - } -} - -/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate -impl Serialize for FlexZeroVec<'_> { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - if serializer.is_human_readable() { - let mut seq = serializer.serialize_seq(Some(self.len()))?; - for value in self.iter() { - seq.serialize_element(&value)?; - } - seq.end() - } else { - serializer.serialize_bytes(self.as_bytes()) - } - } -} - -/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate -impl Serialize for FlexZeroSlice { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.as_flexzerovec().serialize(serializer) - } -} - -#[cfg(test)] -#[allow(non_camel_case_types)] -mod test { - use super::{FlexZeroSlice, FlexZeroVec}; - - #[derive(serde::Serialize, serde::Deserialize)] - struct DeriveTest_FlexZeroVec<'data> { - #[serde(borrow)] - _data: FlexZeroVec<'data>, - } - - #[derive(serde::Serialize, serde::Deserialize)] - struct DeriveTest_FlexZeroSlice<'data> { - #[serde(borrow)] - _data: &'data FlexZeroSlice, - } - - // [1, 22, 333, 4444]; - const BYTES: &[u8] = &[2, 0x01, 0x00, 0x16, 0x00, 0x4D, 0x01, 0x5C, 0x11]; - const JSON_STR: &str = "[1,22,333,4444]"; - const BINCODE_BUF: &[u8] = &[9, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 22, 0, 77, 1, 92, 17]; - - #[test] - fn test_serde_json() { - let zerovec_orig: FlexZeroVec = FlexZeroVec::parse_byte_slice(BYTES).expect("parse"); - let json_str = serde_json::to_string(&zerovec_orig).expect("serialize"); - assert_eq!(JSON_STR, json_str); - // FlexZeroVec should deserialize from JSON to either Vec or FlexZeroVec - let vec_new: Vec = - serde_json::from_str(&json_str).expect("deserialize from buffer to Vec"); - assert_eq!(zerovec_orig.to_vec(), vec_new); - let zerovec_new: FlexZeroVec = - serde_json::from_str(&json_str).expect("deserialize from buffer to FlexZeroVec"); - assert_eq!(zerovec_orig.to_vec(), zerovec_new.to_vec()); - assert!(matches!(zerovec_new, FlexZeroVec::Owned(_))); - } - - #[test] - fn test_serde_bincode() { - let zerovec_orig: FlexZeroVec = FlexZeroVec::parse_byte_slice(BYTES).expect("parse"); - let bincode_buf = bincode::serialize(&zerovec_orig).expect("serialize"); - assert_eq!(BINCODE_BUF, bincode_buf); - let zerovec_new: FlexZeroVec = - bincode::deserialize(&bincode_buf).expect("deserialize from buffer to FlexZeroVec"); - assert_eq!(zerovec_orig.to_vec(), zerovec_new.to_vec()); - assert!(matches!(zerovec_new, FlexZeroVec::Borrowed(_))); - } - - #[test] - fn test_vzv_borrowed() { - let zerovec_orig: &FlexZeroSlice = FlexZeroSlice::parse_byte_slice(BYTES).expect("parse"); - let bincode_buf = bincode::serialize(&zerovec_orig).expect("serialize"); - assert_eq!(BINCODE_BUF, bincode_buf); - let zerovec_new: &FlexZeroSlice = - bincode::deserialize(&bincode_buf).expect("deserialize from buffer to FlexZeroSlice"); - assert_eq!(zerovec_orig.to_vec(), zerovec_new.to_vec()); - } -} diff --git a/utils/zerovec/src/flexzerovec/slice.rs b/utils/zerovec/src/flexzerovec/slice.rs deleted file mode 100644 index 9546bf849b7..00000000000 --- a/utils/zerovec/src/flexzerovec/slice.rs +++ /dev/null @@ -1,722 +0,0 @@ -// 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 super::FlexZeroVec; -use crate::ule::UleError; -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::(); - -/// A zero-copy "slice" that efficiently represents `[usize]`. -#[repr(C, packed)] -pub struct FlexZeroSlice { - // Hard Invariant: 1 <= width <= USIZE_WIDTH (which is target_pointer_width) - // Soft Invariant: width == the width of the largest element - width: u8, - // Hard Invariant: data.len() % width == 0 - data: [u8], -} - -impl fmt::Debug for FlexZeroSlice { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_vec().fmt(f) - } -} - -impl PartialEq for FlexZeroSlice { - fn eq(&self, other: &Self) -> bool { - self.width == other.width && self.data == other.data - } -} -impl Eq for FlexZeroSlice {} - -/// Helper function to decode a little-endian "chunk" (byte slice of a specific length) -/// into a `usize`. We cannot call `usize::from_le_bytes` directly because that function -/// requires the high bits to be set to 0. -#[inline] -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 - bytes[0..width].copy_from_slice(chunk); - usize::from_le_bytes(bytes) -} - -impl FlexZeroSlice { - /// Constructs a new empty [`FlexZeroSlice`]. - /// - /// ``` - /// use zerovec::vecs::FlexZeroSlice; - /// - /// const EMPTY_SLICE: &FlexZeroSlice = FlexZeroSlice::new_empty(); - /// - /// assert!(EMPTY_SLICE.is_empty()); - /// assert_eq!(EMPTY_SLICE.len(), 0); - /// assert_eq!(EMPTY_SLICE.first(), None); - /// ``` - #[inline] - pub const fn new_empty() -> &'static Self { - const ARR: &[u8] = &[1u8]; - // Safety: The slice is a valid empty `FlexZeroSlice` - unsafe { Self::from_byte_slice_unchecked(ARR) } - } - - /// Safely constructs a [`FlexZeroSlice`] from a byte array. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroSlice; - /// - /// const FZS: &FlexZeroSlice = match FlexZeroSlice::parse_byte_slice(&[ - /// 2, // width - /// 0x42, 0x00, // first value - /// 0x07, 0x09, // second value - /// 0xFF, 0xFF, // third value - /// ]) { - /// Ok(v) => v, - /// Err(_) => panic!("invalid bytes"), - /// }; - /// - /// assert!(!FZS.is_empty()); - /// assert_eq!(FZS.len(), 3); - /// assert_eq!(FZS.first(), Some(0x0042)); - /// assert_eq!(FZS.get(0), Some(0x0042)); - /// assert_eq!(FZS.get(1), Some(0x0907)); - /// assert_eq!(FZS.get(2), Some(0xFFFF)); - /// assert_eq!(FZS.get(3), None); - /// assert_eq!(FZS.last(), Some(0xFFFF)); - /// ``` - pub const fn parse_byte_slice(bytes: &[u8]) -> Result<&Self, UleError> { - let (width_u8, data) = match bytes.split_first() { - Some(v) => v, - None => { - return Err(UleError::InvalidLength { - ty: "FlexZeroSlice", - len: 0, - }) - } - }; - let width = *width_u8 as usize; - if width < 1 || width > USIZE_WIDTH { - return Err(UleError::ParseError { - ty: "FlexZeroSlice", - }); - } - if data.len() % width != 0 { - return Err(UleError::InvalidLength { - ty: "FlexZeroSlice", - len: bytes.len(), - }); - } - // Safety: All hard invariants have been checked. - // Note: The soft invariant requires a linear search that we don't do here. - Ok(unsafe { Self::from_byte_slice_unchecked(bytes) }) - } - - /// Constructs a [`FlexZeroSlice`] without checking invariants. - /// - /// # Panics - /// - /// Panics if `bytes` is empty. - /// - /// # Safety - /// - /// Must be called on a valid [`FlexZeroSlice`] byte array. - #[inline] - pub const unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { - // Safety: The DST of FlexZeroSlice is a pointer to the `width` element and has a metadata - // equal to the length of the `data` field, which will be one less than the length of the - // overall array. - #[allow(clippy::panic)] // panic is documented in function contract - if bytes.is_empty() { - panic!("from_byte_slice_unchecked called with empty slice") - } - let slice = core::ptr::slice_from_raw_parts(bytes.as_ptr(), bytes.len() - 1); - &*(slice as *const Self) - } - - #[inline] - pub(crate) unsafe fn from_byte_slice_mut_unchecked(bytes: &mut [u8]) -> &mut Self { - // Safety: See comments in `from_byte_slice_unchecked` - let remainder = core::ptr::slice_from_raw_parts_mut(bytes.as_mut_ptr(), bytes.len() - 1); - &mut *(remainder as *mut Self) - } - - /// Returns this slice as its underlying `&[u8]` byte buffer representation. - /// - /// Useful for serialization. - /// - /// # Example - /// - /// ``` - /// use zerovec::vecs::FlexZeroSlice; - /// - /// let bytes: &[u8] = &[2, 0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80]; - /// let fzv = FlexZeroSlice::parse_byte_slice(bytes).expect("valid bytes"); - /// - /// assert_eq!(bytes, fzv.as_bytes()); - /// ``` - #[inline] - pub fn as_bytes(&self) -> &[u8] { - // Safety: See comments in `from_byte_slice_unchecked` - unsafe { - core::slice::from_raw_parts(self as *const Self as *const u8, self.data.len() + 1) - } - } - - /// Borrows this `FlexZeroSlice` as a [`FlexZeroVec::Borrowed`]. - #[inline] - pub const fn as_flexzerovec(&self) -> FlexZeroVec { - FlexZeroVec::Borrowed(self) - } - - /// Returns the number of elements in the `FlexZeroSlice`. - #[inline] - pub fn len(&self) -> usize { - self.data.len() / self.get_width() - } - - #[inline] - pub(crate) fn get_width(&self) -> usize { - usize::from(self.width) - } - - /// Returns whether there are zero elements in the `FlexZeroSlice`. - #[inline] - pub fn is_empty(&self) -> bool { - self.data.len() == 0 - } - - /// Gets the element at `index`, or `None` if `index >= self.len()`. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let fzv: FlexZeroVec = [22, 33].iter().copied().collect(); - /// assert_eq!(fzv.get(0), Some(22)); - /// assert_eq!(fzv.get(1), Some(33)); - /// assert_eq!(fzv.get(2), None); - /// ``` - #[inline] - pub fn get(&self, index: usize) -> Option { - if index >= self.len() { - None - } else { - Some(unsafe { self.get_unchecked(index) }) - } - } - - /// 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. - /// - /// # Safety - /// - /// `index` must be in-range. - #[inline] - pub unsafe fn get_unchecked(&self, index: usize) -> usize { - match self.width { - 1 => *self.data.get_unchecked(index) as usize, - 2 => { - let ptr = self.data.as_ptr().add(index * 2); - u16::from_le_bytes(core::ptr::read(ptr as *const [u8; 2])) as usize - } - _ => { - let mut bytes = [0; USIZE_WIDTH]; - let w = self.get_width(); - assert!(w <= USIZE_WIDTH); - let ptr = self.data.as_ptr().add(index * w); - core::ptr::copy_nonoverlapping(ptr, bytes.as_mut_ptr(), w); - usize::from_le_bytes(bytes) - } - } - } - - /// Gets the first element of the slice, or `None` if the slice is empty. - #[inline] - pub fn first(&self) -> Option { - let w = self.get_width(); - self.data.get(0..w).map(|chunk| chunk_to_usize(chunk, w)) - } - - /// Gets the last element of the slice, or `None` if the slice is empty. - #[inline] - pub fn last(&self) -> Option { - let l = self.data.len(); - if l == 0 { - None - } else { - let w = self.get_width(); - self.data - .get(l - w..l) - .map(|chunk| chunk_to_usize(chunk, w)) - } - } - - /// Gets an iterator over the elements of the slice as `usize`. - #[inline] - pub fn iter( - &self, - ) -> impl DoubleEndedIterator + '_ + ExactSizeIterator { - let w = self.get_width(); - self.data - .chunks_exact(w) - .map(move |chunk| chunk_to_usize(chunk, w)) - } - - /// Gets an iterator over pairs of elements. - /// - /// The second element of the final pair is `None`. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let nums: &[usize] = &[211, 281, 421, 461]; - /// let fzv: FlexZeroVec = nums.iter().copied().collect(); - /// - /// let mut pairs_it = fzv.iter_pairs(); - /// - /// assert_eq!(pairs_it.next(), Some((211, Some(281)))); - /// assert_eq!(pairs_it.next(), Some((281, Some(421)))); - /// assert_eq!(pairs_it.next(), Some((421, Some(461)))); - /// assert_eq!(pairs_it.next(), Some((461, None))); - /// assert_eq!(pairs_it.next(), None); - /// ``` - pub fn iter_pairs(&self) -> impl Iterator)> + '_ { - self.iter().zip(self.iter().skip(1).map(Some).chain([None])) - } - - /// Creates a `Vec` from a [`FlexZeroSlice`] (or `FlexZeroVec`). - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let nums: &[usize] = &[211, 281, 421, 461]; - /// let fzv: FlexZeroVec = nums.iter().copied().collect(); - /// let vec: Vec = fzv.to_vec(); - /// - /// assert_eq!(nums, vec.as_slice()); - /// ``` - #[inline] - pub fn to_vec(&self) -> Vec { - self.iter().collect() - } - - /// 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 { - self.binary_search_by(|probe| probe.cmp(&needle)) - } - - /// Binary searches a sorted range of a `FlexZeroSlice` for the given `usize` value. - /// - /// The indices in the return value are 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. - /// - /// The indices in the return value are 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)) - } - - /// Binary searches a `FlexZeroSlice` by its indices. - /// - /// The `predicate` function is passed in-bounds indices into the `FlexZeroSlice`. - #[inline] - pub fn binary_search_with_index( - &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_with_index_impl(predicate, scaled_slice) - } - - /// Binary searches a range of a `FlexZeroSlice` by its indices. - /// - /// The `predicate` function is passed in-bounds indices into the `FlexZeroSlice`, which are - /// relative to the start of the entire slice. - /// - /// The indices in the return value are relative to the start of the range. - #[inline] - pub fn binary_search_in_range_with_index( - &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_with_index_impl(predicate, scaled_slice)) - } - - /// # Safety - /// - /// `scaled_slice` must be a subslice of `self.data` - #[inline] - fn binary_search_impl( - &self, - mut predicate: impl FnMut(usize) -> Ordering, - scaled_slice: &[u8], - ) -> Result { - self.binary_search_with_index_impl( - |index| { - // Safety: The contract of `binary_search_with_index_impl` says `index` is in bounds - let actual_probe = unsafe { self.get_unchecked(index) }; - predicate(actual_probe) - }, - scaled_slice, - ) - } - - /// `predicate` is passed a valid index as an argument. - /// - /// # Safety - /// - /// `scaled_slice` must be a subslice of `self.data` - fn binary_search_with_index_impl( - &self, - mut predicate: impl FnMut(usize) -> Ordering, - scaled_slice: &[u8], - ) -> Result { - // This code is an absolute atrocity. This code is not a place of honor. This - // code is known to the State of California to cause cancer. - // - // Unfortunately, the stdlib's `binary_search*` functions can only operate on slices. - // We do not have a slice. We have something we can .get() and index on, but that is not - // a slice. - // - // The `binary_search*` functions also do not have a variant where they give you the element's - // index, which we could otherwise use to directly index `self`. - // We do have `self.indices`, but these are indices into a byte buffer, which cannot in - // isolation be used to recoup the logical index of the element they refer to. - // - // However, `binary_search_by()` provides references to the elements of the slice being iterated. - // Since the layout of Rust slices is well-defined, we can do pointer arithmetic on these references - // to obtain the index being used by the search. - // - // It's worth noting that the slice we choose to search is irrelevant, as long as it has the appropriate - // length. `self.indices` is defined to have length `self.len()`, so it is convenient to use - // here and does not require additional allocations. - // - // The alternative to doing this is to implement our own binary search. This is significantly less fun. - - // Note: We always use zero_index relative to the whole indices array, even if we are - // only searching a subslice of it. - 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; - predicate(index) - }) - } -} - -#[inline] -pub(crate) fn get_item_width(item_bytes: &[u8; USIZE_WIDTH]) -> usize { - USIZE_WIDTH - item_bytes.iter().rev().take_while(|b| **b == 0).count() -} - -/// Pre-computed information about a pending insertion operation. -/// -/// Do not create one of these directly; call `get_insert_info()`. -pub(crate) struct InsertInfo { - /// The bytes to be inserted, with zero-fill. - pub item_bytes: [u8; USIZE_WIDTH], - /// The new item width after insertion. - pub new_width: usize, - /// The new number of items in the vector: self.len() after insertion. - pub new_count: usize, - /// The new number of bytes required for the entire slice (self.data.len() + 1). - pub new_bytes_len: usize, -} - -impl FlexZeroSlice { - /// Compute the [`InsertInfo`] for inserting the specified item anywhere into the vector. - /// - /// # Panics - /// - /// Panics if inserting the element would require allocating more than `usize::MAX` bytes. - pub(crate) fn get_insert_info(&self, new_item: usize) -> InsertInfo { - let item_bytes = new_item.to_le_bytes(); - let item_width = get_item_width(&item_bytes); - let old_width = self.get_width(); - let new_width = core::cmp::max(old_width, item_width); - let new_count = 1 + (self.data.len() / old_width); - #[allow(clippy::unwrap_used)] // panic is documented in function contract - let new_bytes_len = new_count - .checked_mul(new_width) - .unwrap() - .checked_add(1) - .unwrap(); - InsertInfo { - item_bytes, - new_width, - new_count, - new_bytes_len, - } - } - - /// This function should be called on a slice with a data array `new_data_len` long - /// which previously held `new_count - 1` elements. - /// - /// After calling this function, all bytes in the slice will have been written. - pub(crate) fn insert_impl(&mut self, insert_info: InsertInfo, insert_index: usize) { - let InsertInfo { - item_bytes, - new_width, - new_count, - new_bytes_len, - } = insert_info; - debug_assert!(new_width <= USIZE_WIDTH); - debug_assert!(new_width >= self.get_width()); - debug_assert!(insert_index < new_count); - debug_assert_eq!(new_bytes_len, new_count * new_width + 1); - debug_assert_eq!(new_bytes_len, self.data.len() + 1); - // For efficiency, calculate how many items we can skip copying. - let lower_i = if new_width == self.get_width() { - insert_index - } else { - 0 - }; - // Copy elements starting from the end into the new empty section of the vector. - // Note: We could copy fully in place, but we need to set 0 bytes for the high bytes, - // so we stage the new value on the stack. - for i in (lower_i..new_count).rev() { - let bytes_to_write = if i == insert_index { - item_bytes - } else { - let j = if i > insert_index { i - 1 } else { i }; - debug_assert!(j < new_count - 1); - // Safety: j is in range (assertion on previous line), and it has not been - // overwritten yet since we are walking backwards. - unsafe { self.get_unchecked(j).to_le_bytes() } - }; - // Safety: The vector has capacity for `new_width` items at the new index, which is - // later in the array than the bytes that we read above. - unsafe { - core::ptr::copy_nonoverlapping( - bytes_to_write.as_ptr(), - self.data.as_mut_ptr().add(new_width * i), - new_width, - ); - } - } - self.width = new_width as u8; - } -} - -/// Pre-computed information about a pending removal operation. -/// -/// Do not create one of these directly; call `get_remove_info()` or `get_sorted_pop_info()`. -pub(crate) struct RemoveInfo { - /// The index of the item to be removed. - pub remove_index: usize, - /// The new item width after insertion. - pub new_width: usize, - /// The new number of items in the vector: self.len() after insertion. - pub new_count: usize, - /// The new number of bytes required for the entire slice (self.data.len() + 1). - pub new_bytes_len: usize, -} - -impl FlexZeroSlice { - /// Compute the [`RemoveInfo`] for removing the item at the specified index. - pub(crate) fn get_remove_info(&self, remove_index: usize) -> RemoveInfo { - debug_assert!(remove_index < self.len()); - // Safety: remove_index is in range (assertion on previous line) - let item_bytes = unsafe { self.get_unchecked(remove_index).to_le_bytes() }; - let item_width = get_item_width(&item_bytes); - let old_width = self.get_width(); - let old_count = self.data.len() / old_width; - let new_width = if item_width < old_width { - old_width - } else { - debug_assert_eq!(old_width, item_width); - // We might be removing the widest element. If so, we need to scale down. - let mut largest_width = 1; - for i in 0..old_count { - if i == remove_index { - continue; - } - // Safety: i is in range (between 0 and old_count) - let curr_bytes = unsafe { self.get_unchecked(i).to_le_bytes() }; - let curr_width = get_item_width(&curr_bytes); - largest_width = core::cmp::max(curr_width, largest_width); - } - largest_width - }; - let new_count = old_count - 1; - // Note: the following line won't overflow because we are making the slice shorter. - let new_bytes_len = new_count * new_width + 1; - RemoveInfo { - remove_index, - new_width, - new_count, - new_bytes_len, - } - } - - /// Returns the [`RemoveInfo`] for removing the last element. Should be called - /// on a slice sorted in ascending order. - /// - /// This is more efficient than `get_remove_info()` because it doesn't require a - /// linear traversal of the vector in order to calculate `new_width`. - pub(crate) fn get_sorted_pop_info(&self) -> RemoveInfo { - debug_assert!(!self.is_empty()); - let remove_index = self.len() - 1; - let old_count = self.len(); - let new_width = if old_count == 1 { - 1 - } else { - // Safety: the FlexZeroSlice has at least two elements - let largest_item = unsafe { self.get_unchecked(remove_index - 1).to_le_bytes() }; - get_item_width(&largest_item) - }; - let new_count = old_count - 1; - // Note: the following line won't overflow because we are making the slice shorter. - let new_bytes_len = new_count * new_width + 1; - RemoveInfo { - remove_index, - new_width, - new_count, - new_bytes_len, - } - } - - /// This function should be called on a valid slice. - /// - /// After calling this function, the slice data should be truncated to `new_data_len` bytes. - pub(crate) fn remove_impl(&mut self, remove_info: RemoveInfo) { - let RemoveInfo { - remove_index, - new_width, - new_count, - .. - } = remove_info; - debug_assert!(new_width <= self.get_width()); - debug_assert!(new_count < self.len()); - // For efficiency, calculate how many items we can skip copying. - let lower_i = if new_width == self.get_width() { - remove_index - } else { - 0 - }; - // Copy elements starting from the beginning to compress the vector to fewer bytes. - for i in lower_i..new_count { - let j = if i < remove_index { i } else { i + 1 }; - // Safety: j is in range because j <= new_count < self.len() - let bytes_to_write = unsafe { self.get_unchecked(j).to_le_bytes() }; - // Safety: The bytes are being copied to a section of the array that is not after - // the section of the array that currently holds the bytes. - unsafe { - core::ptr::copy_nonoverlapping( - bytes_to_write.as_ptr(), - self.data.as_mut_ptr().add(new_width * i), - new_width, - ); - } - } - self.width = new_width as u8; - } -} diff --git a/utils/zerovec/src/flexzerovec/vec.rs b/utils/zerovec/src/flexzerovec/vec.rs deleted file mode 100644 index e149d51d75d..00000000000 --- a/utils/zerovec/src/flexzerovec/vec.rs +++ /dev/null @@ -1,275 +0,0 @@ -// 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 super::FlexZeroSlice; -use super::FlexZeroVecOwned; -use crate::ule::UleError; -use core::cmp::Ordering; -use core::iter::FromIterator; -use core::ops::Deref; - -/// A zero-copy data structure that efficiently stores integer values. -/// -/// `FlexZeroVec` automatically increases or decreases its storage capacity based on the largest -/// integer stored in the vector. It therefore results in lower memory usage when smaller numbers -/// are usually stored, but larger values must sometimes also be stored. -/// -/// The maximum value that can be stored in `FlexZeroVec` is `usize::MAX` on the current platform. -/// -/// `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; -/// -/// // Create a FlexZeroVec and add a few numbers to it -/// let mut zv1 = FlexZeroVec::new(); -/// zv1.to_mut().push(55); -/// zv1.to_mut().push(33); -/// zv1.to_mut().push(999); -/// assert_eq!(zv1.to_vec(), vec![55, 33, 999]); -/// -/// // Convert it to bytes and back -/// let bytes = zv1.as_bytes(); -/// let zv2 = -/// FlexZeroVec::parse_byte_slice(bytes).expect("bytes should round-trip"); -/// assert_eq!(zv2.to_vec(), vec![55, 33, 999]); -/// -/// // Verify the compact storage -/// 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)] -#[non_exhaustive] -pub enum FlexZeroVec<'a> { - Owned(FlexZeroVecOwned), - Borrowed(&'a FlexZeroSlice), -} - -impl<'a> Deref for FlexZeroVec<'a> { - type Target = FlexZeroSlice; - fn deref(&self) -> &Self::Target { - match self { - FlexZeroVec::Owned(v) => v.deref(), - FlexZeroVec::Borrowed(v) => v, - } - } -} - -impl<'a> AsRef for FlexZeroVec<'a> { - fn as_ref(&self) -> &FlexZeroSlice { - self.deref() - } -} - -impl Eq for FlexZeroVec<'_> {} - -impl<'a, 'b> PartialEq> for FlexZeroVec<'a> { - #[inline] - fn eq(&self, other: &FlexZeroVec<'b>) -> bool { - self.iter().eq(other.iter()) - } -} - -impl<'a> Default for FlexZeroVec<'a> { - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl<'a> PartialOrd for FlexZeroVec<'a> { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl<'a> Ord for FlexZeroVec<'a> { - fn cmp(&self, other: &Self) -> Ordering { - self.iter().cmp(other.iter()) - } -} - -impl<'a> FlexZeroVec<'a> { - #[inline] - /// Creates a new, borrowed, empty `FlexZeroVec`. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let zv: FlexZeroVec = FlexZeroVec::new(); - /// assert!(zv.is_empty()); - /// ``` - pub const fn new() -> Self { - Self::Borrowed(FlexZeroSlice::new_empty()) - } - - /// Parses a `&[u8]` buffer into a `FlexZeroVec`. - /// - /// The bytes within the byte buffer must remain constant for the life of the FlexZeroVec. - /// - /// # Endianness - /// - /// The byte buffer must be encoded in little-endian, even if running in a big-endian - /// environment. This ensures a consistent representation of data across platforms. - /// - /// # Max Value - /// - /// The bytes will fail to parse if the high value is greater than the capacity of `usize` - /// on this platform. For example, a `FlexZeroVec` created on a 64-bit platform might fail - /// to deserialize on a 32-bit platform. - /// - /// # Example - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let bytes: &[u8] = &[2, 0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01]; - /// let zv = FlexZeroVec::parse_byte_slice(bytes).expect("valid slice"); - /// - /// assert!(matches!(zv, FlexZeroVec::Borrowed(_))); - /// assert_eq!(zv.get(2), Some(421)); - /// ``` - pub fn parse_byte_slice(bytes: &'a [u8]) -> Result { - let slice: &'a FlexZeroSlice = FlexZeroSlice::parse_byte_slice(bytes)?; - Ok(Self::Borrowed(slice)) - } - - /// Converts a borrowed FlexZeroVec to an owned FlexZeroVec. No-op if already owned. - /// - /// # Example - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let bytes: &[u8] = &[2, 0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01]; - /// let zv = FlexZeroVec::parse_byte_slice(bytes).expect("valid bytes"); - /// assert!(matches!(zv, FlexZeroVec::Borrowed(_))); - /// - /// let owned = zv.into_owned(); - /// assert!(matches!(owned, FlexZeroVec::Owned(_))); - /// ``` - pub fn into_owned(self) -> FlexZeroVec<'static> { - match self { - Self::Owned(owned) => FlexZeroVec::Owned(owned), - Self::Borrowed(slice) => FlexZeroVec::Owned(FlexZeroVecOwned::from_slice(slice)), - } - } - - /// Allows the FlexZeroVec to be mutated by converting it to an owned variant, and producing - /// a mutable [`FlexZeroVecOwned`]. - /// - /// # Example - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let bytes: &[u8] = &[2, 0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x01]; - /// let mut zv = FlexZeroVec::parse_byte_slice(bytes).expect("valid bytes"); - /// assert!(matches!(zv, FlexZeroVec::Borrowed(_))); - /// - /// zv.to_mut().push(12); - /// assert!(matches!(zv, FlexZeroVec::Owned(_))); - /// assert_eq!(zv.get(4), Some(12)); - /// ``` - pub fn to_mut(&mut self) -> &mut FlexZeroVecOwned { - match self { - Self::Owned(ref mut owned) => owned, - Self::Borrowed(slice) => { - *self = FlexZeroVec::Owned(FlexZeroVecOwned::from_slice(slice)); - // recursion is limited since we are guaranteed to hit the Owned branch - self.to_mut() - } - } - } - - /// Remove all elements from this FlexZeroVec and reset it to an empty borrowed state. - /// - /// # Examples - /// - /// ``` - /// use zerovec::vecs::FlexZeroVec; - /// - /// let mut zv: FlexZeroVec = [1, 2, 3].iter().copied().collect(); - /// assert!(!zv.is_empty()); - /// zv.clear(); - /// assert!(zv.is_empty()); - /// ``` - pub fn clear(&mut self) { - *self = Self::Borrowed(FlexZeroSlice::new_empty()) - } -} - -impl FromIterator for FlexZeroVec<'_> { - /// Creates a [`FlexZeroVec::Owned`] from an iterator of `usize`. - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - 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 ec7c497737c..11dac72c10f 100644 --- a/utils/zerovec/src/lib.rs +++ b/utils/zerovec/src/lib.rs @@ -213,7 +213,6 @@ extern crate alloc; -mod flexzerovec; #[cfg(feature = "hashmap")] pub mod hashmap; mod map; @@ -238,8 +237,6 @@ 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)] // macro use pub mod __zerovec_internal_reexport { pub use zerofrom::ZeroFrom; @@ -294,8 +291,6 @@ pub mod vecs { pub use crate::varzerovec::{VarZeroSlice, VarZeroVec}; pub use crate::varzerovec::{Index16, Index32, Index8, VarZeroVecFormat, VarZeroVecOwned}; - - pub use crate::flexzerovec::{FlexZeroSlice, FlexZeroVec, FlexZeroVecOwned}; } // Proc macro reexports @@ -547,12 +542,10 @@ mod tests { check_size_of!(56 | 48, ZeroMap); check_size_of!(64 | 48, ZeroMap); check_size_of!(120 | 96, ZeroMap2d); - check_size_of!(32 | 24, vecs::FlexZeroVec); check_size_of!(24, Option>); check_size_of!(32 | 24, Option>); check_size_of!(64 | 56 | 48, Option>); check_size_of!(120 | 104 | 96, Option>); - check_size_of!(32 | 24, Option); } } diff --git a/utils/zerovec/src/map/kv.rs b/utils/zerovec/src/map/kv.rs index 1923ed99111..09ed72f53dc 100644 --- a/utils/zerovec/src/map/kv.rs +++ b/utils/zerovec/src/map/kv.rs @@ -4,7 +4,6 @@ use super::vecs::{MutableZeroVecLike, ZeroVecLike}; use crate::ule::*; -use crate::vecs::{FlexZeroSlice, FlexZeroVec}; use crate::vecs::{VarZeroSlice, VarZeroVec}; use crate::zerovec::{ZeroSlice, ZeroVec}; use alloc::boxed::Box; @@ -66,13 +65,6 @@ 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 Option: AsULE + 'static, diff --git a/utils/zerovec/src/map/vecs.rs b/utils/zerovec/src/map/vecs.rs index 5ee93d3fead..b46109dbc17 100644 --- a/utils/zerovec/src/map/vecs.rs +++ b/utils/zerovec/src/map/vecs.rs @@ -4,7 +4,7 @@ use crate::ule::*; use crate::varzerovec::owned::VarZeroVecOwned; -use crate::vecs::{FlexZeroSlice, FlexZeroVec, FlexZeroVecOwned, VarZeroVecFormat}; +use crate::vecs::VarZeroVecFormat; use crate::{VarZeroSlice, VarZeroVec}; use crate::{ZeroSlice, ZeroVec}; use alloc::boxed::Box; @@ -524,151 +524,6 @@ 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 - } - } - - #[allow(clippy::unwrap_used)] // documented panic - fn zvl_permute(&mut self, permutation: &mut [usize]) { - assert_eq!(permutation.len(), self.zvl_len()); - *self = permutation.iter().map(|&i| self.get(i).unwrap()).collect(); - } -} - #[cfg(test)] mod test { use super::*; @@ -712,13 +567,5 @@ mod test { let mut permutation = vec![3, 2, 1, 0, 6, 5, 4]; vzv.zvl_permute(&mut permutation); assert_eq!(&vzv, &["44", "33", "22", "11", "77", "66", "55"]); - - let mut fzv: FlexZeroVec = [11, 22, 33, 44, 55, 66, 77].into_iter().collect(); - let mut permutation = vec![3, 2, 1, 0, 6, 5, 4]; - fzv.zvl_permute(&mut permutation); - assert_eq!( - fzv.iter().collect::>(), - [44, 33, 22, 11, 77, 66, 55].into_iter().collect::>() - ); } } diff --git a/utils/zerovec/src/yoke_impls.rs b/utils/zerovec/src/yoke_impls.rs index 9621478567a..7ae10f74153 100644 --- a/utils/zerovec/src/yoke_impls.rs +++ b/utils/zerovec/src/yoke_impls.rs @@ -9,7 +9,6 @@ #![allow(clippy::forget_copy)] #![allow(clippy::forget_non_drop)] -use crate::flexzerovec::FlexZeroVec; use crate::map::ZeroMapBorrowed; use crate::map::ZeroMapKV; use crate::map2d::ZeroMap2dBorrowed; @@ -74,34 +73,6 @@ 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 requires enabling the optional `yoke` Cargo 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 from = mem::ManuallyDrop::new(from); - let ptr: *const Self = (&*from as *const Self::Output).cast(); - 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 requires enabling the optional `yoke` Cargo feature of the `zerovec` crate #[allow(clippy::transmute_ptr_to_ptr)] unsafe impl<'a, K, V> Yokeable<'a> for ZeroMap<'static, K, V> @@ -294,7 +265,7 @@ where #[allow(non_camel_case_types, non_snake_case)] mod test { use super::*; - use crate::{vecs::FlexZeroSlice, VarZeroSlice, ZeroSlice}; + use crate::{VarZeroSlice, ZeroSlice}; use databake::*; // Note: The following derives cover Yoke as well as Serde and databake. These may partially @@ -340,46 +311,6 @@ mod test { ); } - #[derive(yoke::Yokeable, zerofrom::ZeroFrom)] - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[cfg_attr(feature = "databake", derive(databake::Bake))] - #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] - struct DeriveTest_FlexZeroVec<'data> { - #[cfg_attr(feature = "serde", serde(borrow))] - _data: FlexZeroVec<'data>, - } - - #[test] - fn bake_FlexZeroVec() { - test_bake!( - DeriveTest_FlexZeroVec<'static>, - crate::yoke_impls::test::DeriveTest_FlexZeroVec { - _data: crate::vecs::FlexZeroVec::new(), - }, - zerovec, - ); - } - - #[derive(yoke::Yokeable)] - #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] - #[cfg_attr(feature = "databake", derive(databake::Bake))] - #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] - struct DeriveTest_FlexZeroSlice<'data> { - #[cfg_attr(feature = "serde", serde(borrow))] - _data: &'data FlexZeroSlice, - } - - #[test] - fn bake_FlexZeroSlice() { - test_bake!( - DeriveTest_FlexZeroSlice<'static>, - crate::yoke_impls::test::DeriveTest_FlexZeroSlice { - _data: unsafe { crate::vecs::FlexZeroSlice::from_byte_slice_unchecked(b"\x01\0") }, - }, - zerovec, - ); - } - #[derive(yoke::Yokeable, zerofrom::ZeroFrom)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "databake", derive(databake::Bake))] diff --git a/utils/zerovec/src/zerofrom_impls.rs b/utils/zerovec/src/zerofrom_impls.rs index c2f9d09ae92..1257938e27a 100644 --- a/utils/zerovec/src/zerofrom_impls.rs +++ b/utils/zerovec/src/zerofrom_impls.rs @@ -4,7 +4,6 @@ use crate::map::ZeroMapKV; use crate::ule::*; -use crate::vecs::{FlexZeroSlice, FlexZeroVec}; use crate::{VarZeroSlice, VarZeroVec, ZeroMap, ZeroMap2d, ZeroSlice, ZeroVec}; use zerofrom::ZeroFrom; @@ -38,27 +37,6 @@ 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> ZeroFrom<'zf, FlexZeroSlice> for &'zf FlexZeroSlice { - #[inline] - fn zero_from(other: &'zf FlexZeroSlice) -> Self { - other - } -} - impl<'zf, T> ZeroFrom<'zf, VarZeroSlice> for VarZeroVec<'zf, T> where T: 'static + VarULE + ?Sized,