diff options
Diffstat (limited to 'vendor/zerovec/src')
32 files changed, 880 insertions, 245 deletions
diff --git a/vendor/zerovec/src/flexzerovec/databake.rs b/vendor/zerovec/src/flexzerovec/databake.rs index 4ce5f7dea..bd165352e 100644 --- a/vendor/zerovec/src/flexzerovec/databake.rs +++ b/vendor/zerovec/src/flexzerovec/databake.rs @@ -9,7 +9,7 @@ impl Bake for FlexZeroVec<'_> { fn bake(&self, env: &CrateEnv) -> TokenStream { env.insert("zerovec"); if self.is_empty() { - quote! { ::zerovec::vecs::FlexZeroVec::new() } + quote! { zerovec::vecs::FlexZeroVec::new() } } else { let slice = self.as_ref().bake(env); quote! { #slice.as_flexzerovec() } @@ -21,10 +21,10 @@ impl Bake for &FlexZeroSlice { fn bake(&self, env: &CrateEnv) -> TokenStream { env.insert("zerovec"); if self.is_empty() { - quote! { ::zerovec::vecs::FlexZeroSlice::new_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) } } + quote! { unsafe { zerovec::vecs::FlexZeroSlice::from_byte_slice_unchecked(#bytes) } } } } } diff --git a/vendor/zerovec/src/flexzerovec/slice.rs b/vendor/zerovec/src/flexzerovec/slice.rs index fb58d6215..41cb7116f 100644 --- a/vendor/zerovec/src/flexzerovec/slice.rs +++ b/vendor/zerovec/src/flexzerovec/slice.rs @@ -134,18 +134,18 @@ impl FlexZeroSlice { // 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 - let (_, remainder) = match bytes.split_last() { - Some(v) => v, - None => panic!("slice should be non-empty"), - }; - &*(remainder as *const [u8] as *const Self) + 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::slice::from_raw_parts_mut(bytes.as_mut_ptr(), bytes.len() - 1); - &mut *(remainder as *mut [u8] as *mut Self) + 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. @@ -298,8 +298,7 @@ impl FlexZeroSlice { /// assert_eq!(pairs_it.next(), None); /// ``` pub fn iter_pairs(&self) -> impl Iterator<Item = (usize, Option<usize>)> + '_ { - self.iter() - .zip(self.iter().skip(1).map(Some).chain(core::iter::once(None))) + self.iter().zip(self.iter().skip(1).map(Some).chain([None])) } /// Creates a `Vec<usize>` from a [`FlexZeroSlice`] (or `FlexZeroVec`). diff --git a/vendor/zerovec/src/hashmap/mod.rs b/vendor/zerovec/src/hashmap/mod.rs index 5d91114ad..e3aed1198 100644 --- a/vendor/zerovec/src/hashmap/mod.rs +++ b/vendor/zerovec/src/hashmap/mod.rs @@ -20,8 +20,8 @@ mod serde; /// ``` /// use zerovec::ZeroHashMap; /// -/// let kv = vec![(0, "a"), (1, "b"), (2, "c")]; -/// let hashmap: ZeroHashMap<i32, str> = ZeroHashMap::from_iter(kv.into_iter()); +/// let hashmap = +/// ZeroHashMap::<i32, str>::from_iter([(0, "a"), (1, "b"), (2, "c")]); /// assert_eq!(hashmap.get(&0), Some("a")); /// assert_eq!(hashmap.get(&2), Some("c")); /// assert_eq!(hashmap.get(&4), None); @@ -91,8 +91,7 @@ where /// ``` /// use zerovec::ZeroHashMap; /// - /// let hashmap: ZeroHashMap<str, str> = - /// ZeroHashMap::from_iter(vec![("a", "A"), ("z", "Z")].into_iter()); + /// let hashmap = ZeroHashMap::<str, str>::from_iter([("a", "A"), ("z", "Z")]); /// /// assert_eq!(hashmap.get("a"), Some("A")); /// assert_eq!(hashmap.get("z"), Some("Z")); @@ -111,8 +110,7 @@ where /// ```rust /// use zerovec::ZeroHashMap; /// - /// let hashmap: ZeroHashMap<str, str> = - /// ZeroHashMap::from_iter(vec![("a", "A"), ("z", "Z")].into_iter()); + /// let hashmap = ZeroHashMap::<str, str>::from_iter([("a", "A"), ("z", "Z")]); /// /// assert!(hashmap.contains_key("a")); /// assert!(!hashmap.contains_key("p")); @@ -176,8 +174,12 @@ where /// ``` /// use zerovec::ZeroHashMap; /// - /// let kv: Vec<(i32, &str)> = vec![(1, "a"), (2, "b"), (3, "c"), (4, "d")]; - /// let hashmap: ZeroHashMap<i32, str> = ZeroHashMap::from_iter(kv.into_iter()); + /// let hashmap = ZeroHashMap::<i32, str>::from_iter([ + /// (1, "a"), + /// (2, "b"), + /// (3, "c"), + /// (4, "d"), + /// ]); /// assert_eq!(hashmap.get(&1), Some("a")); /// assert_eq!(hashmap.get(&2), Some("b")); /// assert_eq!(hashmap.get(&3), Some("c")); diff --git a/vendor/zerovec/src/hashmap/serde.rs b/vendor/zerovec/src/hashmap/serde.rs index 2d724ac05..7a4941205 100644 --- a/vendor/zerovec/src/hashmap/serde.rs +++ b/vendor/zerovec/src/hashmap/serde.rs @@ -77,7 +77,7 @@ mod test { } fn make_zerohashmap() -> ZeroHashMap<'static, u32, str> { - ZeroHashMap::from_iter(vec![(0, "a"), (1, "b"), (2, "c")].into_iter()) + ZeroHashMap::from_iter([(0, "a"), (1, "b"), (2, "c")]) } fn build_invalid_hashmap_str( diff --git a/vendor/zerovec/src/lib.rs b/vendor/zerovec/src/lib.rs index add52f113..961d62f34 100644 --- a/vendor/zerovec/src/lib.rs +++ b/vendor/zerovec/src/lib.rs @@ -403,7 +403,8 @@ pub use zerovec_derive::make_ule; /// /// This can be attached to structs containing only [`AsULE`] types with the last fields being /// [`Cow<'a, str>`](alloc::borrow::Cow), [`ZeroSlice`], or [`VarZeroSlice`]. If there is more than one such field, it will be represented -/// using [`MultiFieldsULE`](crate::ule::MultiFieldsULE) and getters will be generated. +/// using [`MultiFieldsULE`](crate::ule::MultiFieldsULE) and getters will be generated. Other VarULE fields will be detected if they are +/// tagged with `#[zerovec::varule(NameOfVarULETy)]`. /// /// The type must be [`PartialEq`] and [`Eq`]. /// @@ -517,3 +518,41 @@ pub use zerovec_derive::make_ule; /// ``` #[cfg(feature = "derive")] pub use zerovec_derive::make_varule; + +#[cfg(test)] +mod tests { + use super::*; + use core::mem::size_of; + + /// Checks that the size of the type is one of the given sizes. + /// The size might differ across Rust versions or channels. + macro_rules! check_size_of { + ($sizes:pat, $type:path) => { + assert!( + matches!(size_of::<$type>(), $sizes), + concat!(stringify!($type), " is of size {}"), + size_of::<$type>() + ); + }; + } + + #[test] + fn check_sizes() { + check_size_of!(24, ZeroVec<u8>); + check_size_of!(24, ZeroVec<u32>); + check_size_of!(32 | 24, VarZeroVec<[u8]>); + check_size_of!(32 | 24, VarZeroVec<str>); + check_size_of!(48, ZeroMap<u32, u32>); + check_size_of!(56 | 48, ZeroMap<u32, str>); + check_size_of!(56 | 48, ZeroMap<str, u32>); + check_size_of!(64 | 48, ZeroMap<str, str>); + check_size_of!(120 | 96, ZeroMap2d<str, str, str>); + check_size_of!(32 | 24, vecs::FlexZeroVec); + + check_size_of!(32, Option<ZeroVec<u8>>); + check_size_of!(32, Option<VarZeroVec<str>>); + check_size_of!(64 | 56, Option<ZeroMap<str, str>>); + check_size_of!(120 | 104, Option<ZeroMap2d<str, str, str>>); + check_size_of!(32, Option<vecs::FlexZeroVec>); + } +} diff --git a/vendor/zerovec/src/map/borrowed.rs b/vendor/zerovec/src/map/borrowed.rs index bc93ee497..98b2d2f9d 100644 --- a/vendor/zerovec/src/map/borrowed.rs +++ b/vendor/zerovec/src/map/borrowed.rs @@ -63,10 +63,7 @@ where V: ?Sized, { fn clone(&self) -> Self { - ZeroMapBorrowed { - keys: self.keys, - values: self.values, - } + *self } } @@ -252,6 +249,12 @@ where self.values.get(index) } + /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE` + pub fn get_copied_by(&self, predicate: impl FnMut(&K) -> Ordering) -> Option<V> { + let index = self.keys.zvl_binary_search_by(predicate).ok()?; + self.values.get(index) + } + /// Similar to [`Self::iter()`] except it returns a direct copy of the values instead of references /// to `V::ULE`, in cases when `V` is fixed-size pub fn iter_copied_values<'b>( diff --git a/vendor/zerovec/src/map/databake.rs b/vendor/zerovec/src/map/databake.rs index fceb6a966..f861e5c29 100644 --- a/vendor/zerovec/src/map/databake.rs +++ b/vendor/zerovec/src/map/databake.rs @@ -16,7 +16,7 @@ where env.insert("zerovec"); let keys = self.keys.bake(env); let values = self.values.bake(env); - quote! { unsafe { #[allow(unused_unsafe)] ::zerovec::ZeroMap::from_parts_unchecked(#keys, #values) } } + quote! { unsafe { #[allow(unused_unsafe)] zerovec::ZeroMap::from_parts_unchecked(#keys, #values) } } } } @@ -31,7 +31,7 @@ where env.insert("zerovec"); let keys = self.keys.bake(env); let values = self.values.bake(env); - quote! { unsafe { #[allow(unused_unsafe)] ::zerovec::maps::ZeroMapBorrowed::from_parts_unchecked(#keys, #values) } } + quote! { unsafe { #[allow(unused_unsafe)] zerovec::maps::ZeroMapBorrowed::from_parts_unchecked(#keys, #values) } } } } diff --git a/vendor/zerovec/src/map2d/borrowed.rs b/vendor/zerovec/src/map2d/borrowed.rs index 209da299b..166f1be74 100644 --- a/vendor/zerovec/src/map2d/borrowed.rs +++ b/vendor/zerovec/src/map2d/borrowed.rs @@ -73,12 +73,7 @@ where V: ?Sized, { fn clone(&self) -> Self { - ZeroMap2dBorrowed { - keys0: self.keys0, - joiner: self.joiner, - keys1: self.keys1, - values: self.values, - } + *self } } diff --git a/vendor/zerovec/src/map2d/databake.rs b/vendor/zerovec/src/map2d/databake.rs index 65e475c32..c5b9aca54 100644 --- a/vendor/zerovec/src/map2d/databake.rs +++ b/vendor/zerovec/src/map2d/databake.rs @@ -20,7 +20,7 @@ where let joiner = self.joiner.bake(env); let keys1 = self.keys1.bake(env); let values = self.values.bake(env); - quote! { unsafe { #[allow(unused_unsafe)] ::zerovec::ZeroMap2d::from_parts_unchecked(#keys0, #joiner, #keys1, #values) } } + quote! { unsafe { #[allow(unused_unsafe)] zerovec::ZeroMap2d::from_parts_unchecked(#keys0, #joiner, #keys1, #values) } } } } @@ -39,7 +39,7 @@ where let joiner = self.joiner.bake(env); let keys1 = self.keys1.bake(env); let values = self.values.bake(env); - quote! { unsafe { #[allow(unused_unsafe)] ::zerovec::maps::ZeroMap2dBorrowed::from_parts_unchecked(#keys0, #joiner, #keys1, #values) } } + quote! { unsafe { #[allow(unused_unsafe)] zerovec::maps::ZeroMap2dBorrowed::from_parts_unchecked(#keys0, #joiner, #keys1, #values) } } } } @@ -52,7 +52,7 @@ fn test_baked_map() { crate::ZeroMap2d::from_parts_unchecked( unsafe { crate::VarZeroVec::from_bytes_unchecked( - b"arcaz\0cu\0en\0ff\0grckk\0ku\0ky\0lifmanmn\0pa\0palsd\0tg\0ug\0unruz\0yuezh\0" + b"\x0E\0\0\0\0\0\x05\0\x07\0\t\0\x0B\0\x10\0\x12\0\x14\0\x1C\0\x1E\0#\0%\0'\0,\0arcazcuenffgrckkkukylifmanmnpapalsdtgugunruzyuezh" ) }, unsafe { @@ -62,12 +62,12 @@ fn test_baked_map() { }, unsafe { crate::VarZeroVec::from_bytes_unchecked( - b"NbatPalmArabGlagShawAdlmLinbArabArabYeziArabLatnLimbNkooMongArabPhlpDevaKhojSindArabCyrlDevaArabHansBopoHanbHant" + b"\x1C\0\0\0\0\0\x04\0\x08\0\x0C\0\x10\0\x14\0\x18\0\x1C\0 \0$\0(\0,\x000\x004\08\0<\0@\0D\0H\0L\0P\0T\0X\0\\\0`\0d\0h\0l\0NbatPalmArabGlagShawAdlmLinbArabArabYeziArabLatnLimbNkooMongArabPhlpDevaKhojSindArabCyrlDevaArabHansBopoHanbHant" ) }, unsafe { crate::VarZeroVec::from_bytes_unchecked( - b"JO\0SY\0IR\0BG\0GB\0GN\0GR\0CN\0IQ\0GE\0CN\0TR\0IN\0GN\0CN\0PK\0CN\0IN\0IN\0IN\0PK\0KZ\0NP\0AF\0CN\0TW\0TW\0TW\0" + b"\x1C\0\0\0\0\0\x02\0\x04\0\x06\0\x08\0\n\0\x0C\0\x0E\0\x10\0\x12\0\x14\0\x16\0\x18\0\x1A\0\x1C\0\x1E\0 \0\"\0$\0&\0(\0*\0,\0.\x000\x002\x004\x006\0JOSYIRBGGBGNGRCNIQGECNTRINGNCNPKCNINININPKKZNPAFCNTWTWTW" ) }, ) @@ -85,7 +85,7 @@ fn test_baked_borrowed_map() { crate::maps::ZeroMap2dBorrowed::from_parts_unchecked( unsafe { crate::VarZeroSlice::from_bytes_unchecked( - b"arcaz\0cu\0en\0ff\0grckk\0ku\0ky\0lifmanmn\0pa\0palsd\0tg\0ug\0unruz\0yuezh\0" + b"\x0E\0\0\0\0\0\x05\0\x07\0\t\0\x0B\0\x10\0\x12\0\x14\0\x1C\0\x1E\0#\0%\0'\0,\0arcazcuenffgrckkkukylifmanmnpapalsdtgugunruzyuezh" ) }, unsafe { @@ -95,12 +95,12 @@ fn test_baked_borrowed_map() { }, unsafe { crate::VarZeroSlice::from_bytes_unchecked( - b"NbatPalmArabGlagShawAdlmLinbArabArabYeziArabLatnLimbNkooMongArabPhlpDevaKhojSindArabCyrlDevaArabHansBopoHanbHant" + b"\x1C\0\0\0\0\0\x04\0\x08\0\x0C\0\x10\0\x14\0\x18\0\x1C\0 \0$\0(\0,\x000\x004\08\0<\0@\0D\0H\0L\0P\0T\0X\0\\\0`\0d\0h\0l\0NbatPalmArabGlagShawAdlmLinbArabArabYeziArabLatnLimbNkooMongArabPhlpDevaKhojSindArabCyrlDevaArabHansBopoHanbHant" ) }, unsafe { crate::VarZeroSlice::from_bytes_unchecked( - b"JO\0SY\0IR\0BG\0GB\0GN\0GR\0CN\0IQ\0GE\0CN\0TR\0IN\0GN\0CN\0PK\0CN\0IN\0IN\0IN\0PK\0KZ\0NP\0AF\0CN\0TW\0TW\0TW\0" + b"\x1C\0\0\0\0\0\x02\0\x04\0\x06\0\x08\0\n\0\x0C\0\x0E\0\x10\0\x12\0\x14\0\x16\0\x18\0\x1A\0\x1C\0\x1E\0 \0\"\0$\0&\0(\0*\0,\0.\x000\x002\x004\x006\0JOSYIRBGGBGNGRCNIQGECNTRINGNCNPKCNINININPKKZNPAFCNTWTWTW" ) }, ) diff --git a/vendor/zerovec/src/map2d/map.rs b/vendor/zerovec/src/map2d/map.rs index 8bdefe792..90854018f 100644 --- a/vendor/zerovec/src/map2d/map.rs +++ b/vendor/zerovec/src/map2d/map.rs @@ -716,7 +716,7 @@ mod test { assert_eq!(zm2d.get0(&0), None); let result = zm2d.try_append(&3, "ccc", "CCC"); - assert!(matches!(result, None)); + assert!(result.is_none()); assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3]), joiner: ZeroVec([1]), keys1: [\"ccc\"], values: [\"CCC\"] }"); assert_eq!(zm2d.get0(&0), None); @@ -725,7 +725,7 @@ mod test { assert_eq!(zm2d.get0(&99), None); let result = zm2d.try_append(&3, "eee", "EEE"); - assert!(matches!(result, None)); + assert!(result.is_none()); assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3]), joiner: ZeroVec([2]), keys1: [\"ccc\", \"eee\"], values: [\"CCC\", \"EEE\"] }"); assert_eq!(zm2d.get0(&0), None); @@ -737,19 +737,19 @@ mod test { // Out of order let result = zm2d.try_append(&3, "ddd", "DD0"); - assert!(matches!(result, Some(_))); + assert!(result.is_some()); // Append a few more elements let result = zm2d.try_append(&5, "ddd", "DD1"); - assert!(matches!(result, None)); + assert!(result.is_none()); let result = zm2d.try_append(&7, "ddd", "DD2"); - assert!(matches!(result, None)); + assert!(result.is_none()); let result = zm2d.try_append(&7, "eee", "EEE"); - assert!(matches!(result, None)); + assert!(result.is_none()); let result = zm2d.try_append(&7, "www", "WWW"); - assert!(matches!(result, None)); + assert!(result.is_none()); let result = zm2d.try_append(&9, "yyy", "YYY"); - assert!(matches!(result, None)); + assert!(result.is_none()); assert_eq!(format!("{zm2d:?}"), "ZeroMap2d { keys0: ZeroVec([3, 5, 7, 9]), joiner: ZeroVec([2, 3, 6, 7]), keys1: [\"ccc\", \"eee\", \"ddd\", \"ddd\", \"eee\", \"www\", \"yyy\"], values: [\"CCC\", \"EEE\", \"DD1\", \"DD2\", \"EEE\", \"WWW\", \"YYY\"] }"); assert_eq!(zm2d.get0(&0), None); diff --git a/vendor/zerovec/src/samples.rs b/vendor/zerovec/src/samples.rs index 7a9a73241..723aacded 100644 --- a/vendor/zerovec/src/samples.rs +++ b/vendor/zerovec/src/samples.rs @@ -44,9 +44,31 @@ pub const BINCODE_BUF: &[u8] = &[ 0, 72, 73, 74, 0, 76, 77, 78, 0, ]; -/// Representation of a VarZeroVec<str> of length 4 as bytes. -/// Safety: The bytes were manually verified to be valid. +/// Representation of a VarZeroVec<str> with contents ["w", "ω", "文", "𑄃"] pub const TEST_VARZEROSLICE_BYTES: &[u8] = &[ - 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 119, 207, 137, 230, 150, 135, 240, - 145, 132, 131, + 4, 0, 0, 0, 0, 0, 1, 0, 3, 0, 6, 0, 119, 207, 137, 230, 150, 135, 240, 145, 132, 131, ]; + +#[test] +fn validate() { + use crate::{VarZeroVec, ZeroVec}; + + assert_eq!( + ZeroVec::<u32>::parse_byte_slice(TEST_BUFFER_LE).unwrap(), + ZeroVec::alloc_from_slice(TEST_SLICE) + ); + + assert_eq!(TEST_SLICE.iter().sum::<u32>(), TEST_SUM); + + assert_eq!( + serde_json::from_str::<ZeroVec::<u32>>(JSON_STR).unwrap(), + ZeroVec::alloc_from_slice(TEST_SLICE) + ); + + assert_eq!( + bincode::deserialize::<ZeroVec::<u32>>(BINCODE_BUF).unwrap(), + ZeroVec::alloc_from_slice(TEST_SLICE) + ); + + VarZeroVec::<str>::parse_byte_slice(TEST_VARZEROSLICE_BYTES).unwrap(); +} diff --git a/vendor/zerovec/src/ule/chars.rs b/vendor/zerovec/src/ule/chars.rs index 7a4a97a4a..e0ec25240 100644 --- a/vendor/zerovec/src/ule/chars.rs +++ b/vendor/zerovec/src/ule/chars.rs @@ -6,10 +6,11 @@ //! ULE implementation for the `char` type. use super::*; +use crate::impl_ule_from_array; use core::cmp::Ordering; use core::convert::TryFrom; -/// A u8 array of little-endian data corresponding to a Unicode code point. +/// A u8 array of little-endian data corresponding to a Unicode scalar value. /// /// The bytes of a `CharULE` are guaranteed to represent a little-endian-encoded u32 that is a /// valid `char` and can be converted without validation. @@ -40,6 +41,20 @@ use core::convert::TryFrom; #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub struct CharULE([u8; 3]); +impl CharULE { + /// Converts a [`char`] to a [`CharULE`]. This is equivalent to calling + /// [`AsULE::to_unaligned()`] + /// + /// See the type-level documentation for [`CharULE`] for more information. + #[inline] + pub const fn from_aligned(c: char) -> Self { + let [u0, u1, u2, _u3] = (c as u32).to_le_bytes(); + Self([u0, u1, u2]) + } + + impl_ule_from_array!(char, CharULE, Self([0; 3])); +} + // Safety (based on the safety checklist on the ULE trait): // 1. CharULE does not include any uninitialized or padding bytes. // (achieved by `#[repr(transparent)]` on a type that satisfies this invariant) @@ -72,13 +87,12 @@ impl AsULE for char { #[inline] fn to_unaligned(self) -> Self::ULE { - let [u0, u1, u2, _u3] = u32::from(self).to_le_bytes(); - CharULE([u0, u1, u2]) + CharULE::from_aligned(self) } #[inline] fn from_unaligned(unaligned: Self::ULE) -> Self { - // Safe because the bytes of CharULE are defined to represent a valid Unicode code point. + // Safe because the bytes of CharULE are defined to represent a valid Unicode scalar value. unsafe { Self::from_u32_unchecked(u32::from_le_bytes([ unaligned.0[0], @@ -107,6 +121,25 @@ mod test { use super::*; #[test] + fn test_from_array() { + const CHARS: [char; 2] = ['a', '🙃']; + const CHARS_ULE: [CharULE; 2] = CharULE::from_array(CHARS); + assert_eq!( + CharULE::as_byte_slice(&CHARS_ULE), + &[0x61, 0x00, 0x00, 0x43, 0xF6, 0x01] + ); + } + + #[test] + fn test_from_array_zst() { + const CHARS: [char; 0] = []; + const CHARS_ULE: [CharULE; 0] = CharULE::from_array(CHARS); + let bytes = CharULE::as_byte_slice(&CHARS_ULE); + let empty: &[u8] = &[]; + assert_eq!(bytes, empty); + } + + #[test] fn test_parse() { // 1-byte, 2-byte, 3-byte, and two 4-byte character in UTF-8 (not as relevant in UTF-32) let chars = ['w', 'ω', '文', '𑄃', '🙃']; @@ -141,7 +174,7 @@ mod test { .collect(); let u32_bytes: &[u8] = RawBytesULE::<4>::as_byte_slice(&u32_ules); let parsed_ules_result = CharULE::parse_byte_slice(u32_bytes); - assert!(matches!(parsed_ules_result, Err(_))); + assert!(parsed_ules_result.is_err()); // 0x20FFFF is out of range for a char let u32s = [0x20FFFF]; @@ -152,6 +185,6 @@ mod test { .collect(); let u32_bytes: &[u8] = RawBytesULE::<4>::as_byte_slice(&u32_ules); let parsed_ules_result = CharULE::parse_byte_slice(u32_bytes); - assert!(matches!(parsed_ules_result, Err(_))); + assert!(parsed_ules_result.is_err()); } } diff --git a/vendor/zerovec/src/ule/custom.rs b/vendor/zerovec/src/ule/custom.rs index b2e4cb0e5..8cc6e9de4 100644 --- a/vendor/zerovec/src/ule/custom.rs +++ b/vendor/zerovec/src/ule/custom.rs @@ -129,8 +129,8 @@ //! } //! //! fn main() { -//! let mut foos = vec![Foo {field1: 'u', field2: 983, field3: ZeroVec::alloc_from_slice(&[1212,2309,500,7000])}, -//! Foo {field1: 'l', field2: 1010, field3: ZeroVec::alloc_from_slice(&[1932, 0, 8888, 91237])}]; +//! let mut foos = [Foo {field1: 'u', field2: 983, field3: ZeroVec::alloc_from_slice(&[1212,2309,500,7000])}, +//! Foo {field1: 'l', field2: 1010, field3: ZeroVec::alloc_from_slice(&[1932, 0, 8888, 91237])}]; //! //! let vzv = VarZeroVec::<_>::from(&foos); //! diff --git a/vendor/zerovec/src/ule/encode.rs b/vendor/zerovec/src/ule/encode.rs index 2091cf06b..adea123aa 100644 --- a/vendor/zerovec/src/ule/encode.rs +++ b/vendor/zerovec/src/ule/encode.rs @@ -8,7 +8,7 @@ use crate::{VarZeroSlice, VarZeroVec, ZeroSlice, ZeroVec}; use alloc::borrow::{Cow, ToOwned}; use alloc::boxed::Box; use alloc::string::String; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; use core::mem; /// Allows types to be encoded as VarULEs. This is highly useful for implementing VarULE on @@ -82,16 +82,14 @@ pub unsafe trait EncodeAsVarULE<T: VarULE + ?Sized> { /// /// This is primarily useful for generating `Deserialize` impls for VarULE types pub fn encode_varule_to_box<S: EncodeAsVarULE<T>, T: VarULE + ?Sized>(x: &S) -> Box<T> { - let mut vec: Vec<u8> = Vec::new(); // zero-fill the vector to avoid uninitialized data UB - vec.resize(x.encode_var_ule_len(), 0); + let mut vec: Vec<u8> = vec![0; x.encode_var_ule_len()]; x.encode_var_ule_write(&mut vec); - let boxed = vec.into_boxed_slice(); + let boxed = mem::ManuallyDrop::new(vec.into_boxed_slice()); unsafe { // Safety: `ptr` is a box, and `T` is a VarULE which guarantees it has the same memory layout as `[u8]` // and can be recouped via from_byte_slice_unchecked() let ptr: *mut T = T::from_byte_slice_unchecked(&boxed) as *const T as *mut T; - mem::forget(boxed); // Safety: we can construct an owned version since we have mem::forgotten the older owner Box::from_raw(ptr) diff --git a/vendor/zerovec/src/ule/macros.rs b/vendor/zerovec/src/ule/macros.rs new file mode 100644 index 000000000..955b1eb2e --- /dev/null +++ b/vendor/zerovec/src/ule/macros.rs @@ -0,0 +1,29 @@ +// 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 ). + +/// Given `Self` (`$aligned`), `Self::ULE` (`$unaligned`), and a conversion function (`$single` or +/// `Self::from_aligned`), implement `from_array` for arrays of `$aligned` to `$unaligned`. +/// +/// The `$default` argument is due to current compiler limitations. +/// Pass any (cheap to construct) value. +#[macro_export] +macro_rules! impl_ule_from_array { + ($aligned:ty, $unaligned:ty, $default:expr, $single:path) => { + #[doc = concat!("Convert an array of `", stringify!($aligned), "` to an array of `", stringify!($unaligned), "`.")] + pub const fn from_array<const N: usize>(arr: [$aligned; N]) -> [Self; N] { + let mut result = [$default; N]; + let mut i = 0; + // Won't panic because i < N and arr has length N + #[allow(clippy::indexing_slicing)] + while i < N { + result[i] = $single(arr[i]); + i += 1; + } + result + } + }; + ($aligned:ty, $unaligned:ty, $default:expr) => { + impl_ule_from_array!($aligned, $unaligned, $default, Self::from_aligned); + }; +} diff --git a/vendor/zerovec/src/ule/mod.rs b/vendor/zerovec/src/ule/mod.rs index e8ecd26e5..5a6d9cd47 100644 --- a/vendor/zerovec/src/ule/mod.rs +++ b/vendor/zerovec/src/ule/mod.rs @@ -14,6 +14,7 @@ mod chars; #[cfg(doc)] pub mod custom; mod encode; +mod macros; mod multi; mod niche; mod option; @@ -29,7 +30,7 @@ pub use multi::MultiFieldsULE; pub use niche::{NicheBytes, NichedOption, NichedOptionULE}; pub use option::{OptionULE, OptionVarULE}; pub use plain::RawBytesULE; -pub use unvalidated::UnvalidatedStr; +pub use unvalidated::{UnvalidatedChar, UnvalidatedStr}; use alloc::alloc::Layout; use alloc::borrow::ToOwned; @@ -156,7 +157,7 @@ where /// A trait for any type that has a 1:1 mapping with an unaligned little-endian (ULE) type. /// -/// If you need to implement this trait, consider using [`#[make_varule]`](crate::make_ule) instead. +/// If you need to implement this trait, consider using [`#[make_ule]`](crate::make_ule) instead. pub trait AsULE: Copy { /// The ULE type corresponding to `Self`. /// @@ -356,13 +357,12 @@ pub unsafe trait VarULE: 'static { #[inline] fn to_boxed(&self) -> Box<Self> { let bytesvec = self.as_byte_slice().to_owned().into_boxed_slice(); + let bytesvec = mem::ManuallyDrop::new(bytesvec); unsafe { // Get the pointer representation let ptr: *mut Self = Self::from_byte_slice_unchecked(&bytesvec) as *const Self as *mut Self; - assert_eq!(Layout::for_value(&*ptr), Layout::for_value(&*bytesvec)); - // Forget the allocation - mem::forget(bytesvec); + assert_eq!(Layout::for_value(&*ptr), Layout::for_value(&**bytesvec)); // Transmute the pointer to an owned pointer Box::from_raw(ptr) } diff --git a/vendor/zerovec/src/ule/multi.rs b/vendor/zerovec/src/ule/multi.rs index 0ba0aea89..3281b2088 100644 --- a/vendor/zerovec/src/ule/multi.rs +++ b/vendor/zerovec/src/ule/multi.rs @@ -44,7 +44,7 @@ impl MultiFieldsULE { lengths, output, ); debug_assert!( - <VarZeroSlice<[u8]>>::validate_byte_slice(output).is_ok(), + <VarZeroSlice<[u8], Index32>>::validate_byte_slice(output).is_ok(), "Encoded slice must be valid VarZeroSlice" ); // Safe since write_serializable_bytes produces a valid VarZeroSlice buffer @@ -141,12 +141,14 @@ unsafe impl VarULE for MultiFieldsULE { /// This impl exists so that EncodeAsVarULE can work. #[inline] fn validate_byte_slice(slice: &[u8]) -> Result<(), ZeroVecError> { - <VarZeroSlice<[u8]>>::validate_byte_slice(slice) + <VarZeroSlice<[u8], Index32>>::validate_byte_slice(slice) } #[inline] unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { // &Self is transparent over &VZS<..> - mem::transmute(<VarZeroSlice<[u8]>>::from_byte_slice_unchecked(bytes)) + mem::transmute(<VarZeroSlice<[u8], Index32>>::from_byte_slice_unchecked( + bytes, + )) } } diff --git a/vendor/zerovec/src/ule/option.rs b/vendor/zerovec/src/ule/option.rs index 50b193aac..9b0dc5b28 100644 --- a/vendor/zerovec/src/ule/option.rs +++ b/vendor/zerovec/src/ule/option.rs @@ -197,9 +197,8 @@ unsafe impl<U: VarULE + ?Sized> VarULE for OptionVarULE<U> { #[inline] unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { - let metadata = bytes.len() - 1; let entire_struct_as_slice: *const [u8] = - ::core::slice::from_raw_parts(bytes.as_ptr(), metadata); + ::core::ptr::slice_from_raw_parts(bytes.as_ptr(), bytes.len() - 1); &*(entire_struct_as_slice as *const Self) } } diff --git a/vendor/zerovec/src/ule/plain.rs b/vendor/zerovec/src/ule/plain.rs index 49455d45f..f244f6b68 100644 --- a/vendor/zerovec/src/ule/plain.rs +++ b/vendor/zerovec/src/ule/plain.rs @@ -6,6 +6,7 @@ //! ULE implementation for Plain Old Data types, including all sized integers. use super::*; +use crate::impl_ule_from_array; use crate::ZeroSlice; use core::num::{NonZeroI8, NonZeroU8}; @@ -15,69 +16,69 @@ use core::num::{NonZeroI8, NonZeroU8}; #[allow(clippy::exhaustive_structs)] // newtype pub struct RawBytesULE<const N: usize>(pub [u8; N]); -macro_rules! impl_byte_slice_size { - ($unsigned:ty, $size:literal) => { - impl From<[u8; $size]> for RawBytesULE<$size> { - #[inline] - fn from(le_bytes: [u8; $size]) -> Self { - Self(le_bytes) - } - } - impl RawBytesULE<$size> { - #[inline] - pub fn as_bytes(&self) -> &[u8] { - &self.0 - } - } - // Safety (based on the safety checklist on the ULE trait): - // 1. RawBytesULE does not include any uninitialized or padding bytes. - // (achieved by `#[repr(transparent)]` on a type that satisfies this invariant) - // 2. RawBytesULE is aligned to 1 byte. - // (achieved by `#[repr(transparent)]` on a type that satisfies this invariant) - // 3. The impl of validate_byte_slice() returns an error if any byte is not valid (never). - // 4. The impl of validate_byte_slice() returns an error if there are leftover bytes. - // 5. The other ULE methods use the default impl. - // 6. RawBytesULE byte equality is semantic equality - unsafe impl ULE for RawBytesULE<$size> { - #[inline] - fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { - if bytes.len() % $size == 0 { - // Safe because Self is transparent over [u8; $size] - Ok(()) - } else { - Err(ZeroVecError::length::<Self>(bytes.len())) - } - } +impl<const N: usize> RawBytesULE<N> { + #[inline] + pub fn as_bytes(&self) -> &[u8] { + &self.0 + } + + #[inline] + pub fn from_byte_slice_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] { + let data = bytes.as_mut_ptr(); + let len = bytes.len() / N; + // Safe because Self is transparent over [u8; N] + unsafe { core::slice::from_raw_parts_mut(data as *mut Self, len) } + } +} + +// Safety (based on the safety checklist on the ULE trait): +// 1. RawBytesULE does not include any uninitialized or padding bytes. +// (achieved by `#[repr(transparent)]` on a type that satisfies this invariant) +// 2. RawBytesULE is aligned to 1 byte. +// (achieved by `#[repr(transparent)]` on a type that satisfies this invariant) +// 3. The impl of validate_byte_slice() returns an error if any byte is not valid (never). +// 4. The impl of validate_byte_slice() returns an error if there are leftover bytes. +// 5. The other ULE methods use the default impl. +// 6. RawBytesULE byte equality is semantic equality +unsafe impl<const N: usize> ULE for RawBytesULE<N> { + #[inline] + fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { + if bytes.len() % N == 0 { + // Safe because Self is transparent over [u8; N] + Ok(()) + } else { + Err(ZeroVecError::length::<Self>(bytes.len())) } + } +} - impl RawBytesULE<$size> { - #[inline] - pub fn from_byte_slice_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] { - let data = bytes.as_mut_ptr(); - let len = bytes.len() / $size; - // Safe because Self is transparent over [u8; $size] - unsafe { core::slice::from_raw_parts_mut(data as *mut Self, len) } - } +impl<const N: usize> From<[u8; N]> for RawBytesULE<N> { + #[inline] + fn from(le_bytes: [u8; N]) -> Self { + Self(le_bytes) + } +} - /// Gets this RawBytesULE as an unsigned int. This is equivalent to calling - /// [AsULE::from_unaligned()] on the appropriately sized type. +macro_rules! impl_byte_slice_size { + ($unsigned:ty, $size:literal) => { + impl RawBytesULE<$size> { + #[doc = concat!("Gets this `RawBytesULE` as a `", stringify!($unsigned), "`. This is equivalent to calling [`AsULE::from_unaligned()`] on the appropriately sized type.")] #[inline] pub fn as_unsigned_int(&self) -> $unsigned { <$unsigned as $crate::ule::AsULE>::from_unaligned(*self) } - /// Convert an array of native-endian aligned integers to an array of RawBytesULE. - pub const fn from_array<const N: usize>(arr: [$unsigned; N]) -> [Self; N] { - let mut result = [RawBytesULE([0; $size]); N]; - let mut i = 0; - // Won't panic because i < N and arr has length N - #[allow(clippy::indexing_slicing)] - while i < N { - result[i].0 = arr[i].to_le_bytes(); - i += 1; - } - result + #[doc = concat!("Converts a `", stringify!($unsigned), "` to a `RawBytesULE`. This is equivalent to calling [`AsULE::to_unaligned()`] on the appropriately sized type.")] + #[inline] + pub const fn from_aligned(value: $unsigned) -> Self { + Self(value.to_le_bytes()) } + + impl_ule_from_array!( + $unsigned, + RawBytesULE<$size>, + RawBytesULE([0; $size]) + ); } }; } @@ -110,7 +111,7 @@ macro_rules! impl_const_constructors { } macro_rules! impl_byte_slice_type { - ($type:ty, $size:literal) => { + ($single_fn:ident, $type:ty, $size:literal) => { impl From<$type> for RawBytesULE<$size> { #[inline] fn from(value: $type) -> Self { @@ -131,6 +132,24 @@ macro_rules! impl_byte_slice_type { // EqULE is true because $type and RawBytesULE<$size> // have the same byte sequence on little-endian unsafe impl EqULE for $type {} + + impl RawBytesULE<$size> { + pub const fn $single_fn(v: $type) -> Self { + RawBytesULE(v.to_le_bytes()) + } + } + }; +} + +macro_rules! impl_byte_slice_unsigned_type { + ($type:ty, $size:literal) => { + impl_byte_slice_type!(from_unsigned, $type, $size); + }; +} + +macro_rules! impl_byte_slice_signed_type { + ($type:ty, $size:literal) => { + impl_byte_slice_type!(from_signed, $type, $size); }; } @@ -139,15 +158,15 @@ impl_byte_slice_size!(u32, 4); impl_byte_slice_size!(u64, 8); impl_byte_slice_size!(u128, 16); -impl_byte_slice_type!(u16, 2); -impl_byte_slice_type!(u32, 4); -impl_byte_slice_type!(u64, 8); -impl_byte_slice_type!(u128, 16); +impl_byte_slice_unsigned_type!(u16, 2); +impl_byte_slice_unsigned_type!(u32, 4); +impl_byte_slice_unsigned_type!(u64, 8); +impl_byte_slice_unsigned_type!(u128, 16); -impl_byte_slice_type!(i16, 2); -impl_byte_slice_type!(i32, 4); -impl_byte_slice_type!(i64, 8); -impl_byte_slice_type!(i128, 16); +impl_byte_slice_signed_type!(i16, 2); +impl_byte_slice_signed_type!(i32, 4); +impl_byte_slice_signed_type!(i64, 8); +impl_byte_slice_signed_type!(i128, 16); impl_const_constructors!(u8, 1); impl_const_constructors!(u16, 2); diff --git a/vendor/zerovec/src/ule/tuple.rs b/vendor/zerovec/src/ule/tuple.rs index c26567e98..3e0f291b3 100644 --- a/vendor/zerovec/src/ule/tuple.rs +++ b/vendor/zerovec/src/ule/tuple.rs @@ -111,10 +111,7 @@ macro_rules! tuple_ule { impl<$($t: ULE),+> Clone for $name<$($t),+> { fn clone(&self) -> Self { - // copy to the stack to avoid hitting a future incompat error - // https://github.com/rust-lang/rust/issues/82523#issuecomment-947900712 - let stack = ($(self.$i),+); - $name($(stack.$i),+) + *self } } @@ -147,7 +144,7 @@ fn test_pairule_validate() { // Test failed validation with a correctly sized but differently constrained tuple // Note: 1234901 is not a valid char let zerovec3 = ZeroVec::<(char, u32)>::parse_byte_slice(bytes); - assert!(matches!(zerovec3, Err(_))); + assert!(zerovec3.is_err()); } #[test] @@ -162,7 +159,7 @@ fn test_tripleule_validate() { // Test failed validation with a correctly sized but differently constrained tuple // Note: 1234901 is not a valid char let zerovec3 = ZeroVec::<(char, i8, u32)>::parse_byte_slice(bytes); - assert!(matches!(zerovec3, Err(_))); + assert!(zerovec3.is_err()); } #[test] @@ -178,5 +175,5 @@ fn test_quadule_validate() { // Test failed validation with a correctly sized but differently constrained tuple // Note: 1234901 is not a valid char let zerovec3 = ZeroVec::<(char, i8, u16, u32)>::parse_byte_slice(bytes); - assert!(matches!(zerovec3, Err(_))); + assert!(zerovec3.is_err()); } diff --git a/vendor/zerovec/src/ule/unvalidated.rs b/vendor/zerovec/src/ule/unvalidated.rs index 4564c8673..21cfb0c0d 100644 --- a/vendor/zerovec/src/ule/unvalidated.rs +++ b/vendor/zerovec/src/ule/unvalidated.rs @@ -2,9 +2,11 @@ // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). -use super::VarULE; +use super::{AsULE, RawBytesULE, VarULE}; +use crate::ule::EqULE; use crate::{map::ZeroMapKV, VarZeroSlice, VarZeroVec, ZeroVecError}; use alloc::boxed::Box; +use core::cmp::Ordering; use core::fmt; use core::ops::Deref; @@ -209,3 +211,317 @@ where } } } + +/// A u8 array of little-endian data that is expected to be a Unicode scalar value, but is not +/// validated as such. +/// +/// Use this type instead of `char` when you want to deal with data that is expected to be valid +/// Unicode scalar values, but you want control over when or if you validate that assumption. +/// +/// # Examples +/// +/// ``` +/// use zerovec::ule::{RawBytesULE, UnvalidatedChar, ULE}; +/// use zerovec::{ZeroSlice, ZeroVec}; +/// +/// // data known to be little-endian three-byte chunks of valid Unicode scalar values +/// let data = [0x68, 0x00, 0x00, 0x69, 0x00, 0x00, 0x4B, 0xF4, 0x01]; +/// // ground truth expectation +/// let real = ['h', 'i', '👋']; +/// +/// let chars: &ZeroSlice<UnvalidatedChar> = ZeroSlice::parse_byte_slice(&data).expect("invalid data length"); +/// let parsed: Vec<_> = chars.iter().map(|c| unsafe { c.to_char_unchecked() }).collect(); +/// assert_eq!(&parsed, &real); +/// +/// let real_chars: ZeroVec<_> = real.iter().copied().map(UnvalidatedChar::from_char).collect(); +/// let serialized_data = chars.as_bytes(); +/// assert_eq!(serialized_data, &data); +/// ``` +#[repr(transparent)] +#[derive(PartialEq, Eq, Clone, Copy, Hash)] +pub struct UnvalidatedChar([u8; 3]); + +impl UnvalidatedChar { + /// Create a [`UnvalidatedChar`] from a `char`. + /// + /// # Examples + /// + /// ``` + /// use zerovec::ule::UnvalidatedChar; + /// + /// let a = UnvalidatedChar::from_char('a'); + /// assert_eq!(a.try_to_char().unwrap(), 'a'); + /// ``` + #[inline] + pub const fn from_char(c: char) -> Self { + let [u0, u1, u2, _u3] = (c as u32).to_le_bytes(); + Self([u0, u1, u2]) + } + + #[inline] + #[doc(hidden)] + pub const fn from_u24(c: u32) -> Self { + let [u0, u1, u2, _u3] = c.to_le_bytes(); + Self([u0, u1, u2]) + } + + /// Attempt to convert a [`UnvalidatedChar`] to a `char`. + /// + /// # Examples + /// + /// ``` + /// use zerovec::ule::{AsULE, UnvalidatedChar}; + /// + /// let a = UnvalidatedChar::from_char('a'); + /// assert_eq!(a.try_to_char(), Ok('a')); + /// + /// let b = UnvalidatedChar::from_unaligned([0xFF, 0xFF, 0xFF].into()); + /// assert!(matches!(b.try_to_char(), Err(_))); + /// ``` + #[inline] + pub fn try_to_char(self) -> Result<char, core::char::CharTryFromError> { + let [u0, u1, u2] = self.0; + char::try_from(u32::from_le_bytes([u0, u1, u2, 0])) + } + + /// Convert a [`UnvalidatedChar`] to a `char', returning [`char::REPLACEMENT_CHARACTER`] + /// if the `UnvalidatedChar` does not represent a valid Unicode scalar value. + /// + /// # Examples + /// + /// ``` + /// use zerovec::ule::{AsULE, UnvalidatedChar}; + /// + /// let a = UnvalidatedChar::from_unaligned([0xFF, 0xFF, 0xFF].into()); + /// assert_eq!(a.to_char_lossy(), char::REPLACEMENT_CHARACTER); + /// ``` + #[inline] + pub fn to_char_lossy(self) -> char { + self.try_to_char().unwrap_or(char::REPLACEMENT_CHARACTER) + } + + /// Convert a [`UnvalidatedChar`] to a `char` without checking that it is + /// a valid Unicode scalar value. + /// + /// # Safety + /// + /// The `UnvalidatedChar` must be a valid Unicode scalar value in little-endian order. + /// + /// # Examples + /// + /// ``` + /// use zerovec::ule::UnvalidatedChar; + /// + /// let a = UnvalidatedChar::from_char('a'); + /// assert_eq!(unsafe { a.to_char_unchecked() }, 'a'); + /// ``` + #[inline] + pub unsafe fn to_char_unchecked(self) -> char { + let [u0, u1, u2] = self.0; + char::from_u32_unchecked(u32::from_le_bytes([u0, u1, u2, 0])) + } +} + +impl RawBytesULE<3> { + /// Converts a [`UnvalidatedChar`] to its ULE type. This is equivalent to calling + /// [`AsULE::to_unaligned`]. + #[inline] + pub const fn from_unvalidated_char(uc: UnvalidatedChar) -> Self { + RawBytesULE(uc.0) + } +} + +impl AsULE for UnvalidatedChar { + type ULE = RawBytesULE<3>; + + #[inline] + fn to_unaligned(self) -> Self::ULE { + RawBytesULE(self.0) + } + + #[inline] + fn from_unaligned(unaligned: Self::ULE) -> Self { + Self(unaligned.0) + } +} + +// Safety: UnvalidatedChar is always the little-endian representation of a char, +// which corresponds to its AsULE::ULE type +unsafe impl EqULE for UnvalidatedChar {} + +impl fmt::Debug for UnvalidatedChar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Debug as a char if possible + match self.try_to_char() { + Ok(c) => fmt::Debug::fmt(&c, f), + Err(_) => fmt::Debug::fmt(&self.0, f), + } + } +} + +impl PartialOrd for UnvalidatedChar { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for UnvalidatedChar { + // custom implementation, as derived Ord would compare lexicographically + fn cmp(&self, other: &Self) -> Ordering { + let [a0, a1, a2] = self.0; + let a = u32::from_le_bytes([a0, a1, a2, 0]); + let [b0, b1, b2] = other.0; + let b = u32::from_le_bytes([b0, b1, b2, 0]); + a.cmp(&b) + } +} + +impl From<char> for UnvalidatedChar { + #[inline] + fn from(value: char) -> Self { + Self::from_char(value) + } +} + +impl TryFrom<UnvalidatedChar> for char { + type Error = core::char::CharTryFromError; + + #[inline] + fn try_from(value: UnvalidatedChar) -> Result<char, Self::Error> { + value.try_to_char() + } +} + +/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate +#[cfg(feature = "serde")] +impl serde::Serialize for UnvalidatedChar { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: serde::Serializer, + { + use serde::ser::Error; + let c = self + .try_to_char() + .map_err(|_| S::Error::custom("invalid Unicode scalar value in UnvalidatedChar"))?; + if serializer.is_human_readable() { + serializer.serialize_char(c) + } else { + self.0.serialize(serializer) + } + } +} + +/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for UnvalidatedChar { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + if deserializer.is_human_readable() { + let c = <char>::deserialize(deserializer)?; + Ok(UnvalidatedChar::from_char(c)) + } else { + let bytes = <[u8; 3]>::deserialize(deserializer)?; + Ok(UnvalidatedChar(bytes)) + } + } +} + +#[cfg(feature = "databake")] +impl databake::Bake for UnvalidatedChar { + fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream { + match self.try_to_char() { + Ok(ch) => { + env.insert("zerovec"); + let ch = ch.bake(env); + databake::quote! { + zerovec::ule::UnvalidatedChar::from_char(#ch) + } + } + Err(_) => { + env.insert("zerovec"); + let u24 = u32::from_le_bytes([self.0[0], self.0[1], self.0[2], 0]); + databake::quote! { + zerovec::ule::UnvalidatedChar::from_u24(#u24) + } + } + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::ZeroVec; + + #[test] + fn test_serde_fail() { + let uc = UnvalidatedChar([0xFF, 0xFF, 0xFF]); + serde_json::to_string(&uc).expect_err("serialize invalid char bytes"); + bincode::serialize(&uc).expect_err("serialize invalid char bytes"); + } + + #[test] + fn test_serde_json() { + let c = '🙃'; + let uc = UnvalidatedChar::from_char(c); + let json_ser = serde_json::to_string(&uc).unwrap(); + + assert_eq!(json_ser, r#""🙃""#); + + let json_de: UnvalidatedChar = serde_json::from_str(&json_ser).unwrap(); + + assert_eq!(uc, json_de); + } + + #[test] + fn test_serde_bincode() { + let c = '🙃'; + let uc = UnvalidatedChar::from_char(c); + let bytes_ser = bincode::serialize(&uc).unwrap(); + + assert_eq!(bytes_ser, [0x43, 0xF6, 0x01]); + + let bytes_de: UnvalidatedChar = bincode::deserialize(&bytes_ser).unwrap(); + + assert_eq!(uc, bytes_de); + } + + #[test] + fn test_representation() { + let chars = ['w', 'ω', '文', '𑄃', '🙃']; + + // backed by [UnvalidatedChar] + let uvchars: Vec<_> = chars + .iter() + .copied() + .map(UnvalidatedChar::from_char) + .collect(); + // backed by [RawBytesULE<3>] + let zvec: ZeroVec<_> = uvchars.clone().into_iter().collect(); + + let ule_bytes = zvec.as_bytes(); + let uvbytes; + unsafe { + let ptr = &uvchars[..] as *const _ as *const u8; + uvbytes = core::slice::from_raw_parts(ptr, ule_bytes.len()); + } + + // UnvalidatedChar is defined as little-endian, so this must be true on all platforms + // also asserts that to_unaligned/from_unaligned are no-ops + assert_eq!(uvbytes, ule_bytes); + + assert_eq!( + &[119, 0, 0, 201, 3, 0, 135, 101, 0, 3, 17, 1, 67, 246, 1], + ule_bytes + ); + } + + #[test] + fn test_char_bake() { + databake::test_bake!(UnvalidatedChar, const: crate::ule::UnvalidatedChar::from_char('b'), zerovec); + // surrogate code point + databake::test_bake!(UnvalidatedChar, const: crate::ule::UnvalidatedChar::from_u24(55296u32), zerovec); + } +} diff --git a/vendor/zerovec/src/varzerovec/components.rs b/vendor/zerovec/src/varzerovec/components.rs index ff26d9029..9b48a5bd6 100644 --- a/vendor/zerovec/src/varzerovec/components.rs +++ b/vendor/zerovec/src/varzerovec/components.rs @@ -53,7 +53,7 @@ pub unsafe trait VarZeroVecFormat: 'static + Sized { /// Will have a smaller data size, but it's more likely for larger arrays /// to be unrepresentable (and error on construction) /// -/// This is the default index size used by all [`VarZeroVec`](super::VarZeroVec) tyoes. +/// This is the default index size used by all [`VarZeroVec`](super::VarZeroVec) types. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[allow(clippy::exhaustive_structs)] // marker pub struct Index16; @@ -128,13 +128,7 @@ pub struct VarZeroVecComponents<'a, T: ?Sized, F> { impl<'a, T: ?Sized, F> Copy for VarZeroVecComponents<'a, T, F> {} impl<'a, T: ?Sized, F> Clone for VarZeroVecComponents<'a, T, F> { fn clone(&self) -> Self { - VarZeroVecComponents { - len: self.len, - indices: self.indices, - things: self.things, - entire_slice: self.entire_slice, - marker: PhantomData, - } + *self } } @@ -161,13 +155,14 @@ impl<'a, T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecComponents<'a, T, F> /// Construct a new VarZeroVecComponents, checking invariants about the overall buffer size: /// /// - There must be either zero or at least four bytes (if four, this is the "length" parsed as a usize) - /// - There must be at least `4*length + 4` bytes total, to form the the array `indices` of indices + /// - There must be at least `4*length + 4` bytes total, to form the array `indices` of indices /// - `indices[i]..indices[i+1]` must index into a valid section of /// `things`, such that it parses to a `T::VarULE` /// - `indices[len - 1]..things.len()` must index into a valid section of /// `things`, such that it parses to a `T::VarULE` #[inline] pub fn parse_byte_slice(slice: &'a [u8]) -> Result<Self, ZeroVecError> { + // The empty VZV is special-cased to the empty slice if slice.is_empty() { return Ok(VarZeroVecComponents { len: 0, @@ -219,6 +214,7 @@ impl<'a, T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecComponents<'a, T, F> /// The bytes must have previously successfully run through /// [`VarZeroVecComponents::parse_byte_slice()`] pub unsafe fn from_bytes_unchecked(slice: &'a [u8]) -> Self { + // The empty VZV is special-cased to the empty slice if slice.is_empty() { return VarZeroVecComponents { len: 0, @@ -369,7 +365,7 @@ impl<'a, T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecComponents<'a, T, F> .copied() .map(F::rawbytes_to_usize) .skip(1) - .chain(core::iter::once(self.things.len())), + .chain([self.things.len()]), ) .map(move |(start, end)| unsafe { self.things.get_unchecked(start..end) }) .map(|bytes| unsafe { T::from_byte_slice_unchecked(bytes) }) @@ -485,12 +481,13 @@ where } /// Collects the bytes for a VarZeroSlice into a Vec. -pub fn get_serializable_bytes<T, A, F>(elements: &[A]) -> Option<Vec<u8>> +pub fn get_serializable_bytes_non_empty<T, A, F>(elements: &[A]) -> Option<Vec<u8>> where T: VarULE + ?Sized, A: EncodeAsVarULE<T>, F: VarZeroVecFormat, { + debug_assert!(!elements.is_empty()); let len = compute_serializable_len::<T, A, F>(elements)?; debug_assert!(len >= LENGTH_WIDTH as u32); let mut output: Vec<u8> = alloc::vec![0; len as usize]; @@ -566,9 +563,7 @@ where let data_len: u32 = elements .iter() .map(|v| u32::try_from(v.encode_var_ule_len()).ok()) - .fold(Some(0u32), |s, v| { - s.and_then(|s| v.and_then(|v| s.checked_add(v))) - })?; + .try_fold(0u32, |s, v| s.checked_add(v?))?; let ret = idx_len.checked_add(data_len); if let Some(r) = ret { if r >= F::MAX_VALUE { diff --git a/vendor/zerovec/src/varzerovec/databake.rs b/vendor/zerovec/src/varzerovec/databake.rs index 8d9fc0bfc..a3f9db2d1 100644 --- a/vendor/zerovec/src/varzerovec/databake.rs +++ b/vendor/zerovec/src/varzerovec/databake.rs @@ -9,11 +9,11 @@ impl<T: VarULE + ?Sized> Bake for VarZeroVec<'_, T> { fn bake(&self, env: &CrateEnv) -> TokenStream { env.insert("zerovec"); if self.is_empty() { - quote! { ::zerovec::VarZeroVec::new() } + quote! { zerovec::VarZeroVec::new() } } else { let bytes = databake::Bake::bake(&self.as_bytes(), env); // Safe because self.as_bytes is a safe input - quote! { unsafe { ::zerovec::VarZeroVec::from_bytes_unchecked(#bytes) } } + quote! { unsafe { zerovec::VarZeroVec::from_bytes_unchecked(#bytes) } } } } } @@ -22,11 +22,11 @@ impl<T: VarULE + ?Sized> Bake for &VarZeroSlice<T> { fn bake(&self, env: &CrateEnv) -> TokenStream { env.insert("zerovec"); if self.is_empty() { - quote! { ::zerovec::VarZeroSlice::new_empty() } + quote! { zerovec::VarZeroSlice::new_empty() } } else { let bytes = databake::Bake::bake(&self.as_bytes(), env); // Safe because self.as_bytes is a safe input - quote! { unsafe { ::zerovec::VarZeroSlice::from_bytes_unchecked(#bytes) } } + quote! { unsafe { zerovec::VarZeroSlice::from_bytes_unchecked(#bytes) } } } } } diff --git a/vendor/zerovec/src/varzerovec/owned.rs b/vendor/zerovec/src/varzerovec/owned.rs index 76de48760..c5556315f 100644 --- a/vendor/zerovec/src/varzerovec/owned.rs +++ b/vendor/zerovec/src/varzerovec/owned.rs @@ -84,13 +84,18 @@ impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecOwned<T, F> { where A: EncodeAsVarULE<T>, { - Ok(Self { - marker: PhantomData, - // TODO(#1410): Rethink length errors in VZV. - entire_slice: components::get_serializable_bytes::<T, A, F>(elements).ok_or( - "Attempted to build VarZeroVec out of elements that \ + Ok(if elements.is_empty() { + Self::from_slice(VarZeroSlice::new_empty()) + } else { + Self { + marker: PhantomData, + // TODO(#1410): Rethink length errors in VZV. + entire_slice: components::get_serializable_bytes_non_empty::<T, A, F>(elements) + .ok_or( + "Attempted to build VarZeroVec out of elements that \ cumulatively are larger than a u32 in size", - )?, + )?, + } }) } @@ -279,7 +284,7 @@ impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecOwned<T, F> { any::type_name::<F>() ); } - self.entire_slice.reserve(shift as usize); + self.entire_slice.resize(new_slice_len, 0); } // Now that we've ensured there's enough space, we can shift the data around. @@ -287,6 +292,7 @@ impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecOwned<T, F> { // Note: There are no references introduced between pointer creation and pointer use, and all // raw pointers are derived from a single &mut. This preserves pointer provenance. let slice_range = self.entire_slice.as_mut_ptr_range(); + let old_slice_end = slice_range.start.add(slice_len); let data_start = slice_range .start .add(LENGTH_WIDTH + METADATA_WIDTH + len * F::INDEX_WIDTH); @@ -316,7 +322,7 @@ impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecOwned<T, F> { // Shift data after the element to its new position. shift_bytes( - prev_element_p.end..slice_range.end, + prev_element_p.end..old_slice_end, prev_element_p .start .offset((new_size as i64 + index_shift) as isize), diff --git a/vendor/zerovec/src/varzerovec/serde.rs b/vendor/zerovec/src/varzerovec/serde.rs index 649b29cfb..8025fc085 100644 --- a/vendor/zerovec/src/varzerovec/serde.rs +++ b/vendor/zerovec/src/varzerovec/serde.rs @@ -72,7 +72,7 @@ where where D: Deserializer<'de>, { - let visitor = VarZeroVecVisitor::default(); + let visitor = VarZeroVecVisitor::<T, F>::default(); if deserializer.is_human_readable() { deserializer.deserialize_seq(visitor) } else { @@ -98,7 +98,7 @@ where "&VarZeroSlice cannot be deserialized from human-readable formats", )) } else { - let deserialized: VarZeroVec<'a, T, F> = VarZeroVec::deserialize(deserializer)?; + let deserialized = VarZeroVec::<'a, T, F>::deserialize(deserializer)?; let borrowed = if let VarZeroVec::Borrowed(b) = deserialized { b } else { @@ -112,6 +112,22 @@ where } /// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate +impl<'de, T, F> Deserialize<'de> for Box<VarZeroSlice<T, F>> +where + T: VarULE + ?Sized, + Box<T>: Deserialize<'de>, + F: VarZeroVecFormat, +{ + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: Deserializer<'de>, + { + let deserialized = VarZeroVec::<T, F>::deserialize(deserializer)?; + Ok(deserialized.to_boxed()) + } +} + +/// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate #[cfg(feature = "serde")] impl<T, F> Serialize for VarZeroVec<'_, T, F> where @@ -166,6 +182,12 @@ mod test { _data: &'data VarZeroSlice<str>, } + #[derive(serde::Serialize, serde::Deserialize)] + struct DeriveTest_VarZeroVec_of_VarZeroSlice<'data> { + #[serde(borrow)] + _data: VarZeroVec<'data, VarZeroSlice<str>>, + } + // ["foo", "bar", "baz", "dolor", "quux", "lorem ipsum"]; const BYTES: &[u8] = &[ 6, 0, 0, 0, 0, 0, 3, 0, 6, 0, 9, 0, 14, 0, 18, 0, 102, 111, 111, 98, 97, 114, 98, 97, 122, diff --git a/vendor/zerovec/src/varzerovec/slice.rs b/vendor/zerovec/src/varzerovec/slice.rs index afdbe80d9..119f1d38f 100644 --- a/vendor/zerovec/src/varzerovec/slice.rs +++ b/vendor/zerovec/src/varzerovec/slice.rs @@ -108,8 +108,8 @@ pub struct VarZeroSlice<T: ?Sized, F = Index16> { impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroSlice<T, F> { /// Construct a new empty VarZeroSlice pub const fn new_empty() -> &'static Self { - let arr: &[u8] = &[]; - unsafe { mem::transmute(arr) } + // The empty VZV is special-cased to the empty slice + unsafe { mem::transmute(&[] as &[u8]) } } /// Obtain a [`VarZeroVecComponents`] borrowing from the internal buffer @@ -192,7 +192,7 @@ impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroSlice<T, F> { self.as_components().iter() } - /// Get one of this slice's elements, returning None if the index is out of bounds + /// Get one of this slice's elements, returning `None` if the index is out of bounds /// /// # Example /// @@ -366,7 +366,7 @@ where /// assert_eq!(vec.binary_search_in_range("g", 1..6), Some(Ok(2))); /// assert_eq!(vec.binary_search_in_range("h", 1..6), Some(Err(3))); /// - /// // Will return None if the range is out of bounds: + /// // Will return `None` if the range is out of bounds: /// assert_eq!(vec.binary_search_in_range("x", 100..200), None); /// assert_eq!(vec.binary_search_in_range("x", 0..200), None); /// # Ok::<(), ZeroVecError>(()) @@ -460,7 +460,7 @@ where /// Some(Err(3)) /// ); /// - /// // Will return None if the range is out of bounds: + /// // Will return `None` if the range is out of bounds: /// assert_eq!( /// vec.binary_search_in_range_by(|v| v.cmp("x"), 100..200), /// None diff --git a/vendor/zerovec/src/varzerovec/vec.rs b/vendor/zerovec/src/varzerovec/vec.rs index 1401a180a..64928509f 100644 --- a/vendor/zerovec/src/varzerovec/vec.rs +++ b/vendor/zerovec/src/varzerovec/vec.rs @@ -425,8 +425,12 @@ where { #[inline] fn from(elements: &[A]) -> Self { - #[allow(clippy::unwrap_used)] // TODO(#1410) Better story for fallibility - VarZeroVecOwned::try_from_elements(elements).unwrap().into() + if elements.is_empty() { + VarZeroSlice::new_empty().into() + } else { + #[allow(clippy::unwrap_used)] // TODO(#1410) Better story for fallibility + VarZeroVecOwned::try_from_elements(elements).unwrap().into() + } } } @@ -451,8 +455,14 @@ where { #[inline] fn eq(&self, other: &VarZeroVec<'b, T, F>) -> bool { - // VarULE has an API guarantee that this is equivalent - // to `T::VarULE::eq()` + // VZV::from_elements used to produce a non-canonical representation of the + // empty VZV, so we cannot use byte equality for empty vecs. + if self.is_empty() || other.is_empty() { + return self.is_empty() && other.is_empty(); + } + // VarULE has an API guarantee that byte equality is semantic equality. + // For non-empty VZVs, there's only a single metadata representation, + // so this guarantee extends to the whole VZV representation. self.as_bytes().eq(other.as_bytes()) } } @@ -503,3 +513,19 @@ impl<'a, T: VarULE + ?Sized + Ord, F: VarZeroVecFormat> Ord for VarZeroVec<'a, T self.iter().cmp(other.iter()) } } + +#[test] +fn assert_single_empty_representation() { + assert_eq!( + VarZeroVec::<str>::new().as_bytes(), + VarZeroVec::<str>::from(&[] as &[&str]).as_bytes() + ); +} + +#[test] +fn weird_empty_representation_equality() { + assert_eq!( + VarZeroVec::<str>::parse_byte_slice(&[0, 0, 0, 0]).unwrap(), + VarZeroVec::<str>::parse_byte_slice(&[]).unwrap() + ); +} diff --git a/vendor/zerovec/src/yoke_impls.rs b/vendor/zerovec/src/yoke_impls.rs index a5064a554..8b196cf9a 100644 --- a/vendor/zerovec/src/yoke_impls.rs +++ b/vendor/zerovec/src/yoke_impls.rs @@ -3,6 +3,9 @@ // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). // This way we can copy-paste Yokeable impls +#![allow(unknown_lints)] // forgetting_copy_types +#![allow(renamed_and_removed_lints)] // forgetting_copy_types +#![allow(forgetting_copy_types)] #![allow(clippy::forget_copy)] #![allow(clippy::forget_non_drop)] @@ -30,8 +33,8 @@ unsafe impl<'a, T: 'static + AsULE + ?Sized> Yokeable<'a> for ZeroVec<'static, T #[inline] unsafe fn make(from: Self::Output) -> Self { debug_assert!(mem::size_of::<Self::Output>() == mem::size_of::<Self>()); - let ptr: *const Self = (&from as *const Self::Output).cast(); - mem::forget(from); + let from = mem::ManuallyDrop::new(from); + let ptr: *const Self = (&*from as *const Self::Output).cast(); ptr::read(ptr) } #[inline] @@ -58,8 +61,8 @@ unsafe impl<'a, T: 'static + VarULE + ?Sized> Yokeable<'a> for VarZeroVec<'stati #[inline] unsafe fn make(from: Self::Output) -> Self { debug_assert!(mem::size_of::<Self::Output>() == mem::size_of::<Self>()); - let ptr: *const Self = (&from as *const Self::Output).cast(); - mem::forget(from); + let from = mem::ManuallyDrop::new(from); + let ptr: *const Self = (&*from as *const Self::Output).cast(); ptr::read(ptr) } #[inline] @@ -86,8 +89,8 @@ unsafe impl<'a> Yokeable<'a> for FlexZeroVec<'static> { #[inline] unsafe fn make(from: Self::Output) -> Self { debug_assert!(mem::size_of::<Self::Output>() == mem::size_of::<Self>()); - let ptr: *const Self = (&from as *const Self::Output).cast(); - mem::forget(from); + let from = mem::ManuallyDrop::new(from); + let ptr: *const Self = (&*from as *const Self::Output).cast(); ptr::read(ptr) } #[inline] @@ -124,16 +127,16 @@ where unsafe { // Similar problem as transform(), but we need to use ptr::read since // the compiler isn't sure of the sizes - let ptr: *const Self::Output = (&self as *const Self).cast(); - mem::forget(self); + let this = mem::ManuallyDrop::new(self); + let ptr: *const Self::Output = (&*this as *const Self).cast(); ptr::read(ptr) } } #[inline] unsafe fn make(from: Self::Output) -> Self { debug_assert!(mem::size_of::<Self::Output>() == mem::size_of::<Self>()); - let ptr: *const Self = (&from as *const Self::Output).cast(); - mem::forget(from); + let from = mem::ManuallyDrop::new(from); + let ptr: *const Self = (&*from as *const Self::Output).cast(); ptr::read(ptr) } #[inline] @@ -170,16 +173,16 @@ where unsafe { // Similar problem as transform(), but we need to use ptr::read since // the compiler isn't sure of the sizes - let ptr: *const Self::Output = (&self as *const Self).cast(); - mem::forget(self); + let this = mem::ManuallyDrop::new(self); + let ptr: *const Self::Output = (&*this as *const Self).cast(); ptr::read(ptr) } } #[inline] unsafe fn make(from: Self::Output) -> Self { debug_assert!(mem::size_of::<Self::Output>() == mem::size_of::<Self>()); - let ptr: *const Self = (&from as *const Self::Output).cast(); - mem::forget(from); + let from = mem::ManuallyDrop::new(from); + let ptr: *const Self = (&*from as *const Self::Output).cast(); ptr::read(ptr) } #[inline] @@ -218,16 +221,16 @@ where unsafe { // Similar problem as transform(), but we need to use ptr::read since // the compiler isn't sure of the sizes - let ptr: *const Self::Output = (&self as *const Self).cast(); - mem::forget(self); + let this = mem::ManuallyDrop::new(self); + let ptr: *const Self::Output = (&*this as *const Self).cast(); ptr::read(ptr) } } #[inline] unsafe fn make(from: Self::Output) -> Self { debug_assert!(mem::size_of::<Self::Output>() == mem::size_of::<Self>()); - let ptr: *const Self = (&from as *const Self::Output).cast(); - mem::forget(from); + let from = mem::ManuallyDrop::new(from); + let ptr: *const Self = (&*from as *const Self::Output).cast(); ptr::read(ptr) } #[inline] @@ -266,16 +269,16 @@ where unsafe { // Similar problem as transform(), but we need to use ptr::read since // the compiler isn't sure of the sizes - let ptr: *const Self::Output = (&self as *const Self).cast(); - mem::forget(self); + let this = mem::ManuallyDrop::new(self); + let ptr: *const Self::Output = (&*this as *const Self).cast(); ptr::read(ptr) } } #[inline] unsafe fn make(from: Self::Output) -> Self { debug_assert!(mem::size_of::<Self::Output>() == mem::size_of::<Self>()); - let ptr: *const Self = (&from as *const Self::Output).cast(); - mem::forget(from); + let from = mem::ManuallyDrop::new(from); + let ptr: *const Self = (&*from as *const Self::Output).cast(); ptr::read(ptr) } #[inline] diff --git a/vendor/zerovec/src/zerovec/databake.rs b/vendor/zerovec/src/zerovec/databake.rs index e46065c38..31f167594 100644 --- a/vendor/zerovec/src/zerovec/databake.rs +++ b/vendor/zerovec/src/zerovec/databake.rs @@ -8,15 +8,15 @@ use databake::*; impl<T> Bake for ZeroVec<'_, T> where - T: AsULE + ?Sized, + T: AsULE + ?Sized + Bake, { fn bake(&self, env: &CrateEnv) -> TokenStream { env.insert("zerovec"); if self.is_empty() { - quote! { ::zerovec::ZeroVec::new() } + quote! { zerovec::ZeroVec::new() } } else { let bytes = databake::Bake::bake(&self.as_bytes(), env); - quote! { unsafe { ::zerovec::ZeroVec::from_bytes_unchecked(#bytes) } } + quote! { unsafe { zerovec::ZeroVec::from_bytes_unchecked(#bytes) } } } } } @@ -28,10 +28,10 @@ where fn bake(&self, env: &CrateEnv) -> TokenStream { env.insert("zerovec"); if self.is_empty() { - quote! { ::zerovec::ZeroSlice::new_empty() } + quote! { zerovec::ZeroSlice::new_empty() } } else { let bytes = databake::Bake::bake(&self.as_bytes(), env); - quote! { unsafe { ::zerovec::ZeroSlice::from_bytes_unchecked(#bytes) } } + quote! { unsafe { zerovec::ZeroSlice::from_bytes_unchecked(#bytes) } } } } } diff --git a/vendor/zerovec/src/zerovec/mod.rs b/vendor/zerovec/src/zerovec/mod.rs index e7c87f68e..e6186be0a 100644 --- a/vendor/zerovec/src/zerovec/mod.rs +++ b/vendor/zerovec/src/zerovec/mod.rs @@ -20,7 +20,9 @@ use core::fmt; use core::iter::FromIterator; use core::marker::PhantomData; use core::mem; +use core::num::NonZeroUsize; use core::ops::Deref; +use core::ptr; /// A zero-copy, byte-aligned vector for fixed-width types. /// @@ -138,8 +140,8 @@ impl<U> EyepatchHackVector<U> { } // Return a slice to the inner data #[inline] - fn as_slice<'a>(&'a self) -> &'a [U] { - unsafe { &*self.buf } + const fn as_slice<'a>(&'a self) -> &'a [U] { + unsafe { &*(self.buf as *const [U]) } } /// Return this type as a vector @@ -255,6 +257,24 @@ impl<'a, T: AsULE + Ord> Ord for ZeroVec<'a, T> { } } +impl<'a, T: AsULE> AsRef<[T::ULE]> for ZeroVec<'a, T> { + fn as_ref(&self) -> &[T::ULE] { + self.as_ule_slice() + } +} + +impl<'a, T: AsULE> From<&'a [T::ULE]> for ZeroVec<'a, T> { + fn from(other: &'a [T::ULE]) -> Self { + ZeroVec::new_borrowed(other) + } +} + +impl<'a, T: AsULE> From<Vec<T::ULE>> for ZeroVec<'a, T> { + fn from(other: Vec<T::ULE>) -> Self { + ZeroVec::new_owned(other) + } +} + impl<'a, T> ZeroVec<'a, T> where T: AsULE + ?Sized, @@ -274,6 +294,11 @@ where Self::new_borrowed(&[]) } + /// Same as `ZeroSlice::len`, which is available through `Deref` and not `const`. + pub const fn const_len(&self) -> usize { + self.vector.as_slice().len() + } + /// Creates a new owned `ZeroVec` using an existing /// allocated backing buffer /// @@ -284,10 +309,10 @@ where // Deconstruct the vector into parts // This is the only part of the code that goes from Vec // to ZeroVec, all other such operations should use this function - let slice: &[T::ULE] = &vec; - let slice = slice as *const [_] as *mut [_]; let capacity = vec.capacity(); - mem::forget(vec); + let len = vec.len(); + let ptr = mem::ManuallyDrop::new(vec).as_mut_ptr(); + let slice = ptr::slice_from_raw_parts_mut(ptr, len); Self { vector: EyepatchHackVector { buf: slice, @@ -352,10 +377,10 @@ where /// `bytes` need to be an output from [`ZeroSlice::as_bytes()`]. pub const unsafe fn from_bytes_unchecked(bytes: &'a [u8]) -> Self { // &[u8] and &[T::ULE] are the same slice with different length metadata. - Self::new_borrowed(core::mem::transmute(( - bytes.as_ptr(), + Self::new_borrowed(core::slice::from_raw_parts( + bytes.as_ptr() as *const T::ULE, bytes.len() / core::mem::size_of::<T::ULE>(), - ))) + )) } /// Converts a `ZeroVec<T>` into a `ZeroVec<u8>`, retaining the current ownership model. @@ -554,6 +579,34 @@ where Some(ZeroSlice::from_ule_slice(ule_slice)) } } + + /// If the ZeroVec is owned, returns the capacity of the vector. + /// + /// Otherwise, if the ZeroVec is borrowed, returns `None`. + /// + /// # Examples + /// + /// ``` + /// use zerovec::ZeroVec; + /// + /// let mut zv = ZeroVec::<u8>::new_borrowed(&[0, 1, 2, 3]); + /// assert!(!zv.is_owned()); + /// assert_eq!(zv.owned_capacity(), None); + /// + /// // Convert to owned without appending anything + /// zv.with_mut(|v| ()); + /// assert!(zv.is_owned()); + /// assert_eq!(zv.owned_capacity(), Some(4.try_into().unwrap())); + /// + /// // Double the size by appending + /// zv.with_mut(|v| v.push(0)); + /// assert!(zv.is_owned()); + /// assert_eq!(zv.owned_capacity(), Some(8.try_into().unwrap())); + /// ``` + #[inline] + pub fn owned_capacity(&self) -> Option<NonZeroUsize> { + NonZeroUsize::try_from(self.vector.capacity).ok() + } } impl<'a> ZeroVec<'a, u8> { @@ -860,21 +913,18 @@ where /// the logical equivalent of this type's internal representation #[inline] pub fn into_cow(self) -> Cow<'a, [T::ULE]> { - if self.is_owned() { + let this = mem::ManuallyDrop::new(self); + if this.is_owned() { let vec = unsafe { // safe to call: we know it's owned, - // and we mem::forget self immediately afterwards - self.vector.get_vec() + // and `self`/`this` are thenceforth no longer used or dropped + { this }.vector.get_vec() }; - mem::forget(self); Cow::Owned(vec) } else { // We can extend the lifetime of the slice to 'a // since we know it is borrowed - let slice = unsafe { self.vector.as_arbitrary_slice() }; - // The borrowed destructor is a no-op, but we want to prevent - // the check being run - mem::forget(self); + let slice = unsafe { { this }.vector.as_arbitrary_slice() }; Cow::Borrowed(slice) } } @@ -890,6 +940,89 @@ impl<T: AsULE> FromIterator<T> for ZeroVec<'_, T> { } } +/// Convenience wrapper for [`ZeroSlice::from_ule_slice`]. The value will be created at compile-time, +/// meaning that all arguments must also be constant. +/// +/// # Arguments +/// +/// * `$aligned` - The type of an element in its canonical, aligned form, e.g., `char`. +/// * `$convert` - A const function that converts an `$aligned` into its unaligned equivalent, e.g., +/// `const fn from_aligned(a: CanonicalType) -> CanonicalType::ULE`. +/// * `$x` - The elements that the `ZeroSlice` will hold. +/// +/// # Examples +/// +/// Using array-conversion functions provided by this crate: +/// +/// ``` +/// use zerovec::{ZeroSlice, zeroslice, ule::AsULE}; +/// use zerovec::ule::UnvalidatedChar; +/// +/// const SIGNATURE: &ZeroSlice<char> = zeroslice!(char; <char as AsULE>::ULE::from_aligned; ['b', 'y', 'e', '✌']); +/// const EMPTY: &ZeroSlice<u32> = zeroslice![]; +/// const UC: &ZeroSlice<UnvalidatedChar> = +/// zeroslice!( +/// UnvalidatedChar; +/// <UnvalidatedChar as AsULE>::ULE::from_unvalidated_char; +/// [UnvalidatedChar::from_char('a')] +/// ); +/// let empty: &ZeroSlice<u32> = zeroslice![]; +/// let nums = zeroslice!(u32; <u32 as AsULE>::ULE::from_unsigned; [1, 2, 3, 4, 5]); +/// assert_eq!(nums.last().unwrap(), 5); +/// ``` +/// +/// Using a custom array-conversion function: +/// +/// ``` +/// use zerovec::{ule::AsULE, ule::RawBytesULE, zeroslice, ZeroSlice}; +/// +/// const fn be_convert(num: i16) -> <i16 as AsULE>::ULE { +/// RawBytesULE(num.to_be_bytes()) +/// } +/// +/// const NUMBERS_BE: &ZeroSlice<i16> = +/// zeroslice!(i16; be_convert; [1, -2, 3, -4, 5]); +/// ``` +#[macro_export] +macro_rules! zeroslice { + () => ( + $crate::ZeroSlice::new_empty() + ); + ($aligned:ty; $convert:expr; [$($x:expr),+ $(,)?]) => ( + $crate::ZeroSlice::<$aligned>::from_ule_slice( + {const X: &[<$aligned as $crate::ule::AsULE>::ULE] = &[ + $($convert($x)),* + ]; X} + ) + ); +} + +/// Creates a borrowed `ZeroVec`. Convenience wrapper for `zeroslice!(...).as_zerovec()`. The value +/// will be created at compile-time, meaning that all arguments must also be constant. +/// +/// See [`zeroslice!`](crate::zeroslice) for more information. +/// +/// # Examples +/// +/// ``` +/// use zerovec::{ZeroVec, zerovec, ule::AsULE}; +/// +/// const SIGNATURE: ZeroVec<char> = zerovec!(char; <char as AsULE>::ULE::from_aligned; ['a', 'y', 'e', '✌']); +/// assert!(!SIGNATURE.is_owned()); +/// +/// const EMPTY: ZeroVec<u32> = zerovec![]; +/// assert!(!EMPTY.is_owned()); +/// ``` +#[macro_export] +macro_rules! zerovec { + () => ( + $crate::ZeroVec::new() + ); + ($aligned:ty; $convert:expr; [$($x:expr),+ $(,)?]) => ( + $crate::zeroslice![$aligned; $convert; [$($x),+]].as_zerovec() + ); +} + #[cfg(test)] mod tests { use super::*; diff --git a/vendor/zerovec/src/zerovec/serde.rs b/vendor/zerovec/src/zerovec/serde.rs index e3141071c..bb180d5a1 100644 --- a/vendor/zerovec/src/zerovec/serde.rs +++ b/vendor/zerovec/src/zerovec/serde.rs @@ -216,6 +216,6 @@ mod test { let zerovec_orig: ZeroVec<u32> = ZeroVec::from_slice_or_alloc(&[119, 0xD800, 120]); let bincode_buf = bincode::serialize(&zerovec_orig).expect("serialize"); let zerovec_result = bincode::deserialize::<ZeroVec<char>>(&bincode_buf); - assert!(matches!(zerovec_result, Err(_))); + assert!(zerovec_result.is_err()); } } diff --git a/vendor/zerovec/src/zerovec/slice.rs b/vendor/zerovec/src/zerovec/slice.rs index 52ddc184b..12d88deff 100644 --- a/vendor/zerovec/src/zerovec/slice.rs +++ b/vendor/zerovec/src/zerovec/slice.rs @@ -65,10 +65,10 @@ where /// `bytes` need to be an output from [`ZeroSlice::as_bytes()`]. pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { // &[u8] and &[T::ULE] are the same slice with different length metadata. - Self::from_ule_slice(core::mem::transmute(( - bytes.as_ptr(), + Self::from_ule_slice(core::slice::from_raw_parts( + bytes.as_ptr() as *const T::ULE, bytes.len() / core::mem::size_of::<T::ULE>(), - ))) + )) } /// Construct a `&ZeroSlice<T>` from a slice of ULEs. @@ -169,7 +169,7 @@ impl<T> ZeroSlice<T> where T: AsULE, { - /// Gets the element at the specified index. Returns None if out of range. + /// Gets the element at the specified index. Returns `None` if out of range. /// /// # Example /// @@ -191,7 +191,7 @@ where .map(T::from_unaligned) } - /// Gets the entire slice as an array of length `N`. Returns None if the slice + /// Gets the entire slice as an array of length `N`. Returns `None` if the slice /// does not have exactly `N` elements. /// /// # Example @@ -212,7 +212,7 @@ where Some(ule_array.map(|u| T::from_unaligned(u))) } - /// Gets a subslice of elements within a certain range. Returns None if the range + /// Gets a subslice of elements within a certain range. Returns `None` if the range /// is out of bounds of this `ZeroSlice`. /// /// # Example @@ -307,7 +307,7 @@ where Ok(ZeroSlice::from_ule_slice(new_slice)) } - /// Gets the first element. Returns None if empty. + /// Gets the first element. Returns `None` if empty. /// /// # Example /// @@ -325,7 +325,7 @@ where self.as_ule_slice().first().copied().map(T::from_unaligned) } - /// Gets the last element. Returns None if empty. + /// Gets the last element. Returns `None` if empty. /// /// # Example /// @@ -567,6 +567,7 @@ where #[cfg(test)] mod test { use super::*; + use crate::zeroslice; #[test] fn test_split_first() { @@ -577,20 +578,16 @@ mod test { { // single element slice const DATA: &ZeroSlice<u16> = - ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([211])); - assert_eq!((211, ZeroSlice::new_empty()), DATA.split_first().unwrap()); + zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [211]); + assert_eq!((211, zeroslice![]), DATA.split_first().unwrap()); } { // slice with many elements. const DATA: &ZeroSlice<u16> = - ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([ - 211, 281, 421, 32973, - ])); + zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [211, 281, 421, 32973]); const EXPECTED_VALUE: (u16, &ZeroSlice<u16>) = ( 211, - ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([ - 281, 421, 32973, - ])), + zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [281, 421, 32973]), ); assert_eq!(EXPECTED_VALUE, DATA.split_first().unwrap()); |