+351
| // This file is part of ICU4X. For terms of use, please see the file | ||
| // called LICENSE at the top level of the ICU4X source tree | ||
| // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). | ||
| use crate::ule::{EncodeAsVarULE, UleError, VarULE}; | ||
| use alloc::boxed::Box; | ||
| use core::fmt; | ||
| use core::marker::PhantomData; | ||
| use core::mem::ManuallyDrop; | ||
| use core::ops::Deref; | ||
| use core::ptr::NonNull; | ||
| use zerofrom::ZeroFrom; | ||
| /// Copy-on-write type that efficiently represents [`VarULE`] types as their bitstream representation. | ||
| /// | ||
| /// The primary use case for [`VarULE`] types is the ability to store complex variable-length datastructures | ||
| /// inside variable-length collections like [`crate::VarZeroVec`]. | ||
| /// | ||
| /// Underlying this ability is the fact that [`VarULE`] types can be efficiently represented as a flat | ||
| /// bytestream. | ||
| /// | ||
| /// In zero-copy cases, sometimes one wishes to unconditionally use this bytestream representation, for example | ||
| /// to save stack size. A struct with five `Cow<'a, str>`s is not as stack-efficient as a single `Cow` containing | ||
| /// the bytestream representation of, say, `Tuple5VarULE<str, str, str, str, str>`. | ||
| /// | ||
| /// This type helps in this case: It is logically a `Cow<'a, V>`, with some optimizations, that is guaranteed | ||
| /// to serialize as a byte stream in machine-readable scenarios. | ||
| /// | ||
| /// During human-readable serialization, it will fall back to the serde impls on `V`, which ought to have | ||
| /// a human-readable variant. | ||
| pub struct VarZeroCow<'a, V: ?Sized> { | ||
| /// Pointer to data | ||
| /// | ||
| /// # Safety Invariants | ||
| /// | ||
| /// 1. This slice must always be valid as a byte slice | ||
| /// 2. This slice must represent a valid `V` | ||
| /// 3. If `owned` is true, this slice can be freed. | ||
| /// | ||
| /// The slice may NOT have the lifetime of `'a`. | ||
| buf: NonNull<[u8]>, | ||
| /// The buffer is `Box<[u8]>` if true | ||
| owned: bool, | ||
| _phantom: PhantomData<(&'a V, Box<V>)>, | ||
| } | ||
| // This is mostly just a `Cow<[u8]>`, safe to implement Send and Sync on | ||
| unsafe impl<'a, V: ?Sized> Send for VarZeroCow<'a, V> {} | ||
| unsafe impl<'a, V: ?Sized> Sync for VarZeroCow<'a, V> {} | ||
| impl<'a, V: ?Sized> Clone for VarZeroCow<'a, V> { | ||
| fn clone(&self) -> Self { | ||
| if self.is_owned() { | ||
| // This clones the box | ||
| let b: Box<[u8]> = self.as_bytes().into(); | ||
| let b = ManuallyDrop::new(b); | ||
| let buf: NonNull<[u8]> = (&**b).into(); | ||
| Self { | ||
| // Invariants upheld: | ||
| // 1 & 2: The bytes came from `self` so they're a valid value and byte slice | ||
| // 3: This is owned (we cloned it), so we set owned to true. | ||
| buf, | ||
| owned: true, | ||
| _phantom: PhantomData, | ||
| } | ||
| } else { | ||
| // Unfortunately we can't just use `new_borrowed(self.deref())` since the lifetime is shorter | ||
| Self { | ||
| // Invariants upheld: | ||
| // 1 & 2: The bytes came from `self` so they're a valid value and byte slice | ||
| // 3: This is borrowed (we're sharing a borrow), so we set owned to false. | ||
| buf: self.buf, | ||
| owned: false, | ||
| _phantom: PhantomData, | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl<'a, V: ?Sized> Drop for VarZeroCow<'a, V> { | ||
| fn drop(&mut self) { | ||
| if self.owned { | ||
| unsafe { | ||
| // Safety: (Invariant 3 on buf) | ||
| // since owned is true, this is a valid Box<[u8]> and can be cleaned up | ||
| let _ = Box::<[u8]>::from_raw(self.buf.as_ptr()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized> VarZeroCow<'a, V> { | ||
| /// Construct from a slice. Errors if the slice doesn't represent a valid `V` | ||
| pub fn parse_bytes(bytes: &'a [u8]) -> Result<Self, UleError> { | ||
| let val = V::parse_bytes(bytes)?; | ||
| Ok(Self::new_borrowed(val)) | ||
| } | ||
| /// Construct from an owned slice. Errors if the slice doesn't represent a valid `V` | ||
| pub fn parse_owned_bytes(bytes: Box<[u8]>) -> Result<Self, UleError> { | ||
| V::validate_bytes(&bytes)?; | ||
| let bytes = ManuallyDrop::new(bytes); | ||
| let buf: NonNull<[u8]> = (&**bytes).into(); | ||
| Ok(Self { | ||
| // Invariants upheld: | ||
| // 1 & 2: The bytes came from `val` so they're a valid value and byte slice | ||
| // 3: This is owned, so we set owned to true. | ||
| buf, | ||
| owned: true, | ||
| _phantom: PhantomData, | ||
| }) | ||
| } | ||
| /// Construct from a slice that is known to represent a valid `V` | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// `bytes` must be a valid `V`, i.e. it must successfully pass through | ||
| /// `V::parse_bytes()` or `V::validate_bytes()`. | ||
| pub const unsafe fn from_bytes_unchecked(bytes: &'a [u8]) -> Self { | ||
| unsafe { | ||
| // Safety: bytes is an &T which is always non-null | ||
| let buf: NonNull<[u8]> = NonNull::new_unchecked(bytes as *const [u8] as *mut [u8]); | ||
| Self { | ||
| // Invariants upheld: | ||
| // 1 & 2: Passed upstream to caller | ||
| // 3: This is borrowed, so we set owned to false. | ||
| buf, | ||
| owned: false, | ||
| _phantom: PhantomData, | ||
| } | ||
| } | ||
| } | ||
| /// Construct this from an [`EncodeAsVarULE`] version of the contained type | ||
| /// | ||
| /// Will always construct an owned version | ||
| pub fn from_encodeable<E: EncodeAsVarULE<V>>(encodeable: &E) -> Self { | ||
| let b = crate::ule::encode_varule_to_box(encodeable); | ||
| Self::new_owned(b) | ||
| } | ||
| /// Construct a new borrowed version of this | ||
| pub fn new_borrowed(val: &'a V) -> Self { | ||
| unsafe { | ||
| // Safety: val is a valid V, by type | ||
| Self::from_bytes_unchecked(val.as_bytes()) | ||
| } | ||
| } | ||
| /// Construct a new borrowed version of this | ||
| pub fn new_owned(val: Box<V>) -> Self { | ||
| let val = ManuallyDrop::new(val); | ||
| let buf: NonNull<[u8]> = val.as_bytes().into(); | ||
| Self { | ||
| // Invariants upheld: | ||
| // 1 & 2: The bytes came from `val` so they're a valid value and byte slice | ||
| // 3: This is owned, so we set owned to true. | ||
| buf, | ||
| owned: true, | ||
| _phantom: PhantomData, | ||
| } | ||
| } | ||
| } | ||
| impl<'a, V: ?Sized> VarZeroCow<'a, V> { | ||
| /// Whether or not this is owned | ||
| pub fn is_owned(&self) -> bool { | ||
| self.owned | ||
| } | ||
| /// Get the byte representation of this type | ||
| /// | ||
| /// Is also always a valid `V` and can be passed to | ||
| /// `V::from_bytes_unchecked()` | ||
| pub fn as_bytes(&self) -> &[u8] { | ||
| // Safety: Invariant 1 on self.buf | ||
| // The valid V invariant comes from Invariant 2 | ||
| unsafe { self.buf.as_ref() } | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized> Deref for VarZeroCow<'a, V> { | ||
| type Target = V; | ||
| fn deref(&self) -> &V { | ||
| // Safety: From invariant 2 on self.buf | ||
| unsafe { V::from_bytes_unchecked(self.as_bytes()) } | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized> From<&'a V> for VarZeroCow<'a, V> { | ||
| fn from(other: &'a V) -> Self { | ||
| Self::new_borrowed(other) | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized> From<Box<V>> for VarZeroCow<'a, V> { | ||
| fn from(other: Box<V>) -> Self { | ||
| Self::new_owned(other) | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized + fmt::Debug> fmt::Debug for VarZeroCow<'a, V> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { | ||
| self.deref().fmt(f) | ||
| } | ||
| } | ||
| // We need manual impls since `#[derive()]` is disallowed on packed types | ||
| impl<'a, V: VarULE + ?Sized + PartialEq> PartialEq for VarZeroCow<'a, V> { | ||
| fn eq(&self, other: &Self) -> bool { | ||
| self.deref().eq(other.deref()) | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized + Eq> Eq for VarZeroCow<'a, V> {} | ||
| impl<'a, V: VarULE + ?Sized + PartialOrd> PartialOrd for VarZeroCow<'a, V> { | ||
| fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||
| self.deref().partial_cmp(other.deref()) | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized + Ord> Ord for VarZeroCow<'a, V> { | ||
| fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||
| self.deref().cmp(other.deref()) | ||
| } | ||
| } | ||
| // # Safety | ||
| // | ||
| // encode_var_ule_len: Produces the length of the contained bytes, which are known to be a valid V by invariant | ||
| // | ||
| // encode_var_ule_write: Writes the contained bytes, which are known to be a valid V by invariant | ||
| unsafe impl<'a, V: VarULE + ?Sized> EncodeAsVarULE<V> for VarZeroCow<'a, V> { | ||
| fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| // unnecessary if the other two are implemented | ||
| unreachable!() | ||
| } | ||
| #[inline] | ||
| fn encode_var_ule_len(&self) -> usize { | ||
| self.as_bytes().len() | ||
| } | ||
| #[inline] | ||
| fn encode_var_ule_write(&self, dst: &mut [u8]) { | ||
| dst.copy_from_slice(self.as_bytes()) | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<'a, V: VarULE + ?Sized + serde::Serialize> serde::Serialize for VarZeroCow<'a, V> { | ||
| fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
| where | ||
| S: serde::Serializer, | ||
| { | ||
| if serializer.is_human_readable() { | ||
| <V as serde::Serialize>::serialize(self.deref(), serializer) | ||
| } else { | ||
| serializer.serialize_bytes(self.as_bytes()) | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<'a, 'de: 'a, V: VarULE + ?Sized> serde::Deserialize<'de> for VarZeroCow<'a, V> | ||
| where | ||
| Box<V>: serde::Deserialize<'de>, | ||
| { | ||
| fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> | ||
| where | ||
| Des: serde::Deserializer<'de>, | ||
| { | ||
| if deserializer.is_human_readable() { | ||
| let b = Box::<V>::deserialize(deserializer)?; | ||
| Ok(Self::new_owned(b)) | ||
| } else { | ||
| let bytes = <&[u8]>::deserialize(deserializer)?; | ||
| Self::parse_bytes(bytes).map_err(serde::de::Error::custom) | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "databake")] | ||
| impl<'a, V: VarULE + ?Sized> databake::Bake for VarZeroCow<'a, V> { | ||
| fn bake(&self, env: &databake::CrateEnv) -> databake::TokenStream { | ||
| env.insert("zerovec"); | ||
| let bytes = self.as_bytes().bake(env); | ||
| databake::quote! { | ||
| // Safety: Known to come from a valid V since self.as_bytes() is always a valid V | ||
| unsafe { | ||
| zerovec::VarZeroCow::from_bytes_unchecked(#bytes) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "databake")] | ||
| impl<'a, V: VarULE + ?Sized> databake::BakeSize for VarZeroCow<'a, V> { | ||
| fn borrows_size(&self) -> usize { | ||
| self.as_bytes().len() | ||
| } | ||
| } | ||
| impl<'a, V: VarULE + ?Sized> ZeroFrom<'a, V> for VarZeroCow<'a, V> { | ||
| #[inline] | ||
| fn zero_from(other: &'a V) -> Self { | ||
| Self::new_borrowed(other) | ||
| } | ||
| } | ||
| impl<'a, 'b, V: VarULE + ?Sized> ZeroFrom<'a, VarZeroCow<'b, V>> for VarZeroCow<'a, V> { | ||
| #[inline] | ||
| fn zero_from(other: &'a VarZeroCow<'b, V>) -> Self { | ||
| Self::new_borrowed(other) | ||
| } | ||
| } | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::VarZeroCow; | ||
| use crate::ule::tuplevar::Tuple3VarULE; | ||
| use crate::vecs::VarZeroSlice; | ||
| #[test] | ||
| fn test_cow_roundtrip() { | ||
| type Messy = Tuple3VarULE<str, [u8], VarZeroSlice<str>>; | ||
| let vec = vec!["one", "two", "three"]; | ||
| let messy: VarZeroCow<Messy> = | ||
| VarZeroCow::from_encodeable(&("hello", &b"g\xFF\xFFdbye"[..], vec)); | ||
| assert_eq!(messy.a(), "hello"); | ||
| assert_eq!(messy.b(), b"g\xFF\xFFdbye"); | ||
| assert_eq!(&messy.c()[1], "two"); | ||
| #[cfg(feature = "serde")] | ||
| { | ||
| let bincode = bincode::serialize(&messy).unwrap(); | ||
| let deserialized: VarZeroCow<Messy> = bincode::deserialize(&bincode).unwrap(); | ||
| assert_eq!( | ||
| messy, deserialized, | ||
| "Single element roundtrips with bincode" | ||
| ); | ||
| assert!(!deserialized.is_owned()); | ||
| let json = serde_json::to_string(&messy).unwrap(); | ||
| let deserialized: VarZeroCow<Messy> = serde_json::from_str(&json).unwrap(); | ||
| assert_eq!(messy, deserialized, "Single element roundtrips with serde"); | ||
| } | ||
| } | ||
| } |
| // 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 ). | ||
| /// Take a VarULE type and serialize it both in human and machine readable contexts, | ||
| /// and ensure it roundtrips correctly | ||
| /// | ||
| /// Note that the concrete type may need to be explicitly specified to prevent issues with | ||
| /// https://github.com/rust-lang/rust/issues/130180 | ||
| #[cfg(feature = "serde")] | ||
| pub(crate) fn assert_serde_roundtrips<T>(var: &T) | ||
| where | ||
| T: crate::ule::VarULE + ?Sized + serde::Serialize, | ||
| for<'a> Box<T>: serde::Deserialize<'a>, | ||
| for<'a> &'a T: serde::Deserialize<'a>, | ||
| T: core::fmt::Debug + PartialEq, | ||
| { | ||
| let bincode = bincode::serialize(var).unwrap(); | ||
| let deserialized: &T = bincode::deserialize(&bincode).unwrap(); | ||
| let deserialized_box: Box<T> = bincode::deserialize(&bincode).unwrap(); | ||
| assert_eq!(var, deserialized, "Single element roundtrips with bincode"); | ||
| assert_eq!( | ||
| var, &*deserialized_box, | ||
| "Single element roundtrips with bincode" | ||
| ); | ||
| let json = serde_json::to_string(var).unwrap(); | ||
| let deserialized: Box<T> = serde_json::from_str(&json).unwrap(); | ||
| assert_eq!(var, &*deserialized, "Single element roundtrips with serde"); | ||
| } |
| // 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 ). | ||
| //! [`VarULE`] impls for tuples. | ||
| //! | ||
| //! This module exports [`Tuple2VarULE`], [`Tuple3VarULE`], ..., the corresponding [`VarULE`] types | ||
| //! of tuples containing purely [`VarULE`] types. | ||
| //! | ||
| //! This can be paired with [`VarTupleULE`] to make arbitrary combinations of [`ULE`] and [`VarULE`] types. | ||
| //! | ||
| //! [`VarTupleULE`]: crate::ule::vartuple::VarTupleULE | ||
| use super::*; | ||
| use crate::varzerovec::{Index16, VarZeroVecFormat}; | ||
| use alloc::borrow::ToOwned; | ||
| use core::fmt; | ||
| use core::marker::PhantomData; | ||
| use core::mem; | ||
| use zerofrom::ZeroFrom; | ||
| macro_rules! tuple_varule { | ||
| // Invocation: Should be called like `tuple_ule!(Tuple2VarULE, 2, [ A a AX 0, B b BX 1 ])` | ||
| // | ||
| // $T is a generic name, $t is a lowercase version of it, $T_alt is an "alternate" name to use when we need two types referring | ||
| // to the same input field, $i is an index. | ||
| // | ||
| // $name is the name of the type, $len MUST be the total number of fields, and then $i must be an integer going from 0 to (n - 1) in sequence | ||
| // (This macro code can rely on $i < $len) | ||
| ($name:ident, $len:literal, [ $($T:ident $t:ident $T_alt: ident $i:tt),+ ]) => { | ||
| #[doc = concat!("VarULE type for tuples with ", $len, " elements. See module docs for more information")] | ||
| #[repr(transparent)] | ||
| #[allow(clippy::exhaustive_structs)] // stable | ||
| pub struct $name<$($T: ?Sized,)+ Format: VarZeroVecFormat = Index16> { | ||
| $($t: PhantomData<$T>,)+ | ||
| // Safety invariant: Each "field" $i of the MultiFieldsULE is a valid instance of $t | ||
| // | ||
| // In other words, calling `.get_field::<$T>($i)` is always safe. | ||
| // | ||
| // This invariant is upheld when this type is constructed during VarULE parsing/validation | ||
| multi: MultiFieldsULE<$len, Format> | ||
| } | ||
| impl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> $name<$($T,)+ Format> { | ||
| $( | ||
| #[doc = concat!("Get field ", $i, "of this tuple")] | ||
| pub fn $t(&self) -> &$T { | ||
| // Safety: See invariant of `multi`. | ||
| unsafe { | ||
| self.multi.get_field::<$T>($i) | ||
| } | ||
| } | ||
| )+ | ||
| } | ||
| // # Safety | ||
| // | ||
| // ## Checklist | ||
| // | ||
| // Safety checklist for `VarULE`: | ||
| // | ||
| // 1. align(1): repr(transparent) around an align(1) VarULE type: MultiFieldsULE | ||
| // 2. No padding: see previous point | ||
| // 3. `validate_bytes` validates that this type is a valid MultiFieldsULE, and that each field is the correct type from the tuple. | ||
| // 4. `validate_bytes` checks length by deferring to the inner ULEs | ||
| // 5. `from_bytes_unchecked` returns a fat pointer to the bytes. | ||
| // 6. All other methods are left at their default impl. | ||
| // 7. The inner ULEs have byte equality, so this composition has byte equality. | ||
| unsafe impl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> VarULE for $name<$($T,)+ Format> | ||
| { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) | ||
| // as in the type def | ||
| let multi = <MultiFieldsULE<$len, Format> as VarULE>::parse_bytes(bytes)?; | ||
| $( | ||
| // Safety invariant: $i < $len, from the macro invocation | ||
| unsafe { | ||
| multi.validate_field::<$T>($i)?; | ||
| } | ||
| )+ | ||
| Ok(()) | ||
| } | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) | ||
| // as in the type def | ||
| let multi = <MultiFieldsULE<$len, Format> as VarULE>::from_bytes_unchecked(bytes); | ||
| // This type is repr(transparent) over MultiFieldsULE<$len>, so its slices can be transmuted | ||
| // Field invariant upheld here: validate_bytes above validates every field for being the right type | ||
| mem::transmute::<&MultiFieldsULE<$len, Format>, &Self>(multi) | ||
| } | ||
| } | ||
| impl<$($T: fmt::Debug + VarULE + ?Sized,)+ Format: VarZeroVecFormat> fmt::Debug for $name<$($T,)+ Format> { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { | ||
| ($(self.$t(),)+).fmt(f) | ||
| } | ||
| } | ||
| // We need manual impls since `#[derive()]` is disallowed on packed types | ||
| impl<$($T: PartialEq + VarULE + ?Sized,)+ Format: VarZeroVecFormat> PartialEq for $name<$($T,)+ Format> { | ||
| fn eq(&self, other: &Self) -> bool { | ||
| ($(self.$t(),)+).eq(&($(other.$t(),)+)) | ||
| } | ||
| } | ||
| impl<$($T: Eq + VarULE + ?Sized,)+ Format: VarZeroVecFormat> Eq for $name<$($T,)+ Format> {} | ||
| impl<$($T: PartialOrd + VarULE + ?Sized,)+ Format: VarZeroVecFormat> PartialOrd for $name<$($T,)+ Format> { | ||
| fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> { | ||
| ($(self.$t(),)+).partial_cmp(&($(other.$t(),)+)) | ||
| } | ||
| } | ||
| impl<$($T: Ord + VarULE + ?Sized,)+ Format: VarZeroVecFormat> Ord for $name<$($T,)+ Format> { | ||
| fn cmp(&self, other: &Self) -> core::cmp::Ordering { | ||
| ($(self.$t(),)+).cmp(&($(other.$t(),)+)) | ||
| } | ||
| } | ||
| // # Safety | ||
| // | ||
| // encode_var_ule_len: returns the length of the individual VarULEs together. | ||
| // | ||
| // encode_var_ule_write: writes bytes by deferring to the inner VarULE impls. | ||
| unsafe impl<$($T,)+ $($T_alt,)+ Format> EncodeAsVarULE<$name<$($T,)+ Format>> for ( $($T_alt),+ ) | ||
| where | ||
| $($T: VarULE + ?Sized,)+ | ||
| $($T_alt: EncodeAsVarULE<$T>,)+ | ||
| Format: VarZeroVecFormat, | ||
| { | ||
| fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| // unnecessary if the other two are implemented | ||
| unreachable!() | ||
| } | ||
| #[inline] | ||
| fn encode_var_ule_len(&self) -> usize { | ||
| // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) | ||
| // as in the type def | ||
| MultiFieldsULE::<$len, Format>::compute_encoded_len_for([$(self.$i.encode_var_ule_len()),+]) | ||
| } | ||
| #[inline] | ||
| fn encode_var_ule_write(&self, dst: &mut [u8]) { | ||
| let lengths = [$(self.$i.encode_var_ule_len()),+]; | ||
| // Safety: We validate that this type is the same kind of MultiFieldsULE (with $len, Format) | ||
| // as in the type def | ||
| let multi = MultiFieldsULE::<$len, Format>::new_from_lengths_partially_initialized(lengths, dst); | ||
| $( | ||
| // Safety: $i < $len, from the macro invocation, and field $i is supposed to be of type $T | ||
| unsafe { | ||
| multi.set_field_at::<$T, $T_alt>($i, &self.$i); | ||
| } | ||
| )+ | ||
| } | ||
| } | ||
| impl<$($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> ToOwned for $name<$($T,)+ Format> { | ||
| type Owned = Box<Self>; | ||
| fn to_owned(&self) -> Self::Owned { | ||
| encode_varule_to_box(self) | ||
| } | ||
| } | ||
| impl<'a, $($T,)+ $($T_alt,)+ Format> ZeroFrom <'a, $name<$($T,)+ Format>> for ($($T_alt),+) | ||
| where | ||
| $($T: VarULE + ?Sized,)+ | ||
| $($T_alt: ZeroFrom<'a, $T>,)+ | ||
| Format: VarZeroVecFormat { | ||
| fn zero_from(other: &'a $name<$($T,)+ Format>) -> Self { | ||
| ( | ||
| $($T_alt::zero_from(other.$t()),)+ | ||
| ) | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<$($T: serde::Serialize,)+ Format> serde::Serialize for $name<$($T,)+ Format> | ||
| where | ||
| $($T: VarULE + ?Sized,)+ | ||
| // This impl should be present on almost all VarULE types. if it isn't, that is a bug | ||
| $(for<'a> &'a $T: ZeroFrom<'a, $T>,)+ | ||
| Format: VarZeroVecFormat | ||
| { | ||
| fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer { | ||
| if serializer.is_human_readable() { | ||
| let this = ( | ||
| $(self.$t()),+ | ||
| ); | ||
| <($(&$T),+) as serde::Serialize>::serialize(&this, serializer) | ||
| } else { | ||
| serializer.serialize_bytes(self.multi.as_bytes()) | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<'de, $($T: VarULE + ?Sized,)+ Format> serde::Deserialize<'de> for Box<$name<$($T,)+ Format>> | ||
| where | ||
| // This impl should be present on almost all deserializable VarULE types | ||
| $( Box<$T>: serde::Deserialize<'de>,)+ | ||
| Format: VarZeroVecFormat { | ||
| fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> where Des: serde::Deserializer<'de> { | ||
| if deserializer.is_human_readable() { | ||
| let this = <( $(Box<$T>),+) as serde::Deserialize>::deserialize(deserializer)?; | ||
| let this_ref = ( | ||
| $(&*this.$i),+ | ||
| ); | ||
| Ok(crate::ule::encode_varule_to_box(&this_ref)) | ||
| } else { | ||
| // This branch should usually not be hit, since Cow-like use cases will hit the Deserialize impl for &'a TupleNVarULE instead. | ||
| let deserialized = <&$name<$($T,)+ Format>>::deserialize(deserializer)?; | ||
| Ok(deserialized.to_boxed()) | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<'a, 'de: 'a, $($T: VarULE + ?Sized,)+ Format: VarZeroVecFormat> serde::Deserialize<'de> for &'a $name<$($T,)+ Format> { | ||
| fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> where Des: serde::Deserializer<'de> { | ||
| if deserializer.is_human_readable() { | ||
| Err(serde::de::Error::custom( | ||
| concat!("&", stringify!($name), " can only deserialize in zero-copy ways"), | ||
| )) | ||
| } else { | ||
| let bytes = <&[u8]>::deserialize(deserializer)?; | ||
| $name::<$($T,)+ Format>::parse_bytes(bytes).map_err(serde::de::Error::custom) | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| tuple_varule!(Tuple2VarULE, 2, [ A a AE 0, B b BE 1 ]); | ||
| tuple_varule!(Tuple3VarULE, 3, [ A a AE 0, B b BE 1, C c CE 2 ]); | ||
| tuple_varule!(Tuple4VarULE, 4, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3 ]); | ||
| tuple_varule!(Tuple5VarULE, 5, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3, E e EE 4 ]); | ||
| tuple_varule!(Tuple6VarULE, 6, [ A a AE 0, B b BE 1, C c CE 2, D d DE 3, E e EE 4, F f FE 5 ]); | ||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
| use crate::varzerovec::{Index16, Index32, Index8, VarZeroVecFormat}; | ||
| use crate::VarZeroSlice; | ||
| use crate::VarZeroVec; | ||
| #[test] | ||
| fn test_pairvarule_validate() { | ||
| let vec: Vec<(&str, &[u8])> = vec![("a", b"b"), ("foo", b"bar"), ("lorem", b"ipsum\xFF")]; | ||
| let zerovec: VarZeroVec<Tuple2VarULE<str, [u8]>> = (&vec).into(); | ||
| let bytes = zerovec.as_bytes(); | ||
| let zerovec2 = VarZeroVec::parse_bytes(bytes).unwrap(); | ||
| assert_eq!(zerovec, zerovec2); | ||
| // Test failed validation with a correctly sized but differently constrained tuple | ||
| // Note: ipsum\xFF is not a valid str | ||
| let zerovec3 = VarZeroVec::<Tuple2VarULE<str, str>>::parse_bytes(bytes); | ||
| assert!(zerovec3.is_err()); | ||
| #[cfg(feature = "serde")] | ||
| for val in zerovec.iter() { | ||
| // Can't use inference due to https://github.com/rust-lang/rust/issues/130180 | ||
| crate::ule::test_utils::assert_serde_roundtrips::<Tuple2VarULE<str, [u8]>>(val); | ||
| } | ||
| } | ||
| fn test_tripleule_validate_inner<Format: VarZeroVecFormat>() { | ||
| let vec: Vec<(&str, &[u8], VarZeroVec<str>)> = vec![ | ||
| ("a", b"b", (&vec!["a", "b", "c"]).into()), | ||
| ("foo", b"bar", (&vec!["baz", "quux"]).into()), | ||
| ( | ||
| "lorem", | ||
| b"ipsum\xFF", | ||
| (&vec!["dolor", "sit", "amet"]).into(), | ||
| ), | ||
| ]; | ||
| let zerovec: VarZeroVec<Tuple3VarULE<str, [u8], VarZeroSlice<str>, Format>> = (&vec).into(); | ||
| let bytes = zerovec.as_bytes(); | ||
| let zerovec2 = VarZeroVec::parse_bytes(bytes).unwrap(); | ||
| assert_eq!(zerovec, zerovec2); | ||
| // Test failed validation with a correctly sized but differently constrained tuple | ||
| // Note: the str is unlikely to be a valid varzerovec | ||
| let zerovec3 = VarZeroVec::<Tuple3VarULE<VarZeroSlice<str>, [u8], VarZeroSlice<str>, Format>>::parse_bytes(bytes); | ||
| assert!(zerovec3.is_err()); | ||
| #[cfg(feature = "serde")] | ||
| for val in zerovec.iter() { | ||
| // Can't use inference due to https://github.com/rust-lang/rust/issues/130180 | ||
| crate::ule::test_utils::assert_serde_roundtrips::< | ||
| Tuple3VarULE<str, [u8], VarZeroSlice<str>, Format>, | ||
| >(val); | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_tripleule_validate() { | ||
| test_tripleule_validate_inner::<Index8>(); | ||
| test_tripleule_validate_inner::<Index16>(); | ||
| test_tripleule_validate_inner::<Index32>(); | ||
| } | ||
| } |
| // 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 ). | ||
| //! Types to help compose fixed-size [`ULE`] and variable-size [`VarULE`] primitives. | ||
| //! | ||
| //! This module exports [`VarTuple`] and [`VarTupleULE`], which allow a single sized type and | ||
| //! a single unsized type to be stored together as a [`VarULE`]. | ||
| //! | ||
| //! # Examples | ||
| //! | ||
| //! ``` | ||
| //! use zerovec::ule::vartuple::{VarTuple, VarTupleULE}; | ||
| //! use zerovec::VarZeroVec; | ||
| //! | ||
| //! struct Employee<'a> { | ||
| //! id: u32, | ||
| //! name: &'a str, | ||
| //! }; | ||
| //! | ||
| //! let employees = [ | ||
| //! Employee { | ||
| //! id: 12345, | ||
| //! name: "Jane Doe", | ||
| //! }, | ||
| //! Employee { | ||
| //! id: 67890, | ||
| //! name: "John Doe", | ||
| //! }, | ||
| //! ]; | ||
| //! | ||
| //! let employees_as_var_tuples = employees | ||
| //! .into_iter() | ||
| //! .map(|x| VarTuple { | ||
| //! sized: x.id, | ||
| //! variable: x.name, | ||
| //! }) | ||
| //! .collect::<Vec<_>>(); | ||
| //! | ||
| //! let employees_vzv: VarZeroVec<VarTupleULE<u32, str>> = | ||
| //! employees_as_var_tuples.as_slice().into(); | ||
| //! | ||
| //! assert_eq!(employees_vzv.len(), 2); | ||
| //! | ||
| //! assert_eq!(employees_vzv.get(0).unwrap().sized.as_unsigned_int(), 12345); | ||
| //! assert_eq!(&employees_vzv.get(0).unwrap().variable, "Jane Doe"); | ||
| //! | ||
| //! assert_eq!(employees_vzv.get(1).unwrap().sized.as_unsigned_int(), 67890); | ||
| //! assert_eq!(&employees_vzv.get(1).unwrap().variable, "John Doe"); | ||
| //! ``` | ||
| use alloc::borrow::ToOwned; | ||
| use alloc::boxed::Box; | ||
| use core::mem::{size_of, transmute_copy}; | ||
| use zerofrom::ZeroFrom; | ||
| use super::{AsULE, EncodeAsVarULE, UleError, VarULE, ULE}; | ||
| /// A sized type that can be converted to a [`VarTupleULE`]. | ||
| /// | ||
| /// See the module for examples. | ||
| #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] | ||
| #[allow(clippy::exhaustive_structs)] // well-defined type | ||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| pub struct VarTuple<A, B> { | ||
| pub sized: A, | ||
| pub variable: B, | ||
| } | ||
| /// A dynamically-sized type combining a sized and an unsized type. | ||
| /// | ||
| /// See the module for examples. | ||
| #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] | ||
| #[allow(clippy::exhaustive_structs)] // well-defined type | ||
| #[repr(C)] | ||
| pub struct VarTupleULE<A: AsULE, V: VarULE + ?Sized> { | ||
| pub sized: A::ULE, | ||
| pub variable: V, | ||
| } | ||
| // # Safety | ||
| // | ||
| // ## Representation | ||
| // | ||
| // The type `VarTupleULE` is align(1) because it is repr(C) and its fields | ||
| // are all align(1), since they are themselves ULE and VarULE, which have | ||
| // this same safety constraint. Further, there is no padding, because repr(C) | ||
| // does not add padding when all fields are align(1). | ||
| // | ||
| // <https://doc.rust-lang.org/reference/type-layout.html#the-c-representation> | ||
| // | ||
| // Pointers to `VarTupleULE` are fat pointers with metadata equal to the | ||
| // metadata of the inner DST field V. | ||
| // | ||
| // <https://doc.rust-lang.org/stable/std/ptr/trait.Pointee.html> | ||
| // | ||
| // ## Checklist | ||
| // | ||
| // Safety checklist for `VarULE`: | ||
| // | ||
| // 1. align(1): see "Representation" above. | ||
| // 2. No padding: see "Representation" above. | ||
| // 3. `validate_bytes` checks length and defers to the inner ULEs. | ||
| // 4. `validate_bytes` checks length and defers to the inner ULEs. | ||
| // 5. `from_bytes_unchecked` returns a fat pointer to the bytes. | ||
| // 6. All other methods are left at their default impl. | ||
| // 7. The two ULEs have byte equality, so this composition has byte equality. | ||
| unsafe impl<A, V> VarULE for VarTupleULE<A, V> | ||
| where | ||
| A: AsULE + 'static, | ||
| V: VarULE + ?Sized, | ||
| { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| // TODO: use split_first_chunk_mut in 1.77 | ||
| if bytes.len() < size_of::<A::ULE>() { | ||
| return Err(UleError::length::<Self>(bytes.len())); | ||
| } | ||
| let (sized_chunk, variable_chunk) = bytes.split_at(size_of::<A::ULE>()); | ||
| A::ULE::validate_bytes(sized_chunk)?; | ||
| V::validate_bytes(variable_chunk)?; | ||
| Ok(()) | ||
| } | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| #[allow(clippy::panic)] // panic is documented in function contract | ||
| if bytes.len() < size_of::<A::ULE>() { | ||
| panic!("from_bytes_unchecked called with short slice") | ||
| } | ||
| let (_sized_chunk, variable_chunk) = bytes.split_at(size_of::<A::ULE>()); | ||
| // Safety: variable_chunk is a valid V because of this function's precondition: bytes is a valid Self, | ||
| // and a valid Self contains a valid V after the space needed for A::ULE. | ||
| let variable_ref = V::from_bytes_unchecked(variable_chunk); | ||
| let variable_ptr: *const V = variable_ref; | ||
| // Safety: The DST of VarTupleULE is a pointer to the `sized` element and has a metadata | ||
| // equal to the metadata of the `variable` field (see "Representation" comments on the impl). | ||
| // We should use the pointer metadata APIs here when they are stable: https://github.com/rust-lang/rust/issues/81513 | ||
| // For now we rely on all DST metadata being a usize. | ||
| // Extract metadata from V's DST | ||
| // Rust doesn't know that `&V` is a fat pointer so we have to use transmute_copy | ||
| assert_eq!(size_of::<*const V>(), size_of::<(*const u8, usize)>()); | ||
| // Safety: We have asserted that the transmute Src and Dst are the same size. Furthermore, | ||
| // DST pointers are a pointer and usize length metadata | ||
| let (_v_ptr, metadata) = transmute_copy::<*const V, (*const u8, usize)>(&variable_ptr); | ||
| // Construct a new DST with the same metadata as V | ||
| assert_eq!(size_of::<*const Self>(), size_of::<(*const u8, usize)>()); | ||
| // Safety: Same as above but in the other direction. | ||
| let composed_ptr = | ||
| transmute_copy::<(*const u8, usize), *const Self>(&(bytes.as_ptr(), metadata)); | ||
| &*(composed_ptr) | ||
| } | ||
| } | ||
| // # Safety | ||
| // | ||
| // encode_var_ule_len: returns the length of the two ULEs together. | ||
| // | ||
| // encode_var_ule_write: writes bytes by deferring to the inner ULE impls. | ||
| unsafe impl<A, B, V> EncodeAsVarULE<VarTupleULE<A, V>> for VarTuple<A, B> | ||
| where | ||
| A: AsULE + 'static, | ||
| B: EncodeAsVarULE<V>, | ||
| V: VarULE + ?Sized, | ||
| { | ||
| fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| // unnecessary if the other two are implemented | ||
| unreachable!() | ||
| } | ||
| #[inline] | ||
| fn encode_var_ule_len(&self) -> usize { | ||
| size_of::<A::ULE>() + self.variable.encode_var_ule_len() | ||
| } | ||
| #[inline] | ||
| fn encode_var_ule_write(&self, dst: &mut [u8]) { | ||
| // TODO: use split_first_chunk_mut in 1.77 | ||
| let (sized_chunk, variable_chunk) = dst.split_at_mut(size_of::<A::ULE>()); | ||
| sized_chunk.clone_from_slice([self.sized.to_unaligned()].as_bytes()); | ||
| self.variable.encode_var_ule_write(variable_chunk); | ||
| } | ||
| } | ||
| impl<A, V> ToOwned for VarTupleULE<A, V> | ||
| where | ||
| A: AsULE + 'static, | ||
| V: VarULE + ?Sized, | ||
| { | ||
| type Owned = Box<Self>; | ||
| fn to_owned(&self) -> Self::Owned { | ||
| crate::ule::encode_varule_to_box(self) | ||
| } | ||
| } | ||
| impl<'a, A, B, V> ZeroFrom<'a, VarTupleULE<A, V>> for VarTuple<A, B> | ||
| where | ||
| A: AsULE + 'static, | ||
| V: VarULE + ?Sized, | ||
| B: ZeroFrom<'a, V>, | ||
| { | ||
| fn zero_from(other: &'a VarTupleULE<A, V>) -> Self { | ||
| VarTuple { | ||
| sized: AsULE::from_unaligned(other.sized), | ||
| variable: B::zero_from(&other.variable), | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<A, V> serde::Serialize for VarTupleULE<A, V> | ||
| where | ||
| A: AsULE + 'static, | ||
| V: VarULE + ?Sized, | ||
| A: serde::Serialize, | ||
| V: serde::Serialize, | ||
| { | ||
| fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
| where | ||
| S: serde::Serializer, | ||
| { | ||
| if serializer.is_human_readable() { | ||
| let this = VarTuple { | ||
| sized: A::from_unaligned(self.sized), | ||
| variable: &self.variable, | ||
| }; | ||
| this.serialize(serializer) | ||
| } else { | ||
| serializer.serialize_bytes(self.as_bytes()) | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<'a, 'de: 'a, A, V> serde::Deserialize<'de> for &'a VarTupleULE<A, V> | ||
| where | ||
| A: AsULE + 'static, | ||
| V: VarULE + ?Sized, | ||
| A: serde::Deserialize<'de>, | ||
| { | ||
| fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> | ||
| where | ||
| Des: serde::Deserializer<'de>, | ||
| { | ||
| if !deserializer.is_human_readable() { | ||
| let bytes = <&[u8]>::deserialize(deserializer)?; | ||
| VarTupleULE::<A, V>::parse_bytes(bytes).map_err(serde::de::Error::custom) | ||
| } else { | ||
| Err(serde::de::Error::custom( | ||
| "&VarTupleULE can only deserialize in zero-copy ways", | ||
| )) | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "serde")] | ||
| impl<'de, A, V> serde::Deserialize<'de> for Box<VarTupleULE<A, V>> | ||
| where | ||
| A: AsULE + 'static, | ||
| V: VarULE + ?Sized, | ||
| A: serde::Deserialize<'de>, | ||
| Box<V>: serde::Deserialize<'de>, | ||
| { | ||
| fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error> | ||
| where | ||
| Des: serde::Deserializer<'de>, | ||
| { | ||
| if deserializer.is_human_readable() { | ||
| let this = VarTuple::<A, Box<V>>::deserialize(deserializer)?; | ||
| Ok(crate::ule::encode_varule_to_box(&this)) | ||
| } else { | ||
| // This branch should usually not be hit, since Cow-like use cases will hit the Deserialize impl for &'a TupleNVarULE instead. | ||
| let deserialized = <&VarTupleULE<A, V>>::deserialize(deserializer)?; | ||
| Ok(deserialized.to_boxed()) | ||
| } | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_simple() { | ||
| let var_tuple = VarTuple { | ||
| sized: 1500u16, | ||
| variable: "hello", | ||
| }; | ||
| let var_tuple_ule = super::encode_varule_to_box(&var_tuple); | ||
| assert_eq!(var_tuple_ule.sized.as_unsigned_int(), 1500); | ||
| assert_eq!(&var_tuple_ule.variable, "hello"); | ||
| // Can't use inference due to https://github.com/rust-lang/rust/issues/130180 | ||
| #[cfg(feature = "serde")] | ||
| crate::ule::test_utils::assert_serde_roundtrips::<VarTupleULE<u16, str>>(&var_tuple_ule); | ||
| } | ||
| #[test] | ||
| fn test_nested() { | ||
| use crate::{ZeroSlice, ZeroVec}; | ||
| let var_tuple = VarTuple { | ||
| sized: 2000u16, | ||
| variable: VarTuple { | ||
| sized: '🦙', | ||
| variable: ZeroVec::alloc_from_slice(b"ICU"), | ||
| }, | ||
| }; | ||
| let var_tuple_ule = super::encode_varule_to_box(&var_tuple); | ||
| assert_eq!(var_tuple_ule.sized.as_unsigned_int(), 2000u16); | ||
| assert_eq!(var_tuple_ule.variable.sized.to_char(), '🦙'); | ||
| assert_eq!( | ||
| &var_tuple_ule.variable.variable, | ||
| ZeroSlice::from_ule_slice(b"ICU") | ||
| ); | ||
| // Can't use inference due to https://github.com/rust-lang/rust/issues/130180 | ||
| #[cfg(feature = "serde")] | ||
| crate::ule::test_utils::assert_serde_roundtrips::< | ||
| VarTupleULE<u16, VarTupleULE<char, ZeroSlice<_>>>, | ||
| >(&var_tuple_ule); | ||
| } |
| // 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 ). | ||
| #[derive(Debug)] | ||
| pub enum VarZeroVecFormatError { | ||
| /// The byte buffer was not in the appropriate format for VarZeroVec. | ||
| Metadata, | ||
| #[allow(dead_code)] | ||
| Values(crate::ule::UleError), | ||
| } |
| // 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::components::VarZeroVecComponents; | ||
| use super::*; | ||
| use crate::ule::*; | ||
| use core::marker::PhantomData; | ||
| use core::mem; | ||
| /// A slice representing the index and data tables of a VarZeroVec, | ||
| /// *without* any length fields. The length field is expected to be stored elsewhere. | ||
| /// | ||
| /// Without knowing the length this is of course unsafe to use directly. | ||
| #[repr(transparent)] | ||
| #[derive(PartialEq, Eq)] | ||
| pub(crate) struct VarZeroLengthlessSlice<T: ?Sized, F> { | ||
| marker: PhantomData<(F, T)>, | ||
| /// The original slice this was constructed from | ||
| // Safety invariant: This field must have successfully passed through | ||
| // VarZeroVecComponents::parse_bytes_with_length() with the length | ||
| // associated with this value. | ||
| entire_slice: [u8], | ||
| } | ||
| impl<T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroLengthlessSlice<T, F> { | ||
| /// Obtain a [`VarZeroVecComponents`] borrowing from the internal buffer | ||
| /// | ||
| /// Safety: `len` must be the length associated with this value | ||
| #[inline] | ||
| pub(crate) unsafe fn as_components<'a>(&'a self, len: u32) -> VarZeroVecComponents<'a, T, F> { | ||
| unsafe { | ||
| // safety: VarZeroSlice is guaranteed to parse here | ||
| VarZeroVecComponents::from_bytes_unchecked_with_length(len, &self.entire_slice) | ||
| } | ||
| } | ||
| /// Parse a VarZeroLengthlessSlice from a slice of the appropriate format | ||
| /// | ||
| /// Slices of the right format can be obtained via [`VarZeroSlice::as_bytes()`] | ||
| pub fn parse_bytes<'a>(len: u32, slice: &'a [u8]) -> Result<&'a Self, UleError> { | ||
| let _ = VarZeroVecComponents::<T, F>::parse_bytes_with_length(len, slice) | ||
| .map_err(|_| UleError::parse::<Self>())?; | ||
| unsafe { | ||
| // Safety: We just verified that it is of the correct format. | ||
| Ok(Self::from_bytes_unchecked(slice)) | ||
| } | ||
| } | ||
| /// Uses a `&[u8]` buffer as a `VarZeroLengthlessSlice<T>` without any verification. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// `bytes` need to be an output from [`VarZeroLengthlessSlice::as_bytes()`], or alternatively | ||
| /// successfully pass through `parse_bytes` (with `len`) | ||
| /// | ||
| /// The length associated with this value will be the length associated with the original slice. | ||
| pub(crate) const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| // self is really just a wrapper around a byte slice | ||
| mem::transmute(bytes) | ||
| } | ||
| /// Uses a `&mut [u8]` buffer as a `VarZeroLengthlessSlice<T>` without any verification. | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// `bytes` need to be an output from [`VarZeroLengthlessSlice::as_bytes()`], or alternatively | ||
| /// be valid to be passed to `from_bytes_unchecked_with_length` | ||
| /// | ||
| /// The length associated with this value will be the length associated with the original slice. | ||
| pub(crate) unsafe fn from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut Self { | ||
| // self is really just a wrapper around a byte slice | ||
| mem::transmute(bytes) | ||
| } | ||
| /// Get one of this slice's elements | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// `index` must be in range, and `len` must be the length associated with this | ||
| /// instance of VarZeroLengthlessSlice. | ||
| pub(crate) unsafe fn get_unchecked(&self, len: u32, idx: usize) -> &T { | ||
| self.as_components(len).get_unchecked(idx) | ||
| } | ||
| /// Get a reference to the entire encoded backing buffer of this slice | ||
| /// | ||
| /// The bytes can be passed back to [`Self::parse_bytes()`]. | ||
| /// | ||
| /// To take the bytes as a vector, see [`VarZeroVec::into_bytes()`]. | ||
| #[inline] | ||
| pub(crate) const fn as_bytes(&self) -> &[u8] { | ||
| &self.entire_slice | ||
| } | ||
| /// Get the bytes behind this as a mutable slice | ||
| /// | ||
| /// # Safety | ||
| /// | ||
| /// - `len` is the length associated with this VarZeroLengthlessSlice | ||
| /// - The resultant slice is only mutated in a way such that it remains a valid `T` | ||
| /// | ||
| /// # Panics | ||
| /// | ||
| /// Panics when idx is not in bounds for this slice | ||
| pub(crate) unsafe fn get_bytes_at_mut(&mut self, len: u32, idx: usize) -> &mut [u8] { | ||
| let components = self.as_components(len); | ||
| let range = components.get_things_range(idx); | ||
| let offset = components.get_indices_size(); | ||
| // get_indices_size() returns the start of the things slice, and get_things_range() | ||
| // returns a range in-bounds of the things slice | ||
| #[allow(clippy::indexing_slicing)] | ||
| &mut self.entire_slice[offset..][range] | ||
| } | ||
| } |
| { | ||
| "git": { | ||
| "sha1": "3c47d82f8ba9f36699b71566844ed28b3f742b21" | ||
| "sha1": "6bd4893cc44c2ca2718de47a119a31cc40045fe5", | ||
| "dirty": true | ||
| }, | ||
| "path_in_vcs": "utils/zerovec" | ||
| } |
+4
-4
@@ -45,3 +45,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| let bytes: Vec<u8> = VarZeroVec::<str>::from(&string_vec).into_bytes(); | ||
| let vzv = VarZeroVec::<str>::parse_byte_slice(black_box(bytes.as_slice())).unwrap(); | ||
| let vzv = VarZeroVec::<str>::parse_bytes(black_box(bytes.as_slice())).unwrap(); | ||
@@ -74,3 +74,3 @@ c.bench_function("vzv/overview", |b| { | ||
| let bytes: Vec<u8> = VarZeroVec::<str>::from(&string_vec).into_bytes(); | ||
| let vzv = VarZeroVec::<str>::parse_byte_slice(black_box(bytes.as_slice())).unwrap(); | ||
| let vzv = VarZeroVec::<str>::parse_bytes(black_box(bytes.as_slice())).unwrap(); | ||
@@ -102,3 +102,3 @@ // *** Count chars in vec of 100 strings *** | ||
| let bytes: Vec<u8> = VarZeroVec::<str>::from(&string_vec).into_bytes(); | ||
| let vzv = VarZeroVec::<str>::parse_byte_slice(black_box(bytes.as_slice())).unwrap(); | ||
| let vzv = VarZeroVec::<str>::parse_bytes(black_box(bytes.as_slice())).unwrap(); | ||
| let single_needle = "lmnop".to_owned(); | ||
@@ -168,3 +168,3 @@ | ||
| let bytes: Vec<u8> = VarZeroVec::<str>::from(&string_vec).into_bytes(); | ||
| let vzv = VarZeroVec::<str>::parse_byte_slice(black_box(bytes.as_slice())).unwrap(); | ||
| let vzv = VarZeroVec::<str>::parse_bytes(black_box(bytes.as_slice())).unwrap(); | ||
| let borrowed = vzv.as_components(); | ||
@@ -171,0 +171,0 @@ let slice = vzv.as_slice(); |
+27
-26
@@ -32,15 +32,15 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| const POSTCARD: [u8; 282] = [ | ||
| 102, 16, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 10, 0, 0, 0, 12, 0, 0, 0, 14, | ||
| 0, 0, 0, 16, 0, 0, 0, 18, 0, 0, 0, 20, 0, 0, 0, 22, 0, 0, 0, 24, 0, 0, 0, 26, 0, 0, 0, 28, 0, | ||
| 0, 0, 30, 0, 0, 0, 32, 0, 0, 0, 97, 114, 98, 110, 99, 99, 112, 99, 104, 114, 101, 108, 101, | ||
| 110, 101, 111, 101, 115, 102, 114, 105, 117, 106, 97, 114, 117, 115, 114, 116, 104, 116, 114, | ||
| 122, 104, 177, 1, 16, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 12, 0, 0, 0, 18, 0, 0, 0, 26, 0, 0, 0, | ||
| 31, 0, 0, 0, 38, 0, 0, 0, 47, 0, 0, 0, 54, 0, 0, 0, 60, 0, 0, 0, 69, 0, 0, 0, 77, 0, 0, 0, 84, | ||
| 0, 0, 0, 91, 0, 0, 0, 95, 0, 0, 0, 102, 0, 0, 0, 65, 114, 97, 98, 105, 99, 66, 97, 110, 103, | ||
| 108, 97, 67, 104, 97, 107, 109, 97, 67, 104, 101, 114, 111, 107, 101, 101, 71, 114, 101, 101, | ||
| 107, 69, 110, 103, 108, 105, 115, 104, 69, 115, 112, 101, 114, 97, 110, 116, 111, 83, 112, 97, | ||
| 110, 105, 115, 104, 70, 114, 101, 110, 99, 104, 73, 110, 117, 107, 116, 105, 116, 117, 116, 74, | ||
| 97, 112, 97, 110, 101, 115, 101, 82, 117, 115, 115, 105, 97, 110, 83, 101, 114, 98, 105, 97, | ||
| 110, 84, 104, 97, 105, 84, 117, 114, 107, 105, 115, 104, 67, 104, 105, 110, 101, 115, 101, | ||
| const POSTCARD: [u8; 274] = [ | ||
| 98, 16, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 10, 0, 0, 0, 12, 0, 0, 0, 14, 0, 0, 0, 16, | ||
| 0, 0, 0, 18, 0, 0, 0, 20, 0, 0, 0, 22, 0, 0, 0, 24, 0, 0, 0, 26, 0, 0, 0, 28, 0, 0, 0, 30, 0, | ||
| 0, 0, 32, 0, 0, 0, 97, 114, 98, 110, 99, 99, 112, 99, 104, 114, 101, 108, 101, 110, 101, 111, | ||
| 101, 115, 102, 114, 105, 117, 106, 97, 114, 117, 115, 114, 116, 104, 116, 114, 122, 104, 173, | ||
| 1, 16, 0, 0, 0, 6, 0, 0, 0, 12, 0, 0, 0, 18, 0, 0, 0, 26, 0, 0, 0, 31, 0, 0, 0, 38, 0, 0, 0, | ||
| 47, 0, 0, 0, 54, 0, 0, 0, 60, 0, 0, 0, 69, 0, 0, 0, 77, 0, 0, 0, 84, 0, 0, 0, 91, 0, 0, 0, 95, | ||
| 0, 0, 0, 102, 0, 0, 0, 65, 114, 97, 98, 105, 99, 66, 97, 110, 103, 108, 97, 67, 104, 97, 107, | ||
| 109, 97, 67, 104, 101, 114, 111, 107, 101, 101, 71, 114, 101, 101, 107, 69, 110, 103, 108, 105, | ||
| 115, 104, 69, 115, 112, 101, 114, 97, 110, 116, 111, 83, 112, 97, 110, 105, 115, 104, 70, 114, | ||
| 101, 110, 99, 104, 73, 110, 117, 107, 116, 105, 116, 117, 116, 74, 97, 112, 97, 110, 101, 115, | ||
| 101, 82, 117, 115, 115, 105, 97, 110, 83, 101, 114, 98, 105, 97, 110, 84, 104, 97, 105, 84, | ||
| 117, 114, 107, 105, 115, 104, 67, 104, 105, 110, 101, 115, 101, | ||
| ]; | ||
@@ -60,3 +60,3 @@ | ||
| const POSTCARD_ZEROHASHMAP: [u8; 412] = [ | ||
| const POSTCARD_ZEROHASHMAP: [u8; 404] = [ | ||
| 128, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, | ||
@@ -66,14 +66,14 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, | ||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, | ||
| 0, 0, 0, 102, 16, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 8, 0, 0, 0, 10, 0, | ||
| 0, 0, 13, 0, 0, 0, 15, 0, 0, 0, 17, 0, 0, 0, 19, 0, 0, 0, 21, 0, 0, 0, 24, 0, 0, 0, 26, 0, 0, | ||
| 0, 28, 0, 0, 0, 30, 0, 0, 0, 32, 0, 0, 0, 115, 114, 101, 111, 116, 114, 97, 114, 105, 117, 99, | ||
| 99, 112, 102, 114, 101, 115, 106, 97, 122, 104, 99, 104, 114, 98, 110, 101, 110, 101, 108, 114, | ||
| 117, 116, 104, 177, 1, 16, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 16, 0, 0, 0, 23, 0, 0, 0, 29, 0, 0, | ||
| 0, 38, 0, 0, 0, 44, 0, 0, 0, 50, 0, 0, 0, 57, 0, 0, 0, 65, 0, 0, 0, 72, 0, 0, 0, 80, 0, 0, 0, | ||
| 86, 0, 0, 0, 93, 0, 0, 0, 98, 0, 0, 0, 105, 0, 0, 0, 83, 101, 114, 98, 105, 97, 110, 69, 115, | ||
| 112, 101, 114, 97, 110, 116, 111, 84, 117, 114, 107, 105, 115, 104, 65, 114, 97, 98, 105, 99, | ||
| 73, 110, 117, 107, 116, 105, 116, 117, 116, 67, 104, 97, 107, 109, 97, 70, 114, 101, 110, 99, | ||
| 104, 83, 112, 97, 110, 105, 115, 104, 74, 97, 112, 97, 110, 101, 115, 101, 67, 104, 105, 110, | ||
| 101, 115, 101, 67, 104, 101, 114, 111, 107, 101, 101, 66, 97, 110, 103, 108, 97, 69, 110, 103, | ||
| 108, 105, 115, 104, 71, 114, 101, 101, 107, 82, 117, 115, 115, 105, 97, 110, 84, 104, 97, 105, | ||
| 0, 0, 0, 98, 16, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 8, 0, 0, 0, 10, 0, 0, 0, 13, 0, | ||
| 0, 0, 15, 0, 0, 0, 17, 0, 0, 0, 19, 0, 0, 0, 21, 0, 0, 0, 24, 0, 0, 0, 26, 0, 0, 0, 28, 0, 0, | ||
| 0, 30, 0, 0, 0, 32, 0, 0, 0, 115, 114, 101, 111, 116, 114, 97, 114, 105, 117, 99, 99, 112, 102, | ||
| 114, 101, 115, 106, 97, 122, 104, 99, 104, 114, 98, 110, 101, 110, 101, 108, 114, 117, 116, | ||
| 104, 173, 1, 16, 0, 0, 0, 7, 0, 0, 0, 16, 0, 0, 0, 23, 0, 0, 0, 29, 0, 0, 0, 38, 0, 0, 0, 44, | ||
| 0, 0, 0, 50, 0, 0, 0, 57, 0, 0, 0, 65, 0, 0, 0, 72, 0, 0, 0, 80, 0, 0, 0, 86, 0, 0, 0, 93, 0, | ||
| 0, 0, 98, 0, 0, 0, 105, 0, 0, 0, 83, 101, 114, 98, 105, 97, 110, 69, 115, 112, 101, 114, 97, | ||
| 110, 116, 111, 84, 117, 114, 107, 105, 115, 104, 65, 114, 97, 98, 105, 99, 73, 110, 117, 107, | ||
| 116, 105, 116, 117, 116, 67, 104, 97, 107, 109, 97, 70, 114, 101, 110, 99, 104, 83, 112, 97, | ||
| 110, 105, 115, 104, 74, 97, 112, 97, 110, 101, 115, 101, 67, 104, 105, 110, 101, 115, 101, 67, | ||
| 104, 101, 114, 111, 107, 101, 101, 66, 97, 110, 103, 108, 97, 69, 110, 103, 108, 105, 115, 104, | ||
| 71, 114, 101, 101, 107, 82, 117, 115, 115, 105, 97, 110, 84, 104, 97, 105, | ||
| ]; | ||
@@ -317,2 +317,3 @@ | ||
| #[cfg(feature = "bench")] | ||
| fn bench_deserialize_large_zerohashmap(c: &mut Criterion) { | ||
@@ -319,0 +320,0 @@ let buf = large_zerohashmap_postcard_bytes(); |
@@ -11,3 +11,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use zerovec::ule::VarULE; | ||
| use zerovec::VarZeroSlice; | ||
@@ -21,3 +20,3 @@ use zerovec::ZeroVec; | ||
| fn sum_zerovec() -> u32 { | ||
| ZeroVec::<u32>::parse_byte_slice(black_box(TEST_BUFFER_LE)) | ||
| ZeroVec::<u32>::parse_bytes(black_box(TEST_BUFFER_LE)) | ||
| .unwrap() | ||
@@ -33,3 +32,3 @@ .iter() | ||
| fn binarysearch_zerovec() -> Result<usize, usize> { | ||
| ZeroVec::<u32>::parse_byte_slice(black_box(TEST_BUFFER_LE)) | ||
| ZeroVec::<u32>::parse_bytes(black_box(TEST_BUFFER_LE)) | ||
| .unwrap() | ||
@@ -41,3 +40,3 @@ .binary_search(&0x0c0d0c) | ||
| let slice: &'static VarZeroSlice<str> = | ||
| VarZeroSlice::parse_byte_slice(black_box(TEST_VARZEROSLICE_BYTES)).unwrap(); | ||
| VarZeroSlice::parse_bytes(black_box(TEST_VARZEROSLICE_BYTES)).unwrap(); | ||
| slice.get(black_box(1)) | ||
@@ -49,3 +48,3 @@ } | ||
| let slice: &'static VarZeroSlice<str> = | ||
| unsafe { VarZeroSlice::from_byte_slice_unchecked(black_box(TEST_VARZEROSLICE_BYTES)) }; | ||
| unsafe { VarZeroSlice::from_bytes_unchecked(black_box(TEST_VARZEROSLICE_BYTES)) }; | ||
| slice.get(black_box(1)) | ||
@@ -57,3 +56,3 @@ } | ||
| let slice: &'static VarZeroSlice<str> = | ||
| unsafe { VarZeroSlice::from_byte_slice_unchecked(black_box(TEST_VARZEROSLICE_BYTES)) }; | ||
| unsafe { VarZeroSlice::from_bytes_unchecked(black_box(TEST_VARZEROSLICE_BYTES)) }; | ||
| // Safety: The VarZeroVec has length 4. | ||
@@ -60,0 +59,0 @@ unsafe { slice.get_unchecked(black_box(1)) } |
@@ -34,6 +34,5 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| // Same as "zerovec_serde/deserialize_sum/u32/zerovec" | ||
| let buffer = bincode::serialize( | ||
| &ZeroVec::<u32>::parse_byte_slice(black_box(TEST_BUFFER_LE)).unwrap(), | ||
| ) | ||
| .unwrap(); | ||
| let buffer = | ||
| bincode::serialize(&ZeroVec::<u32>::parse_bytes(black_box(TEST_BUFFER_LE)).unwrap()) | ||
| .unwrap(); | ||
| b.iter(|| { | ||
@@ -76,6 +75,5 @@ bincode::deserialize::<ZeroVec<u32>>(&buffer) | ||
| c.bench_function("zerovec_serde/deserialize_sum/u32/zerovec", |b| { | ||
| let buffer = bincode::serialize( | ||
| &ZeroVec::<u32>::parse_byte_slice(black_box(TEST_BUFFER_LE)).unwrap(), | ||
| ) | ||
| .unwrap(); | ||
| let buffer = | ||
| bincode::serialize(&ZeroVec::<u32>::parse_bytes(black_box(TEST_BUFFER_LE)).unwrap()) | ||
| .unwrap(); | ||
| b.iter(|| { | ||
@@ -140,3 +138,3 @@ bincode::deserialize::<ZeroVec<u32>>(&buffer) | ||
| // *** Compute sum of vec of 100 `u32` *** | ||
| let zerovec = ZeroVec::<u32>::parse_byte_slice(zerovec_aligned.as_bytes()).unwrap(); | ||
| let zerovec = ZeroVec::<u32>::parse_bytes(zerovec_aligned.as_bytes()).unwrap(); | ||
| c.bench_function("zerovec_serde/sum/stress/zerovec", |b| { | ||
@@ -143,0 +141,0 @@ b.iter(|| black_box(&zerovec).iter().sum::<u32>()); |
@@ -57,3 +57,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| .extend(ZeroVec::from_slice_or_alloc(vec.as_slice()).as_bytes()); | ||
| ZeroVec::<T>::parse_byte_slice(&buffer.0[1..]).unwrap() | ||
| ZeroVec::<T>::parse_bytes(&buffer.0[1..]).unwrap() | ||
| } | ||
@@ -64,3 +64,3 @@ | ||
| b.iter(|| { | ||
| ZeroVec::<u32>::parse_byte_slice(black_box(TEST_BUFFER_LE)) | ||
| ZeroVec::<u32>::parse_bytes(black_box(TEST_BUFFER_LE)) | ||
| .unwrap() | ||
@@ -82,4 +82,6 @@ .iter() | ||
| let normal_slice = &TEST_SLICE[0..19]; | ||
| let aligned_ule_slice = <u32 as AsULE>::ULE::parse_byte_slice(&TEST_BUFFER_LE[0..76]).unwrap(); | ||
| let unalign_ule_slice = <u32 as AsULE>::ULE::parse_byte_slice(&TEST_BUFFER_LE[1..77]).unwrap(); | ||
| let aligned_ule_slice = | ||
| <u32 as AsULE>::ULE::parse_bytes_to_slice(&TEST_BUFFER_LE[0..76]).unwrap(); | ||
| let unalign_ule_slice = | ||
| <u32 as AsULE>::ULE::parse_bytes_to_slice(&TEST_BUFFER_LE[1..77]).unwrap(); | ||
@@ -122,3 +124,3 @@ assert_eq!(normal_slice.len(), aligned_ule_slice.len()); | ||
| c.bench_function("zerovec/binary_search/sample/zerovec", |b| { | ||
| let zerovec = ZeroVec::<u32>::parse_byte_slice(black_box(TEST_BUFFER_LE)).unwrap(); | ||
| let zerovec = ZeroVec::<u32>::parse_bytes(black_box(TEST_BUFFER_LE)).unwrap(); | ||
| b.iter(|| zerovec.binary_search(&0x0c0d0c)); | ||
@@ -125,0 +127,0 @@ }); |
+252
-273
@@ -6,2 +6,11 @@ # This file is automatically @generated by Cargo. | ||
| [[package]] | ||
| name = "aho-corasick" | ||
| version = "1.1.3" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" | ||
| dependencies = [ | ||
| "memchr", | ||
| ] | ||
| [[package]] | ||
| name = "anes" | ||
@@ -13,6 +22,12 @@ version = "0.1.6" | ||
| [[package]] | ||
| name = "anstyle" | ||
| version = "1.0.10" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" | ||
| [[package]] | ||
| name = "autocfg" | ||
| version = "1.1.0" | ||
| version = "1.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" | ||
| checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" | ||
@@ -29,24 +44,12 @@ [[package]] | ||
| [[package]] | ||
| name = "bitflags" | ||
| version = "1.3.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||
| [[package]] | ||
| name = "bitflags" | ||
| version = "2.4.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" | ||
| [[package]] | ||
| name = "bumpalo" | ||
| version = "3.14.0" | ||
| version = "3.15.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" | ||
| checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" | ||
| [[package]] | ||
| name = "byteorder" | ||
| version = "1.4.3" | ||
| version = "1.5.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" | ||
| checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" | ||
@@ -67,5 +70,5 @@ [[package]] | ||
| name = "ciborium" | ||
| version = "0.2.1" | ||
| version = "0.2.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" | ||
| checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" | ||
| dependencies = [ | ||
@@ -79,11 +82,11 @@ "ciborium-io", | ||
| name = "ciborium-io" | ||
| version = "0.2.1" | ||
| version = "0.2.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" | ||
| checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" | ||
| [[package]] | ||
| name = "ciborium-ll" | ||
| version = "0.2.1" | ||
| version = "0.2.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" | ||
| checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" | ||
| dependencies = [ | ||
@@ -96,5 +99,5 @@ "ciborium-io", | ||
| name = "clap" | ||
| version = "4.2.1" | ||
| version = "4.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" | ||
| checksum = "1d5f1946157a96594eb2d2c10eb7ad9a2b27518cb3000209dec700c35df9197d" | ||
| dependencies = [ | ||
@@ -106,7 +109,7 @@ "clap_builder", | ||
| name = "clap_builder" | ||
| version = "4.2.1" | ||
| version = "4.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" | ||
| checksum = "78116e32a042dd73c2901f0dc30790d20ff3447f3e3472fad359e8c3d282bcd6" | ||
| dependencies = [ | ||
| "bitflags 1.3.2", | ||
| "anstyle", | ||
| "clap_lex", | ||
@@ -117,5 +120,5 @@ ] | ||
| name = "clap_lex" | ||
| version = "0.4.1" | ||
| version = "0.5.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" | ||
| checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" | ||
@@ -166,7 +169,6 @@ [[package]] | ||
| name = "crossbeam-deque" | ||
| version = "0.8.3" | ||
| version = "0.8.5" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" | ||
| checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" | ||
| dependencies = [ | ||
| "cfg-if", | ||
| "crossbeam-epoch", | ||
@@ -178,11 +180,7 @@ "crossbeam-utils", | ||
| name = "crossbeam-epoch" | ||
| version = "0.9.15" | ||
| version = "0.9.18" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" | ||
| checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" | ||
| dependencies = [ | ||
| "autocfg", | ||
| "cfg-if", | ||
| "crossbeam-utils", | ||
| "memoffset", | ||
| "scopeguard", | ||
| ] | ||
@@ -192,11 +190,17 @@ | ||
| name = "crossbeam-utils" | ||
| version = "0.8.19" | ||
| version = "0.8.20" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" | ||
| checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" | ||
| [[package]] | ||
| name = "crunchy" | ||
| version = "0.2.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" | ||
| [[package]] | ||
| name = "databake" | ||
| version = "0.1.8" | ||
| version = "0.2.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "6a04fbfbecca8f0679c8c06fef907594adcc3e2052e11163a6d30535a1a5604d" | ||
| checksum = "ff6ee9e2d2afb173bcdeee45934c89ec341ab26f91c9933774fc15c2b58f83ef" | ||
| dependencies = [ | ||
@@ -210,5 +214,5 @@ "databake-derive", | ||
| name = "databake-derive" | ||
| version = "0.1.8" | ||
| version = "0.2.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b" | ||
| checksum = "6834770958c7b84223607e49758ec0dde273c4df915e734aad50f62968a4c134" | ||
| dependencies = [ | ||
@@ -223,21 +227,23 @@ "proc-macro2", | ||
| name = "either" | ||
| version = "1.9.0" | ||
| version = "1.13.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" | ||
| checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" | ||
| [[package]] | ||
| name = "errno" | ||
| version = "0.3.8" | ||
| name = "embedded-io" | ||
| version = "0.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" | ||
| dependencies = [ | ||
| "libc", | ||
| "windows-sys 0.52.0", | ||
| ] | ||
| checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" | ||
| [[package]] | ||
| name = "embedded-io" | ||
| version = "0.6.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" | ||
| [[package]] | ||
| name = "getrandom" | ||
| version = "0.2.10" | ||
| version = "0.2.15" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" | ||
| checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" | ||
| dependencies = [ | ||
@@ -253,11 +259,15 @@ "cfg-if", | ||
| name = "half" | ||
| version = "1.8.2" | ||
| version = "2.4.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" | ||
| checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" | ||
| dependencies = [ | ||
| "cfg-if", | ||
| "crunchy", | ||
| ] | ||
| [[package]] | ||
| name = "hermit-abi" | ||
| version = "0.3.3" | ||
| version = "0.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" | ||
| checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" | ||
@@ -272,9 +282,9 @@ [[package]] | ||
| name = "is-terminal" | ||
| version = "0.4.9" | ||
| version = "0.4.13" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" | ||
| checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" | ||
| dependencies = [ | ||
| "hermit-abi", | ||
| "rustix", | ||
| "windows-sys 0.48.0", | ||
| "libc", | ||
| "windows-sys 0.52.0", | ||
| ] | ||
@@ -293,11 +303,11 @@ | ||
| name = "itoa" | ||
| version = "1.0.9" | ||
| version = "1.0.13" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" | ||
| checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" | ||
| [[package]] | ||
| name = "js-sys" | ||
| version = "0.3.64" | ||
| version = "0.3.72" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" | ||
| checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" | ||
| dependencies = [ | ||
@@ -309,38 +319,29 @@ "wasm-bindgen", | ||
| name = "libc" | ||
| version = "0.2.153" | ||
| version = "0.2.164" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" | ||
| checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" | ||
| [[package]] | ||
| name = "libm" | ||
| version = "0.2.7" | ||
| version = "0.2.11" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" | ||
| checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" | ||
| [[package]] | ||
| name = "linux-raw-sys" | ||
| version = "0.4.13" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" | ||
| [[package]] | ||
| name = "log" | ||
| version = "0.4.20" | ||
| version = "0.4.22" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" | ||
| checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" | ||
| [[package]] | ||
| name = "memoffset" | ||
| version = "0.9.0" | ||
| name = "memchr" | ||
| version = "2.7.4" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" | ||
| dependencies = [ | ||
| "autocfg", | ||
| ] | ||
| checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" | ||
| [[package]] | ||
| name = "num-traits" | ||
| version = "0.2.18" | ||
| version = "0.2.19" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" | ||
| checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" | ||
| dependencies = [ | ||
@@ -353,23 +354,23 @@ "autocfg", | ||
| name = "once_cell" | ||
| version = "1.18.0" | ||
| version = "1.20.2" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" | ||
| checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" | ||
| [[package]] | ||
| name = "oorandom" | ||
| version = "11.1.3" | ||
| version = "11.1.4" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" | ||
| checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" | ||
| [[package]] | ||
| name = "paste" | ||
| version = "1.0.14" | ||
| version = "1.0.15" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" | ||
| checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" | ||
| [[package]] | ||
| name = "plotters" | ||
| version = "0.3.5" | ||
| version = "0.3.7" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" | ||
| checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" | ||
| dependencies = [ | ||
@@ -385,11 +386,11 @@ "num-traits", | ||
| name = "plotters-backend" | ||
| version = "0.3.5" | ||
| version = "0.3.7" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" | ||
| checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" | ||
| [[package]] | ||
| name = "plotters-svg" | ||
| version = "0.3.5" | ||
| version = "0.3.7" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" | ||
| checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" | ||
| dependencies = [ | ||
@@ -401,7 +402,9 @@ "plotters-backend", | ||
| name = "postcard" | ||
| version = "1.0.7" | ||
| version = "1.0.10" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d534c6e61df1c7166e636ca612d9820d486fe96ddad37f7abc671517b297488e" | ||
| checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" | ||
| dependencies = [ | ||
| "cobs", | ||
| "embedded-io 0.4.0", | ||
| "embedded-io 0.6.1", | ||
| "serde", | ||
@@ -412,11 +415,14 @@ ] | ||
| name = "ppv-lite86" | ||
| version = "0.2.17" | ||
| version = "0.2.20" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" | ||
| checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" | ||
| dependencies = [ | ||
| "zerocopy", | ||
| ] | ||
| [[package]] | ||
| name = "proc-macro2" | ||
| version = "1.0.82" | ||
| version = "1.0.92" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" | ||
| checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" | ||
| dependencies = [ | ||
@@ -428,5 +434,5 @@ "unicode-ident", | ||
| name = "quote" | ||
| version = "1.0.35" | ||
| version = "1.0.37" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" | ||
| checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" | ||
| dependencies = [ | ||
@@ -487,5 +493,5 @@ "proc-macro2", | ||
| name = "rayon" | ||
| version = "1.8.0" | ||
| version = "1.10.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" | ||
| checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" | ||
| dependencies = [ | ||
@@ -498,5 +504,5 @@ "either", | ||
| name = "rayon-core" | ||
| version = "1.12.0" | ||
| version = "1.12.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" | ||
| checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" | ||
| dependencies = [ | ||
@@ -509,6 +515,9 @@ "crossbeam-deque", | ||
| name = "regex" | ||
| version = "1.8.4" | ||
| version = "1.11.1" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" | ||
| checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" | ||
| dependencies = [ | ||
| "aho-corasick", | ||
| "memchr", | ||
| "regex-automata", | ||
| "regex-syntax", | ||
@@ -518,6 +527,17 @@ ] | ||
| [[package]] | ||
| name = "regex-automata" | ||
| version = "0.4.9" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" | ||
| dependencies = [ | ||
| "aho-corasick", | ||
| "memchr", | ||
| "regex-syntax", | ||
| ] | ||
| [[package]] | ||
| name = "regex-syntax" | ||
| version = "0.7.5" | ||
| version = "0.8.5" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" | ||
| checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" | ||
@@ -537,5 +557,5 @@ [[package]] | ||
| name = "rmp-serde" | ||
| version = "1.2.0" | ||
| version = "1.3.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "938a142ab806f18b88a97b0dea523d39e0fd730a064b035726adcfc58a8a5188" | ||
| checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" | ||
| dependencies = [ | ||
@@ -548,19 +568,6 @@ "byteorder", | ||
| [[package]] | ||
| name = "rustix" | ||
| version = "0.38.31" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" | ||
| dependencies = [ | ||
| "bitflags 2.4.2", | ||
| "errno", | ||
| "libc", | ||
| "linux-raw-sys", | ||
| "windows-sys 0.52.0", | ||
| ] | ||
| [[package]] | ||
| name = "ryu" | ||
| version = "1.0.15" | ||
| version = "1.0.18" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" | ||
| checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" | ||
@@ -577,12 +584,6 @@ [[package]] | ||
| [[package]] | ||
| name = "scopeguard" | ||
| version = "1.2.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | ||
| [[package]] | ||
| name = "serde" | ||
| version = "1.0.188" | ||
| version = "1.0.215" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" | ||
| checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" | ||
| dependencies = [ | ||
@@ -594,5 +595,5 @@ "serde_derive", | ||
| name = "serde_derive" | ||
| version = "1.0.188" | ||
| version = "1.0.215" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" | ||
| checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" | ||
| dependencies = [ | ||
@@ -606,7 +607,8 @@ "proc-macro2", | ||
| name = "serde_json" | ||
| version = "1.0.107" | ||
| version = "1.0.133" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" | ||
| checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" | ||
| dependencies = [ | ||
| "itoa", | ||
| "memchr", | ||
| "ryu", | ||
@@ -630,5 +632,5 @@ "serde", | ||
| name = "syn" | ||
| version = "2.0.58" | ||
| version = "2.0.89" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" | ||
| checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" | ||
| dependencies = [ | ||
@@ -673,11 +675,11 @@ "proc-macro2", | ||
| name = "unicode-ident" | ||
| version = "1.0.12" | ||
| version = "1.0.14" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" | ||
| checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" | ||
| [[package]] | ||
| name = "walkdir" | ||
| version = "2.4.0" | ||
| version = "2.5.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" | ||
| checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" | ||
| dependencies = [ | ||
@@ -696,7 +698,8 @@ "same-file", | ||
| name = "wasm-bindgen" | ||
| version = "0.2.87" | ||
| version = "0.2.95" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" | ||
| checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" | ||
| dependencies = [ | ||
| "cfg-if", | ||
| "once_cell", | ||
| "wasm-bindgen-macro", | ||
@@ -707,5 +710,5 @@ ] | ||
| name = "wasm-bindgen-backend" | ||
| version = "0.2.87" | ||
| version = "0.2.95" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" | ||
| checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" | ||
| dependencies = [ | ||
@@ -723,5 +726,5 @@ "bumpalo", | ||
| name = "wasm-bindgen-macro" | ||
| version = "0.2.87" | ||
| version = "0.2.95" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" | ||
| checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" | ||
| dependencies = [ | ||
@@ -734,5 +737,5 @@ "quote", | ||
| name = "wasm-bindgen-macro-support" | ||
| version = "0.2.87" | ||
| version = "0.2.95" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" | ||
| checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" | ||
| dependencies = [ | ||
@@ -748,11 +751,11 @@ "proc-macro2", | ||
| name = "wasm-bindgen-shared" | ||
| version = "0.2.87" | ||
| version = "0.2.95" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" | ||
| checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" | ||
| [[package]] | ||
| name = "web-sys" | ||
| version = "0.3.64" | ||
| version = "0.3.72" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" | ||
| checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" | ||
| dependencies = [ | ||
@@ -764,43 +767,12 @@ "js-sys", | ||
| [[package]] | ||
| name = "winapi" | ||
| version = "0.3.9" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||
| dependencies = [ | ||
| "winapi-i686-pc-windows-gnu", | ||
| "winapi-x86_64-pc-windows-gnu", | ||
| ] | ||
| [[package]] | ||
| name = "winapi-i686-pc-windows-gnu" | ||
| version = "0.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||
| [[package]] | ||
| name = "winapi-util" | ||
| version = "0.1.6" | ||
| version = "0.1.9" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" | ||
| checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" | ||
| dependencies = [ | ||
| "winapi", | ||
| "windows-sys 0.59.0", | ||
| ] | ||
| [[package]] | ||
| name = "winapi-x86_64-pc-windows-gnu" | ||
| version = "0.4.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||
| [[package]] | ||
| name = "windows-sys" | ||
| version = "0.48.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" | ||
| dependencies = [ | ||
| "windows-targets 0.48.5", | ||
| ] | ||
| [[package]] | ||
| name = "windows-sys" | ||
| version = "0.52.0" | ||
@@ -810,18 +782,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| dependencies = [ | ||
| "windows-targets 0.52.0", | ||
| "windows-targets", | ||
| ] | ||
| [[package]] | ||
| name = "windows-targets" | ||
| version = "0.48.5" | ||
| name = "windows-sys" | ||
| version = "0.59.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" | ||
| checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" | ||
| dependencies = [ | ||
| "windows_aarch64_gnullvm 0.48.5", | ||
| "windows_aarch64_msvc 0.48.5", | ||
| "windows_i686_gnu 0.48.5", | ||
| "windows_i686_msvc 0.48.5", | ||
| "windows_x86_64_gnu 0.48.5", | ||
| "windows_x86_64_gnullvm 0.48.5", | ||
| "windows_x86_64_msvc 0.48.5", | ||
| "windows-targets", | ||
| ] | ||
@@ -831,13 +797,14 @@ | ||
| name = "windows-targets" | ||
| version = "0.52.0" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" | ||
| checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" | ||
| dependencies = [ | ||
| "windows_aarch64_gnullvm 0.52.0", | ||
| "windows_aarch64_msvc 0.52.0", | ||
| "windows_i686_gnu 0.52.0", | ||
| "windows_i686_msvc 0.52.0", | ||
| "windows_x86_64_gnu 0.52.0", | ||
| "windows_x86_64_gnullvm 0.52.0", | ||
| "windows_x86_64_msvc 0.52.0", | ||
| "windows_aarch64_gnullvm", | ||
| "windows_aarch64_msvc", | ||
| "windows_i686_gnu", | ||
| "windows_i686_gnullvm", | ||
| "windows_i686_msvc", | ||
| "windows_x86_64_gnu", | ||
| "windows_x86_64_gnullvm", | ||
| "windows_x86_64_msvc", | ||
| ] | ||
@@ -847,93 +814,90 @@ | ||
| name = "windows_aarch64_gnullvm" | ||
| version = "0.48.5" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" | ||
| checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" | ||
| [[package]] | ||
| name = "windows_aarch64_gnullvm" | ||
| version = "0.52.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" | ||
| [[package]] | ||
| name = "windows_aarch64_msvc" | ||
| version = "0.48.5" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" | ||
| checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" | ||
| [[package]] | ||
| name = "windows_aarch64_msvc" | ||
| version = "0.52.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" | ||
| [[package]] | ||
| name = "windows_i686_gnu" | ||
| version = "0.48.5" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" | ||
| checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" | ||
| [[package]] | ||
| name = "windows_i686_gnu" | ||
| version = "0.52.0" | ||
| name = "windows_i686_gnullvm" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" | ||
| checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" | ||
| [[package]] | ||
| name = "windows_i686_msvc" | ||
| version = "0.48.5" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" | ||
| checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" | ||
| [[package]] | ||
| name = "windows_i686_msvc" | ||
| version = "0.52.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" | ||
| [[package]] | ||
| name = "windows_x86_64_gnu" | ||
| version = "0.48.5" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" | ||
| checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" | ||
| [[package]] | ||
| name = "windows_x86_64_gnu" | ||
| version = "0.52.0" | ||
| name = "windows_x86_64_gnullvm" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" | ||
| checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" | ||
| [[package]] | ||
| name = "windows_x86_64_gnullvm" | ||
| version = "0.48.5" | ||
| name = "windows_x86_64_msvc" | ||
| version = "0.52.6" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" | ||
| checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | ||
| [[package]] | ||
| name = "windows_x86_64_gnullvm" | ||
| version = "0.52.0" | ||
| name = "yoke" | ||
| version = "0.7.5" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" | ||
| checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" | ||
| dependencies = [ | ||
| "stable_deref_trait", | ||
| "yoke-derive", | ||
| "zerofrom", | ||
| ] | ||
| [[package]] | ||
| name = "windows_x86_64_msvc" | ||
| version = "0.48.5" | ||
| name = "yoke-derive" | ||
| version = "0.7.5" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | ||
| checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" | ||
| dependencies = [ | ||
| "proc-macro2", | ||
| "quote", | ||
| "syn", | ||
| "synstructure", | ||
| ] | ||
| [[package]] | ||
| name = "windows_x86_64_msvc" | ||
| version = "0.52.0" | ||
| name = "zerocopy" | ||
| version = "0.7.35" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" | ||
| checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" | ||
| dependencies = [ | ||
| "byteorder", | ||
| "zerocopy-derive", | ||
| ] | ||
| [[package]] | ||
| name = "yoke" | ||
| version = "0.7.4" | ||
| name = "zerocopy-derive" | ||
| version = "0.7.35" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" | ||
| checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" | ||
| dependencies = [ | ||
| "serde", | ||
| "stable_deref_trait", | ||
| "zerofrom", | ||
| "proc-macro2", | ||
| "quote", | ||
| "syn", | ||
| ] | ||
@@ -946,6 +910,21 @@ | ||
| checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" | ||
| dependencies = [ | ||
| "zerofrom-derive", | ||
| ] | ||
| [[package]] | ||
| name = "zerofrom-derive" | ||
| version = "0.1.4" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" | ||
| dependencies = [ | ||
| "proc-macro2", | ||
| "quote", | ||
| "syn", | ||
| "synstructure", | ||
| ] | ||
| [[package]] | ||
| name = "zerovec" | ||
| version = "0.10.4" | ||
| version = "0.11.0" | ||
| dependencies = [ | ||
@@ -972,5 +951,5 @@ "bincode", | ||
| name = "zerovec-derive" | ||
| version = "0.10.3" | ||
| version = "0.11.0" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" | ||
| checksum = "996c67268f00e216986ac140d8de9f47968c330b96aeefcae9ed296f23934448" | ||
| dependencies = [ | ||
@@ -977,0 +956,0 @@ "proc-macro2", |
+39
-17
@@ -14,6 +14,7 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| edition = "2021" | ||
| rust-version = "1.67" | ||
| rust-version = "1.71.1" | ||
| name = "zerovec" | ||
| version = "0.10.4" | ||
| version = "0.11.0" | ||
| authors = ["The ICU4X Project Developers"] | ||
| build = false | ||
| include = [ | ||
@@ -29,2 +30,6 @@ "data/**/*", | ||
| ] | ||
| autobins = false | ||
| autoexamples = false | ||
| autotests = false | ||
| autobenches = false | ||
| description = "Zero-copy vector backed by a byte array" | ||
@@ -59,2 +64,4 @@ readme = "README.md" | ||
| [lib] | ||
| name = "zerovec" | ||
| path = "src/lib.rs" | ||
| bench = false | ||
@@ -64,15 +71,23 @@ | ||
| name = "zv_serde" | ||
| path = "examples/zv_serde.rs" | ||
| required-features = ["serde"] | ||
| [[bench]] | ||
| name = "zerovec" | ||
| name = "vzv" | ||
| path = "benches/vzv.rs" | ||
| harness = false | ||
| [[bench]] | ||
| name = "zerovec_serde" | ||
| name = "zeromap" | ||
| path = "benches/zeromap.rs" | ||
| harness = false | ||
| required-features = ["serde"] | ||
| required-features = [ | ||
| "serde", | ||
| "hashmap", | ||
| "derive", | ||
| ] | ||
| [[bench]] | ||
| name = "vzv" | ||
| name = "zerovec" | ||
| path = "benches/zerovec.rs" | ||
| harness = false | ||
@@ -82,15 +97,13 @@ | ||
| name = "zerovec_iai" | ||
| path = "benches/zerovec_iai.rs" | ||
| harness = false | ||
| [[bench]] | ||
| name = "zeromap" | ||
| name = "zerovec_serde" | ||
| path = "benches/zerovec_serde.rs" | ||
| harness = false | ||
| required-features = [ | ||
| "serde", | ||
| "hashmap", | ||
| "derive", | ||
| ] | ||
| required-features = ["serde"] | ||
| [dependencies.databake] | ||
| version = "0.1.8" | ||
| version = "0.2.0" | ||
| features = ["derive"] | ||
@@ -102,3 +115,6 @@ optional = true | ||
| version = "1.0.110" | ||
| features = ["alloc"] | ||
| features = [ | ||
| "alloc", | ||
| "derive", | ||
| ] | ||
| optional = true | ||
@@ -113,4 +129,5 @@ default-features = false | ||
| [dependencies.yoke] | ||
| version = ">=0.6.0, <0.8.0" | ||
| version = "0.7.5" | ||
| optional = true | ||
| default-features = false | ||
@@ -122,3 +139,3 @@ [dependencies.zerofrom] | ||
| [dependencies.zerovec-derive] | ||
| version = "0.10.2" | ||
| version = "0.11.0" | ||
| optional = true | ||
@@ -162,2 +179,7 @@ default-features = false | ||
| [dev-dependencies.yoke] | ||
| version = "0.7.5" | ||
| features = ["derive"] | ||
| default-features = false | ||
| [features] | ||
@@ -175,3 +197,3 @@ bench = [ | ||
| [target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.criterion] | ||
| [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies.criterion] | ||
| version = "0.5.0" |
@@ -9,5 +9,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| #![no_main] // https://github.com/unicode-org/icu4x/issues/395 | ||
| icu_benchmark_macros::instrument!(); | ||
| icu_benchmark_macros::static_setup!(); | ||
| use zerovec::ZeroVec; | ||
@@ -40,6 +39,3 @@ | ||
| #[no_mangle] | ||
| fn main(_argc: isize, _argv: *const *const u8) -> isize { | ||
| icu_benchmark_macros::main_setup!(); | ||
| fn main() { | ||
| // Un-comment the following line to generate postcard data: | ||
@@ -51,4 +47,2 @@ // serialize(); | ||
| assert_eq!(8141, result); | ||
| 0 | ||
| } |
+2
-2
@@ -77,3 +77,3 @@ # zerovec [](https://crates.io/crates/zerovec) | ||
| bincode::serialize(&data).expect("Serialization should be successful"); | ||
| assert_eq!(bincode_bytes.len(), 67); | ||
| assert_eq!(bincode_bytes.len(), 63); | ||
@@ -150,3 +150,3 @@ let deserialized: DataStruct = bincode::deserialize(&bincode_bytes) | ||
| .expect("Serialization should be successful"); | ||
| assert_eq!(bincode_bytes.len(), 168); | ||
| assert_eq!(bincode_bytes.len(), 160); | ||
@@ -153,0 +153,0 @@ let deserialized: Data = bincode::deserialize(&bincode_bytes) |
@@ -14,2 +14,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// Split the 64bit `hash` into (g, f0, f1). | ||
| /// | ||
| /// g denotes the highest 16bits of the hash modulo `m`, and is referred to as first level hash. | ||
@@ -51,2 +52,3 @@ /// (f0, f1) denotes the middle, and lower 24bits of the hash respectively. | ||
| /// two-level hashing schema. | ||
| /// | ||
| /// Returns a tuple of where the first item is the displacement array and the second item is the | ||
@@ -53,0 +55,0 @@ /// reverse mapping used to permute keys, values into their slots. |
@@ -69,4 +69,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, | ||
| 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 0, | ||
| 3, 0, 0, 0, 0, 0, 1, 0, 2, 0, 98, 99, 97, | ||
| 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, | ||
| 3, 0, 1, 0, 2, 0, 98, 99, 97, | ||
| ]; | ||
@@ -73,0 +73,0 @@ |
+18
-13
@@ -78,3 +78,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| //! bincode::serialize(&data).expect("Serialization should be successful"); | ||
| //! assert_eq!(bincode_bytes.len(), 67); | ||
| //! assert_eq!(bincode_bytes.len(), 63); | ||
| //! | ||
@@ -153,3 +153,3 @@ //! let deserialized: DataStruct = bincode::deserialize(&bincode_bytes) | ||
| //! .expect("Serialization should be successful"); | ||
| //! assert_eq!(bincode_bytes.len(), 168); | ||
| //! assert_eq!(bincode_bytes.len(), 160); | ||
| //! | ||
@@ -218,4 +218,3 @@ //! let deserialized: Data = bincode::deserialize(&bincode_bytes) | ||
| mod error; | ||
| mod flexzerovec; | ||
| mod cow; | ||
| #[cfg(feature = "hashmap")] | ||
@@ -233,3 +232,2 @@ pub mod hashmap; | ||
| pub mod ule; | ||
| #[cfg(feature = "yoke")] | ||
@@ -239,3 +237,3 @@ mod yoke_impls; | ||
| pub use crate::error::ZeroVecError; | ||
| pub use crate::cow::VarZeroCow; | ||
| #[cfg(feature = "hashmap")] | ||
@@ -248,8 +246,7 @@ pub use crate::hashmap::ZeroHashMap; | ||
| pub(crate) use flexzerovec::chunk_to_usize; | ||
| #[doc(hidden)] | ||
| #[doc(hidden)] // macro use | ||
| pub mod __zerovec_internal_reexport { | ||
| pub use zerofrom::ZeroFrom; | ||
| pub use alloc::borrow; | ||
| pub use alloc::boxed; | ||
@@ -304,5 +301,8 @@ | ||
| pub use crate::varzerovec::{Index16, Index32, VarZeroVecFormat, VarZeroVecOwned}; | ||
| pub use crate::varzerovec::{Index16, Index32, Index8, VarZeroVecFormat, VarZeroVecOwned}; | ||
| pub use crate::flexzerovec::{FlexZeroSlice, FlexZeroVec, FlexZeroVecOwned}; | ||
| pub type VarZeroVec16<'a, T> = VarZeroVec<'a, T, Index16>; | ||
| pub type VarZeroVec32<'a, T> = VarZeroVec<'a, T, Index32>; | ||
| pub type VarZeroSlice16<T> = VarZeroSlice<T, Index16>; | ||
| pub type VarZeroSlice32<T> = VarZeroSlice<T, Index32>; | ||
| } | ||
@@ -427,2 +427,3 @@ | ||
| /// - [`ZeroMapKV`] | ||
| /// - [`alloc::borrow::ToOwned`] | ||
| /// | ||
@@ -448,2 +449,6 @@ /// To disable one of the automatic derives, use `#[zerovec::skip_derive(...)]` like so: `#[zerovec::skip_derive(ZeroMapKV)]`. | ||
| /// | ||
| /// In case there are multiple [`VarULE`] (i.e., variable-sized) fields, this macro will produce private fields that | ||
| /// appropriately pack the data together, with the packing format by default being [`crate::vecs::Index16`], but can be | ||
| /// overridden with `#[zerovec::format(zerovec::vecs::Index8)]`. | ||
| /// | ||
| /// [`EncodeAsVarULE`]: ule::EncodeAsVarULE | ||
@@ -532,2 +537,4 @@ /// [`VarULE`]: ule::VarULE | ||
| #[cfg(test)] | ||
| // Expected sizes are based on a 64-bit architecture | ||
| #[cfg(target_pointer_width = "64")] | ||
| mod tests { | ||
@@ -560,3 +567,2 @@ use super::*; | ||
| check_size_of!(120 | 96, ZeroMap2d<str, str, str>); | ||
| check_size_of!(32 | 24, vecs::FlexZeroVec); | ||
@@ -567,4 +573,3 @@ check_size_of!(24, Option<ZeroVec<u8>>); | ||
| check_size_of!(120 | 104 | 96, Option<ZeroMap2d<str, str, str>>); | ||
| check_size_of!(32 | 24, Option<vecs::FlexZeroVec>); | ||
| } | ||
| } |
@@ -26,5 +26,5 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// // Example byte buffer representing the map { 1: "one" } | ||
| /// let BINCODE_BYTES: &[u8; 29] = &[ | ||
| /// 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, | ||
| /// 0, 0, 111, 110, 101, | ||
| /// let BINCODE_BYTES: &[u8; 25] = &[ | ||
| /// 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 1, 0, 111, | ||
| /// 110, 101, | ||
| /// ]; | ||
@@ -31,0 +31,0 @@ /// |
+32
-6
@@ -23,2 +23,14 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| impl<'a, K, V> BakeSize for ZeroMap<'a, K, V> | ||
| where | ||
| K: ZeroMapKV<'a> + ?Sized, | ||
| V: ZeroMapKV<'a> + ?Sized, | ||
| K::Container: BakeSize, | ||
| V::Container: BakeSize, | ||
| { | ||
| fn borrows_size(&self) -> usize { | ||
| self.keys.borrows_size() + self.values.borrows_size() | ||
| } | ||
| } | ||
| impl<'a, K, V> Bake for ZeroMapBorrowed<'a, K, V> | ||
@@ -39,2 +51,14 @@ where | ||
| impl<'a, K, V> BakeSize for ZeroMapBorrowed<'a, K, V> | ||
| where | ||
| K: ZeroMapKV<'a> + ?Sized, | ||
| V: ZeroMapKV<'a> + ?Sized, | ||
| &'a K::Slice: BakeSize, | ||
| &'a V::Slice: BakeSize, | ||
| { | ||
| fn borrows_size(&self) -> usize { | ||
| self.keys.borrows_size() + self.values.borrows_size() | ||
| } | ||
| } | ||
| #[test] | ||
@@ -44,7 +68,8 @@ fn test_baked_map() { | ||
| ZeroMap<str, str>, | ||
| const: unsafe { | ||
| const, | ||
| unsafe { | ||
| #[allow(unused_unsafe)] | ||
| crate::ZeroMap::from_parts_unchecked( | ||
| unsafe { | ||
| crate::VarZeroVec::from_bytes_unchecked( | ||
| crate::vecs::VarZeroVec16::from_bytes_unchecked( | ||
| b"\x02\0\0\0\0\0\0\0\x02\0\0\0adbc" | ||
@@ -54,3 +79,3 @@ ) | ||
| unsafe { | ||
| crate::VarZeroVec::from_bytes_unchecked( | ||
| crate::vecs::VarZeroVec16::from_bytes_unchecked( | ||
| b"\x02\0\0\0\0\0\0\0\x04\0\0\0ERA1ERA0" | ||
@@ -69,7 +94,8 @@ ) | ||
| ZeroMapBorrowed<str, str>, | ||
| const: unsafe { | ||
| const, | ||
| unsafe { | ||
| #[allow(unused_unsafe)] | ||
| crate::maps::ZeroMapBorrowed::from_parts_unchecked( | ||
| unsafe { | ||
| crate::VarZeroSlice::from_bytes_unchecked( | ||
| crate::vecs::VarZeroSlice16::from_bytes_unchecked( | ||
| b"\x02\0\0\0\0\0\0\0\x02\0\0\0adbc" | ||
@@ -79,3 +105,3 @@ ) | ||
| unsafe { | ||
| crate::VarZeroSlice::from_bytes_unchecked( | ||
| crate::vecs::VarZeroSlice16::from_bytes_unchecked( | ||
| b"\x02\0\0\0\0\0\0\0\x04\0\0\0ERA1ERA0" | ||
@@ -82,0 +108,0 @@ ) |
+0
-8
@@ -7,3 +7,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::ule::*; | ||
| use crate::vecs::{FlexZeroSlice, FlexZeroVec}; | ||
| use crate::vecs::{VarZeroSlice, VarZeroVec}; | ||
@@ -70,9 +69,2 @@ use crate::zerovec::{ZeroSlice, ZeroVec}; | ||
| 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<T> | ||
@@ -79,0 +71,0 @@ where |
+14
-20
@@ -6,4 +6,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use super::*; | ||
| use crate::ule::{AsULE, EncodeAsVarULE, VarULE}; | ||
| use crate::{VarZeroVec, ZeroSlice, ZeroVec, ZeroVecError}; | ||
| use crate::ule::{AsULE, EncodeAsVarULE, UleError, VarULE}; | ||
| use crate::{VarZeroVec, ZeroSlice, ZeroVec}; | ||
| use alloc::borrow::Borrow; | ||
@@ -327,5 +327,4 @@ use alloc::boxed::Box; | ||
| where | ||
| K: ZeroMapKV<'a, Container = ZeroVec<'a, K>> + ?Sized, | ||
| K: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, K>>, | ||
| V: ZeroMapKV<'a> + ?Sized, | ||
| K: AsULE, | ||
| { | ||
@@ -340,3 +339,3 @@ /// Cast a `ZeroMap<K, V>` to `ZeroMap<P, V>` where `K` and `P` are [`AsULE`] types | ||
| where | ||
| P: AsULE<ULE = K::ULE> + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized, | ||
| P: AsULE<ULE = K::ULE> + ZeroMapKV<'a, Container = ZeroVec<'a, P>>, | ||
| { | ||
@@ -359,5 +358,5 @@ ZeroMap { | ||
| /// Panics if `K::ULE` and `P::ULE` are not the same size. | ||
| pub fn try_convert_zv_k_unchecked<P>(self) -> Result<ZeroMap<'a, P, V>, ZeroVecError> | ||
| pub fn try_convert_zv_k_unchecked<P>(self) -> Result<ZeroMap<'a, P, V>, UleError> | ||
| where | ||
| P: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized, | ||
| P: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, P>>, | ||
| { | ||
@@ -374,4 +373,3 @@ Ok(ZeroMap { | ||
| K: ZeroMapKV<'a> + ?Sized, | ||
| V: ZeroMapKV<'a, Container = ZeroVec<'a, V>> + ?Sized, | ||
| V: AsULE, | ||
| V: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, V>>, | ||
| { | ||
@@ -386,3 +384,3 @@ /// Cast a `ZeroMap<K, V>` to `ZeroMap<K, P>` where `V` and `P` are [`AsULE`] types | ||
| where | ||
| P: AsULE<ULE = V::ULE> + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized, | ||
| P: AsULE<ULE = V::ULE> + ZeroMapKV<'a, Container = ZeroVec<'a, P>>, | ||
| { | ||
@@ -405,5 +403,5 @@ ZeroMap { | ||
| /// Panics if `V::ULE` and `P::ULE` are not the same size. | ||
| pub fn try_convert_zv_v_unchecked<P>(self) -> Result<ZeroMap<'a, K, P>, ZeroVecError> | ||
| pub fn try_convert_zv_v_unchecked<P>(self) -> Result<ZeroMap<'a, K, P>, UleError> | ||
| where | ||
| P: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, P>> + ?Sized, | ||
| P: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, P>>, | ||
| { | ||
@@ -478,4 +476,3 @@ Ok(ZeroMap { | ||
| K: ZeroMapKV<'a> + ?Sized + Ord, | ||
| V: ZeroMapKV<'a> + ?Sized, | ||
| V: Copy, | ||
| V: Copy + ZeroMapKV<'a>, | ||
| { | ||
@@ -534,4 +531,3 @@ /// For cases when `V` is fixed-size, obtain a direct copy of `V` instead of `V::ULE`. | ||
| K: ZeroMapKV<'a> + ?Sized, | ||
| V: ZeroMapKV<'a, Container = ZeroVec<'a, V>> + ?Sized, | ||
| V: AsULE + Copy, | ||
| V: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, V>>, | ||
| { | ||
@@ -556,6 +552,4 @@ /// Similar to [`Self::iter()`] except it returns a direct copy of the values instead of references | ||
| where | ||
| K: ZeroMapKV<'a, Container = ZeroVec<'a, K>> + ?Sized, | ||
| V: ZeroMapKV<'a, Container = ZeroVec<'a, V>> + ?Sized, | ||
| K: AsULE + Copy, | ||
| V: AsULE + Copy, | ||
| K: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, K>>, | ||
| V: AsULE + ZeroMapKV<'a, Container = ZeroVec<'a, V>>, | ||
| { | ||
@@ -562,0 +556,0 @@ /// Similar to [`Self::iter()`] except it returns a direct copy of the keys values instead of references |
+2
-2
@@ -252,4 +252,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| const BINCODE_BYTES: &[u8] = &[ | ||
| 12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 3, 0, | ||
| 0, 0, 0, 0, 3, 0, 6, 0, 117, 110, 111, 100, 111, 115, 116, 114, 101, 115, | ||
| 12, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 3, 0, | ||
| 3, 0, 6, 0, 117, 110, 111, 100, 111, 115, 116, 114, 101, 115, | ||
| ]; | ||
@@ -256,0 +256,0 @@ |
+6
-158
@@ -7,3 +7,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::varzerovec::owned::VarZeroVecOwned; | ||
| use crate::vecs::{FlexZeroSlice, FlexZeroVec, FlexZeroVecOwned, VarZeroVecFormat}; | ||
| use crate::varzerovec::vec::VarZeroVecInner; | ||
| use crate::vecs::VarZeroVecFormat; | ||
| use crate::{VarZeroSlice, VarZeroVec}; | ||
@@ -491,3 +492,3 @@ use crate::{ZeroSlice, ZeroVec}; | ||
| } else { | ||
| VarZeroVec::Owned(VarZeroVecOwned::with_capacity(cap)) | ||
| Self::from(VarZeroVecOwned::with_capacity(cap)) | ||
| } | ||
@@ -510,3 +511,3 @@ } | ||
| fn zvl_as_borrowed_inner(&self) -> Option<&'a VarZeroSlice<T, F>> { | ||
| if let VarZeroVec::Borrowed(b) = *self { | ||
| if let Self(VarZeroVecInner::Borrowed(b)) = *self { | ||
| Some(b) | ||
@@ -526,151 +527,6 @@ } else { | ||
| } | ||
| *self = VarZeroVec::Owned(result); | ||
| *self = Self(VarZeroVecInner::Owned(result)); | ||
| } | ||
| } | ||
| impl<'a> ZeroVecLike<usize> 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<usize, usize> { | ||
| FlexZeroSlice::binary_search(self, *k) | ||
| } | ||
| fn zvl_binary_search_in_range( | ||
| &self, | ||
| k: &usize, | ||
| range: Range<usize>, | ||
| ) -> Option<Result<usize, usize>> { | ||
| FlexZeroSlice::binary_search_in_range(self, *k, range) | ||
| } | ||
| fn zvl_binary_search_by( | ||
| &self, | ||
| mut predicate: impl FnMut(&usize) -> Ordering, | ||
| ) -> Result<usize, usize> { | ||
| FlexZeroSlice::binary_search_by(self, |probe| predicate(&probe)) | ||
| } | ||
| fn zvl_binary_search_in_range_by( | ||
| &self, | ||
| mut predicate: impl FnMut(&usize) -> Ordering, | ||
| range: Range<usize>, | ||
| ) -> Option<Result<usize, usize>> { | ||
| 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<R>(g: &[u8], f: impl FnOnce(&usize) -> R) -> R { | ||
| f(&crate::chunk_to_usize(g, g.len())) | ||
| } | ||
| } | ||
| impl ZeroVecLike<usize> 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<usize, usize> { | ||
| FlexZeroSlice::binary_search(self, *k) | ||
| } | ||
| fn zvl_binary_search_in_range( | ||
| &self, | ||
| k: &usize, | ||
| range: Range<usize>, | ||
| ) -> Option<Result<usize, usize>> { | ||
| FlexZeroSlice::binary_search_in_range(self, *k, range) | ||
| } | ||
| fn zvl_binary_search_by( | ||
| &self, | ||
| mut predicate: impl FnMut(&usize) -> Ordering, | ||
| ) -> Result<usize, usize> { | ||
| FlexZeroSlice::binary_search_by(self, |probe| predicate(&probe)) | ||
| } | ||
| fn zvl_binary_search_in_range_by( | ||
| &self, | ||
| mut predicate: impl FnMut(&usize) -> Ordering, | ||
| range: Range<usize>, | ||
| ) -> Option<Result<usize, usize>> { | ||
| 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<R>(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)] | ||
@@ -712,3 +568,3 @@ mod test { | ||
| let mut vzv: VarZeroVec<str> = VarZeroVec::Owned( | ||
| let mut vzv: VarZeroVec<str> = VarZeroVec::from( | ||
| VarZeroVecOwned::try_from_elements(&["11", "22", "33", "44", "55", "66", "77"]) | ||
@@ -720,11 +576,3 @@ .unwrap(), | ||
| 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::<Vec<_>>(), | ||
| [44, 33, 22, 11, 77, 66, 55].into_iter().collect::<Vec<_>>() | ||
| ); | ||
| } | ||
| } |
@@ -26,6 +26,6 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// // Example byte buffer representing the map { 1: {2: "three" } } | ||
| /// let BINCODE_BYTES: &[u8; 51] = &[ | ||
| /// let BINCODE_BYTES: &[u8; 47] = &[ | ||
| /// 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, | ||
| /// 0, 0, 0, 0, 0, 0, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 116, | ||
| /// 104, 114, 101, 101, | ||
| /// 0, 0, 0, 0, 0, 0, 2, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1, 0, 116, 104, 114, | ||
| /// 101, 101, | ||
| /// ]; | ||
@@ -278,3 +278,3 @@ /// | ||
| /// Produce an ordered iterator over keys0 | ||
| pub fn iter0<'l>(&'l self) -> impl Iterator<Item = ZeroMap2dCursor<'a, 'a, K0, K1, V>> + '_ { | ||
| pub fn iter0<'l>(&'l self) -> impl Iterator<Item = ZeroMap2dCursor<'a, 'a, K0, K1, V>> + 'l { | ||
| (0..self.keys0.zvl_len()).map(move |idx| ZeroMap2dCursor::from_borrowed(self, idx)) | ||
@@ -281,0 +281,0 @@ } |
+11
-12
@@ -102,3 +102,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| &self, | ||
| ) -> impl Iterator< | ||
| ) -> impl DoubleEndedIterator< | ||
| Item = ( | ||
@@ -108,3 +108,4 @@ &'l <K1 as ZeroMapKV<'a>>::GetType, | ||
| ), | ||
| > + '_ { | ||
| > + ExactSizeIterator | ||
| + '_ { | ||
| let range = self.get_range(); | ||
@@ -123,3 +124,3 @@ #[allow(clippy::unwrap_used)] // `self.get_range()` returns a valid range | ||
| self, | ||
| ) -> impl Iterator< | ||
| ) -> impl DoubleEndedIterator< | ||
| Item = ( | ||
@@ -129,3 +130,3 @@ &'l <K1 as ZeroMapKV<'a>>::GetType, | ||
| ), | ||
| > { | ||
| > + ExactSizeIterator { | ||
| let range = self.get_range(); | ||
@@ -177,9 +178,6 @@ #[allow(clippy::unwrap_used)] // `self.get_range()` returns a valid range | ||
| /// | ||
| /// let zm2d: ZeroMap2d<str, u8, usize> = [ | ||
| /// ("a", 0u8, 1usize), | ||
| /// ("b", 1u8, 1000usize), | ||
| /// ("b", 2u8, 2000usize), | ||
| /// ] | ||
| /// .into_iter() | ||
| /// .collect(); | ||
| /// let zm2d: ZeroMap2d<str, u8, u16> = | ||
| /// [("a", 0u8, 1u16), ("b", 1u8, 1000u16), ("b", 2u8, 2000u16)] | ||
| /// .into_iter() | ||
| /// .collect(); | ||
| /// | ||
@@ -198,3 +196,4 @@ /// let mut total_value = 0; | ||
| &self, | ||
| ) -> impl Iterator<Item = (&'l <K1 as ZeroMapKV<'a>>::GetType, V)> + '_ { | ||
| ) -> impl DoubleEndedIterator<Item = (&'l <K1 as ZeroMapKV<'a>>::GetType, V)> + ExactSizeIterator + '_ | ||
| { | ||
| let range = self.get_range(); | ||
@@ -201,0 +200,0 @@ #[allow(clippy::unwrap_used)] // `self.get_range()` returns a valid range |
@@ -27,2 +27,19 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| impl<'a, K0, K1, V> BakeSize for ZeroMap2d<'a, K0, K1, V> | ||
| where | ||
| K0: ZeroMapKV<'a> + ?Sized, | ||
| K1: ZeroMapKV<'a> + ?Sized, | ||
| V: ZeroMapKV<'a> + ?Sized, | ||
| K0::Container: BakeSize, | ||
| K1::Container: BakeSize, | ||
| V::Container: BakeSize, | ||
| { | ||
| fn borrows_size(&self) -> usize { | ||
| self.keys0.borrows_size() | ||
| + self.joiner.borrows_size() | ||
| + self.keys1.borrows_size() | ||
| + self.values.borrows_size() | ||
| } | ||
| } | ||
| impl<'a, K0, K1, V> Bake for ZeroMap2dBorrowed<'a, K0, K1, V> | ||
@@ -47,2 +64,19 @@ where | ||
| impl<'a, K0, K1, V> BakeSize for ZeroMap2dBorrowed<'a, K0, K1, V> | ||
| where | ||
| K0: ZeroMapKV<'a> + ?Sized, | ||
| K1: ZeroMapKV<'a> + ?Sized, | ||
| V: ZeroMapKV<'a> + ?Sized, | ||
| &'a K0::Slice: BakeSize, | ||
| &'a K1::Slice: BakeSize, | ||
| &'a V::Slice: BakeSize, | ||
| { | ||
| fn borrows_size(&self) -> usize { | ||
| self.keys0.borrows_size() | ||
| + self.joiner.borrows_size() | ||
| + self.keys1.borrows_size() | ||
| + self.values.borrows_size() | ||
| } | ||
| } | ||
| #[test] | ||
@@ -52,7 +86,8 @@ fn test_baked_map() { | ||
| ZeroMap2d<str, str, str>, | ||
| const: unsafe { | ||
| const, | ||
| unsafe { | ||
| #[allow(unused_unsafe)] | ||
| crate::ZeroMap2d::from_parts_unchecked( | ||
| unsafe { | ||
| crate::VarZeroVec::from_bytes_unchecked( | ||
| crate::vecs::VarZeroVec16::from_bytes_unchecked( | ||
| 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" | ||
@@ -67,3 +102,3 @@ ) | ||
| unsafe { | ||
| crate::VarZeroVec::from_bytes_unchecked( | ||
| crate::vecs::VarZeroVec16::from_bytes_unchecked( | ||
| 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" | ||
@@ -73,3 +108,3 @@ ) | ||
| unsafe { | ||
| crate::VarZeroVec::from_bytes_unchecked( | ||
| crate::vecs::VarZeroVec16::from_bytes_unchecked( | ||
| 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" | ||
@@ -88,7 +123,8 @@ ) | ||
| ZeroMap2dBorrowed<str, str, str>, | ||
| const: unsafe { | ||
| const, | ||
| unsafe { | ||
| #[allow(unused_unsafe)] | ||
| crate::maps::ZeroMap2dBorrowed::from_parts_unchecked( | ||
| unsafe { | ||
| crate::VarZeroSlice::from_bytes_unchecked( | ||
| crate::vecs::VarZeroSlice16::from_bytes_unchecked( | ||
| 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" | ||
@@ -103,3 +139,3 @@ ) | ||
| unsafe { | ||
| crate::VarZeroSlice::from_bytes_unchecked( | ||
| crate::vecs::VarZeroSlice16::from_bytes_unchecked( | ||
| 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" | ||
@@ -109,3 +145,3 @@ ) | ||
| unsafe { | ||
| crate::VarZeroSlice::from_bytes_unchecked( | ||
| crate::vecs::VarZeroSlice16::from_bytes_unchecked( | ||
| 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" | ||
@@ -112,0 +148,0 @@ ) |
+3
-3
@@ -37,6 +37,6 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// // Example byte buffer representing the map { 1: {2: "three" } } | ||
| /// let BINCODE_BYTES: &[u8; 51] = &[ | ||
| /// let BINCODE_BYTES: &[u8; 47] = &[ | ||
| /// 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, | ||
| /// 0, 0, 0, 0, 0, 0, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 116, | ||
| /// 104, 114, 101, 101, | ||
| /// 0, 0, 0, 0, 0, 0, 2, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1, 0, 116, 104, 114, | ||
| /// 101, 101, | ||
| /// ]; | ||
@@ -43,0 +43,0 @@ /// |
@@ -372,4 +372,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, | ||
| 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 20, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, | ||
| 3, 0, 6, 0, 117, 110, 111, 100, 111, 115, 116, 114, 101, 115, | ||
| 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 3, 0, 16, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 6, 0, | ||
| 117, 110, 111, 100, 111, 115, 116, 114, 101, 115, | ||
| ]; | ||
@@ -435,3 +435,3 @@ | ||
| 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, | ||
| 0, 0, 2, 0, 11, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 116, 104, 114, 101, 101 | ||
| 0, 0, 2, 0, 7, 0, 0, 0, 0, 0, 0, 0, 1, 0, 116, 104, 114, 101, 101 | ||
| ] | ||
@@ -438,0 +438,0 @@ ); |
+2
-2
@@ -57,3 +57,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| assert_eq!( | ||
| ZeroVec::<u32>::parse_byte_slice(TEST_BUFFER_LE).unwrap(), | ||
| ZeroVec::<u32>::parse_bytes(TEST_BUFFER_LE).unwrap(), | ||
| ZeroVec::alloc_from_slice(TEST_SLICE) | ||
@@ -74,3 +74,3 @@ ); | ||
| VarZeroVec::<str>::parse_byte_slice(TEST_VARZEROSLICE_BYTES).unwrap(); | ||
| VarZeroVec::<str>::parse_bytes(TEST_VARZEROSLICE_BYTES).unwrap(); | ||
| } |
+27
-24
@@ -27,3 +27,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// let ule = c1.to_unaligned(); | ||
| /// assert_eq!(CharULE::as_byte_slice(&[ule]), &[0x03, 0x11, 0x01]); | ||
| /// assert_eq!(CharULE::slice_as_bytes(&[ule]), &[0x03, 0x11, 0x01]); | ||
| /// let c2 = char::from_unaligned(ule); | ||
@@ -39,3 +39,3 @@ /// assert_eq!(c1, c2); | ||
| /// let bytes: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF]; | ||
| /// CharULE::parse_byte_slice(bytes).expect_err("Invalid bytes"); | ||
| /// CharULE::parse_bytes_to_slice(bytes).expect_err("Invalid bytes"); | ||
| /// ``` | ||
@@ -57,2 +57,13 @@ #[repr(transparent)] | ||
| /// Converts this [`CharULE`] to a [`char`]. This is equivalent to calling | ||
| /// [`AsULE::from_unaligned`] | ||
| /// | ||
| /// See the type-level documentation for [`CharULE`] for more information. | ||
| #[inline] | ||
| pub fn to_char(self) -> char { | ||
| let [b0, b1, b2] = self.0; | ||
| // Safe because the bytes of CharULE are defined to represent a valid Unicode scalar value. | ||
| unsafe { char::from_u32_unchecked(u32::from_le_bytes([b0, b1, b2, 0])) } | ||
| } | ||
| impl_ule_from_array!(char, CharULE, Self([0; 3])); | ||
@@ -66,4 +77,4 @@ } | ||
| // (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. | ||
| // 4. The impl of validate_byte_slice() returns an error if there are extra bytes. | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid. | ||
| // 4. The impl of validate_bytes() returns an error if there are extra bytes. | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -73,5 +84,5 @@ // 6. CharULE byte equality is semantic equality | ||
| #[inline] | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| if bytes.len() % 3 != 0 { | ||
| return Err(ZeroVecError::length::<Self>(bytes.len())); | ||
| return Err(UleError::length::<Self>(bytes.len())); | ||
| } | ||
@@ -84,3 +95,3 @@ // Validate the bytes | ||
| let u = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], 0]); | ||
| char::try_from(u).map_err(|_| ZeroVecError::parse::<Self>())?; | ||
| char::try_from(u).map_err(|_| UleError::parse::<Self>())?; | ||
| } | ||
@@ -101,11 +112,3 @@ Ok(()) | ||
| fn from_unaligned(unaligned: Self::ULE) -> Self { | ||
| // 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], | ||
| unaligned.0[1], | ||
| unaligned.0[2], | ||
| 0, | ||
| ])) | ||
| } | ||
| unaligned.to_char() | ||
| } | ||
@@ -135,3 +138,3 @@ } | ||
| assert_eq!( | ||
| CharULE::as_byte_slice(&CHARS_ULE), | ||
| CharULE::slice_as_bytes(&CHARS_ULE), | ||
| &[0x61, 0x00, 0x00, 0x43, 0xF6, 0x01] | ||
@@ -145,3 +148,3 @@ ); | ||
| const CHARS_ULE: [CharULE; 0] = CharULE::from_array(CHARS); | ||
| let bytes = CharULE::as_byte_slice(&CHARS_ULE); | ||
| let bytes = CharULE::slice_as_bytes(&CHARS_ULE); | ||
| let empty: &[u8] = &[]; | ||
@@ -156,6 +159,6 @@ assert_eq!(bytes, empty); | ||
| let char_ules: Vec<CharULE> = chars.iter().copied().map(char::to_unaligned).collect(); | ||
| let char_bytes: &[u8] = CharULE::as_byte_slice(&char_ules); | ||
| let char_bytes: &[u8] = CharULE::slice_as_bytes(&char_ules); | ||
| // Check parsing | ||
| let parsed_ules: &[CharULE] = CharULE::parse_byte_slice(char_bytes).unwrap(); | ||
| let parsed_ules: &[CharULE] = CharULE::parse_bytes_to_slice(char_bytes).unwrap(); | ||
| assert_eq!(char_ules, parsed_ules); | ||
@@ -185,4 +188,4 @@ let parsed_chars: Vec<char> = parsed_ules | ||
| .collect(); | ||
| let u32_bytes: &[u8] = RawBytesULE::<4>::as_byte_slice(&u32_ules); | ||
| let parsed_ules_result = CharULE::parse_byte_slice(u32_bytes); | ||
| let u32_bytes: &[u8] = RawBytesULE::<4>::slice_as_bytes(&u32_ules); | ||
| let parsed_ules_result = CharULE::parse_bytes_to_slice(u32_bytes); | ||
| assert!(parsed_ules_result.is_err()); | ||
@@ -197,6 +200,6 @@ | ||
| .collect(); | ||
| let u32_bytes: &[u8] = RawBytesULE::<4>::as_byte_slice(&u32_ules); | ||
| let parsed_ules_result = CharULE::parse_byte_slice(u32_bytes); | ||
| let u32_bytes: &[u8] = RawBytesULE::<4>::slice_as_bytes(&u32_ules); | ||
| let parsed_ules_result = CharULE::parse_bytes_to_slice(u32_bytes); | ||
| assert!(parsed_ules_result.is_err()); | ||
| } | ||
| } |
+10
-10
@@ -64,16 +64,16 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| //! // a struct with only ULE fields) | ||
| //! // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. | ||
| //! // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety | ||
| //! // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. | ||
| //! // 3. The impl of `validate_bytes()` returns an error if any byte is not valid. | ||
| //! // 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety | ||
| //! // 5. The impl of `from_bytes_unchecked()` returns a reference to the same data. | ||
| //! // 6. The other VarULE methods use the default impl. | ||
| //! // 7. FooULE byte equality is semantic equality | ||
| //! unsafe impl VarULE for FooULE { | ||
| //! fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| //! fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| //! // validate each field | ||
| //! <char as AsULE>::ULE::validate_byte_slice(&bytes[0..3]).map_err(|_| ZeroVecError::parse::<Self>())?; | ||
| //! <u32 as AsULE>::ULE::validate_byte_slice(&bytes[3..7]).map_err(|_| ZeroVecError::parse::<Self>())?; | ||
| //! let _ = ZeroVec::<u32>::parse_byte_slice(&bytes[7..]).map_err(|_| ZeroVecError::parse::<Self>())?; | ||
| //! <char as AsULE>::ULE::validate_bytes(&bytes[0..3]).map_err(|_| UleError::parse::<Self>())?; | ||
| //! <u32 as AsULE>::ULE::validate_bytes(&bytes[3..7]).map_err(|_| UleError::parse::<Self>())?; | ||
| //! let _ = ZeroVec::<u32>::parse_bytes(&bytes[7..]).map_err(|_| UleError::parse::<Self>())?; | ||
| //! Ok(()) | ||
| //! } | ||
| //! unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| //! unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| //! let ptr = bytes.as_ptr(); | ||
@@ -93,4 +93,4 @@ //! let len = bytes.len(); | ||
| //! // take each field, convert to ULE byte slices, and pass them through | ||
| //! cb(&[<char as AsULE>::ULE::as_byte_slice(&[self.field1.to_unaligned()]), | ||
| //! <u32 as AsULE>::ULE::as_byte_slice(&[self.field2.to_unaligned()]), | ||
| //! cb(&[<char as AsULE>::ULE::slice_as_bytes(&[self.field1.to_unaligned()]), | ||
| //! <u32 as AsULE>::ULE::slice_as_bytes(&[self.field2.to_unaligned()]), | ||
| //! // the ZeroVec is already in the correct slice format | ||
@@ -97,0 +97,0 @@ //! self.field3.as_bytes()]) |
+29
-11
@@ -29,3 +29,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// A typical implementation will take each field in the order found in the [`VarULE`] type, | ||
| /// convert it to ULE, call [`ULE::as_byte_slice()`] on them, and pass the slices to `cb` in order. | ||
| /// convert it to ULE, call [`ULE::slice_as_bytes()`] on them, and pass the slices to `cb` in order. | ||
| /// A trailing [`ZeroVec`](crate::ZeroVec) or [`VarZeroVec`](crate::VarZeroVec) can have their underlying | ||
@@ -43,3 +43,3 @@ /// byte representation passed through. | ||
| /// - The slices passed to `cb`, if concatenated, should be a valid instance of the `T` [`VarULE`] type | ||
| /// (i.e. if fed to [`VarULE::validate_byte_slice()`] they must produce a successful result) | ||
| /// (i.e. if fed to [`VarULE::validate_bytes()`] they must produce a successful result) | ||
| /// - It must return the return value of `cb` to the caller | ||
@@ -86,3 +86,3 @@ /// | ||
| /// 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> { | ||
| pub fn encode_varule_to_box<S: EncodeAsVarULE<T> + ?Sized, T: VarULE + ?Sized>(x: &S) -> Box<T> { | ||
| // zero-fill the vector to avoid uninitialized data UB | ||
@@ -94,4 +94,4 @@ let mut vec: Vec<u8> = vec![0; x.encode_var_ule_len()]; | ||
| // 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; | ||
| // and can be recouped via from_bytes_unchecked() | ||
| let ptr: *mut T = T::from_bytes_unchecked(&boxed) as *const T as *mut T; | ||
@@ -105,3 +105,3 @@ // Safety: we can construct an owned version since we have mem::forgotten the older owner | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[T::as_byte_slice(self)]) | ||
| cb(&[T::as_bytes(self)]) | ||
| } | ||
@@ -112,6 +112,12 @@ } | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[T::as_byte_slice(self)]) | ||
| cb(&[T::as_bytes(self)]) | ||
| } | ||
| } | ||
| unsafe impl<T: VarULE + ?Sized> EncodeAsVarULE<T> for &'_ &'_ T { | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[T::as_bytes(self)]) | ||
| } | ||
| } | ||
| unsafe impl<T: VarULE + ?Sized> EncodeAsVarULE<T> for Cow<'_, T> | ||
@@ -122,3 +128,3 @@ where | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[T::as_byte_slice(self.as_ref())]) | ||
| cb(&[T::as_bytes(self.as_ref())]) | ||
| } | ||
@@ -129,6 +135,12 @@ } | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[T::as_byte_slice(self)]) | ||
| cb(&[T::as_bytes(self)]) | ||
| } | ||
| } | ||
| unsafe impl<T: VarULE + ?Sized> EncodeAsVarULE<T> for &'_ Box<T> { | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[T::as_bytes(self)]) | ||
| } | ||
| } | ||
| unsafe impl EncodeAsVarULE<str> for String { | ||
@@ -140,2 +152,8 @@ fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| unsafe impl EncodeAsVarULE<str> for &'_ String { | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[self.as_bytes()]) | ||
| } | ||
| } | ||
| // Note: This impl could technically use `T: AsULE`, but we want users to prefer `ZeroSlice<T>` | ||
@@ -148,3 +166,3 @@ // for cases where T is not a ULE. Therefore, we can use the more efficient `memcpy` impl here. | ||
| fn encode_var_ule_as_slices<R>(&self, cb: impl FnOnce(&[&[u8]]) -> R) -> R { | ||
| cb(&[<[T] as VarULE>::as_byte_slice(self)]) | ||
| cb(&[<[T] as VarULE>::as_bytes(self)]) | ||
| } | ||
@@ -173,3 +191,3 @@ } | ||
| let ule = item.to_unaligned(); | ||
| chunk.copy_from_slice(ULE::as_byte_slice(core::slice::from_ref(&ule))); | ||
| chunk.copy_from_slice(ULE::slice_as_bytes(core::slice::from_ref(&ule))); | ||
| } | ||
@@ -176,0 +194,0 @@ } |
+98
-43
@@ -23,6 +23,8 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| mod slices; | ||
| mod unvalidated; | ||
| #[cfg(test)] | ||
| pub mod test_utils; | ||
| pub mod tuple; | ||
| pub use super::ZeroVecError; | ||
| pub mod tuplevar; | ||
| pub mod vartuple; | ||
| pub use chars::CharULE; | ||
@@ -34,3 +36,2 @@ pub use encode::{encode_varule_to_box, EncodeAsVarULE}; | ||
| pub use plain::RawBytesULE; | ||
| pub use unvalidated::{UnvalidatedChar, UnvalidatedStr}; | ||
@@ -40,3 +41,3 @@ use alloc::alloc::Layout; | ||
| use alloc::boxed::Box; | ||
| use core::{mem, slice}; | ||
| use core::{any, fmt, mem, slice}; | ||
@@ -58,5 +59,5 @@ /// Fixed-width, byte-aligned data that can be cast to and from a little-endian byte slice. | ||
| /// 2. The type must have an alignment of 1 byte. | ||
| /// 3. The impl of [`ULE::validate_byte_slice()`] *must* return an error if the given byte slice | ||
| /// 3. The impl of [`ULE::validate_bytes()`] *must* return an error if the given byte slice | ||
| /// would not represent a valid slice of this type. | ||
| /// 4. The impl of [`ULE::validate_byte_slice()`] *must* return an error if the given byte slice | ||
| /// 4. The impl of [`ULE::validate_bytes()`] *must* return an error if the given byte slice | ||
| /// cannot be used in its entirety (if its length is not a multiple of `size_of::<Self>()`). | ||
@@ -73,6 +74,6 @@ /// 5. All other methods *must* be left with their default impl, or else implemented according to | ||
| /// A non-safety invariant is that if `Self` implements `PartialEq`, the it *must* be logically | ||
| /// equivalent to byte equality on [`Self::as_byte_slice()`]. | ||
| /// equivalent to byte equality on [`Self::slice_as_bytes()`]. | ||
| /// | ||
| /// It may be necessary to introduce a "canonical form" of the ULE if logical equality does not | ||
| /// equal byte equality. In such a case, [`Self::validate_byte_slice()`] should return an error | ||
| /// equal byte equality. In such a case, [`Self::validate_bytes()`] should return an error | ||
| /// for any values that are not in canonical form. For example, the decimal strings "1.23e4" and | ||
@@ -93,4 +94,4 @@ /// "12.3e3" are logically equal, but not byte-for-byte equal, so we could define a canonical form | ||
| /// If the bytes can be transmuted, *in their entirety*, to a valid slice of `Self`, then `Ok` | ||
| /// should be returned; otherwise, `Self::Error` should be returned. | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError>; | ||
| /// should be returned; otherwise, `Err` should be returned. | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError>; | ||
@@ -100,17 +101,17 @@ /// Parses a byte slice, `&[u8]`, and return it as `&[Self]` with the same lifetime. | ||
| /// If `Self` is not well-defined for all possible bit values, the bytes should be validated, | ||
| /// and an error should be returned in the same cases as [`Self::validate_byte_slice()`]. | ||
| /// and an error should be returned in the same cases as [`Self::validate_bytes()`]. | ||
| /// | ||
| /// The default implementation executes [`Self::validate_byte_slice()`] followed by | ||
| /// [`Self::from_byte_slice_unchecked`]. | ||
| /// The default implementation executes [`Self::validate_bytes()`] followed by | ||
| /// [`Self::slice_from_bytes_unchecked`]. | ||
| /// | ||
| /// Note: The following equality should hold: `bytes.len() % size_of::<Self>() == 0`. This | ||
| /// means that the returned slice can span the entire byte slice. | ||
| fn parse_byte_slice(bytes: &[u8]) -> Result<&[Self], ZeroVecError> { | ||
| Self::validate_byte_slice(bytes)?; | ||
| fn parse_bytes_to_slice(bytes: &[u8]) -> Result<&[Self], UleError> { | ||
| Self::validate_bytes(bytes)?; | ||
| debug_assert_eq!(bytes.len() % mem::size_of::<Self>(), 0); | ||
| Ok(unsafe { Self::from_byte_slice_unchecked(bytes) }) | ||
| Ok(unsafe { Self::slice_from_bytes_unchecked(bytes) }) | ||
| } | ||
| /// Takes a byte slice, `&[u8]`, and return it as `&[Self]` with the same lifetime, assuming | ||
| /// that this byte slice has previously been run through [`Self::parse_byte_slice()`] with | ||
| /// that this byte slice has previously been run through [`Self::parse_bytes_to_slice()`] with | ||
| /// success. | ||
@@ -125,3 +126,3 @@ /// | ||
| /// Callers of this method must take care to ensure that `bytes` was previously passed through | ||
| /// [`Self::validate_byte_slice()`] with success (and was not changed since then). | ||
| /// [`Self::validate_bytes()`] with success (and was not changed since then). | ||
| /// | ||
@@ -137,6 +138,6 @@ /// ## Implementors | ||
| /// | ||
| /// 1. This method *must* return the same result as [`Self::parse_byte_slice()`]. | ||
| /// 1. This method *must* return the same result as [`Self::parse_bytes_to_slice()`]. | ||
| /// 2. This method *must* return a slice to the same region of memory as the argument. | ||
| #[inline] | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &[Self] { | ||
| unsafe fn slice_from_bytes_unchecked(bytes: &[u8]) -> &[Self] { | ||
| let data = bytes.as_ptr(); | ||
@@ -160,3 +161,3 @@ let len = bytes.len() / mem::size_of::<Self>(); | ||
| #[allow(clippy::wrong_self_convention)] // https://github.com/rust-lang/rust-clippy/issues/7219 | ||
| fn as_byte_slice(slice: &[Self]) -> &[u8] { | ||
| fn slice_as_bytes(slice: &[Self]) -> &[u8] { | ||
| unsafe { | ||
@@ -201,4 +202,6 @@ slice::from_raw_parts(slice as *const [Self] as *const u8, mem::size_of_val(slice)) | ||
| /// An [`EqULE`] type is one whose byte sequence equals the byte sequence of its ULE type on | ||
| /// little-endian platforms. This enables certain performance optimizations, such as | ||
| /// A type whose byte sequence equals the byte sequence of its ULE type on | ||
| /// little-endian platforms. | ||
| /// | ||
| /// This enables certain performance optimizations, such as | ||
| /// [`ZeroVec::try_from_slice`](crate::ZeroVec::try_from_slice). | ||
@@ -274,7 +277,7 @@ /// | ||
| /// 2. The type must have an alignment of 1 byte. | ||
| /// 3. The impl of [`VarULE::validate_byte_slice()`] *must* return an error if the given byte slice | ||
| /// 3. The impl of [`VarULE::validate_bytes()`] *must* return an error if the given byte slice | ||
| /// would not represent a valid slice of this type. | ||
| /// 4. The impl of [`VarULE::validate_byte_slice()`] *must* return an error if the given byte slice | ||
| /// 4. The impl of [`VarULE::validate_bytes()`] *must* return an error if the given byte slice | ||
| /// cannot be used in its entirety. | ||
| /// 5. The impl of [`VarULE::from_byte_slice_unchecked()`] must produce a reference to the same | ||
| /// 5. The impl of [`VarULE::from_bytes_unchecked()`] must produce a reference to the same | ||
| /// underlying data assuming that the given bytes previously passed validation. | ||
@@ -291,6 +294,6 @@ /// 6. All other methods *must* be left with their default impl, or else implemented according to | ||
| /// A non-safety invariant is that if `Self` implements `PartialEq`, the it *must* be logically | ||
| /// equivalent to byte equality on [`Self::as_byte_slice()`]. | ||
| /// equivalent to byte equality on [`Self::as_bytes()`]. | ||
| /// | ||
| /// It may be necessary to introduce a "canonical form" of the ULE if logical equality does not | ||
| /// equal byte equality. In such a case, [`Self::validate_byte_slice()`] should return an error | ||
| /// equal byte equality. In such a case, [`Self::validate_bytes()`] should return an error | ||
| /// for any values that are not in canonical form. For example, the decimal strings "1.23e4" and | ||
@@ -314,3 +317,3 @@ /// "12.3e3" are logically equal, but not byte-for-byte equal, so we could define a canonical form | ||
| /// be returned; otherwise, `Self::Error` should be returned. | ||
| fn validate_byte_slice(_bytes: &[u8]) -> Result<(), ZeroVecError>; | ||
| fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError>; | ||
@@ -320,6 +323,6 @@ /// Parses a byte slice, `&[u8]`, and return it as `&Self` with the same lifetime. | ||
| /// If `Self` is not well-defined for all possible bit values, the bytes should be validated, | ||
| /// and an error should be returned in the same cases as [`Self::validate_byte_slice()`]. | ||
| /// and an error should be returned in the same cases as [`Self::validate_bytes()`]. | ||
| /// | ||
| /// The default implementation executes [`Self::validate_byte_slice()`] followed by | ||
| /// [`Self::from_byte_slice_unchecked`]. | ||
| /// The default implementation executes [`Self::validate_bytes()`] followed by | ||
| /// [`Self::from_bytes_unchecked`]. | ||
| /// | ||
@@ -329,5 +332,5 @@ /// Note: The following equality should hold: `size_of_val(result) == size_of_val(bytes)`, | ||
| /// value spans the entire byte slice. | ||
| fn parse_byte_slice(bytes: &[u8]) -> Result<&Self, ZeroVecError> { | ||
| Self::validate_byte_slice(bytes)?; | ||
| let result = unsafe { Self::from_byte_slice_unchecked(bytes) }; | ||
| fn parse_bytes(bytes: &[u8]) -> Result<&Self, UleError> { | ||
| Self::validate_bytes(bytes)?; | ||
| let result = unsafe { Self::from_bytes_unchecked(bytes) }; | ||
| debug_assert_eq!(mem::size_of_val(result), mem::size_of_val(bytes)); | ||
@@ -338,3 +341,3 @@ Ok(result) | ||
| /// Takes a byte slice, `&[u8]`, and return it as `&Self` with the same lifetime, assuming | ||
| /// that this byte slice has previously been run through [`Self::parse_byte_slice()`] with | ||
| /// that this byte slice has previously been run through [`Self::parse_bytes()`] with | ||
| /// success. | ||
@@ -347,3 +350,3 @@ /// | ||
| /// Callers of this method must take care to ensure that `bytes` was previously passed through | ||
| /// [`Self::validate_byte_slice()`] with success (and was not changed since then). | ||
| /// [`Self::validate_bytes()`] with success (and was not changed since then). | ||
| /// | ||
@@ -357,5 +360,5 @@ /// ## Implementors | ||
| /// | ||
| /// 1. This method *must* return the same result as [`Self::parse_byte_slice()`]. | ||
| /// 1. This method *must* return the same result as [`Self::parse_bytes()`]. | ||
| /// 2. This method *must* return a slice to the same region of memory as the argument. | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self; | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self; | ||
@@ -371,3 +374,3 @@ /// Given `&Self`, returns a `&[u8]` with the same lifetime. | ||
| #[inline] | ||
| fn as_byte_slice(&self) -> &[u8] { | ||
| fn as_bytes(&self) -> &[u8] { | ||
| unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of_val(self)) } | ||
@@ -379,8 +382,7 @@ } | ||
| fn to_boxed(&self) -> Box<Self> { | ||
| let bytesvec = self.as_byte_slice().to_owned().into_boxed_slice(); | ||
| let bytesvec = self.as_bytes().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; | ||
| let ptr: *mut Self = Self::from_bytes_unchecked(&bytesvec) as *const Self as *mut Self; | ||
| assert_eq!(Layout::for_value(&*ptr), Layout::for_value(&**bytesvec)); | ||
@@ -416,1 +418,54 @@ // Transmute the pointer to an owned pointer | ||
| pub use zerovec_derive::VarULE; | ||
| /// An error type to be used for decoding slices of ULE types | ||
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| #[non_exhaustive] | ||
| pub enum UleError { | ||
| /// Attempted to parse a buffer into a slice of the given ULE type but its | ||
| /// length was not compatible. | ||
| /// | ||
| /// Typically created by a [`ULE`] impl via [`UleError::length()`]. | ||
| /// | ||
| /// [`ULE`]: crate::ule::ULE | ||
| InvalidLength { ty: &'static str, len: usize }, | ||
| /// The byte sequence provided for `ty` failed to parse correctly in the | ||
| /// given ULE type. | ||
| /// | ||
| /// Typically created by a [`ULE`] impl via [`UleError::parse()`]. | ||
| /// | ||
| /// [`ULE`]: crate::ule::ULE | ||
| ParseError { ty: &'static str }, | ||
| } | ||
| impl fmt::Display for UleError { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { | ||
| match *self { | ||
| UleError::InvalidLength { ty, len } => { | ||
| write!(f, "Invalid length {len} for slice of type {ty}") | ||
| } | ||
| UleError::ParseError { ty } => { | ||
| write!(f, "Could not parse bytes to slice of type {ty}") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl UleError { | ||
| /// Construct a parse error for the given type | ||
| pub fn parse<T: ?Sized + 'static>() -> UleError { | ||
| UleError::ParseError { | ||
| ty: any::type_name::<T>(), | ||
| } | ||
| } | ||
| /// Construct an "invalid length" error for the given type and length | ||
| pub fn length<T: ?Sized + 'static>(len: usize) -> UleError { | ||
| UleError::InvalidLength { | ||
| ty: any::type_name::<T>(), | ||
| len, | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "std")] | ||
| impl ::std::error::Error for UleError {} |
+56
-51
@@ -6,9 +6,9 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use super::*; | ||
| use crate::varzerovec::Index32; | ||
| use crate::VarZeroSlice; | ||
| use core::mem; | ||
| use crate::varzerovec::lengthless::VarZeroLengthlessSlice; | ||
| use crate::vecs::VarZeroVecFormat; | ||
| use core::{fmt, mem}; | ||
| /// This type is used by the custom derive to represent multiple [`VarULE`] | ||
| /// fields packed into a single end-of-struct field. It is not recommended | ||
| /// to use this type directly. | ||
| /// to use this type directly, use [`Tuple2VarULE`](crate::ule::tuplevar::Tuple2VarULE) etc instead. | ||
| /// | ||
@@ -18,18 +18,19 @@ /// Logically, consider it to be `(V1, V2, V3, ..)` | ||
| /// | ||
| /// Internally, it is represented by a VarZeroSlice. | ||
| #[derive(PartialEq, Eq, Debug)] | ||
| /// Internally, it is represented by a VarZeroSlice without the length part. | ||
| #[derive(PartialEq, Eq)] | ||
| #[repr(transparent)] | ||
| pub struct MultiFieldsULE(VarZeroSlice<[u8], Index32>); | ||
| pub struct MultiFieldsULE<const LEN: usize, Format: VarZeroVecFormat>( | ||
| VarZeroLengthlessSlice<[u8], Format>, | ||
| ); | ||
| impl MultiFieldsULE { | ||
| impl<const LEN: usize, Format: VarZeroVecFormat> MultiFieldsULE<LEN, Format> { | ||
| /// Compute the amount of bytes needed to support elements with lengths `lengths` | ||
| #[inline] | ||
| pub fn compute_encoded_len_for(lengths: &[usize]) -> usize { | ||
| #[allow(clippy::expect_used)] // See #1410 | ||
| unsafe { | ||
| // safe since BlankSliceEncoder is transparent over usize | ||
| let lengths = &*(lengths as *const [usize] as *const [BlankSliceEncoder]); | ||
| crate::varzerovec::components::compute_serializable_len::<_, _, Index32>(lengths) | ||
| .expect("Too many bytes to encode") as usize | ||
| } | ||
| #[allow(clippy::expect_used)] // See #1410 | ||
| pub fn compute_encoded_len_for(lengths: [usize; LEN]) -> usize { | ||
| let lengths = lengths.map(BlankSliceEncoder); | ||
| crate::varzerovec::components::compute_serializable_len_without_length::<_, _, Format>( | ||
| &lengths, | ||
| ) | ||
| .expect("Too many bytes to encode") as usize | ||
| } | ||
@@ -39,19 +40,18 @@ | ||
| pub fn new_from_lengths_partially_initialized<'a>( | ||
| lengths: &[usize], | ||
| lengths: [usize; LEN], | ||
| output: &'a mut [u8], | ||
| ) -> &'a mut Self { | ||
| let lengths = lengths.map(BlankSliceEncoder); | ||
| crate::varzerovec::components::write_serializable_bytes_without_length::<_, _, Format>( | ||
| &lengths, output, | ||
| ); | ||
| debug_assert!( | ||
| <VarZeroLengthlessSlice<[u8], Format>>::parse_bytes(LEN as u32, output).is_ok(), | ||
| "Encoded slice must be valid VarZeroSlice" | ||
| ); | ||
| unsafe { | ||
| // safe since BlankSliceEncoder is transparent over usize | ||
| let lengths = &*(lengths as *const [usize] as *const [BlankSliceEncoder]); | ||
| crate::varzerovec::components::write_serializable_bytes::<_, _, Index32>( | ||
| lengths, output, | ||
| ); | ||
| debug_assert!( | ||
| <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 | ||
| let slice = <VarZeroSlice<[u8], Index32>>::from_byte_slice_unchecked_mut(output); | ||
| // safe since `Self` is transparent over VarZeroSlice | ||
| mem::transmute::<&mut VarZeroSlice<_, Index32>, &mut Self>(slice) | ||
| // Safe since write_serializable_bytes produces a valid VarZeroLengthlessSlice buffer with the right format | ||
| let slice = <VarZeroLengthlessSlice<[u8], Format>>::from_bytes_unchecked_mut(output); | ||
| // safe since `Self` is transparent over VarZeroLengthlessSlice<[u8], Format> | ||
| mem::transmute::<&mut VarZeroLengthlessSlice<[u8], Format>, &mut Self>(slice) | ||
| } | ||
@@ -71,3 +71,3 @@ } | ||
| ) { | ||
| value.encode_var_ule_write(self.0.get_bytes_at_mut(idx)) | ||
| value.encode_var_ule_write(self.0.get_bytes_at_mut(LEN as u32, idx)) | ||
| } | ||
@@ -81,7 +81,4 @@ | ||
| #[inline] | ||
| pub unsafe fn validate_field<T: VarULE + ?Sized>( | ||
| &self, | ||
| index: usize, | ||
| ) -> Result<(), ZeroVecError> { | ||
| T::validate_byte_slice(self.0.get_unchecked(index)) | ||
| pub unsafe fn validate_field<T: VarULE + ?Sized>(&self, index: usize) -> Result<(), UleError> { | ||
| T::validate_bytes(self.0.get_unchecked(LEN as u32, index)) | ||
| } | ||
@@ -97,3 +94,3 @@ | ||
| pub unsafe fn get_field<T: VarULE + ?Sized>(&self, index: usize) -> &T { | ||
| T::from_byte_slice_unchecked(self.0.get_unchecked(index)) | ||
| T::from_bytes_unchecked(self.0.get_unchecked(LEN as u32, index)) | ||
| } | ||
@@ -104,10 +101,20 @@ | ||
| /// # Safety | ||
| /// - byte slice must be a valid VarZeroSlice<[u8]> | ||
| /// - byte slice must be a valid VarZeroLengthlessSlice<[u8], Format> with length LEN | ||
| #[inline] | ||
| pub unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| // &Self is transparent over &VZS<..> | ||
| mem::transmute(<VarZeroSlice<[u8]>>::from_byte_slice_unchecked(bytes)) | ||
| pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| // &Self is transparent over &VZS<..> with the right format | ||
| mem::transmute(<VarZeroLengthlessSlice<[u8], Format>>::from_bytes_unchecked(bytes)) | ||
| } | ||
| /// Get the bytes behind this value | ||
| pub fn as_bytes(&self) -> &[u8] { | ||
| self.0.as_bytes() | ||
| } | ||
| } | ||
| impl<const LEN: usize, Format: VarZeroVecFormat> fmt::Debug for MultiFieldsULE<LEN, Format> { | ||
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
| write!(f, "MultiFieldsULE<{LEN}>({:?})", self.0.as_bytes()) | ||
| } | ||
| } | ||
| /// This lets us conveniently use the EncodeAsVarULE functionality to create | ||
@@ -139,8 +146,8 @@ /// `VarZeroVec<[u8]>`s that have the right amount of space for elements | ||
| // 2. MultiFieldsULE is aligned to 1 byte (achieved by being transparent over a VarULE type) | ||
| // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. | ||
| // 3. The impl of `validate_bytes()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_bytes_unchecked()` returns a reference to the same data. | ||
| // 6. All other methods are defaulted | ||
| // 7. `MultiFieldsULE` byte equality is semantic equality (achieved by being transparent over a VarULE type) | ||
| unsafe impl VarULE for MultiFieldsULE { | ||
| unsafe impl<const LEN: usize, Format: VarZeroVecFormat> VarULE for MultiFieldsULE<LEN, Format> { | ||
| /// Note: MultiFieldsULE is usually used in cases where one should be calling .validate_field() directly for | ||
@@ -151,13 +158,11 @@ /// each field, rather than using the regular VarULE impl. | ||
| #[inline] | ||
| fn validate_byte_slice(slice: &[u8]) -> Result<(), ZeroVecError> { | ||
| <VarZeroSlice<[u8], Index32>>::validate_byte_slice(slice) | ||
| fn validate_bytes(slice: &[u8]) -> Result<(), UleError> { | ||
| <VarZeroLengthlessSlice<[u8], Format>>::parse_bytes(LEN as u32, slice).map(|_| ()) | ||
| } | ||
| #[inline] | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| // &Self is transparent over &VZS<..> | ||
| mem::transmute(<VarZeroSlice<[u8], Index32>>::from_byte_slice_unchecked( | ||
| bytes, | ||
| )) | ||
| mem::transmute(<VarZeroLengthlessSlice<[u8], Format>>::from_bytes_unchecked(bytes)) | ||
| } | ||
| } |
+22
-21
@@ -38,4 +38,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// let zv_no: ZeroVec<NichedOption<NonZeroI8, 1>> = | ||
| /// ZeroVec::parse_byte_slice(bytes) | ||
| /// .expect("Unable to parse as NichedOption."); | ||
| /// ZeroVec::parse_bytes(bytes).expect("Unable to parse as NichedOption."); | ||
| /// | ||
@@ -89,2 +88,14 @@ /// assert_eq!(zv_no.get(0).map(|e| e.0), Some(None)); | ||
| } | ||
| /// Borrows as an `Option<&U>`. | ||
| pub fn as_ref(&self) -> Option<&U> { | ||
| // Safety: The union stores NICHE_BIT_PATTERN when None otherwise a valid U | ||
| unsafe { | ||
| if self.niche == <U as NicheBytes<N>>::NICHE_BIT_PATTERN { | ||
| None | ||
| } else { | ||
| Some(&self.valid) | ||
| } | ||
| } | ||
| } | ||
| } | ||
@@ -115,4 +126,4 @@ | ||
| /// ULE fields. | ||
| /// 3. validate_byte_slice impl returns an error if invalid bytes are encountered. | ||
| /// 4. validate_byte_slice impl returns an error there are extra bytes. | ||
| /// 3. validate_bytes impl returns an error if invalid bytes are encountered. | ||
| /// 4. validate_bytes impl returns an error there are extra bytes. | ||
| /// 5. The other ULE methods are left to their default impl. | ||
@@ -122,3 +133,3 @@ /// 6. NichedOptionULE equality is based on ULE equality of the subfield, assuming that NicheBytes | ||
| unsafe impl<U: NicheBytes<N> + ULE, const N: usize> ULE for NichedOptionULE<U, N> { | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), crate::ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), crate::ule::UleError> { | ||
| let size = size_of::<Self>(); | ||
@@ -131,3 +142,3 @@ // The implemention is only correct if NICHE_BIT_PATTERN has same number of bytes as the | ||
| if bytes.len() % size != 0 { | ||
| return Err(crate::ZeroVecError::length::<Self>(bytes.len())); | ||
| return Err(crate::ule::UleError::length::<Self>(bytes.len())); | ||
| } | ||
@@ -140,3 +151,3 @@ bytes.chunks(size).try_for_each(|chunk| { | ||
| } else { | ||
| U::validate_byte_slice(chunk) | ||
| U::validate_bytes(chunk) | ||
| } | ||
@@ -148,15 +159,11 @@ }) | ||
| /// Optional type which uses [`NichedOptionULE<U,N>`] as ULE type. | ||
| /// The implementors guarantee that `N == core::mem::sizeo_of::<Self>()` | ||
| /// | ||
| /// The implementors guarantee that `N == core::mem::size_of::<Self>()` | ||
| /// [`repr(transparent)`] guarantees that the layout is same as [`Option<U>`] | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
| #[repr(transparent)] | ||
| #[non_exhaustive] | ||
| #[allow(clippy::exhaustive_structs)] // newtype | ||
| #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
| pub struct NichedOption<U, const N: usize>(pub Option<U>); | ||
| impl<U, const N: usize> NichedOption<U, N> { | ||
| pub const fn new(o: Option<U>) -> Self { | ||
| Self(o) | ||
| } | ||
| } | ||
| impl<U, const N: usize> Default for NichedOption<U, N> { | ||
@@ -168,8 +175,2 @@ fn default() -> Self { | ||
| impl<U, const N: usize> From<Option<U>> for NichedOption<U, N> { | ||
| fn from(o: Option<U>) -> Self { | ||
| Self(o) | ||
| } | ||
| } | ||
| impl<U: AsULE, const N: usize> AsULE for NichedOption<U, N> | ||
@@ -176,0 +177,0 @@ where |
+17
-17
@@ -70,4 +70,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| // (achieved by `#[repr(C, packed)]` on a struct containing only ULE fields, in the context of this impl) | ||
| // 3. The impl of validate_byte_slice() returns an error if any byte is not valid. | ||
| // 4. The impl of validate_byte_slice() returns an error if there are extra bytes. | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid. | ||
| // 4. The impl of validate_bytes() returns an error if there are extra bytes. | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -77,6 +77,6 @@ // 6. OptionULE byte equality is semantic equality by relying on the ULE equality | ||
| unsafe impl<U: ULE> ULE for OptionULE<U> { | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| let size = mem::size_of::<Self>(); | ||
| if bytes.len() % size != 0 { | ||
| return Err(ZeroVecError::length::<Self>(bytes.len())); | ||
| return Err(UleError::length::<Self>(bytes.len())); | ||
| } | ||
@@ -90,7 +90,7 @@ for chunk in bytes.chunks(size) { | ||
| if !chunk[1..].iter().all(|x| *x == 0) { | ||
| return Err(ZeroVecError::parse::<Self>()); | ||
| return Err(UleError::parse::<Self>()); | ||
| } | ||
| } | ||
| 1 => U::validate_byte_slice(&chunk[1..])?, | ||
| _ => return Err(ZeroVecError::parse::<Self>()), | ||
| 1 => U::validate_bytes(&chunk[1..])?, | ||
| _ => return Err(UleError::parse::<Self>()), | ||
| } | ||
@@ -156,3 +156,3 @@ } | ||
| // Safety: byte field is a valid T if boolean field is true | ||
| Some(U::from_byte_slice_unchecked(&self.2)) | ||
| Some(U::from_bytes_unchecked(&self.2)) | ||
| } | ||
@@ -175,5 +175,5 @@ } else { | ||
| // 2. OptionVarULE<T> is aligned to 1 byte (achieved by being repr(C, packed) on ULE types) | ||
| // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. | ||
| // 3. The impl of `validate_bytes()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_bytes_unchecked()` returns a reference to the same data. | ||
| // 6. All other methods are defaulted | ||
@@ -183,5 +183,5 @@ // 7. OptionVarULE<T> byte equality is semantic equality (achieved by being an aggregate) | ||
| #[inline] | ||
| fn validate_byte_slice(slice: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(slice: &[u8]) -> Result<(), UleError> { | ||
| if slice.is_empty() { | ||
| return Err(ZeroVecError::length::<Self>(slice.len())); | ||
| return Err(UleError::length::<Self>(slice.len())); | ||
| } | ||
@@ -194,3 +194,3 @@ #[allow(clippy::indexing_slicing)] // slice already verified to be nonempty | ||
| if slice.len() != 1 { | ||
| Err(ZeroVecError::length::<Self>(slice.len())) | ||
| Err(UleError::length::<Self>(slice.len())) | ||
| } else { | ||
@@ -200,4 +200,4 @@ Ok(()) | ||
| } | ||
| 1 => U::validate_byte_slice(&slice[1..]), | ||
| _ => Err(ZeroVecError::parse::<Self>()), | ||
| 1 => U::validate_bytes(&slice[1..]), | ||
| _ => Err(UleError::parse::<Self>()), | ||
| } | ||
@@ -207,3 +207,3 @@ } | ||
| #[inline] | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| let entire_struct_as_slice: *const [u8] = | ||
@@ -210,0 +210,0 @@ ::core::ptr::slice_from_raw_parts(bytes.as_ptr(), bytes.len() - 1); |
+22
-22
@@ -26,3 +26,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| #[inline] | ||
| pub fn from_byte_slice_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] { | ||
| pub fn from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] { | ||
| let data = bytes.as_mut_ptr(); | ||
@@ -40,4 +40,4 @@ let len = bytes.len() / N; | ||
| // (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. | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid (never). | ||
| // 4. The impl of validate_bytes() returns an error if there are leftover bytes. | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -47,3 +47,3 @@ // 6. RawBytesULE byte equality is semantic equality | ||
| #[inline] | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| if bytes.len() % N == 0 { | ||
@@ -53,3 +53,3 @@ // Safe because Self is transparent over [u8; N] | ||
| } else { | ||
| Err(ZeroVecError::length::<Self>(bytes.len())) | ||
| Err(UleError::length::<Self>(bytes.len())) | ||
| } | ||
@@ -97,7 +97,7 @@ } | ||
| /// This cannot be generic over T because of current limitations in `const`, but if | ||
| /// this method is needed in a non-const context, check out [`ZeroSlice::parse_byte_slice()`] | ||
| /// this method is needed in a non-const context, check out [`ZeroSlice::parse_bytes()`] | ||
| /// instead. | ||
| /// | ||
| /// See [`ZeroSlice::cast()`] for an example. | ||
| pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, ZeroVecError> { | ||
| pub const fn try_from_bytes(bytes: &[u8]) -> Result<&Self, UleError> { | ||
| let len = bytes.len(); | ||
@@ -108,3 +108,3 @@ #[allow(clippy::modulo_one)] | ||
| } else { | ||
| Err(ZeroVecError::InvalidLength { | ||
| Err(UleError::InvalidLength { | ||
| ty: concat!("<const construct: ", $size, ">"), | ||
@@ -191,4 +191,4 @@ len, | ||
| // 2. u8 is aligned to 1 byte. | ||
| // 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 (never). | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid (never). | ||
| // 4. The impl of validate_bytes() returns an error if there are leftover bytes (never). | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -198,3 +198,3 @@ // 6. u8 byte equality is semantic equality | ||
| #[inline] | ||
| fn validate_byte_slice(_bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> { | ||
| Ok(()) | ||
@@ -222,4 +222,4 @@ } | ||
| // 2. NonZeroU8 is aligned to 1 byte. | ||
| // 3. The impl of validate_byte_slice() returns an error if any byte is not valid (0x00). | ||
| // 4. The impl of validate_byte_slice() returns an error if there are leftover bytes (never). | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid (0x00). | ||
| // 4. The impl of validate_bytes() returns an error if there are leftover bytes (never). | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -229,6 +229,6 @@ // 6. NonZeroU8 byte equality is semantic equality | ||
| #[inline] | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| bytes.iter().try_for_each(|b| { | ||
| if *b == 0x00 { | ||
| Err(ZeroVecError::parse::<Self>()) | ||
| Err(UleError::parse::<Self>()) | ||
| } else { | ||
@@ -262,4 +262,4 @@ Ok(()) | ||
| // 2. i8 is aligned to 1 byte. | ||
| // 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 (never). | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid (never). | ||
| // 4. The impl of validate_bytes() returns an error if there are leftover bytes (never). | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -269,3 +269,3 @@ // 6. i8 byte equality is semantic equality | ||
| #[inline] | ||
| fn validate_byte_slice(_bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(_bytes: &[u8]) -> Result<(), UleError> { | ||
| Ok(()) | ||
@@ -347,4 +347,4 @@ } | ||
| // 2. bool is aligned to 1 byte. | ||
| // 3. The impl of validate_byte_slice() returns an error if any byte is not valid (bytes that are not 0 or 1). | ||
| // 4. The impl of validate_byte_slice() returns an error if there are leftover bytes (never). | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid (bytes that are not 0 or 1). | ||
| // 4. The impl of validate_bytes() returns an error if there are leftover bytes (never). | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -354,3 +354,3 @@ // 6. bool byte equality is semantic equality | ||
| #[inline] | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| for byte in bytes { | ||
@@ -360,3 +360,3 @@ // https://doc.rust-lang.org/reference/types/boolean.html | ||
| if *byte > 1 { | ||
| return Err(ZeroVecError::parse::<Self>()); | ||
| return Err(UleError::parse::<Self>()); | ||
| } | ||
@@ -363,0 +363,0 @@ } |
+22
-23
@@ -6,3 +6,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::ule::*; | ||
| use core::str; | ||
@@ -12,4 +11,4 @@ // Safety (based on the safety checklist on the ULE trait): | ||
| // 2. [T; N] is aligned to 1 byte since T is ULE | ||
| // 3. The impl of validate_byte_slice() returns an error if any byte is not valid. | ||
| // 4. The impl of validate_byte_slice() returns an error if there are leftover bytes. | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid. | ||
| // 4. The impl of validate_bytes() returns an error if there are leftover bytes. | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -19,5 +18,5 @@ // 6. [T; N] byte equality is semantic equality since T is ULE | ||
| #[inline] | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| // a slice of multiple Selfs is equivalent to just a larger slice of Ts | ||
| T::validate_byte_slice(bytes) | ||
| T::validate_bytes(bytes) | ||
| } | ||
@@ -43,11 +42,11 @@ } | ||
| // 2. str is aligned to 1 byte. | ||
| // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. | ||
| // 6. `parse_byte_slice()` is equivalent to `validate_byte_slice()` followed by `from_byte_slice_unchecked()` | ||
| // 3. The impl of `validate_bytes()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_bytes_unchecked()` returns a reference to the same data. | ||
| // 6. `parse_bytes()` is equivalent to `validate_bytes()` followed by `from_bytes_unchecked()` | ||
| // 7. str byte equality is semantic equality | ||
| unsafe impl VarULE for str { | ||
| #[inline] | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| str::from_utf8(bytes).map_err(|_| ZeroVecError::parse::<Self>())?; | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| core::str::from_utf8(bytes).map_err(|_| UleError::parse::<Self>())?; | ||
| Ok(()) | ||
@@ -57,10 +56,10 @@ } | ||
| #[inline] | ||
| fn parse_byte_slice(bytes: &[u8]) -> Result<&Self, ZeroVecError> { | ||
| str::from_utf8(bytes).map_err(|_| ZeroVecError::parse::<Self>()) | ||
| fn parse_bytes(bytes: &[u8]) -> Result<&Self, UleError> { | ||
| core::str::from_utf8(bytes).map_err(|_| UleError::parse::<Self>()) | ||
| } | ||
| /// Invariant: must be safe to call when called on a slice that previously | ||
| /// succeeded with `parse_byte_slice` | ||
| /// succeeded with `parse_bytes` | ||
| #[inline] | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| str::from_utf8_unchecked(bytes) | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| core::str::from_utf8_unchecked(bytes) | ||
| } | ||
@@ -90,5 +89,5 @@ } | ||
| // 2. [T] is aligned to 1 byte (achieved by being a slice of a ULE type) | ||
| // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. | ||
| // 3. The impl of `validate_bytes()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_bytes_unchecked()` returns a reference to the same data. | ||
| // 6. All other methods are defaulted | ||
@@ -101,10 +100,10 @@ // 7. `[T]` byte equality is semantic equality (achieved by being a slice of a ULE type) | ||
| #[inline] | ||
| fn validate_byte_slice(slice: &[u8]) -> Result<(), ZeroVecError> { | ||
| T::validate_byte_slice(slice) | ||
| fn validate_bytes(slice: &[u8]) -> Result<(), UleError> { | ||
| T::validate_bytes(slice) | ||
| } | ||
| #[inline] | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| T::from_byte_slice_unchecked(bytes) | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| T::slice_from_bytes_unchecked(bytes) | ||
| } | ||
| } |
+11
-11
@@ -42,4 +42,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| // (achieved by `#[repr(C, packed)]` on a struct containing only ULE fields) | ||
| // 3. The impl of validate_byte_slice() returns an error if any byte is not valid. | ||
| // 4. The impl of validate_byte_slice() returns an error if there are extra bytes. | ||
| // 3. The impl of validate_bytes() returns an error if any byte is not valid. | ||
| // 4. The impl of validate_bytes() returns an error if there are extra bytes. | ||
| // 5. The other ULE methods use the default impl. | ||
@@ -49,7 +49,7 @@ // 6. TupleULE byte equality is semantic equality by relying on the ULE equality | ||
| unsafe impl<$($t: ULE),+> ULE for $name<$($t),+> { | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| // expands to: 0size + mem::size_of::<A>() + mem::size_of::<B>(); | ||
| let ule_bytes = 0usize $(+ mem::size_of::<$t>())+; | ||
| if bytes.len() % ule_bytes != 0 { | ||
| return Err(ZeroVecError::length::<Self>(bytes.len())); | ||
| return Err(UleError::length::<Self>(bytes.len())); | ||
| } | ||
@@ -62,3 +62,3 @@ for chunk in bytes.chunks(ule_bytes) { | ||
| #[allow(clippy::indexing_slicing)] // length checked | ||
| <$t>::validate_byte_slice(&chunk[j..i])?; | ||
| <$t>::validate_bytes(&chunk[j..i])?; | ||
| )+ | ||
@@ -144,3 +144,3 @@ } | ||
| let bytes = zerovec.as_bytes(); | ||
| let zerovec2 = ZeroVec::parse_byte_slice(bytes).unwrap(); | ||
| let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap(); | ||
| assert_eq!(zerovec, zerovec2); | ||
@@ -150,3 +150,3 @@ | ||
| // Note: 1234901 is not a valid char | ||
| let zerovec3 = ZeroVec::<(char, u32)>::parse_byte_slice(bytes); | ||
| let zerovec3 = ZeroVec::<(char, u32)>::parse_bytes(bytes); | ||
| assert!(zerovec3.is_err()); | ||
@@ -161,3 +161,3 @@ } | ||
| let bytes = zerovec.as_bytes(); | ||
| let zerovec2 = ZeroVec::parse_byte_slice(bytes).unwrap(); | ||
| let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap(); | ||
| assert_eq!(zerovec, zerovec2); | ||
@@ -167,3 +167,3 @@ | ||
| // Note: 1234901 is not a valid char | ||
| let zerovec3 = ZeroVec::<(char, i8, u32)>::parse_byte_slice(bytes); | ||
| let zerovec3 = ZeroVec::<(char, i8, u32)>::parse_bytes(bytes); | ||
| assert!(zerovec3.is_err()); | ||
@@ -179,3 +179,3 @@ } | ||
| let bytes = zerovec.as_bytes(); | ||
| let zerovec2 = ZeroVec::parse_byte_slice(bytes).unwrap(); | ||
| let zerovec2 = ZeroVec::parse_bytes(bytes).unwrap(); | ||
| assert_eq!(zerovec, zerovec2); | ||
@@ -185,4 +185,4 @@ | ||
| // Note: 1234901 is not a valid char | ||
| let zerovec3 = ZeroVec::<(char, i8, u16, u32)>::parse_byte_slice(bytes); | ||
| let zerovec3 = ZeroVec::<(char, i8, u16, u32)>::parse_bytes(bytes); | ||
| assert!(zerovec3.is_err()); | ||
| } |
+379
-190
@@ -5,2 +5,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use super::VarZeroVecFormatError; | ||
| use crate::ule::*; | ||
@@ -14,14 +15,9 @@ use alloc::boxed::Box; | ||
| use core::marker::PhantomData; | ||
| use core::mem; | ||
| use core::ops::Range; | ||
| // Also used by owned.rs | ||
| pub(super) const LENGTH_WIDTH: usize = 4; | ||
| pub(super) const METADATA_WIDTH: usize = 0; | ||
| pub(super) const MAX_LENGTH: usize = u32::MAX as usize; | ||
| pub(super) const MAX_INDEX: usize = u32::MAX as usize; | ||
| /// This trait allows switching between different possible internal | ||
| /// representations of VarZeroVec. | ||
| /// | ||
| /// Currently this crate supports two formats: [`Index16`] and [`Index32`], | ||
| /// Currently this crate supports three formats: [`Index8`], [`Index16`] and [`Index32`], | ||
| /// with [`Index16`] being the default for all [`VarZeroVec`](super::VarZeroVec) | ||
@@ -32,25 +28,58 @@ /// types unless explicitly specified otherwise. | ||
| /// and all of its associated items are hidden from the docs. | ||
| pub trait VarZeroVecFormat: 'static + Sized { | ||
| /// The type to use for the indexing array | ||
| /// | ||
| /// Safety: must be a ULE for which all byte sequences are allowed | ||
| #[doc(hidden)] | ||
| type Index: IntegerULE; | ||
| /// The type to use for the length segment | ||
| /// | ||
| /// Safety: must be a ULE for which all byte sequences are allowed | ||
| #[doc(hidden)] | ||
| type Len: IntegerULE; | ||
| } | ||
| /// This trait represents various ULE types that can be used to represent an integer | ||
| /// | ||
| /// Do not implement this trait, its internals may be changed in the future, | ||
| /// and all of its associated items are hidden from the docs. | ||
| #[allow(clippy::missing_safety_doc)] // no safety section for you, don't implement this trait period | ||
| pub unsafe trait VarZeroVecFormat: 'static + Sized { | ||
| #[doc(hidden)] | ||
| pub unsafe trait IntegerULE: ULE { | ||
| /// The error to show when unable to construct a vec | ||
| #[doc(hidden)] | ||
| const INDEX_WIDTH: usize; | ||
| const TOO_LARGE_ERROR: &'static str; | ||
| /// Safety: must be sizeof(self) | ||
| #[doc(hidden)] | ||
| const SIZE: usize; | ||
| /// Safety: must be maximum integral value represented here | ||
| #[doc(hidden)] | ||
| const MAX_VALUE: u32; | ||
| /// This is always `RawBytesULE<Self::INDEX_WIDTH>` however | ||
| /// Rust does not currently support using associated constants in const | ||
| /// generics | ||
| /// Safety: Must roundtrip with from_usize and represent the correct | ||
| /// integral value | ||
| #[doc(hidden)] | ||
| type RawBytes: ULE; | ||
| fn iule_to_usize(self) -> usize; | ||
| // various conversions because RawBytes is an associated constant now | ||
| #[doc(hidden)] | ||
| fn rawbytes_to_usize(raw: Self::RawBytes) -> usize; | ||
| #[doc(hidden)] | ||
| fn usize_to_rawbytes(u: usize) -> Self::RawBytes; | ||
| fn iule_from_usize(x: usize) -> Option<Self>; | ||
| /// Safety: Should always convert a buffer into an array of Self with the correct length | ||
| #[doc(hidden)] | ||
| fn rawbytes_from_byte_slice_unchecked_mut(bytes: &mut [u8]) -> &mut [Self::RawBytes]; | ||
| fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self]; | ||
| } | ||
| /// This is a [`VarZeroVecFormat`] that stores u16s in the index array. | ||
| /// This is a [`VarZeroVecFormat`] that stores u8s in the index array, and a u8 for a length. | ||
| /// | ||
| /// Will have a smaller data size, but it's *extremely* likely for larger arrays | ||
| /// to be unrepresentable (and error on construction). Should probably be used | ||
| /// for known-small arrays, where all but the last field are known-small. | ||
| #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] | ||
| #[allow(clippy::exhaustive_structs)] // marker | ||
| pub struct Index8; | ||
| /// This is a [`VarZeroVecFormat`] that stores u16s in the index array, and a u16 for a length. | ||
| /// | ||
| /// Will have a smaller data size, but it's more likely for larger arrays | ||
@@ -64,3 +93,3 @@ /// to be unrepresentable (and error on construction) | ||
| /// This is a [`VarZeroVecFormat`] that stores u32s in the index array. | ||
| /// This is a [`VarZeroVecFormat`] that stores u32s in the index array, and a u32 for a length. | ||
| /// Will have a larger data size, but will support large arrays without | ||
@@ -72,35 +101,71 @@ /// problems. | ||
| unsafe impl VarZeroVecFormat for Index16 { | ||
| const INDEX_WIDTH: usize = 2; | ||
| impl VarZeroVecFormat for Index8 { | ||
| type Index = u8; | ||
| type Len = u8; | ||
| } | ||
| impl VarZeroVecFormat for Index16 { | ||
| type Index = RawBytesULE<2>; | ||
| type Len = RawBytesULE<2>; | ||
| } | ||
| impl VarZeroVecFormat for Index32 { | ||
| type Index = RawBytesULE<4>; | ||
| type Len = RawBytesULE<4>; | ||
| } | ||
| unsafe impl IntegerULE for u8 { | ||
| const TOO_LARGE_ERROR: &'static str = "Attempted to build VarZeroVec out of elements that \ | ||
| cumulatively are larger than a u8 in size"; | ||
| const SIZE: usize = mem::size_of::<Self>(); | ||
| const MAX_VALUE: u32 = u8::MAX as u32; | ||
| #[inline] | ||
| fn iule_to_usize(self) -> usize { | ||
| self as usize | ||
| } | ||
| #[inline] | ||
| fn iule_from_usize(u: usize) -> Option<Self> { | ||
| u8::try_from(u).ok() | ||
| } | ||
| #[inline] | ||
| fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] { | ||
| bytes | ||
| } | ||
| } | ||
| unsafe impl IntegerULE for RawBytesULE<2> { | ||
| const TOO_LARGE_ERROR: &'static str = "Attempted to build VarZeroVec out of elements that \ | ||
| cumulatively are larger than a u16 in size"; | ||
| const SIZE: usize = mem::size_of::<Self>(); | ||
| const MAX_VALUE: u32 = u16::MAX as u32; | ||
| type RawBytes = RawBytesULE<2>; | ||
| #[inline] | ||
| fn rawbytes_to_usize(raw: Self::RawBytes) -> usize { | ||
| raw.as_unsigned_int() as usize | ||
| fn iule_to_usize(self) -> usize { | ||
| self.as_unsigned_int() as usize | ||
| } | ||
| #[inline] | ||
| fn usize_to_rawbytes(u: usize) -> Self::RawBytes { | ||
| (u as u16).to_unaligned() | ||
| fn iule_from_usize(u: usize) -> Option<Self> { | ||
| u16::try_from(u).ok().map(u16::to_unaligned) | ||
| } | ||
| #[inline] | ||
| fn rawbytes_from_byte_slice_unchecked_mut(bytes: &mut [u8]) -> &mut [Self::RawBytes] { | ||
| Self::RawBytes::from_byte_slice_unchecked_mut(bytes) | ||
| fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] { | ||
| Self::from_bytes_unchecked_mut(bytes) | ||
| } | ||
| } | ||
| unsafe impl VarZeroVecFormat for Index32 { | ||
| const INDEX_WIDTH: usize = 4; | ||
| unsafe impl IntegerULE for RawBytesULE<4> { | ||
| const TOO_LARGE_ERROR: &'static str = "Attempted to build VarZeroVec out of elements that \ | ||
| cumulatively are larger than a u32 in size"; | ||
| const SIZE: usize = mem::size_of::<Self>(); | ||
| const MAX_VALUE: u32 = u32::MAX; | ||
| type RawBytes = RawBytesULE<4>; | ||
| #[inline] | ||
| fn rawbytes_to_usize(raw: Self::RawBytes) -> usize { | ||
| raw.as_unsigned_int() as usize | ||
| fn iule_to_usize(self) -> usize { | ||
| self.as_unsigned_int() as usize | ||
| } | ||
| #[inline] | ||
| fn usize_to_rawbytes(u: usize) -> Self::RawBytes { | ||
| (u as u32).to_unaligned() | ||
| fn iule_from_usize(u: usize) -> Option<Self> { | ||
| u32::try_from(u).ok().map(u32::to_unaligned) | ||
| } | ||
| #[inline] | ||
| fn rawbytes_from_byte_slice_unchecked_mut(bytes: &mut [u8]) -> &mut [Self::RawBytes] { | ||
| Self::RawBytes::from_byte_slice_unchecked_mut(bytes) | ||
| fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] { | ||
| Self::from_bytes_unchecked_mut(bytes) | ||
| } | ||
@@ -117,3 +182,3 @@ } | ||
| /// | ||
| /// See [`VarZeroVecComponents::parse_byte_slice()`] for information on the internal invariants involved | ||
| /// See [`VarZeroVecComponents::parse_bytes()`] for information on the internal invariants involved | ||
| #[derive(Debug)] | ||
@@ -124,7 +189,6 @@ pub struct VarZeroVecComponents<'a, T: ?Sized, F> { | ||
| /// The list of indices into the `things` slice | ||
| /// Since the first element is always at things[0], the first element of the indices array is for the *second* element | ||
| indices: &'a [u8], | ||
| /// The contiguous list of `T::VarULE`s | ||
| things: &'a [u8], | ||
| /// The original slice this was constructed from | ||
| entire_slice: &'a [u8], | ||
| marker: PhantomData<(&'a T, F)>, | ||
@@ -156,3 +220,2 @@ } | ||
| things: &[], | ||
| entire_slice: &[], | ||
| marker: PhantomData, | ||
@@ -166,9 +229,11 @@ } | ||
| /// - 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 array `indices` of indices | ||
| /// - `indices[i]..indices[i+1]` must index into a valid section of | ||
| /// - There must be at least `4*(length - 1) + 4` bytes total, to form the array `indices` of indices | ||
| /// - `0..indices[0]` must index into a valid section of | ||
| /// `things` (the data after `indices`), such that it parses to a `T::VarULE` | ||
| /// - `indices[i - 1]..indices[i]` must index into a valid section of | ||
| /// `things` (the data after `indices`), such that it parses to a `T::VarULE` | ||
| /// - `indices[len - 2]..things.len()` 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> { | ||
| pub fn parse_bytes(slice: &'a [u8]) -> Result<Self, VarZeroVecFormatError> { | ||
| // The empty VZV is special-cased to the empty slice | ||
@@ -180,3 +245,2 @@ if slice.is_empty() { | ||
| things: &[], | ||
| entire_slice: slice, | ||
| marker: PhantomData, | ||
@@ -186,20 +250,50 @@ }); | ||
| let len_bytes = slice | ||
| .get(0..LENGTH_WIDTH) | ||
| .ok_or(ZeroVecError::VarZeroVecFormatError)?; | ||
| let len_ule = RawBytesULE::<LENGTH_WIDTH>::parse_byte_slice(len_bytes) | ||
| .map_err(|_| ZeroVecError::VarZeroVecFormatError)?; | ||
| .get(0..F::Len::SIZE) | ||
| .ok_or(VarZeroVecFormatError::Metadata)?; | ||
| let len_ule = | ||
| F::Len::parse_bytes_to_slice(len_bytes).map_err(|_| VarZeroVecFormatError::Metadata)?; | ||
| let len = len_ule | ||
| .first() | ||
| .ok_or(ZeroVecError::VarZeroVecFormatError)? | ||
| .as_unsigned_int(); | ||
| .ok_or(VarZeroVecFormatError::Metadata)? | ||
| .iule_to_usize(); | ||
| let rest = slice | ||
| .get(F::Len::SIZE..) | ||
| .ok_or(VarZeroVecFormatError::Metadata)?; | ||
| let len_u32 = u32::try_from(len).map_err(|_| VarZeroVecFormatError::Metadata); | ||
| // We pass down the rest of the invariants | ||
| Self::parse_bytes_with_length(len_u32?, rest) | ||
| } | ||
| /// Construct a new VarZeroVecComponents, checking invariants about the overall buffer size: | ||
| /// | ||
| /// - There must be at least `4*len` bytes total, to form the array `indices` of indices. | ||
| /// - `indices[i]..indices[i+1]` must index into a valid section of | ||
| /// `things` (the data after `indices`), 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_bytes_with_length( | ||
| len: u32, | ||
| slice: &'a [u8], | ||
| ) -> Result<Self, VarZeroVecFormatError> { | ||
| let len_minus_one = len.checked_sub(1); | ||
| // The empty VZV is special-cased to the empty slice | ||
| let Some(len_minus_one) = len_minus_one else { | ||
| return Ok(VarZeroVecComponents { | ||
| len: 0, | ||
| indices: &[], | ||
| things: &[], | ||
| marker: PhantomData, | ||
| }); | ||
| }; | ||
| // The indices array is one element shorter since the first index is always 0, | ||
| // so we use len_minus_one | ||
| let indices_bytes = slice | ||
| .get( | ||
| LENGTH_WIDTH + METADATA_WIDTH | ||
| ..LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * (len as usize), | ||
| ) | ||
| .ok_or(ZeroVecError::VarZeroVecFormatError)?; | ||
| .get(..F::Index::SIZE * (len_minus_one as usize)) | ||
| .ok_or(VarZeroVecFormatError::Metadata)?; | ||
| let things = slice | ||
| .get(F::INDEX_WIDTH * (len as usize) + LENGTH_WIDTH + METADATA_WIDTH..) | ||
| .ok_or(ZeroVecError::VarZeroVecFormatError)?; | ||
| .get(F::Index::SIZE * (len_minus_one as usize)..) | ||
| .ok_or(VarZeroVecFormatError::Metadata)?; | ||
@@ -210,3 +304,2 @@ let borrowed = VarZeroVecComponents { | ||
| things, | ||
| entire_slice: slice, | ||
| marker: PhantomData, | ||
@@ -222,8 +315,8 @@ }; | ||
| /// successfully returned a [`VarZeroVecComponents`] when passed to | ||
| /// [`VarZeroVecComponents::parse_byte_slice()`]. Will return the same | ||
| /// object as one would get from calling [`VarZeroVecComponents::parse_byte_slice()`]. | ||
| /// [`VarZeroVecComponents::parse_bytes()`]. Will return the same | ||
| /// object as one would get from calling [`VarZeroVecComponents::parse_bytes()`]. | ||
| /// | ||
| /// # Safety | ||
| /// The bytes must have previously successfully run through | ||
| /// [`VarZeroVecComponents::parse_byte_slice()`] | ||
| /// [`VarZeroVecComponents::parse_bytes()`] | ||
| pub unsafe fn from_bytes_unchecked(slice: &'a [u8]) -> Self { | ||
@@ -236,17 +329,44 @@ // The empty VZV is special-cased to the empty slice | ||
| things: &[], | ||
| entire_slice: slice, | ||
| marker: PhantomData, | ||
| }; | ||
| } | ||
| let len_bytes = slice.get_unchecked(0..LENGTH_WIDTH); | ||
| let len_ule = RawBytesULE::<LENGTH_WIDTH>::from_byte_slice_unchecked(len_bytes); | ||
| // MSRV Rust 1.79: Use split_at_unchecked | ||
| let len_bytes = slice.get_unchecked(0..F::Len::SIZE); | ||
| // Safety: F::Len allows all byte sequences | ||
| let len_ule = F::Len::slice_from_bytes_unchecked(len_bytes); | ||
| let len = len_ule.get_unchecked(0).as_unsigned_int(); | ||
| let indices_bytes = slice.get_unchecked( | ||
| LENGTH_WIDTH + METADATA_WIDTH | ||
| ..LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * (len as usize), | ||
| ); | ||
| let things = | ||
| slice.get_unchecked(LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * (len as usize)..); | ||
| let len = len_ule.get_unchecked(0).iule_to_usize(); | ||
| let len_u32 = len as u32; | ||
| // Safety: This method requires the bytes to have passed through `parse_bytes()` | ||
| // whereas we're calling something that asks for `parse_bytes_with_length()`. | ||
| // The two methods perform similar validation, with parse_bytes() validating an additional | ||
| // 4-byte `length` header. | ||
| Self::from_bytes_unchecked_with_length(len_u32, slice.get_unchecked(F::Len::SIZE..)) | ||
| } | ||
| /// Construct a [`VarZeroVecComponents`] from a byte slice that has previously | ||
| /// successfully returned a [`VarZeroVecComponents`] when passed to | ||
| /// [`VarZeroVecComponents::parse_bytes()`]. Will return the same | ||
| /// object as one would get from calling [`VarZeroVecComponents::parse_bytes()`]. | ||
| /// | ||
| /// # Safety | ||
| /// The len,bytes must have previously successfully run through | ||
| /// [`VarZeroVecComponents::parse_bytes_with_length()`] | ||
| pub unsafe fn from_bytes_unchecked_with_length(len: u32, slice: &'a [u8]) -> Self { | ||
| let len_minus_one = len.checked_sub(1); | ||
| // The empty VZV is special-cased to the empty slice | ||
| let Some(len_minus_one) = len_minus_one else { | ||
| return VarZeroVecComponents { | ||
| len: 0, | ||
| indices: &[], | ||
| things: &[], | ||
| marker: PhantomData, | ||
| }; | ||
| }; | ||
| // The indices array is one element shorter since the first index is always 0, | ||
| // so we use len_minus_one | ||
| let indices_bytes = slice.get_unchecked(..F::Index::SIZE * (len_minus_one as usize)); | ||
| let things = slice.get_unchecked(F::Index::SIZE * (len_minus_one as usize)..); | ||
| VarZeroVecComponents { | ||
@@ -256,3 +376,2 @@ len, | ||
| things, | ||
| entire_slice: slice, | ||
| marker: PhantomData, | ||
@@ -271,3 +390,3 @@ } | ||
| pub fn is_empty(self) -> bool { | ||
| self.indices.is_empty() | ||
| self.len == 0 | ||
| } | ||
@@ -292,3 +411,3 @@ | ||
| let things_slice = self.things.get_unchecked(range); | ||
| T::from_byte_slice_unchecked(things_slice) | ||
| T::from_bytes_unchecked(things_slice) | ||
| } | ||
@@ -301,8 +420,14 @@ | ||
| #[inline] | ||
| unsafe fn get_things_range(self, idx: usize) -> Range<usize> { | ||
| let start = F::rawbytes_to_usize(*self.indices_slice().get_unchecked(idx)); | ||
| pub(crate) unsafe fn get_things_range(self, idx: usize) -> Range<usize> { | ||
| let start = if let Some(idx_minus_one) = idx.checked_sub(1) { | ||
| self.indices_slice() | ||
| .get_unchecked(idx_minus_one) | ||
| .iule_to_usize() | ||
| } else { | ||
| 0 | ||
| }; | ||
| let end = if idx + 1 == self.len() { | ||
| self.things.len() | ||
| } else { | ||
| F::rawbytes_to_usize(*self.indices_slice().get_unchecked(idx + 1)) | ||
| self.indices_slice().get_unchecked(idx).iule_to_usize() | ||
| }; | ||
@@ -313,13 +438,5 @@ debug_assert!(start <= end); | ||
| /// Get the range in `entire_slice` for the element at `idx`. Does not bounds check. | ||
| /// | ||
| /// Safety: | ||
| /// - `idx` must be in bounds (`idx < self.len()`) | ||
| #[inline] | ||
| pub(crate) unsafe fn get_range(self, idx: usize) -> Range<usize> { | ||
| let range = self.get_things_range(idx); | ||
| let offset = (self.things as *const [u8] as *const u8) | ||
| .offset_from(self.entire_slice as *const [u8] as *const u8) | ||
| as usize; | ||
| range.start + offset..range.end + offset | ||
| /// Get the size, in bytes, of the indices array | ||
| pub(crate) unsafe fn get_indices_size(self) -> usize { | ||
| self.indices.len() | ||
| } | ||
@@ -339,7 +456,6 @@ | ||
| #[allow(clippy::len_zero)] // more explicit to enforce safety invariants | ||
| fn check_indices_and_things(self) -> Result<(), ZeroVecError> { | ||
| assert_eq!(self.len(), self.indices_slice().len()); | ||
| fn check_indices_and_things(self) -> Result<(), VarZeroVecFormatError> { | ||
| if self.len() == 0 { | ||
| if self.things.len() > 0 { | ||
| return Err(ZeroVecError::VarZeroVecFormatError); | ||
| return Err(VarZeroVecFormatError::Metadata); | ||
| } else { | ||
@@ -349,23 +465,28 @@ return Ok(()); | ||
| } | ||
| let indices_slice = self.indices_slice(); | ||
| assert_eq!(self.len(), indices_slice.len() + 1); | ||
| // Safety: i is in bounds (assertion above) | ||
| let mut start = F::rawbytes_to_usize(unsafe { *self.indices_slice().get_unchecked(0) }); | ||
| if start != 0 { | ||
| return Err(ZeroVecError::VarZeroVecFormatError); | ||
| } | ||
| let mut start = 0; | ||
| for i in 0..self.len() { | ||
| let end = if i == self.len() - 1 { | ||
| // The indices array is offset by 1: indices[0] is the end of the first | ||
| // element and the start of the next, since the start of the first element | ||
| // is always things[0]. So to get the end we get element `i`. | ||
| let end = if let Some(end) = indices_slice.get(i) { | ||
| end.iule_to_usize() | ||
| } else { | ||
| // This only happens at i = self.len() - 1 = indices_slice.len() + 1 - 1 | ||
| // = indices_slice.len(). This is the last `end`, which is always the size of | ||
| // `things` and thus never stored in the array | ||
| self.things.len() | ||
| } else { | ||
| // Safety: i+1 is in bounds (assertion above) | ||
| F::rawbytes_to_usize(unsafe { *self.indices_slice().get_unchecked(i + 1) }) | ||
| }; | ||
| if start > end { | ||
| return Err(ZeroVecError::VarZeroVecFormatError); | ||
| return Err(VarZeroVecFormatError::Metadata); | ||
| } | ||
| if end > self.things.len() { | ||
| return Err(ZeroVecError::VarZeroVecFormatError); | ||
| return Err(VarZeroVecFormatError::Metadata); | ||
| } | ||
| // Safety: start..end is a valid range in self.things | ||
| let bytes = unsafe { self.things.get_unchecked(start..end) }; | ||
| T::parse_byte_slice(bytes)?; | ||
| T::parse_bytes(bytes).map_err(VarZeroVecFormatError::Values)?; | ||
| start = end; | ||
@@ -379,6 +500,17 @@ } | ||
| pub fn iter(self) -> impl Iterator<Item = &'a T> { | ||
| self.indices_slice() | ||
| .iter() | ||
| .copied() | ||
| .map(F::rawbytes_to_usize) | ||
| // The indices array doesn't contain 0 or len, we need to graft it on | ||
| // However we don't want to graft it on for an empty vector. | ||
| let (begin, end) = if self.is_empty() { | ||
| (None, None) | ||
| } else { | ||
| (Some(0), Some(self.things.len())) | ||
| }; | ||
| begin | ||
| .into_iter() | ||
| .chain( | ||
| self.indices_slice() | ||
| .iter() | ||
| .copied() | ||
| .map(IntegerULE::iule_to_usize), | ||
| ) | ||
| .zip( | ||
@@ -388,8 +520,7 @@ self.indices_slice() | ||
| .copied() | ||
| .map(F::rawbytes_to_usize) | ||
| .skip(1) | ||
| .chain([self.things.len()]), | ||
| .map(IntegerULE::iule_to_usize) | ||
| .chain(end), | ||
| ) | ||
| .map(move |(start, end)| unsafe { self.things.get_unchecked(start..end) }) | ||
| .map(|bytes| unsafe { T::from_byte_slice_unchecked(bytes) }) | ||
| .map(|bytes| unsafe { T::from_bytes_unchecked(bytes) }) | ||
| } | ||
@@ -402,4 +533,4 @@ | ||
| #[inline] | ||
| fn indices_slice(&self) -> &'a [F::RawBytes] { | ||
| unsafe { F::RawBytes::from_byte_slice_unchecked(self.indices) } | ||
| fn indices_slice(&self) -> &'a [F::Index] { | ||
| unsafe { F::Index::slice_from_bytes_unchecked(self.indices) } | ||
| } | ||
@@ -414,3 +545,3 @@ | ||
| .copied() | ||
| .map(F::rawbytes_to_usize) | ||
| .map(IntegerULE::iule_to_usize) | ||
| .collect::<Vec<_>>(); | ||
@@ -431,3 +562,3 @@ format!("VarZeroVecComponents {{ indices: {indices:?} }}") | ||
| pub fn binary_search(&self, needle: &T) -> Result<usize, usize> { | ||
| self.binary_search_impl(|probe| probe.cmp(needle), self.indices_slice()) | ||
| self.binary_search_by(|probe| probe.cmp(needle)) | ||
| } | ||
@@ -440,4 +571,3 @@ | ||
| ) -> Option<Result<usize, usize>> { | ||
| let indices_slice = self.indices_slice().get(range)?; | ||
| Some(self.binary_search_impl(|probe| probe.cmp(needle), indices_slice)) | ||
| self.binary_search_in_range_by(|probe| probe.cmp(needle), range) | ||
| } | ||
@@ -455,5 +585,8 @@ } | ||
| pub fn binary_search_by(&self, predicate: impl FnMut(&T) -> Ordering) -> Result<usize, usize> { | ||
| self.binary_search_impl(predicate, self.indices_slice()) | ||
| // Safety: 0 and len are in range | ||
| unsafe { self.binary_search_in_range_unchecked(predicate, 0..self.len()) } | ||
| } | ||
| // Binary search within a range. | ||
| // Values returned are relative to the range start! | ||
| pub fn binary_search_in_range_by( | ||
@@ -464,46 +597,59 @@ &self, | ||
| ) -> Option<Result<usize, usize>> { | ||
| let indices_slice = self.indices_slice().get(range)?; | ||
| Some(self.binary_search_impl(predicate, indices_slice)) | ||
| if range.end > self.len() { | ||
| return None; | ||
| } | ||
| if range.end < range.start { | ||
| return None; | ||
| } | ||
| // Safety: We bounds checked above: end is in-bounds or len, and start is <= end | ||
| let range_absolute = | ||
| unsafe { self.binary_search_in_range_unchecked(predicate, range.clone()) }; | ||
| // The values returned are relative to the range start | ||
| Some( | ||
| range_absolute | ||
| .map(|o| o - range.start) | ||
| .map_err(|e| e - range.start), | ||
| ) | ||
| } | ||
| /// Binary searches a sorted `VarZeroVecComponents<T>` with the given predicate. For more information, see | ||
| /// the primitive function [`binary_search`](slice::binary_search). | ||
| fn binary_search_impl( | ||
| /// Safety: range must be in range for the slice (start <= len, end <= len, start <= end) | ||
| unsafe fn binary_search_in_range_unchecked( | ||
| &self, | ||
| mut predicate: impl FnMut(&T) -> Ordering, | ||
| indices_slice: &[F::RawBytes], | ||
| range: Range<usize>, | ||
| ) -> Result<usize, usize> { | ||
| // 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. | ||
| // Function invariant: size is always end - start | ||
| let mut start = range.start; | ||
| let mut end = range.end; | ||
| let mut size; | ||
| // 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.indices.as_ptr() as *const _ as usize; | ||
| indices_slice.binary_search_by(|probe: &_| { | ||
| // `self.indices` is a vec of unaligned F::INDEX_WIDTH values, so we divide by F::INDEX_WIDTH | ||
| // to get the actual index | ||
| let index = (probe as *const _ as usize - zero_index) / F::INDEX_WIDTH; | ||
| // safety: we know this is in bounds | ||
| let actual_probe = unsafe { self.get_unchecked(index) }; | ||
| predicate(actual_probe) | ||
| }) | ||
| // Loop invariant: 0 <= start < end <= len | ||
| // This invariant is initialized by the function safety invariants and the loop condition | ||
| while start < end { | ||
| size = end - start; | ||
| // This establishes mid < end (which implies mid < len) | ||
| // size is end - start. start + size is end (which is <= len). | ||
| // mid = start + size/2 will be less than end | ||
| let mid = start + size / 2; | ||
| // Safety: mid is < end <= len, so in-range | ||
| let cmp = predicate(self.get_unchecked(mid)); | ||
| match cmp { | ||
| Ordering::Less => { | ||
| // This retains the loop invariant since it | ||
| // increments start, and we already have 0 <= start | ||
| // start < end is enforced by the loop condition | ||
| start = mid + 1; | ||
| } | ||
| Ordering::Greater => { | ||
| // mid < end, so this decreases end. | ||
| // This means end <= len is still true, and | ||
| // end > start is enforced by the loop condition | ||
| end = mid; | ||
| } | ||
| Ordering::Equal => return Ok(mid), | ||
| } | ||
| } | ||
| Err(start) | ||
| } | ||
@@ -521,3 +667,6 @@ } | ||
| let len = compute_serializable_len::<T, A, F>(elements)?; | ||
| debug_assert!(len >= LENGTH_WIDTH as u32); | ||
| debug_assert!( | ||
| len >= F::Len::SIZE as u32, | ||
| "Must have at least F::Len::SIZE bytes to hold the length of the vector" | ||
| ); | ||
| let mut output: Vec<u8> = alloc::vec![0; len as usize]; | ||
@@ -528,3 +677,4 @@ write_serializable_bytes::<T, A, F>(elements, &mut output); | ||
| /// Writes the bytes for a VarZeroSlice into an output buffer. | ||
| /// Writes the bytes for a VarZeroLengthlessSlice into an output buffer. | ||
| /// Usable for a VarZeroSlice if you first write the length bytes. | ||
| /// | ||
@@ -536,3 +686,3 @@ /// Every byte in the buffer will be initialized after calling this function. | ||
| /// Panics if the buffer is not exactly the correct length. | ||
| pub fn write_serializable_bytes<T, A, F>(elements: &[A], output: &mut [u8]) | ||
| pub fn write_serializable_bytes_without_length<T, A, F>(elements: &[A], output: &mut [u8]) | ||
| where | ||
@@ -543,26 +693,32 @@ T: VarULE + ?Sized, | ||
| { | ||
| assert!(elements.len() <= MAX_LENGTH); | ||
| let num_elements_bytes = elements.len().to_le_bytes(); | ||
| #[allow(clippy::indexing_slicing)] // Function contract allows panicky behavior | ||
| output[0..LENGTH_WIDTH].copy_from_slice(&num_elements_bytes[0..LENGTH_WIDTH]); | ||
| assert!(elements.len() <= F::Len::MAX_VALUE as usize); | ||
| if elements.is_empty() { | ||
| return; | ||
| } | ||
| // idx_offset = offset from the start of the buffer for the next index | ||
| let mut idx_offset: usize = LENGTH_WIDTH + METADATA_WIDTH; | ||
| let mut idx_offset: usize = 0; | ||
| // first_dat_offset = offset from the start of the buffer of the first data block | ||
| let first_dat_offset: usize = idx_offset + elements.len() * F::INDEX_WIDTH; | ||
| let first_dat_offset: usize = idx_offset + (elements.len() - 1) * F::Index::SIZE; | ||
| // dat_offset = offset from the start of the buffer of the next data block | ||
| let mut dat_offset: usize = first_dat_offset; | ||
| for element in elements.iter() { | ||
| for (i, element) in elements.iter().enumerate() { | ||
| let element_len = element.encode_var_ule_len(); | ||
| let idx_limit = idx_offset + F::INDEX_WIDTH; | ||
| #[allow(clippy::indexing_slicing)] // Function contract allows panicky behavior | ||
| let idx_slice = &mut output[idx_offset..idx_limit]; | ||
| // VZV expects data offsets to be stored relative to the first data block | ||
| let idx = dat_offset - first_dat_offset; | ||
| assert!(idx <= MAX_INDEX); | ||
| #[allow(clippy::indexing_slicing)] // this function is explicitly panicky | ||
| idx_slice.copy_from_slice(&idx.to_le_bytes()[..F::INDEX_WIDTH]); | ||
| // The first index is always 0. We don't write it, or update the idx offset. | ||
| if i != 0 { | ||
| let idx_limit = idx_offset + F::Index::SIZE; | ||
| #[allow(clippy::indexing_slicing)] // Function contract allows panicky behavior | ||
| let idx_slice = &mut output[idx_offset..idx_limit]; | ||
| // VZV expects data offsets to be stored relative to the first data block | ||
| let idx = dat_offset - first_dat_offset; | ||
| assert!(idx <= F::Index::MAX_VALUE as usize); | ||
| #[allow(clippy::expect_used)] // this function is explicitly panicky | ||
| let bytes_to_write = F::Index::iule_from_usize(idx).expect(F::Index::TOO_LARGE_ERROR); | ||
| idx_slice.copy_from_slice(ULE::slice_as_bytes(&[bytes_to_write])); | ||
| idx_offset = idx_limit; | ||
| } | ||
| let dat_limit = dat_offset + element_len; | ||
@@ -572,16 +728,18 @@ #[allow(clippy::indexing_slicing)] // Function contract allows panicky behavior | ||
| element.encode_var_ule_write(dat_slice); | ||
| debug_assert_eq!(T::validate_byte_slice(dat_slice), Ok(())); | ||
| idx_offset = idx_limit; | ||
| debug_assert_eq!(T::validate_bytes(dat_slice), Ok(())); | ||
| dat_offset = dat_limit; | ||
| } | ||
| debug_assert_eq!( | ||
| idx_offset, | ||
| LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * elements.len() | ||
| ); | ||
| debug_assert_eq!(idx_offset, F::Index::SIZE * (elements.len() - 1)); | ||
| assert_eq!(dat_offset, output.len()); | ||
| } | ||
| pub fn compute_serializable_len<T, A, F>(elements: &[A]) -> Option<u32> | ||
| /// Writes the bytes for a VarZeroSlice into an output buffer. | ||
| /// | ||
| /// Every byte in the buffer will be initialized after calling this function. | ||
| /// | ||
| /// # Panics | ||
| /// | ||
| /// Panics if the buffer is not exactly the correct length. | ||
| pub fn write_serializable_bytes<T, A, F>(elements: &[A], output: &mut [u8]) | ||
| where | ||
@@ -592,7 +750,29 @@ T: VarULE + ?Sized, | ||
| { | ||
| let idx_len: u32 = u32::try_from(elements.len()) | ||
| if elements.is_empty() { | ||
| return; | ||
| } | ||
| assert!(elements.len() <= F::Len::MAX_VALUE as usize); | ||
| #[allow(clippy::expect_used)] // This function is explicitly panicky | ||
| let num_elements_ule = F::Len::iule_from_usize(elements.len()).expect(F::Len::TOO_LARGE_ERROR); | ||
| #[allow(clippy::indexing_slicing)] // Function contract allows panicky behavior | ||
| output[0..F::Len::SIZE].copy_from_slice(ULE::slice_as_bytes(&[num_elements_ule])); | ||
| #[allow(clippy::indexing_slicing)] // Function contract allows panicky behavior | ||
| write_serializable_bytes_without_length::<T, A, F>(elements, &mut output[F::Len::SIZE..]); | ||
| } | ||
| pub fn compute_serializable_len_without_length<T, A, F>(elements: &[A]) -> Option<u32> | ||
| where | ||
| T: VarULE + ?Sized, | ||
| A: EncodeAsVarULE<T>, | ||
| F: VarZeroVecFormat, | ||
| { | ||
| let elements_len = elements.len(); | ||
| let Some(elements_len_minus_one) = elements_len.checked_sub(1) else { | ||
| // Empty vec is optimized to an empty byte representation | ||
| return Some(0); | ||
| }; | ||
| let idx_len: u32 = u32::try_from(elements_len_minus_one) | ||
| .ok()? | ||
| .checked_mul(F::INDEX_WIDTH as u32)? | ||
| .checked_add(LENGTH_WIDTH as u32)? | ||
| .checked_add(METADATA_WIDTH as u32)?; | ||
| .checked_mul(F::Index::SIZE as u32)?; | ||
| let data_len: u32 = elements | ||
@@ -604,3 +784,3 @@ .iter() | ||
| if let Some(r) = ret { | ||
| if r >= F::MAX_VALUE { | ||
| if r >= F::Index::MAX_VALUE { | ||
| return None; | ||
@@ -611,1 +791,10 @@ } | ||
| } | ||
| pub fn compute_serializable_len<T, A, F>(elements: &[A]) -> Option<u32> | ||
| where | ||
| T: VarULE + ?Sized, | ||
| A: EncodeAsVarULE<T>, | ||
| F: VarZeroVecFormat, | ||
| { | ||
| compute_serializable_len_without_length::<T, A, F>(elements).map(|x| x + F::Len::SIZE as u32) | ||
| } |
@@ -5,14 +5,15 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::vecs::{Index16, Index32}; | ||
| use crate::{ule::VarULE, VarZeroSlice, VarZeroVec}; | ||
| use databake::*; | ||
| impl<T: VarULE + ?Sized> Bake for VarZeroVec<'_, T> { | ||
| impl<T: VarULE + ?Sized> Bake for VarZeroVec<'_, T, Index16> { | ||
| fn bake(&self, env: &CrateEnv) -> TokenStream { | ||
| env.insert("zerovec"); | ||
| if self.is_empty() { | ||
| quote! { zerovec::VarZeroVec::new() } | ||
| quote! { zerovec::vecs::VarZeroVec16::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::vecs::VarZeroVec16::from_bytes_unchecked(#bytes) } } | ||
| } | ||
@@ -22,11 +23,11 @@ } | ||
| impl<T: VarULE + ?Sized> Bake for &VarZeroSlice<T> { | ||
| impl<T: VarULE + ?Sized> Bake for VarZeroVec<'_, T, Index32> { | ||
| fn bake(&self, env: &CrateEnv) -> TokenStream { | ||
| env.insert("zerovec"); | ||
| if self.is_empty() { | ||
| quote! { zerovec::VarZeroSlice::new_empty() } | ||
| quote! { zerovec::vecs::VarZeroVec32::new() } | ||
| } 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::vecs::VarZeroVec32::from_bytes_unchecked(#bytes) } } | ||
| } | ||
@@ -36,2 +37,60 @@ } | ||
| impl<T: VarULE + ?Sized> BakeSize for VarZeroVec<'_, T, Index16> { | ||
| fn borrows_size(&self) -> usize { | ||
| self.as_bytes().len() | ||
| } | ||
| } | ||
| impl<T: VarULE + ?Sized> BakeSize for VarZeroVec<'_, T, Index32> { | ||
| fn borrows_size(&self) -> usize { | ||
| self.as_bytes().len() | ||
| } | ||
| } | ||
| impl<T: VarULE + ?Sized> Bake for &VarZeroSlice<T, Index16> { | ||
| fn bake(&self, env: &CrateEnv) -> TokenStream { | ||
| env.insert("zerovec"); | ||
| if self.is_empty() { | ||
| quote! { zerovec::vecs::VarZeroSlice16::new_empty() } | ||
| } else { | ||
| let bytes = databake::Bake::bake(&self.as_bytes(), env); | ||
| // Safe because self.as_bytes is a safe input | ||
| quote! { unsafe { zerovec::vecs::VarZeroSlice16::from_bytes_unchecked(#bytes) } } | ||
| } | ||
| } | ||
| } | ||
| impl<T: VarULE + ?Sized> Bake for &VarZeroSlice<T, Index32> { | ||
| fn bake(&self, env: &CrateEnv) -> TokenStream { | ||
| env.insert("zerovec"); | ||
| if self.is_empty() { | ||
| quote! { zerovec::vecs::VarZeroSlice32::new_empty() } | ||
| } else { | ||
| let bytes = databake::Bake::bake(&self.as_bytes(), env); | ||
| // Safe because self.as_bytes is a safe input | ||
| quote! { unsafe { zerovec::vecs::VarZeroSlice32::from_bytes_unchecked(#bytes) } } | ||
| } | ||
| } | ||
| } | ||
| impl<T: VarULE + ?Sized> BakeSize for &VarZeroSlice<T, Index16> { | ||
| fn borrows_size(&self) -> usize { | ||
| if self.is_empty() { | ||
| 0 | ||
| } else { | ||
| self.as_bytes().len() | ||
| } | ||
| } | ||
| } | ||
| impl<T: VarULE + ?Sized> BakeSize for &VarZeroSlice<T, Index32> { | ||
| fn borrows_size(&self) -> usize { | ||
| if self.is_empty() { | ||
| 0 | ||
| } else { | ||
| self.as_bytes().len() | ||
| } | ||
| } | ||
| } | ||
| #[test] | ||
@@ -41,3 +100,4 @@ fn test_baked_vec() { | ||
| VarZeroVec<str>, | ||
| const: crate::VarZeroVec::new(), | ||
| const, | ||
| crate::vecs::VarZeroVec16::new(), | ||
| zerovec | ||
@@ -48,6 +108,5 @@ ); | ||
| VarZeroVec<str>, | ||
| const: unsafe { | ||
| crate::VarZeroVec::from_bytes_unchecked( | ||
| b"\x02\0\0\0\0\0\x05\0helloworld" | ||
| ) | ||
| const, | ||
| unsafe { | ||
| crate::vecs::VarZeroVec16::from_bytes_unchecked(b"\x02\0\0\0\0\0\x05\0helloworld") | ||
| }, | ||
@@ -62,3 +121,4 @@ zerovec | ||
| &VarZeroSlice<str>, | ||
| const: crate::VarZeroSlice::new_empty(), | ||
| const, | ||
| crate::vecs::VarZeroSlice16::new_empty(), | ||
| zerovec | ||
@@ -68,6 +128,5 @@ ); | ||
| &VarZeroSlice<str>, | ||
| const: unsafe { | ||
| crate::VarZeroSlice::from_bytes_unchecked( | ||
| b"\x02\0\0\0\0\0\x05\0helloworld" | ||
| ) | ||
| const, | ||
| unsafe { | ||
| crate::vecs::VarZeroSlice16::from_bytes_unchecked(b"\x02\0\0\0\0\0\x05\0helloworld") | ||
| }, | ||
@@ -74,0 +133,0 @@ zerovec |
@@ -8,2 +8,4 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| pub(crate) mod components; | ||
| pub(crate) mod error; | ||
| pub(crate) mod lengthless; | ||
| pub(crate) mod owned; | ||
@@ -25,4 +27,6 @@ pub(crate) mod slice; | ||
| pub use components::{Index16, Index32, VarZeroVecFormat}; | ||
| pub use components::{Index16, Index32, Index8, VarZeroVecFormat}; | ||
| pub use owned::VarZeroVecOwned; | ||
| pub use error::VarZeroVecFormatError; |
+121
-92
@@ -22,6 +22,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use super::components::LENGTH_WIDTH; | ||
| use super::components::MAX_INDEX; | ||
| use super::components::MAX_LENGTH; | ||
| use super::components::METADATA_WIDTH; | ||
| use super::components::IntegerULE; | ||
@@ -95,6 +92,3 @@ /// A fully-owned [`VarZeroVec`]. This type has no lifetime but has the same | ||
| 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", | ||
| )?, | ||
| .ok_or(F::Index::TOO_LARGE_ERROR)?, | ||
| } | ||
@@ -109,3 +103,3 @@ }) | ||
| // safety: the slice is known to come from a valid parsed VZV | ||
| VarZeroSlice::from_byte_slice_unchecked(slice) | ||
| VarZeroSlice::from_bytes_unchecked(slice) | ||
| } | ||
@@ -120,3 +114,3 @@ } | ||
| marker: PhantomData, | ||
| entire_slice: Vec::with_capacity(capacity * (F::INDEX_WIDTH + 4)), | ||
| entire_slice: Vec::with_capacity(capacity * (F::Index::SIZE + 4)), | ||
| } | ||
@@ -129,3 +123,3 @@ } | ||
| pub(crate) fn reserve(&mut self, capacity: usize) { | ||
| self.entire_slice.reserve(capacity * (F::INDEX_WIDTH + 4)) | ||
| self.entire_slice.reserve(capacity * (F::Index::SIZE + 4)) | ||
| } | ||
@@ -142,9 +136,9 @@ | ||
| let out = if idx == len { | ||
| self.entire_slice.len() - LENGTH_WIDTH - METADATA_WIDTH - (F::INDEX_WIDTH * len) | ||
| self.entire_slice.len() - F::Len::SIZE - (F::Index::SIZE * (len - 1)) | ||
| } else if let Some(idx) = self.index_data(idx) { | ||
| idx.iule_to_usize() | ||
| } else { | ||
| F::rawbytes_to_usize(*self.index_data(idx)) | ||
| 0 | ||
| }; | ||
| debug_assert!( | ||
| out + LENGTH_WIDTH + METADATA_WIDTH + len * F::INDEX_WIDTH <= self.entire_slice.len() | ||
| ); | ||
| debug_assert!(out + F::Len::SIZE + (len - 1) * F::Index::SIZE <= self.entire_slice.len()); | ||
| out | ||
@@ -169,23 +163,28 @@ } | ||
| unsafe fn set_len(&mut self, len: usize) { | ||
| assert!(len <= MAX_LENGTH); | ||
| assert!(len <= F::Len::MAX_VALUE as usize); | ||
| let len_bytes = len.to_le_bytes(); | ||
| self.entire_slice[0..LENGTH_WIDTH].copy_from_slice(&len_bytes[0..LENGTH_WIDTH]); | ||
| let len_ule = F::Len::iule_from_usize(len).expect(F::Len::TOO_LARGE_ERROR); | ||
| self.entire_slice[0..F::Len::SIZE].copy_from_slice(ULE::slice_as_bytes(&[len_ule])); | ||
| // Double-check that the length fits in the length field | ||
| assert_eq!(len_bytes[LENGTH_WIDTH..].iter().sum::<u8>(), 0); | ||
| assert_eq!(len_bytes[F::Len::SIZE..].iter().sum::<u8>(), 0); | ||
| } | ||
| fn index_range(index: usize) -> Range<usize> { | ||
| let pos = LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * index; | ||
| pos..pos + F::INDEX_WIDTH | ||
| /// Get the range in the full data for a given index. Returns None for index 0 | ||
| /// since there is no stored index for it. | ||
| fn index_range(index: usize) -> Option<Range<usize>> { | ||
| let index_minus_one = index.checked_sub(1)?; | ||
| let pos = F::Len::SIZE + F::Index::SIZE * index_minus_one; | ||
| Some(pos..pos + F::Index::SIZE) | ||
| } | ||
| /// Return the raw bytes representing the given `index`. | ||
| /// Return the raw bytes representing the given `index`. Returns None when given index 0 | ||
| /// | ||
| /// ## Safety | ||
| /// The index must be valid, and self.as_encoded_bytes() must be well-formed | ||
| unsafe fn index_data(&self, index: usize) -> &F::RawBytes { | ||
| &F::RawBytes::from_byte_slice_unchecked(&self.entire_slice[Self::index_range(index)])[0] | ||
| unsafe fn index_data(&self, index: usize) -> Option<&F::Index> { | ||
| let index_range = Self::index_range(index)?; | ||
| Some(&F::Index::slice_from_bytes_unchecked(&self.entire_slice[index_range])[0]) | ||
| } | ||
| /// Return the mutable slice representing the given `index`. | ||
| /// Return the mutable slice representing the given `index`. Returns None when given index 0 | ||
| /// | ||
@@ -195,5 +194,5 @@ /// ## Safety | ||
| /// for this index, but need not have its length appropriately set. | ||
| unsafe fn index_data_mut(&mut self, index: usize) -> &mut F::RawBytes { | ||
| unsafe fn index_data_mut(&mut self, index: usize) -> Option<&mut F::Index> { | ||
| let ptr = self.entire_slice.as_mut_ptr(); | ||
| let range = Self::index_range(index); | ||
| let range = Self::index_range(index)?; | ||
@@ -203,5 +202,4 @@ // Doing this instead of just `get_unchecked_mut()` because it's unclear | ||
| // if we know the buffer is larger. | ||
| let data = slice::from_raw_parts_mut(ptr.add(range.start), F::INDEX_WIDTH); | ||
| &mut F::rawbytes_from_byte_slice_unchecked_mut(data)[0] | ||
| let data = slice::from_raw_parts_mut(ptr.add(range.start), F::Index::SIZE); | ||
| Some(&mut F::Index::iule_from_bytes_unchecked_mut(data)[0]) | ||
| } | ||
@@ -211,2 +209,5 @@ | ||
| /// | ||
| /// ## Panics | ||
| /// Should never be called with a starting index of 0, since that index cannot be shifted. | ||
| /// | ||
| /// ## Safety | ||
@@ -216,9 +217,11 @@ /// Adding `amount` to each index after `starting_index` must not result in the slice from becoming malformed. | ||
| unsafe fn shift_indices(&mut self, starting_index: usize, amount: i32) { | ||
| let normalized_idx = starting_index | ||
| .checked_sub(1) | ||
| .expect("shift_indices called with a 0 starting index"); | ||
| let len = self.len(); | ||
| let indices = F::rawbytes_from_byte_slice_unchecked_mut( | ||
| &mut self.entire_slice[LENGTH_WIDTH + METADATA_WIDTH | ||
| ..LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * len], | ||
| let indices = F::Index::iule_from_bytes_unchecked_mut( | ||
| &mut self.entire_slice[F::Len::SIZE..F::Len::SIZE + F::Index::SIZE * (len - 1)], | ||
| ); | ||
| for idx in &mut indices[starting_index..] { | ||
| let mut new_idx = F::rawbytes_to_usize(*idx); | ||
| for idx in &mut indices[normalized_idx..] { | ||
| let mut new_idx = idx.iule_to_usize(); | ||
| if amount > 0 { | ||
@@ -229,3 +232,3 @@ new_idx = new_idx.checked_add(amount.try_into().unwrap()).unwrap(); | ||
| } | ||
| *idx = F::usize_to_rawbytes(new_idx); | ||
| *idx = F::Index::iule_from_usize(new_idx).expect(F::Index::TOO_LARGE_ERROR); | ||
| } | ||
@@ -255,2 +258,5 @@ } | ||
| /// Also updates affected indices and the length. | ||
| /// | ||
| /// `new_size` is the encoded byte size of the element that is going to be inserted | ||
| /// | ||
| /// Returns a slice to the new element data - it doesn't contain uninitialized data but its value is indeterminate. | ||
@@ -260,3 +266,3 @@ /// | ||
| /// - `index` must be a valid index, or, if `shift_type == ShiftType::Insert`, `index == self.len()` is allowed. | ||
| /// - `new_size` musn't result in the data segment growing larger than `F::MAX_VALUE`. | ||
| /// - `new_size` musn't result in the data segment growing larger than `F::Index::MAX_VALUE`. | ||
| unsafe fn shift(&mut self, index: usize, new_size: usize, shift_type: ShiftType) -> &mut [u8] { | ||
@@ -286,5 +292,5 @@ // The format of the encoded data is: | ||
| let index_shift: i64 = match shift_type { | ||
| ShiftType::Insert => F::INDEX_WIDTH as i64, | ||
| ShiftType::Insert => F::Index::SIZE as i64, | ||
| ShiftType::Replace => 0, | ||
| ShiftType::Remove => -(F::INDEX_WIDTH as i64), | ||
| ShiftType::Remove => -(F::Index::SIZE as i64), | ||
| }; | ||
@@ -296,3 +302,3 @@ // The total shift in byte size of the owned slice. | ||
| if shift > 0 { | ||
| if new_slice_len > F::MAX_VALUE as usize { | ||
| if new_slice_len > F::Index::MAX_VALUE as usize { | ||
| panic!( | ||
@@ -311,6 +317,6 @@ "Attempted to grow VarZeroVec to an encoded size that does not fit within the length size used by {}", | ||
| let slice_range = self.entire_slice.as_mut_ptr_range(); | ||
| // The start of the indices buffer | ||
| let indices_start = slice_range.start.add(F::Len::SIZE); | ||
| 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); | ||
| let data_start = indices_start.add((len - 1) * F::Index::SIZE); | ||
| let prev_element_p = | ||
@@ -323,7 +329,8 @@ data_start.add(prev_element.start)..data_start.add(prev_element.end); | ||
| // When replacing: unused. | ||
| let index_range = { | ||
| let index_start = slice_range | ||
| .start | ||
| .add(LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH * index); | ||
| index_start..index_start.add(F::INDEX_WIDTH) | ||
| // Will be None when the affected index is index 0, which is special | ||
| let index_range = if let Some(index_minus_one) = index.checked_sub(1) { | ||
| let index_start = indices_start.add(F::Index::SIZE * index_minus_one); | ||
| Some(index_start..index_start.add(F::Index::SIZE)) | ||
| } else { | ||
| None | ||
| }; | ||
@@ -337,4 +344,12 @@ | ||
| if shift_type == ShiftType::Remove { | ||
| // Move the data before the element back by 4 to remove the index. | ||
| shift_bytes(index_range.end..prev_element_p.start, index_range.start); | ||
| if let Some(ref index_range) = index_range { | ||
| shift_bytes(index_range.end..prev_element_p.start, index_range.start); | ||
| } else { | ||
| // We are removing the first index, so we skip the second index and copy it over. The second index | ||
| // is now zero and unnecessary. | ||
| shift_bytes( | ||
| indices_start.add(F::Index::SIZE)..prev_element_p.start, | ||
| indices_start, | ||
| ) | ||
| } | ||
| } | ||
@@ -352,6 +367,25 @@ | ||
| ShiftType::Insert => { | ||
| // Move data before the element forward by 4 to make space for a new index. | ||
| shift_bytes(index_range.start..prev_element_p.start, index_range.end); | ||
| if let Some(index_range) = index_range { | ||
| // Move data before the element forward by 4 to make space for a new index. | ||
| shift_bytes(index_range.start..prev_element_p.start, index_range.end); | ||
| let index_data = self | ||
| .index_data_mut(index) | ||
| .expect("If index_range is some, index is > 0 and should not panic in index_data_mut"); | ||
| *index_data = F::Index::iule_from_usize(prev_element.start) | ||
| .expect(F::Index::TOO_LARGE_ERROR); | ||
| } else { | ||
| // We are adding a new index 0. There's nothing in the indices array for index 0, but the element | ||
| // that is currently at index 0 will become index 1 and need a value | ||
| // We first shift bytes to make space | ||
| shift_bytes( | ||
| indices_start..prev_element_p.start, | ||
| indices_start.add(F::Index::SIZE), | ||
| ); | ||
| // And then we write a temporary zero to the zeroeth index, which will get shifted later | ||
| let index_data = self | ||
| .index_data_mut(1) | ||
| .expect("Should be able to write to index 1"); | ||
| *index_data = F::Index::iule_from_usize(0).expect("0 is always valid!"); | ||
| } | ||
| *self.index_data_mut(index) = F::usize_to_rawbytes(prev_element.start); | ||
| self.set_len(len + 1); | ||
@@ -362,3 +396,8 @@ index + 1 | ||
| self.set_len(len - 1); | ||
| index | ||
| if index == 0 { | ||
| // We don't need to shift index 0 since index 0 is not stored in the indices buffer | ||
| index + 1 | ||
| } else { | ||
| index | ||
| } | ||
| } | ||
@@ -371,3 +410,2 @@ ShiftType::Replace => index + 1, | ||
| self.entire_slice.set_len(new_slice_len); | ||
| // Shift the affected indices. | ||
@@ -380,5 +418,4 @@ self.shift_indices(first_affected_index, (shift - index_shift) as i32); | ||
| // Return a mut slice to the new element data. | ||
| let element_pos = LENGTH_WIDTH | ||
| + METADATA_WIDTH | ||
| + self.len() * F::INDEX_WIDTH | ||
| let element_pos = F::Len::SIZE | ||
| + (self.len() - 1) * F::Index::SIZE | ||
| + self.element_position_unchecked(index); | ||
@@ -394,30 +431,25 @@ &mut self.entire_slice[element_pos..element_pos + new_size] | ||
| fn verify_integrity(&self) -> bool { | ||
| if self.is_empty() && !self.entire_slice.is_empty() { | ||
| return false; | ||
| if self.is_empty() { | ||
| if self.entire_slice.is_empty() { | ||
| return true; | ||
| } else { | ||
| panic!( | ||
| "VarZeroVecOwned integrity: Found empty VarZeroVecOwned with a nonempty slice" | ||
| ); | ||
| } | ||
| } | ||
| let slice_len = self.entire_slice.len(); | ||
| match slice_len { | ||
| 0 => return true, | ||
| 1..=3 => return false, | ||
| _ => (), | ||
| } | ||
| let len = unsafe { | ||
| RawBytesULE::<LENGTH_WIDTH>::from_byte_slice_unchecked( | ||
| &self.entire_slice[..LENGTH_WIDTH], | ||
| )[0] | ||
| .as_unsigned_int() | ||
| <F::Len as ULE>::slice_from_bytes_unchecked(&self.entire_slice[..F::Len::SIZE])[0] | ||
| .iule_to_usize() | ||
| }; | ||
| if len == 0 { | ||
| // An empty vec must have an empty slice: there is only a single valid byte representation. | ||
| return false; | ||
| panic!("VarZeroVecOwned integrity: Found empty VarZeroVecOwned with a nonempty slice"); | ||
| } | ||
| if slice_len < LENGTH_WIDTH + METADATA_WIDTH + len as usize * F::INDEX_WIDTH { | ||
| // Not enough room for the indices. | ||
| return false; | ||
| if self.entire_slice.len() < F::Len::SIZE + (len - 1) * F::Index::SIZE { | ||
| panic!("VarZeroVecOwned integrity: Not enough room for the indices"); | ||
| } | ||
| let data_len = | ||
| self.entire_slice.len() - LENGTH_WIDTH - METADATA_WIDTH - len as usize * F::INDEX_WIDTH; | ||
| if data_len > MAX_INDEX { | ||
| // The data segment is too long. | ||
| return false; | ||
| let data_len = self.entire_slice.len() - F::Len::SIZE - (len - 1) * F::Index::SIZE; | ||
| if data_len > F::Index::MAX_VALUE as usize { | ||
| panic!("VarZeroVecOwned integrity: Data segment is too long"); | ||
| } | ||
@@ -427,17 +459,14 @@ | ||
| let indices = unsafe { | ||
| F::RawBytes::from_byte_slice_unchecked( | ||
| &self.entire_slice[LENGTH_WIDTH + METADATA_WIDTH | ||
| ..LENGTH_WIDTH + METADATA_WIDTH + len as usize * F::INDEX_WIDTH], | ||
| F::Index::slice_from_bytes_unchecked( | ||
| &self.entire_slice[F::Len::SIZE..F::Len::SIZE + (len - 1) * F::Index::SIZE], | ||
| ) | ||
| }; | ||
| for idx in indices { | ||
| if F::rawbytes_to_usize(*idx) > data_len { | ||
| // Indices must not point past the data segment. | ||
| return false; | ||
| if idx.iule_to_usize() > data_len { | ||
| panic!("VarZeroVecOwned integrity: Indices must not point past the data segment"); | ||
| } | ||
| } | ||
| for window in indices.windows(2) { | ||
| if F::rawbytes_to_usize(window[0]) > F::rawbytes_to_usize(window[1]) { | ||
| // Indices must be in non-decreasing order. | ||
| return false; | ||
| if window[0].iule_to_usize() > window[1].iule_to_usize() { | ||
| panic!("VarZeroVecOwned integrity: Indices must be in non-decreasing order"); | ||
| } | ||
@@ -463,3 +492,3 @@ } | ||
| if len == 0 { | ||
| let header_len = LENGTH_WIDTH + METADATA_WIDTH + F::INDEX_WIDTH; | ||
| let header_len = F::Len::SIZE; // Index array is size 0 for len = 1 | ||
| let cap = header_len + value_len; | ||
@@ -472,3 +501,3 @@ self.entire_slice.resize(cap, 0); | ||
| assert!(value_len < MAX_INDEX); | ||
| assert!(value_len < F::Index::MAX_VALUE as usize); | ||
| unsafe { | ||
@@ -505,3 +534,3 @@ let place = self.shift(index, value_len, ShiftType::Insert); | ||
| assert!(value_len < MAX_INDEX); | ||
| assert!(value_len < F::Index::MAX_VALUE as usize); | ||
| unsafe { | ||
@@ -508,0 +537,0 @@ let place = self.shift(index, value_len, ShiftType::Replace); |
+13
-24
@@ -44,3 +44,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| { | ||
| VarZeroVec::parse_byte_slice(bytes).map_err(de::Error::custom) | ||
| VarZeroVec::parse_bytes(bytes).map_err(de::Error::custom) | ||
| } | ||
@@ -89,3 +89,2 @@ | ||
| T: VarULE + ?Sized, | ||
| Box<T>: Deserialize<'de>, | ||
| F: VarZeroVecFormat, | ||
@@ -103,11 +102,4 @@ 'de: 'a, | ||
| } else { | ||
| let deserialized = VarZeroVec::<'a, T, F>::deserialize(deserializer)?; | ||
| let borrowed = if let VarZeroVec::Borrowed(b) = deserialized { | ||
| b | ||
| } else { | ||
| return Err(de::Error::custom( | ||
| "&VarZeroSlice can only deserialize in zero-copy ways", | ||
| )); | ||
| }; | ||
| Ok(borrowed) | ||
| let bytes = <&[u8]>::deserialize(deserializer)?; | ||
| VarZeroSlice::<T, F>::parse_bytes(bytes).map_err(de::Error::custom) | ||
| } | ||
@@ -196,11 +188,10 @@ } | ||
| 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, | ||
| 100, 111, 108, 111, 114, 113, 117, 117, 120, 108, 111, 114, 101, 109, 32, 105, 112, 115, | ||
| 117, 109, | ||
| 6, 0, 3, 0, 6, 0, 9, 0, 14, 0, 18, 0, 102, 111, 111, 98, 97, 114, 98, 97, 122, 100, 111, | ||
| 108, 111, 114, 113, 117, 117, 120, 108, 111, 114, 101, 109, 32, 105, 112, 115, 117, 109, | ||
| ]; | ||
| const JSON_STR: &str = "[\"foo\",\"bar\",\"baz\",\"dolor\",\"quux\",\"lorem ipsum\"]"; | ||
| const BINCODE_BUF: &[u8] = &[ | ||
| 45, 0, 0, 0, 0, 0, 0, 0, 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, 100, 111, 108, 111, 114, 113, 117, 117, 120, 108, 111, 114, 101, | ||
| 109, 32, 105, 112, 115, 117, 109, | ||
| 41, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, 6, 0, 9, 0, 14, 0, 18, 0, 102, 111, 111, 98, 97, 114, | ||
| 98, 97, 122, 100, 111, 108, 111, 114, 113, 117, 117, 120, 108, 111, 114, 101, 109, 32, 105, | ||
| 112, 115, 117, 109, | ||
| ]; | ||
@@ -211,7 +202,7 @@ | ||
| const NONASCII_BYTES: &[u8] = &[ | ||
| 4, 0, 0, 0, 0, 0, 1, 0, 3, 0, 6, 0, 119, 207, 137, 230, 150, 135, 240, 145, 132, 131, | ||
| 4, 0, 1, 0, 3, 0, 6, 0, 119, 207, 137, 230, 150, 135, 240, 145, 132, 131, | ||
| ]; | ||
| #[test] | ||
| fn test_serde_json() { | ||
| let zerovec_orig: VarZeroVec<str> = VarZeroVec::parse_byte_slice(BYTES).expect("parse"); | ||
| let zerovec_orig: VarZeroVec<str> = VarZeroVec::parse_bytes(BYTES).expect("parse"); | ||
| let json_str = serde_json::to_string(&zerovec_orig).expect("serialize"); | ||
@@ -231,3 +222,3 @@ assert_eq!(JSON_STR, json_str); | ||
| fn test_serde_bincode() { | ||
| let zerovec_orig: VarZeroVec<str> = VarZeroVec::parse_byte_slice(BYTES).expect("parse"); | ||
| let zerovec_orig: VarZeroVec<str> = VarZeroVec::parse_bytes(BYTES).expect("parse"); | ||
| let bincode_buf = bincode::serialize(&zerovec_orig).expect("serialize"); | ||
@@ -243,4 +234,3 @@ assert_eq!(BINCODE_BUF, bincode_buf); | ||
| fn test_vzv_borrowed() { | ||
| let zerovec_orig: &VarZeroSlice<str> = | ||
| VarZeroSlice::parse_byte_slice(BYTES).expect("parse"); | ||
| let zerovec_orig: &VarZeroSlice<str> = VarZeroSlice::parse_bytes(BYTES).expect("parse"); | ||
| let bincode_buf = bincode::serialize(&zerovec_orig).expect("serialize"); | ||
@@ -260,4 +250,3 @@ assert_eq!(BINCODE_BUF, bincode_buf); | ||
| .collect::<Vec<_>>(); | ||
| let mut zerovec: VarZeroVec<str> = | ||
| VarZeroVec::parse_byte_slice(NONASCII_BYTES).expect("parse"); | ||
| let mut zerovec: VarZeroVec<str> = VarZeroVec::parse_bytes(NONASCII_BYTES).expect("parse"); | ||
| assert_eq!(zerovec.to_vec(), src_vec); | ||
@@ -264,0 +253,0 @@ let bincode_buf = bincode::serialize(&zerovec).expect("serialize"); |
+16
-53
@@ -6,2 +6,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use super::components::VarZeroVecComponents; | ||
| use super::vec::VarZeroVecInner; | ||
| use super::*; | ||
@@ -76,3 +77,3 @@ use crate::ule::*; | ||
| /// let vzv_from_bytes: VarZeroVec<VarZeroSlice<VarZeroSlice<str>>> = | ||
| /// VarZeroVec::parse_byte_slice(bytes).unwrap(); | ||
| /// VarZeroVec::parse_bytes(bytes).unwrap(); | ||
| /// assert_eq!(vzv_from_bytes, vzv_all); | ||
@@ -140,3 +141,2 @@ /// ``` | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -148,3 +148,2 @@ /// | ||
| /// assert_eq!(vec.len(), 4); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -160,3 +159,2 @@ pub fn len(&self) -> usize { | ||
| /// ``` | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -168,3 +166,2 @@ /// | ||
| /// assert!(vec.is_empty()); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -180,3 +177,2 @@ pub fn is_empty(&self) -> bool { | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -192,3 +188,2 @@ /// | ||
| /// assert_eq!(iter_results[3], "quux"); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -204,3 +199,2 @@ pub fn iter<'b>(&'b self) -> impl Iterator<Item = &'b T> { | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -217,3 +211,2 @@ /// | ||
| /// assert_eq!(vec.get(4), None); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -233,3 +226,2 @@ pub fn get(&self, idx: usize) -> Option<&T> { | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -247,3 +239,2 @@ /// | ||
| /// } | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -261,3 +252,3 @@ pub unsafe fn get_unchecked(&self, idx: usize) -> &T { | ||
| /// | ||
| /// The bytes can be passed back to [`Self::parse_byte_slice()`]. | ||
| /// The bytes can be passed back to [`Self::parse_bytes()`]. | ||
| /// | ||
@@ -269,3 +260,2 @@ /// To take the bytes as a vector, see [`VarZeroVec::into_bytes()`]. | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -276,5 +266,3 @@ /// | ||
| /// | ||
| /// assert_eq!(vzv, VarZeroVec::parse_byte_slice(vzv.as_bytes()).unwrap()); | ||
| /// | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// assert_eq!(vzv, VarZeroVec::parse_bytes(vzv.as_bytes()).unwrap()); | ||
| /// ``` | ||
@@ -291,3 +279,3 @@ #[inline] | ||
| pub const fn as_varzerovec<'a>(&'a self) -> VarZeroVec<'a, T, F> { | ||
| VarZeroVec::Borrowed(self) | ||
| VarZeroVec(VarZeroVecInner::Borrowed(self)) | ||
| } | ||
@@ -298,20 +286,5 @@ | ||
| /// Slices of the right format can be obtained via [`VarZeroSlice::as_bytes()`] | ||
| pub fn parse_byte_slice<'a>(slice: &'a [u8]) -> Result<&'a Self, ZeroVecError> { | ||
| <Self as VarULE>::parse_byte_slice(slice) | ||
| pub fn parse_bytes<'a>(slice: &'a [u8]) -> Result<&'a Self, UleError> { | ||
| <Self as VarULE>::parse_bytes(slice) | ||
| } | ||
| /// Convert a `bytes` array known to represent a `VarZeroSlice` to a mutable reference to a `VarZeroSlice` | ||
| /// | ||
| /// # Safety | ||
| /// - `bytes` must be a valid sequence of bytes for this VarZeroVec | ||
| pub(crate) unsafe fn from_byte_slice_unchecked_mut(bytes: &mut [u8]) -> &mut Self { | ||
| // self is really just a wrapper around a byte slice | ||
| mem::transmute(bytes) | ||
| } | ||
| pub(crate) unsafe fn get_bytes_at_mut(&mut self, idx: usize) -> &mut [u8] { | ||
| let range = self.as_components().get_range(idx); | ||
| #[allow(clippy::indexing_slicing)] // get_range() is known to return in-bounds ranges | ||
| &mut self.entire_slice[range] | ||
| } | ||
| } | ||
@@ -332,3 +305,2 @@ | ||
| /// ``` | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -341,3 +313,2 @@ /// | ||
| /// assert_eq!(vec.binary_search("e"), Err(2)); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -361,5 +332,3 @@ /// | ||
| /// ``` | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
| /// | ||
| /// let strings = vec!["a", "b", "f", "g", "m", "n", "q"]; | ||
@@ -383,3 +352,2 @@ /// let vec = VarZeroVec::<str>::from(&strings); | ||
| /// assert_eq!(vec.binary_search_in_range("x", 0..200), None); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -410,5 +378,3 @@ /// | ||
| /// ``` | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
| /// | ||
| /// let strings = vec!["a", "b", "f", "g"]; | ||
@@ -419,3 +385,2 @@ /// let vec = VarZeroVec::<str>::from(&strings); | ||
| /// assert_eq!(vec.binary_search_by(|probe| probe.cmp("e")), Err(2)); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -439,5 +404,3 @@ /// | ||
| /// ``` | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
| /// | ||
| /// let strings = vec!["a", "b", "f", "g", "m", "n", "q"]; | ||
@@ -482,3 +445,2 @@ /// let vec = VarZeroVec::<str>::from(&strings); | ||
| /// assert_eq!(vec.binary_search_in_range_by(|v| v.cmp("x"), 0..200), None); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -501,14 +463,15 @@ /// | ||
| // `[u8]` slice which satisfies this invariant) | ||
| // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. | ||
| // 6. `as_byte_slice()` is equivalent to a regular transmute of the underlying data | ||
| // 3. The impl of `validate_bytes()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_bytes_unchecked()` returns a reference to the same data. | ||
| // 6. `as_bytes()` is equivalent to a regular transmute of the underlying data | ||
| // 7. VarZeroSlice byte equality is semantic equality (relying on the guideline of the underlying VarULE type) | ||
| unsafe impl<T: VarULE + ?Sized + 'static, F: VarZeroVecFormat> VarULE for VarZeroSlice<T, F> { | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| let _: VarZeroVecComponents<T, F> = VarZeroVecComponents::parse_byte_slice(bytes)?; | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| let _: VarZeroVecComponents<T, F> = | ||
| VarZeroVecComponents::parse_bytes(bytes).map_err(|_| UleError::parse::<Self>())?; | ||
| Ok(()) | ||
| } | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| // self is really just a wrapper around a byte slice | ||
@@ -518,3 +481,3 @@ mem::transmute(bytes) | ||
| fn as_byte_slice(&self) -> &[u8] { | ||
| fn as_bytes(&self) -> &[u8] { | ||
| &self.entire_slice | ||
@@ -521,0 +484,0 @@ } |
+61
-82
@@ -59,3 +59,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// use zerovec::VarZeroVec; | ||
@@ -85,3 +84,2 @@ /// | ||
| /// assert_eq!(deserialized.strings, &*strings); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -92,3 +90,2 @@ /// | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// use zerovec::VarZeroVec; | ||
@@ -123,4 +120,2 @@ /// use zerovec::ZeroSlice; | ||
| /// assert_eq!(deserialized.vecs[1], *numbers[1]); | ||
| /// | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -136,45 +131,21 @@ /// | ||
| /// | ||
| /// - 4 bytes for `length` (interpreted as a little-endian u32) | ||
| /// - `4 * length` bytes of `indices` (interpreted as little-endian u32) | ||
| /// - 2 bytes for `length` (interpreted as a little-endian u16) | ||
| /// - `2 * (length - 1)` bytes of `indices` (interpreted as little-endian u16s) | ||
| /// - Remaining bytes for actual `data` | ||
| /// | ||
| /// Each element in the `indices` array points to the starting index of its corresponding | ||
| /// data part in the `data` list. The ending index can be calculated from the starting index | ||
| /// of the next element (or the length of the slice if dealing with the last element). | ||
| /// The format is tweakable by setting the `F` parameter, by default it uses u16 indices and lengths but other | ||
| /// `VarZeroVecFormat` types can set other sizes. | ||
| /// | ||
| /// Each element in the `indices` array points to the ending index of its corresponding | ||
| /// data part in the `data` list. The starting index can be calculated from the ending index | ||
| /// of the next element (or 0 for the first element). The last ending index, not stored in the array, is | ||
| /// the length of the `data` segment. | ||
| /// | ||
| /// See [the design doc](https://github.com/unicode-org/icu4x/blob/main/utils/zerovec/design_doc.md) for more details. | ||
| /// | ||
| /// [`ule`]: crate::ule | ||
| #[non_exhaustive] | ||
| pub enum VarZeroVec<'a, T: ?Sized, F = Index16> { | ||
| /// An allocated VarZeroVec, allowing for mutations. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use zerovec::VarZeroVec; | ||
| /// | ||
| /// let mut vzv = VarZeroVec::<str>::default(); | ||
| /// vzv.make_mut().push("foo"); | ||
| /// vzv.make_mut().push("bar"); | ||
| /// assert!(matches!(vzv, VarZeroVec::Owned(_))); | ||
| /// ``` | ||
| pub struct VarZeroVec<'a, T: ?Sized, F = Index16>(pub(crate) VarZeroVecInner<'a, T, F>); | ||
| pub(crate) enum VarZeroVecInner<'a, T: ?Sized, F = Index16> { | ||
| Owned(VarZeroVecOwned<T, F>), | ||
| /// A borrowed VarZeroVec, requiring no allocations. | ||
| /// | ||
| /// If a mutating operation is invoked on VarZeroVec, the Borrowed is converted to Owned. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use zerovec::VarZeroVec; | ||
| /// | ||
| /// let bytes = &[ | ||
| /// 4, 0, 0, 0, 0, 0, 1, 0, 3, 0, 6, 0, 119, 207, 137, 230, 150, 135, 240, | ||
| /// 145, 132, 131, | ||
| /// ]; | ||
| /// | ||
| /// let vzv: VarZeroVec<str> = VarZeroVec::parse_byte_slice(bytes).unwrap(); | ||
| /// assert!(matches!(vzv, VarZeroVec::Borrowed(_))); | ||
| /// ``` | ||
| Borrowed(&'a VarZeroSlice<T, F>), | ||
@@ -185,5 +156,5 @@ } | ||
| fn clone(&self) -> Self { | ||
| match *self { | ||
| VarZeroVec::Owned(ref o) => o.clone().into(), | ||
| VarZeroVec::Borrowed(b) => b.into(), | ||
| match self.0 { | ||
| VarZeroVecInner::Owned(ref o) => o.clone().into(), | ||
| VarZeroVecInner::Borrowed(b) => b.into(), | ||
| } | ||
@@ -205,3 +176,3 @@ } | ||
| fn from(other: VarZeroVecOwned<T, F>) -> Self { | ||
| VarZeroVec::Owned(other) | ||
| Self(VarZeroVecInner::Owned(other)) | ||
| } | ||
@@ -212,3 +183,3 @@ } | ||
| fn from(other: &'a VarZeroSlice<T, F>) -> Self { | ||
| VarZeroVec::Borrowed(other) | ||
| Self(VarZeroVecInner::Borrowed(other)) | ||
| } | ||
@@ -222,5 +193,5 @@ } | ||
| fn from(other: VarZeroVec<'a, T, F>) -> Self { | ||
| match other { | ||
| VarZeroVec::Owned(o) => o, | ||
| VarZeroVec::Borrowed(b) => b.into(), | ||
| match other.0 { | ||
| VarZeroVecInner::Owned(o) => o, | ||
| VarZeroVecInner::Borrowed(b) => b.into(), | ||
| } | ||
@@ -257,3 +228,3 @@ } | ||
| pub const fn new() -> Self { | ||
| Self::Borrowed(VarZeroSlice::new_empty()) | ||
| Self(VarZeroVecInner::Borrowed(VarZeroSlice::new_empty())) | ||
| } | ||
@@ -268,3 +239,2 @@ | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -279,8 +249,7 @@ /// | ||
| /// assert_eq!(&vec[3], "quux"); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
| pub fn parse_byte_slice(slice: &'a [u8]) -> Result<Self, ZeroVecError> { | ||
| let borrowed = VarZeroSlice::<T, F>::parse_byte_slice(slice)?; | ||
| pub fn parse_bytes(slice: &'a [u8]) -> Result<Self, UleError> { | ||
| let borrowed = VarZeroSlice::<T, F>::parse_bytes(slice)?; | ||
| Ok(VarZeroVec::Borrowed(borrowed)) | ||
| Ok(Self(VarZeroVecInner::Borrowed(borrowed))) | ||
| } | ||
@@ -294,3 +263,6 @@ | ||
| pub const unsafe fn from_bytes_unchecked(bytes: &'a [u8]) -> Self { | ||
| Self::Borrowed(core::mem::transmute::<&[u8], &VarZeroSlice<T, F>>(bytes)) | ||
| Self(VarZeroVecInner::Borrowed(core::mem::transmute::< | ||
| &[u8], | ||
| &VarZeroSlice<T, F>, | ||
| >(bytes))) | ||
| } | ||
@@ -304,5 +276,3 @@ | ||
| /// ```rust,ignore | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
| /// | ||
| /// let strings = vec!["foo", "bar", "baz", "quux"]; | ||
@@ -320,3 +290,2 @@ /// let mut vec = VarZeroVec::<str>::from(&strings); | ||
| /// assert_eq!(&vec[4], "lorem ipsum"); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
@@ -327,5 +296,5 @@ // | ||
| pub fn make_mut(&mut self) -> &mut VarZeroVecOwned<T, F> { | ||
| match self { | ||
| VarZeroVec::Owned(ref mut vec) => vec, | ||
| VarZeroVec::Borrowed(slice) => { | ||
| match self.0 { | ||
| VarZeroVecInner::Owned(ref mut vec) => vec, | ||
| VarZeroVecInner::Borrowed(slice) => { | ||
| let new_self = VarZeroVecOwned::from_slice(slice); | ||
@@ -344,3 +313,2 @@ *self = new_self.into(); | ||
| /// ``` | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -354,8 +322,7 @@ /// | ||
| /// let owned = vec.into_owned(); | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
| pub fn into_owned(mut self) -> VarZeroVec<'static, T, F> { | ||
| self.make_mut(); | ||
| match self { | ||
| VarZeroVec::Owned(vec) => vec.into(), | ||
| match self.0 { | ||
| VarZeroVecInner::Owned(vec) => vec.into(), | ||
| _ => unreachable!(), | ||
@@ -367,5 +334,5 @@ } | ||
| pub fn as_slice(&self) -> &VarZeroSlice<T, F> { | ||
| match *self { | ||
| VarZeroVec::Owned(ref owned) => owned, | ||
| VarZeroVec::Borrowed(b) => b, | ||
| match self.0 { | ||
| VarZeroVecInner::Owned(ref owned) => owned, | ||
| VarZeroVecInner::Borrowed(b) => b, | ||
| } | ||
@@ -377,3 +344,3 @@ } | ||
| /// | ||
| /// The bytes can be passed back to [`Self::parse_byte_slice()`]. | ||
| /// The bytes can be passed back to [`Self::parse_bytes()`]. | ||
| /// | ||
@@ -385,3 +352,2 @@ /// To get a reference to the bytes without moving, see [`VarZeroSlice::as_bytes()`]. | ||
| /// ```rust | ||
| /// # use zerovec::ule::ZeroVecError; | ||
| /// # use zerovec::VarZeroVec; | ||
@@ -392,11 +358,10 @@ /// | ||
| /// | ||
| /// let mut borrowed: VarZeroVec<str> = VarZeroVec::parse_byte_slice(&bytes)?; | ||
| /// let mut borrowed: VarZeroVec<str> = | ||
| /// VarZeroVec::parse_bytes(&bytes).unwrap(); | ||
| /// assert_eq!(borrowed, &*strings); | ||
| /// | ||
| /// # Ok::<(), ZeroVecError>(()) | ||
| /// ``` | ||
| pub fn into_bytes(self) -> Vec<u8> { | ||
| match self { | ||
| VarZeroVec::Owned(vec) => vec.into_bytes(), | ||
| VarZeroVec::Borrowed(vec) => vec.as_bytes().to_vec(), | ||
| match self.0 { | ||
| VarZeroVecInner::Owned(vec) => vec.into_bytes(), | ||
| VarZeroVecInner::Borrowed(vec) => vec.as_bytes().to_vec(), | ||
| } | ||
@@ -409,5 +374,5 @@ } | ||
| pub fn is_owned(&self) -> bool { | ||
| match self { | ||
| VarZeroVec::Owned(..) => true, | ||
| VarZeroVec::Borrowed(..) => false, | ||
| match self.0 { | ||
| VarZeroVecInner::Owned(..) => true, | ||
| VarZeroVecInner::Borrowed(..) => false, | ||
| } | ||
@@ -538,2 +503,16 @@ } | ||
| ); | ||
| use crate::map::MutableZeroVecLike; | ||
| let mut vzv = VarZeroVec::<str>::from(&["hello", "world"][..]); | ||
| assert_eq!(vzv.len(), 2); | ||
| assert!(!vzv.as_bytes().is_empty()); | ||
| vzv.zvl_remove(0); | ||
| assert_eq!(vzv.len(), 1); | ||
| assert!(!vzv.as_bytes().is_empty()); | ||
| vzv.zvl_remove(0); | ||
| assert_eq!(vzv.len(), 0); | ||
| assert!(vzv.as_bytes().is_empty()); | ||
| vzv.zvl_insert(0, "something"); | ||
| assert_eq!(vzv.len(), 1); | ||
| assert!(!vzv.as_bytes().is_empty()); | ||
| } | ||
@@ -544,5 +523,5 @@ | ||
| assert_eq!( | ||
| VarZeroVec::<str>::parse_byte_slice(&[0, 0, 0, 0]).unwrap(), | ||
| VarZeroVec::<str>::parse_byte_slice(&[]).unwrap() | ||
| VarZeroVec::<str>::parse_bytes(&[0, 0, 0, 0]).unwrap(), | ||
| VarZeroVec::<str>::parse_bytes(&[]).unwrap() | ||
| ); | ||
| } |
+30
-89
@@ -12,3 +12,2 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::flexzerovec::FlexZeroVec; | ||
| use crate::map::ZeroMapBorrowed; | ||
@@ -24,3 +23,3 @@ use crate::map::ZeroMapKV; | ||
| /// This impl requires enabling the optional `yoke` Cargo feature of the `zerovec` crate | ||
| unsafe impl<'a, T: 'static + AsULE + ?Sized> Yokeable<'a> for ZeroVec<'static, T> { | ||
| unsafe impl<'a, T: 'static + AsULE> Yokeable<'a> for ZeroVec<'static, T> { | ||
| type Output = ZeroVec<'a, T>; | ||
@@ -79,31 +78,3 @@ #[inline] | ||
| // 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::<Self::Output>() == mem::size_of::<Self>()); | ||
| let from = mem::ManuallyDrop::new(from); | ||
| let ptr: *const Self = (&*from as *const Self::Output).cast(); | ||
| ptr::read(ptr) | ||
| } | ||
| #[inline] | ||
| fn transform_mut<F>(&'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)] | ||
@@ -300,3 +271,3 @@ unsafe impl<'a, K, V> Yokeable<'a> for ZeroMap<'static, K, V> | ||
| use super::*; | ||
| use crate::{vecs::FlexZeroSlice, VarZeroSlice, ZeroSlice}; | ||
| use crate::{VarZeroSlice, ZeroSlice}; | ||
| use databake::*; | ||
@@ -309,3 +280,4 @@ | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| struct DeriveTest_ZeroVec<'data> { | ||
@@ -317,3 +289,2 @@ #[cfg_attr(feature = "serde", serde(borrow))] | ||
| #[test] | ||
| #[ignore] // https://github.com/rust-lang/rust/issues/98906 | ||
| fn bake_ZeroVec() { | ||
@@ -331,3 +302,4 @@ test_bake!( | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| struct DeriveTest_ZeroSlice<'data> { | ||
@@ -351,41 +323,4 @@ #[cfg_attr(feature = "serde", serde(borrow))] | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), 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), 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), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| struct DeriveTest_VarZeroVec<'data> { | ||
@@ -401,3 +336,3 @@ #[cfg_attr(feature = "serde", serde(borrow))] | ||
| crate::yoke_impls::test::DeriveTest_VarZeroVec { | ||
| _data: crate::VarZeroVec::new(), | ||
| _data: crate::vecs::VarZeroVec16::new(), | ||
| }, | ||
@@ -410,3 +345,4 @@ zerovec, | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| struct DeriveTest_VarZeroSlice<'data> { | ||
@@ -422,3 +358,3 @@ #[cfg_attr(feature = "serde", serde(borrow))] | ||
| crate::yoke_impls::test::DeriveTest_VarZeroSlice { | ||
| _data: crate::VarZeroSlice::new_empty() | ||
| _data: crate::vecs::VarZeroSlice16::new_empty() | ||
| }, | ||
@@ -431,3 +367,4 @@ zerovec, | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| #[yoke(prove_covariance_manually)] | ||
@@ -447,4 +384,4 @@ struct DeriveTest_ZeroMap<'data> { | ||
| crate::ZeroMap::from_parts_unchecked( | ||
| crate::VarZeroVec::new(), | ||
| crate::VarZeroVec::new(), | ||
| crate::vecs::VarZeroVec16::new(), | ||
| crate::vecs::VarZeroVec16::new(), | ||
| ) | ||
@@ -459,3 +396,4 @@ }, | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| #[yoke(prove_covariance_manually)] | ||
@@ -475,4 +413,4 @@ struct DeriveTest_ZeroMapBorrowed<'data> { | ||
| crate::maps::ZeroMapBorrowed::from_parts_unchecked( | ||
| crate::VarZeroSlice::new_empty(), | ||
| crate::VarZeroSlice::new_empty(), | ||
| crate::vecs::VarZeroSlice16::new_empty(), | ||
| crate::vecs::VarZeroSlice16::new_empty(), | ||
| ) | ||
@@ -487,3 +425,4 @@ }, | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| #[yoke(prove_covariance_manually)] | ||
@@ -503,4 +442,4 @@ struct DeriveTest_ZeroMapWithULE<'data> { | ||
| crate::ZeroMap::from_parts_unchecked( | ||
| crate::VarZeroVec::new(), | ||
| crate::VarZeroVec::new(), | ||
| crate::vecs::VarZeroVec16::new(), | ||
| crate::vecs::VarZeroVec16::new(), | ||
| ) | ||
@@ -515,3 +454,4 @@ }, | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| #[yoke(prove_covariance_manually)] | ||
@@ -534,3 +474,3 @@ struct DeriveTest_ZeroMap2d<'data> { | ||
| crate::ZeroVec::new(), | ||
| crate::VarZeroVec::new(), | ||
| crate::vecs::VarZeroVec16::new(), | ||
| ) | ||
@@ -545,3 +485,4 @@ }, | ||
| #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake), databake(path = zerovec::yoke_impls::test))] | ||
| #[cfg_attr(feature = "databake", derive(databake::Bake))] | ||
| #[cfg_attr(feature = "databake", databake(path = zerovec::yoke_impls::test))] | ||
| #[yoke(prove_covariance_manually)] | ||
@@ -564,3 +505,3 @@ struct DeriveTest_ZeroMap2dBorrowed<'data> { | ||
| crate::ZeroSlice::new_empty(), | ||
| crate::VarZeroSlice::new_empty(), | ||
| crate::vecs::VarZeroSlice16::new_empty(), | ||
| ) | ||
@@ -567,0 +508,0 @@ }, |
@@ -7,3 +7,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use crate::ule::*; | ||
| use crate::vecs::{FlexZeroSlice, FlexZeroVec}; | ||
| use crate::vecs::VarZeroVecFormat; | ||
| use crate::{VarZeroSlice, VarZeroVec, ZeroMap, ZeroMap2d, ZeroSlice, ZeroVec}; | ||
@@ -14,3 +14,3 @@ use zerofrom::ZeroFrom; | ||
| where | ||
| T: 'static + AsULE + ?Sized, | ||
| T: 'static + AsULE, | ||
| { | ||
@@ -25,3 +25,3 @@ #[inline] | ||
| where | ||
| T: 'static + AsULE + ?Sized, | ||
| T: 'static + AsULE, | ||
| { | ||
@@ -36,3 +36,3 @@ #[inline] | ||
| where | ||
| T: 'static + AsULE + ?Sized, | ||
| T: 'static + AsULE, | ||
| { | ||
@@ -45,24 +45,3 @@ #[inline] | ||
| 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<T>> for VarZeroVec<'zf, T> | ||
| impl<'zf, T, F: VarZeroVecFormat> ZeroFrom<'zf, VarZeroSlice<T, F>> for VarZeroVec<'zf, T, F> | ||
| where | ||
@@ -72,3 +51,3 @@ T: 'static + VarULE + ?Sized, | ||
| #[inline] | ||
| fn zero_from(other: &'zf VarZeroSlice<T>) -> Self { | ||
| fn zero_from(other: &'zf VarZeroSlice<T, F>) -> Self { | ||
| other.into() | ||
@@ -78,3 +57,3 @@ } | ||
| impl<'zf, T> ZeroFrom<'zf, VarZeroVec<'_, T>> for VarZeroVec<'zf, T> | ||
| impl<'zf, T, F: VarZeroVecFormat> ZeroFrom<'zf, VarZeroVec<'_, T, F>> for VarZeroVec<'zf, T, F> | ||
| where | ||
@@ -84,3 +63,3 @@ T: 'static + VarULE + ?Sized, | ||
| #[inline] | ||
| fn zero_from(other: &'zf VarZeroVec<'_, T>) -> Self { | ||
| fn zero_from(other: &'zf VarZeroVec<'_, T, F>) -> Self { | ||
| other.as_slice().into() | ||
@@ -87,0 +66,0 @@ } |
+21
-20
@@ -9,6 +9,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| impl<T> Bake for ZeroVec<'_, T> | ||
| where | ||
| T: AsULE + ?Sized + Bake, | ||
| { | ||
| impl<T: AsULE> Bake for ZeroVec<'_, T> { | ||
| fn bake(&self, env: &CrateEnv) -> TokenStream { | ||
@@ -25,6 +22,9 @@ env.insert("zerovec"); | ||
| impl<T> Bake for &ZeroSlice<T> | ||
| where | ||
| T: AsULE + ?Sized, | ||
| { | ||
| impl<T: AsULE> BakeSize for ZeroVec<'_, T> { | ||
| fn borrows_size(&self) -> usize { | ||
| self.as_bytes().len() | ||
| } | ||
| } | ||
| impl<T: AsULE> Bake for &ZeroSlice<T> { | ||
| fn bake(&self, env: &CrateEnv) -> TokenStream { | ||
@@ -41,16 +41,17 @@ env.insert("zerovec"); | ||
| impl<T: AsULE> BakeSize for &ZeroSlice<T> { | ||
| fn borrows_size(&self) -> usize { | ||
| self.as_bytes().len() | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_baked_vec() { | ||
| test_bake!(ZeroVec<u32>, const, crate::ZeroVec::new(), zerovec); | ||
| test_bake!( | ||
| ZeroVec<u32>, | ||
| const: crate::ZeroVec::new(), | ||
| const, | ||
| unsafe { crate::ZeroVec::from_bytes_unchecked(b"\x02\x01\0\x16\0M\x01\\") }, | ||
| zerovec | ||
| ); | ||
| test_bake!( | ||
| ZeroVec<u32>, | ||
| const: unsafe { | ||
| crate::ZeroVec::from_bytes_unchecked(b"\x02\x01\0\x16\0M\x01\\") | ||
| }, | ||
| zerovec | ||
| ); | ||
| } | ||
@@ -62,3 +63,4 @@ | ||
| &ZeroSlice<u32>, | ||
| const: crate::ZeroSlice::new_empty(), | ||
| const, | ||
| crate::ZeroSlice::new_empty(), | ||
| zerovec | ||
@@ -68,7 +70,6 @@ ); | ||
| &ZeroSlice<u32>, | ||
| const: unsafe { | ||
| crate::ZeroSlice::from_bytes_unchecked(b"\x02\x01\0\x16\0M\x01\\") | ||
| }, | ||
| const, | ||
| unsafe { crate::ZeroSlice::from_bytes_unchecked(b"\x02\x01\0\x16\0M\x01\\") }, | ||
| zerovec | ||
| ); | ||
| } |
+70
-76
@@ -25,3 +25,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| use core::ops::Deref; | ||
| use core::ptr::{self, NonNull}; | ||
| use core::ptr::NonNull; | ||
@@ -113,4 +113,3 @@ /// A zero-copy, byte-aligned vector for fixed-width types. | ||
| fn deref(&self) -> &Self::Target { | ||
| let slice: &[T::ULE] = self.vector.as_slice(); | ||
| ZeroSlice::from_ule_slice(slice) | ||
| self.as_slice() | ||
| } | ||
@@ -198,3 +197,3 @@ } | ||
| fn as_ref(&self) -> &ZeroSlice<T> { | ||
| self.deref() | ||
| self.as_slice() | ||
| } | ||
@@ -212,7 +211,7 @@ } | ||
| impl<T> Eq for ZeroVec<'_, T> where T: AsULE + Eq + ?Sized {} | ||
| impl<T> Eq for ZeroVec<'_, T> where T: AsULE + Eq {} | ||
| impl<'a, 'b, T> PartialEq<ZeroVec<'b, T>> for ZeroVec<'a, T> | ||
| where | ||
| T: AsULE + PartialEq + ?Sized, | ||
| T: AsULE + PartialEq, | ||
| { | ||
@@ -228,3 +227,3 @@ #[inline] | ||
| where | ||
| T: AsULE + PartialEq + ?Sized, | ||
| T: AsULE + PartialEq, | ||
| { | ||
@@ -239,3 +238,3 @@ #[inline] | ||
| where | ||
| T: AsULE + PartialEq + ?Sized, | ||
| T: AsULE + PartialEq, | ||
| { | ||
@@ -285,6 +284,3 @@ #[inline] | ||
| impl<'a, T> ZeroVec<'a, T> | ||
| where | ||
| T: AsULE + ?Sized, | ||
| { | ||
| impl<'a, T: AsULE> ZeroVec<'a, T> { | ||
| /// Creates a new, borrowed, empty `ZeroVec<T>`. | ||
@@ -323,12 +319,9 @@ /// | ||
| let ptr = mem::ManuallyDrop::new(vec).as_mut_ptr(); | ||
| // Note: starting in 1.70 we can use NonNull::slice_from_raw_parts | ||
| let slice = ptr::slice_from_raw_parts_mut(ptr, len); | ||
| // Safety: `ptr` comes from Vec::as_mut_ptr, which says: | ||
| // "Returns an unsafe mutable pointer to the vector’s buffer, | ||
| // or a dangling raw pointer valid for zero sized reads" | ||
| let ptr = unsafe { NonNull::new_unchecked(ptr) }; | ||
| let buf = NonNull::slice_from_raw_parts(ptr, len); | ||
| Self { | ||
| vector: EyepatchHackVector { | ||
| // Safety: `ptr` comes from Vec::as_mut_ptr, which says: | ||
| // "Returns an unsafe mutable pointer to the vector’s buffer, | ||
| // or a dangling raw pointer valid for zero sized reads" | ||
| buf: unsafe { NonNull::new_unchecked(slice) }, | ||
| capacity, | ||
| }, | ||
| vector: EyepatchHackVector { buf, capacity }, | ||
| marker: PhantomData, | ||
@@ -362,3 +355,3 @@ } | ||
| /// This function is infallible for built-in integer types, but fallible for other types, | ||
| /// such as `char`. For more information, see [`ULE::parse_byte_slice`]. | ||
| /// such as `char`. For more information, see [`ULE::parse_bytes_to_slice`]. | ||
| /// | ||
@@ -379,3 +372,3 @@ /// The bytes within the byte buffer must remain constant for the life of the ZeroVec. | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -385,4 +378,4 @@ /// assert!(!zerovec.is_owned()); | ||
| /// ``` | ||
| pub fn parse_byte_slice(bytes: &'a [u8]) -> Result<Self, ZeroVecError> { | ||
| let slice: &'a [T::ULE] = T::ULE::parse_byte_slice(bytes)?; | ||
| pub fn parse_bytes(bytes: &'a [u8]) -> Result<Self, UleError> { | ||
| let slice: &'a [T::ULE] = T::ULE::parse_bytes_to_slice(bytes)?; | ||
| Ok(Self::new_borrowed(slice)) | ||
@@ -417,3 +410,3 @@ } | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// let zv_bytes = zerovec.into_bytes(); | ||
@@ -440,7 +433,7 @@ /// | ||
| Cow::Borrowed(slice) => { | ||
| let bytes: &'a [u8] = T::ULE::as_byte_slice(slice); | ||
| let bytes: &'a [u8] = T::ULE::slice_as_bytes(slice); | ||
| ZeroVec::new_borrowed(bytes) | ||
| } | ||
| Cow::Owned(vec) => { | ||
| let bytes = Vec::from(T::ULE::as_byte_slice(&vec)); | ||
| let bytes = Vec::from(T::ULE::slice_as_bytes(&vec)); | ||
| ZeroVec::new_owned(bytes) | ||
@@ -451,2 +444,12 @@ } | ||
| /// Returns this [`ZeroVec`] as a [`ZeroSlice`]. | ||
| /// | ||
| /// To get a reference with a longer lifetime from a borrowed [`ZeroVec`], | ||
| /// use [`ZeroVec::as_maybe_borrowed`]. | ||
| #[inline] | ||
| pub const fn as_slice(&self) -> &ZeroSlice<T> { | ||
| let slice: &[T::ULE] = self.vector.as_slice(); | ||
| ZeroSlice::from_ule_slice(slice) | ||
| } | ||
| /// Casts a `ZeroVec<T>` to a compatible `ZeroVec<P>`. | ||
@@ -467,3 +470,3 @@ /// | ||
| /// let zerovec_u16: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// assert_eq!(zerovec_u16.get(3), Some(32973)); | ||
@@ -501,3 +504,3 @@ /// | ||
| /// let zv_char: ZeroVec<char> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("valid code points"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("valid code points"); | ||
| /// let zv_u8_3: ZeroVec<[u8; 3]> = | ||
@@ -531,3 +534,3 @@ /// zv_char.try_into_converted().expect("infallible conversion"); | ||
| /// let zv_char: ZeroVec<char> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("valid code points"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("valid code points"); | ||
| /// | ||
@@ -545,3 +548,3 @@ /// // Panics! mem::size_of::<char::ULE> != mem::size_of::<u16::ULE> | ||
| /// let zv_char: ZeroVec<char> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("valid code points"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("valid code points"); | ||
| /// let zv_u16: ZeroVec<u16> = | ||
@@ -553,3 +556,3 @@ /// zv_char.into_bytes().try_into_parsed().expect("infallible"); | ||
| /// ``` | ||
| pub fn try_into_converted<P: AsULE>(self) -> Result<ZeroVec<'a, P>, ZeroVecError> { | ||
| pub fn try_into_converted<P: AsULE>(self) -> Result<ZeroVec<'a, P>, UleError> { | ||
| assert_eq!( | ||
@@ -561,9 +564,9 @@ core::mem::size_of::<<T as AsULE>::ULE>(), | ||
| Cow::Borrowed(old_slice) => { | ||
| let bytes: &'a [u8] = T::ULE::as_byte_slice(old_slice); | ||
| let new_slice = P::ULE::parse_byte_slice(bytes)?; | ||
| let bytes: &'a [u8] = T::ULE::slice_as_bytes(old_slice); | ||
| let new_slice = P::ULE::parse_bytes_to_slice(bytes)?; | ||
| Ok(ZeroVec::new_borrowed(new_slice)) | ||
| } | ||
| Cow::Owned(old_vec) => { | ||
| let bytes: &[u8] = T::ULE::as_byte_slice(&old_vec); | ||
| P::ULE::validate_byte_slice(bytes)?; | ||
| let bytes: &[u8] = T::ULE::slice_as_bytes(&old_vec); | ||
| P::ULE::validate_bytes(bytes)?; | ||
| // Feature "vec_into_raw_parts" is not yet stable (#65816). Polyfill: | ||
@@ -596,4 +599,7 @@ let (ptr, len, cap) = { | ||
| /// If this is a borrowed ZeroVec, return it as a slice that covers | ||
| /// its lifetime parameter | ||
| /// If this is a borrowed [`ZeroVec`], return it as a slice that covers | ||
| /// its lifetime parameter. | ||
| /// | ||
| /// To infallibly get a [`ZeroSlice`] with a shorter lifetime, use | ||
| /// [`ZeroVec::as_slice`]. | ||
| #[inline] | ||
@@ -672,10 +678,10 @@ pub fn as_maybe_borrowed(&self) -> Option<&'a ZeroSlice<T>> { | ||
| /// ``` | ||
| pub fn try_into_parsed<T: AsULE>(self) -> Result<ZeroVec<'a, T>, ZeroVecError> { | ||
| pub fn try_into_parsed<T: AsULE>(self) -> Result<ZeroVec<'a, T>, UleError> { | ||
| match self.into_cow() { | ||
| Cow::Borrowed(bytes) => { | ||
| let slice: &'a [T::ULE] = T::ULE::parse_byte_slice(bytes)?; | ||
| let slice: &'a [T::ULE] = T::ULE::parse_bytes_to_slice(bytes)?; | ||
| Ok(ZeroVec::new_borrowed(slice)) | ||
| } | ||
| Cow::Owned(vec) => { | ||
| let slice = Vec::from(T::ULE::parse_byte_slice(&vec)?); | ||
| let slice = Vec::from(T::ULE::parse_bytes_to_slice(&vec)?); | ||
| Ok(ZeroVec::new_owned(slice)) | ||
@@ -802,3 +808,3 @@ } | ||
| /// let mut zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -828,3 +834,3 @@ /// zerovec.for_each_mut(|item| *item += 1); | ||
| /// let mut zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -862,3 +868,3 @@ /// zerovec.try_for_each_mut(|item| { | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// assert!(!zerovec.is_owned()); | ||
@@ -891,3 +897,3 @@ /// | ||
| /// let mut zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// assert!(!zerovec.is_owned()); | ||
@@ -923,3 +929,3 @@ /// | ||
| /// let mut zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// assert!(!zerovec.is_owned()); | ||
@@ -955,3 +961,3 @@ /// | ||
| /// let mut zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// assert!(!zerovec.is_owned()); | ||
@@ -1000,3 +1006,3 @@ /// | ||
| /// let mut zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// assert!(!zerovec.is_owned()); | ||
@@ -1078,12 +1084,6 @@ /// | ||
| /// 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![]; | ||
@@ -1160,3 +1160,3 @@ /// let nums = zeroslice!(u32; <u32 as AsULE>::ULE::from_unsigned; [1, 2, 3, 4, 5]); | ||
| { | ||
| let zerovec = ZeroVec::<u32>::parse_byte_slice(TEST_BUFFER_LE).unwrap(); | ||
| let zerovec = ZeroVec::<u32>::parse_bytes(TEST_BUFFER_LE).unwrap(); | ||
| assert_eq!(zerovec.get(0), Some(TEST_SLICE[0])); | ||
@@ -1176,3 +1176,3 @@ assert_eq!(zerovec.get(1), Some(TEST_SLICE[1])); | ||
| { | ||
| let zerovec = ZeroVec::<u32>::parse_byte_slice(TEST_BUFFER_LE).unwrap(); | ||
| let zerovec = ZeroVec::<u32>::parse_bytes(TEST_BUFFER_LE).unwrap(); | ||
| assert_eq!(Ok(3), zerovec.binary_search(&0x0e0d0c)); | ||
@@ -1187,9 +1187,7 @@ assert_eq!(Err(3), zerovec.binary_search(&0x0c0d0c)); | ||
| Some(0x020100), | ||
| ZeroVec::<u32>::parse_byte_slice(TEST_BUFFER_LE) | ||
| .unwrap() | ||
| .get(0) | ||
| ZeroVec::<u32>::parse_bytes(TEST_BUFFER_LE).unwrap().get(0) | ||
| ); | ||
| assert_eq!( | ||
| Some(0x04000201), | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[1..77]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[1..77]) | ||
| .unwrap() | ||
@@ -1200,3 +1198,3 @@ .get(0) | ||
| Some(0x05040002), | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[2..78]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[2..78]) | ||
| .unwrap() | ||
@@ -1207,3 +1205,3 @@ .get(0) | ||
| Some(0x06050400), | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[3..79]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[3..79]) | ||
| .unwrap() | ||
@@ -1214,3 +1212,3 @@ .get(0) | ||
| Some(0x060504), | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[4..]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[4..]) | ||
| .unwrap() | ||
@@ -1221,3 +1219,3 @@ .get(0) | ||
| Some(0x4e4d4c00), | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[75..79]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[75..79]) | ||
| .unwrap() | ||
@@ -1228,3 +1226,3 @@ .get(0) | ||
| Some(0x4e4d4c00), | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[3..79]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[3..79]) | ||
| .unwrap() | ||
@@ -1235,3 +1233,3 @@ .get(18) | ||
| Some(0x4e4d4c), | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[76..]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[76..]) | ||
| .unwrap() | ||
@@ -1242,5 +1240,3 @@ .get(0) | ||
| Some(0x4e4d4c), | ||
| ZeroVec::<u32>::parse_byte_slice(TEST_BUFFER_LE) | ||
| .unwrap() | ||
| .get(19) | ||
| ZeroVec::<u32>::parse_bytes(TEST_BUFFER_LE).unwrap().get(19) | ||
| ); | ||
@@ -1250,3 +1246,3 @@ // TODO(#1144): Check for correct slice length in RawBytesULE | ||
| // None, | ||
| // ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[77..]) | ||
| // ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[77..]) | ||
| // .unwrap() | ||
@@ -1257,9 +1253,7 @@ // .get(0) | ||
| None, | ||
| ZeroVec::<u32>::parse_byte_slice(TEST_BUFFER_LE) | ||
| .unwrap() | ||
| .get(20) | ||
| ZeroVec::<u32>::parse_bytes(TEST_BUFFER_LE).unwrap().get(20) | ||
| ); | ||
| assert_eq!( | ||
| None, | ||
| ZeroVec::<u32>::parse_byte_slice(&TEST_BUFFER_LE[3..79]) | ||
| ZeroVec::<u32>::parse_bytes(&TEST_BUFFER_LE[3..79]) | ||
| .unwrap() | ||
@@ -1266,0 +1260,0 @@ .get(19) |
@@ -42,3 +42,3 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| { | ||
| ZeroVec::parse_byte_slice(bytes).map_err(de::Error::custom) | ||
| ZeroVec::parse_bytes(bytes).map_err(de::Error::custom) | ||
| } | ||
@@ -45,0 +45,0 @@ |
+27
-26
@@ -10,3 +10,5 @@ // This file is part of ICU4X. For terms of use, please see the file | ||
| /// A zero-copy "slice", i.e. the zero-copy version of `[T]`. This behaves | ||
| /// A zero-copy "slice", i.e. the zero-copy version of `[T]`. | ||
| /// | ||
| /// This behaves | ||
| /// similarly to [`ZeroVec<T>`], however [`ZeroVec<T>`] is allowed to contain | ||
@@ -58,4 +60,4 @@ /// owned data and as such is ideal for deserialization since most human readable | ||
| /// if it's not a valid byte sequence | ||
| pub fn parse_byte_slice(bytes: &[u8]) -> Result<&Self, ZeroVecError> { | ||
| T::ULE::parse_byte_slice(bytes).map(Self::from_ule_slice) | ||
| pub fn parse_bytes(bytes: &[u8]) -> Result<&Self, UleError> { | ||
| T::ULE::parse_bytes_to_slice(bytes).map(Self::from_ule_slice) | ||
| } | ||
@@ -116,3 +118,3 @@ | ||
| pub fn as_bytes(&self) -> &[u8] { | ||
| T::ULE::as_byte_slice(self.as_ule_slice()) | ||
| T::ULE::slice_as_bytes(self.as_ule_slice()) | ||
| } | ||
@@ -136,3 +138,3 @@ | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -159,7 +161,6 @@ /// assert_eq!(4, zerovec.len()); | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// assert!(!zerovec.is_empty()); | ||
| /// | ||
| /// let emptyvec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(&[]).expect("infallible"); | ||
| /// let emptyvec: ZeroVec<u16> = ZeroVec::parse_bytes(&[]).expect("infallible"); | ||
| /// assert!(emptyvec.is_empty()); | ||
@@ -186,3 +187,3 @@ /// ``` | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -210,3 +211,3 @@ /// assert_eq!(zerovec.get(2), Some(421)); | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// let array: [u16; 4] = | ||
@@ -232,3 +233,3 @@ /// zerovec.get_as_array().expect("should be 4 items in array"); | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -313,4 +314,4 @@ /// assert_eq!( | ||
| #[inline] | ||
| pub fn try_as_converted<P: AsULE>(&self) -> Result<&ZeroSlice<P>, ZeroVecError> { | ||
| let new_slice = P::ULE::parse_byte_slice(self.as_bytes())?; | ||
| pub fn try_as_converted<P: AsULE>(&self) -> Result<&ZeroSlice<P>, UleError> { | ||
| let new_slice = P::ULE::parse_bytes_to_slice(self.as_bytes())?; | ||
| Ok(ZeroSlice::from_ule_slice(new_slice)) | ||
@@ -328,3 +329,3 @@ } | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -347,3 +348,3 @@ /// assert_eq!(zerovec.first(), Some(211)); | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -366,3 +367,3 @@ /// assert_eq!(zerovec.last(), Some(32973)); | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// let mut it = zerovec.iter(); | ||
@@ -429,3 +430,3 @@ /// | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -458,3 +459,3 @@ /// assert_eq!(zerovec.binary_search(&281), Ok(1)); | ||
| /// let zerovec: ZeroVec<u16> = | ||
| /// ZeroVec::parse_byte_slice(bytes).expect("infallible"); | ||
| /// ZeroVec::parse_bytes(bytes).expect("infallible"); | ||
| /// | ||
@@ -480,16 +481,16 @@ /// assert_eq!(zerovec.binary_search_by(|x| x.cmp(&281)), Ok(1)); | ||
| // 2. [T::ULE] is aligned to 1 byte (achieved by being a slice of a ULE type) | ||
| // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data. | ||
| // 6. `as_byte_slice()` and `parse_byte_slice()` are defaulted | ||
| // 3. The impl of `validate_bytes()` returns an error if any byte is not valid. | ||
| // 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety | ||
| // 5. The impl of `from_bytes_unchecked()` returns a reference to the same data. | ||
| // 6. `as_bytes()` and `parse_bytes()` are defaulted | ||
| // 7. `[T::ULE]` byte equality is semantic equality (relying on the guideline of the underlying `ULE` type) | ||
| unsafe impl<T: AsULE + 'static> VarULE for ZeroSlice<T> { | ||
| #[inline] | ||
| fn validate_byte_slice(bytes: &[u8]) -> Result<(), ZeroVecError> { | ||
| T::ULE::validate_byte_slice(bytes) | ||
| fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> { | ||
| T::ULE::validate_bytes(bytes) | ||
| } | ||
| #[inline] | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| Self::from_ule_slice(T::ULE::from_byte_slice_unchecked(bytes)) | ||
| unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { | ||
| Self::from_ule_slice(T::ULE::slice_from_bytes_unchecked(bytes)) | ||
| } | ||
@@ -496,0 +497,0 @@ } |
-55
| // 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 core::any; | ||
| use core::fmt; | ||
| /// A generic error type to be used for decoding slices of ULE types | ||
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
| #[non_exhaustive] | ||
| pub enum ZeroVecError { | ||
| /// Attempted to parse a buffer into a slice of the given ULE type but its | ||
| /// length was not compatible | ||
| InvalidLength { ty: &'static str, len: usize }, | ||
| /// The byte sequence provided for `ty` failed to parse correctly | ||
| ParseError { ty: &'static str }, | ||
| /// The byte buffer was not in the appropriate format for VarZeroVec | ||
| VarZeroVecFormatError, | ||
| } | ||
| impl fmt::Display for ZeroVecError { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { | ||
| match *self { | ||
| ZeroVecError::InvalidLength { ty, len } => { | ||
| write!(f, "Invalid length {len} for slice of type {ty}") | ||
| } | ||
| ZeroVecError::ParseError { ty } => { | ||
| write!(f, "Could not parse bytes to slice of type {ty}") | ||
| } | ||
| ZeroVecError::VarZeroVecFormatError => { | ||
| write!(f, "Invalid format for VarZeroVec buffer") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| impl ZeroVecError { | ||
| /// Construct a parse error for the given type | ||
| pub fn parse<T: ?Sized + 'static>() -> ZeroVecError { | ||
| ZeroVecError::ParseError { | ||
| ty: any::type_name::<T>(), | ||
| } | ||
| } | ||
| /// Construct an "invalid length" error for the given type and length | ||
| pub fn length<T: ?Sized + 'static>(len: usize) -> ZeroVecError { | ||
| ZeroVecError::InvalidLength { | ||
| ty: any::type_name::<T>(), | ||
| len, | ||
| } | ||
| } | ||
| } | ||
| #[cfg(feature = "std")] | ||
| impl ::std::error::Error for ZeroVecError {} |
| // 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 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) } } | ||
| } | ||
| } | ||
| } | ||
| #[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 | ||
| ); | ||
| } |
| // 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; |
| // 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<u8>); | ||
| 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<usize> for FlexZeroVecOwned { | ||
| /// Creates a [`FlexZeroVecOwned`] from an iterator of `usize`. | ||
| fn from_iter<I>(iter: I) -> Self | ||
| where | ||
| I: IntoIterator<Item = usize>, | ||
| { | ||
| 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, &[]); | ||
| } | ||
| } |
| // 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<E>(self, bytes: &'de [u8]) -> Result<Self::Value, E> | ||
| where | ||
| E: de::Error, | ||
| { | ||
| FlexZeroVec::parse_byte_slice(bytes).map_err(de::Error::custom) | ||
| } | ||
| fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error> | ||
| where | ||
| A: SeqAccess<'de>, | ||
| { | ||
| let mut vec: Vec<usize> = if let Some(capacity) = seq.size_hint() { | ||
| Vec::with_capacity(capacity) | ||
| } else { | ||
| Vec::new() | ||
| }; | ||
| while let Some(value) = seq.next_element::<usize>()? { | ||
| 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<D>(deserializer: D) -> Result<Self, D::Error> | ||
| 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<D>(deserializer: D) -> Result<Self, D::Error> | ||
| 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
| 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<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
| 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<usize> = | ||
| 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()); | ||
| } | ||
| } |
| // 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::ZeroVecError; | ||
| use alloc::vec::Vec; | ||
| use core::cmp::Ordering; | ||
| use core::fmt; | ||
| use core::mem; | ||
| use core::ops::Range; | ||
| const USIZE_WIDTH: usize = mem::size_of::<usize>(); | ||
| /// 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, ZeroVecError> { | ||
| let (width_u8, data) = match bytes.split_first() { | ||
| Some(v) => v, | ||
| None => { | ||
| return Err(ZeroVecError::InvalidLength { | ||
| ty: "FlexZeroSlice", | ||
| len: 0, | ||
| }) | ||
| } | ||
| }; | ||
| let width = *width_u8 as usize; | ||
| if width < 1 || width > USIZE_WIDTH { | ||
| return Err(ZeroVecError::ParseError { | ||
| ty: "FlexZeroSlice", | ||
| }); | ||
| } | ||
| if data.len() % width != 0 { | ||
| return Err(ZeroVecError::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<usize> { | ||
| 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<usize> { | ||
| 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<usize> { | ||
| 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<Item = usize> + '_ + ExactSizeIterator<Item = usize> { | ||
| 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<Item = (usize, Option<usize>)> + '_ { | ||
| self.iter().zip(self.iter().skip(1).map(Some).chain([None])) | ||
| } | ||
| /// Creates a `Vec<usize>` 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<usize> = fzv.to_vec(); | ||
| /// | ||
| /// assert_eq!(nums, vec.as_slice()); | ||
| /// ``` | ||
| #[inline] | ||
| pub fn to_vec(&self) -> Vec<usize> { | ||
| 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<usize, usize> { | ||
| 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<usize>, | ||
| ) -> Option<Result<usize, usize>> { | ||
| 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<usize, usize> { | ||
| 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<usize>, | ||
| ) -> Option<Result<usize, usize>> { | ||
| // 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<usize, usize> { | ||
| 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<usize>, | ||
| ) -> Option<Result<usize, usize>> { | ||
| // 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<usize, usize> { | ||
| 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<usize, usize> { | ||
| // 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; | ||
| } | ||
| } |
| // 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::ZeroVecError; | ||
| 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::<usize, usize>::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<FlexZeroSlice> for FlexZeroVec<'a> { | ||
| fn as_ref(&self) -> &FlexZeroSlice { | ||
| self.deref() | ||
| } | ||
| } | ||
| impl Eq for FlexZeroVec<'_> {} | ||
| impl<'a, 'b> PartialEq<FlexZeroVec<'b>> 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<Ordering> { | ||
| 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<Self, ZeroVecError> { | ||
| 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<usize> for FlexZeroVec<'_> { | ||
| /// Creates a [`FlexZeroVec::Owned`] from an iterator of `usize`. | ||
| fn from_iter<I>(iter: I) -> Self | ||
| where | ||
| I: IntoIterator<Item = usize>, | ||
| { | ||
| FlexZeroVecOwned::from_iter(iter).into_flexzerovec() | ||
| } | ||
| } | ||
| #[test] | ||
| fn test_zeromap_usize() { | ||
| use crate::ZeroMap; | ||
| let mut zm = ZeroMap::<usize, usize>::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); | ||
| } |
| // 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::{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; | ||
| /// A byte slice that is expected to be a UTF-8 string but does not enforce that invariant. | ||
| /// | ||
| /// Use this type instead of `str` if you don't need to enforce UTF-8 during deserialization. For | ||
| /// example, strings that are keys of a map don't need to ever be reified as `str`s. | ||
| /// | ||
| /// [`UnvalidatedStr`] derefs to `[u8]`. To obtain a `str`, use [`Self::try_as_str()`]. | ||
| /// | ||
| /// The main advantage of this type over `[u8]` is that it serializes as a string in | ||
| /// human-readable formats like JSON. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// Using an [`UnvalidatedStr`] as the key of a [`ZeroMap`]: | ||
| /// | ||
| /// ``` | ||
| /// use zerovec::ule::UnvalidatedStr; | ||
| /// use zerovec::ZeroMap; | ||
| /// | ||
| /// let map: ZeroMap<UnvalidatedStr, usize> = [ | ||
| /// (UnvalidatedStr::from_str("abc"), 11), | ||
| /// (UnvalidatedStr::from_str("def"), 22), | ||
| /// (UnvalidatedStr::from_str("ghi"), 33), | ||
| /// ] | ||
| /// .into_iter() | ||
| /// .collect(); | ||
| /// | ||
| /// let key = "abc"; | ||
| /// let value = map.get_copied_by(|uvstr| uvstr.as_bytes().cmp(key.as_bytes())); | ||
| /// assert_eq!(Some(11), value); | ||
| /// ``` | ||
| /// | ||
| /// [`ZeroMap`]: crate::ZeroMap | ||
| #[repr(transparent)] | ||
| #[derive(PartialEq, Eq, PartialOrd, Ord)] | ||
| #[allow(clippy::exhaustive_structs)] // transparent newtype | ||
| pub struct UnvalidatedStr([u8]); | ||
| impl fmt::Debug for UnvalidatedStr { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| // Debug as a string if possible | ||
| match self.try_as_str() { | ||
| Ok(s) => fmt::Debug::fmt(s, f), | ||
| Err(_) => fmt::Debug::fmt(&self.0, f), | ||
| } | ||
| } | ||
| } | ||
| impl UnvalidatedStr { | ||
| /// Create a [`UnvalidatedStr`] from a byte slice. | ||
| #[inline] | ||
| pub const fn from_bytes(other: &[u8]) -> &Self { | ||
| // Safety: UnvalidatedStr is transparent over [u8] | ||
| unsafe { core::mem::transmute(other) } | ||
| } | ||
| /// Create a [`UnvalidatedStr`] from a string slice. | ||
| #[inline] | ||
| pub const fn from_str(s: &str) -> &Self { | ||
| Self::from_bytes(s.as_bytes()) | ||
| } | ||
| /// Create a [`UnvalidatedStr`] from boxed bytes. | ||
| #[inline] | ||
| pub fn from_boxed_bytes(other: Box<[u8]>) -> Box<Self> { | ||
| // Safety: UnvalidatedStr is transparent over [u8] | ||
| unsafe { core::mem::transmute(other) } | ||
| } | ||
| /// Create a [`UnvalidatedStr`] from a boxed `str`. | ||
| #[inline] | ||
| pub fn from_boxed_str(other: Box<str>) -> Box<Self> { | ||
| Self::from_boxed_bytes(other.into_boxed_bytes()) | ||
| } | ||
| /// Get the bytes from a [`UnvalidatedStr]. | ||
| #[inline] | ||
| pub const fn as_bytes(&self) -> &[u8] { | ||
| &self.0 | ||
| } | ||
| /// Attempt to convert a [`UnvalidatedStr`] to a `str`. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
| /// use zerovec::ule::UnvalidatedStr; | ||
| /// | ||
| /// static A: &UnvalidatedStr = UnvalidatedStr::from_bytes(b"abc"); | ||
| /// | ||
| /// let b = A.try_as_str().unwrap(); | ||
| /// assert_eq!(b, "abc"); | ||
| /// ``` | ||
| // Note: this is const starting in 1.63 | ||
| #[inline] | ||
| pub fn try_as_str(&self) -> Result<&str, core::str::Utf8Error> { | ||
| core::str::from_utf8(&self.0) | ||
| } | ||
| } | ||
| impl<'a> From<&'a str> for &'a UnvalidatedStr { | ||
| #[inline] | ||
| fn from(other: &'a str) -> Self { | ||
| UnvalidatedStr::from_str(other) | ||
| } | ||
| } | ||
| impl From<Box<str>> for Box<UnvalidatedStr> { | ||
| #[inline] | ||
| fn from(other: Box<str>) -> Self { | ||
| UnvalidatedStr::from_boxed_str(other) | ||
| } | ||
| } | ||
| impl Deref for UnvalidatedStr { | ||
| type Target = [u8]; | ||
| fn deref(&self) -> &Self::Target { | ||
| &self.0 | ||
| } | ||
| } | ||
| impl<'a> ZeroMapKV<'a> for UnvalidatedStr { | ||
| type Container = VarZeroVec<'a, UnvalidatedStr>; | ||
| type Slice = VarZeroSlice<UnvalidatedStr>; | ||
| type GetType = UnvalidatedStr; | ||
| type OwnedType = Box<UnvalidatedStr>; | ||
| } | ||
| // Safety (based on the safety checklist on the VarULE trait): | ||
| // 1. UnvalidatedStr does not include any uninitialized or padding bytes (transparent over a ULE) | ||
| // 2. UnvalidatedStr is aligned to 1 byte (transparent over a ULE) | ||
| // 3. The impl of `validate_byte_slice()` returns an error if any byte is not valid (impossible) | ||
| // 4. The impl of `validate_byte_slice()` returns an error if the slice cannot be used in its entirety (impossible) | ||
| // 5. The impl of `from_byte_slice_unchecked()` returns a reference to the same data (returns the argument directly) | ||
| // 6. All other methods are defaulted | ||
| // 7. `[T]` byte equality is semantic equality (transparent over a ULE) | ||
| unsafe impl VarULE for UnvalidatedStr { | ||
| #[inline] | ||
| fn validate_byte_slice(_: &[u8]) -> Result<(), ZeroVecError> { | ||
| Ok(()) | ||
| } | ||
| #[inline] | ||
| unsafe fn from_byte_slice_unchecked(bytes: &[u8]) -> &Self { | ||
| UnvalidatedStr::from_bytes(bytes) | ||
| } | ||
| } | ||
| /// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate | ||
| #[cfg(feature = "serde")] | ||
| impl serde::Serialize for UnvalidatedStr { | ||
| fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||
| where | ||
| S: serde::Serializer, | ||
| { | ||
| use serde::ser::Error; | ||
| let s = self | ||
| .try_as_str() | ||
| .map_err(|_| S::Error::custom("invalid UTF-8 in UnvalidatedStr"))?; | ||
| if serializer.is_human_readable() { | ||
| serializer.serialize_str(s) | ||
| } else { | ||
| serializer.serialize_bytes(s.as_bytes()) | ||
| } | ||
| } | ||
| } | ||
| /// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate | ||
| #[cfg(feature = "serde")] | ||
| impl<'de> serde::Deserialize<'de> for Box<UnvalidatedStr> { | ||
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
| where | ||
| D: serde::Deserializer<'de>, | ||
| { | ||
| if deserializer.is_human_readable() { | ||
| let boxed_str = Box::<str>::deserialize(deserializer)?; | ||
| Ok(UnvalidatedStr::from_boxed_str(boxed_str)) | ||
| } else { | ||
| let boxed_bytes = Box::<[u8]>::deserialize(deserializer)?; | ||
| Ok(UnvalidatedStr::from_boxed_bytes(boxed_bytes)) | ||
| } | ||
| } | ||
| } | ||
| /// This impl requires enabling the optional `serde` Cargo feature of the `zerovec` crate | ||
| #[cfg(feature = "serde")] | ||
| impl<'de, 'a> serde::Deserialize<'de> for &'a UnvalidatedStr | ||
| where | ||
| 'de: 'a, | ||
| { | ||
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
| where | ||
| D: serde::Deserializer<'de>, | ||
| { | ||
| if deserializer.is_human_readable() { | ||
| let s = <&str>::deserialize(deserializer)?; | ||
| Ok(UnvalidatedStr::from_str(s)) | ||
| } else { | ||
| let bytes = <&[u8]>::deserialize(deserializer)?; | ||
| Ok(UnvalidatedStr::from_bytes(bytes)) | ||
| } | ||
| } | ||
| } | ||
| /// 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::UnvalidatedChar; | ||
| /// 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); | ||
| } | ||
| } |
Sorry, the diff of this file is not supported yet