Sign In

zerovec

Package Overview
Dependencies
Maintainers
1
Versions
34
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

zerovec - cargo Package Compare versions

Comparing version
0.10.4
to
0.11.0
+351
src/cow.rs
// 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]
}
}
+2
-1
{
"git": {
"sha1": "3c47d82f8ba9f36699b71566844ed28b3f742b21"
"sha1": "6bd4893cc44c2ca2718de47a119a31cc40045fe5",
"dirty": true
},
"path_in_vcs": "utils/zerovec"
}

@@ -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();

@@ -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",

@@ -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
}

@@ -77,3 +77,3 @@ # zerovec [![crates.io](https://img.shields.io/crates/v/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 @@

@@ -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 @@ ///

@@ -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 @@ )

@@ -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

@@ -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

@@ -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 @@

@@ -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 @@ }

@@ -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 @@ )

@@ -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 @@ );

@@ -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,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());
}
}

@@ -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,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 @@ }

@@ -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 {}

@@ -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))
}
}

@@ -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

@@ -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);

@@ -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 @@ }

@@ -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)
}
}

@@ -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());
}

@@ -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;

@@ -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);

@@ -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");

@@ -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 @@ }

@@ -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()
);
}

@@ -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 @@ }

@@ -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
);
}

@@ -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 @@

@@ -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 @@ }

// 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