1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
45use super::VarZeroVecFormatError;
6use crate::ule::*;
7use core::cmp::Ordering;
8use core::convert::TryFrom;
9use core::marker::PhantomData;
10use core::mem;
11use core::ops::Range;
1213/// This trait allows switching between different possible internal
14/// representations of VarZeroVec.
15///
16/// Currently this crate supports three formats: [`Index8`], [`Index16`] and [`Index32`],
17/// with [`Index16`] being the default for all [`VarZeroVec`](super::VarZeroVec)
18/// types unless explicitly specified otherwise.
19///
20/// Do not implement this trait, its internals may be changed in the future,
21/// and all of its associated items are hidden from the docs.
22pub trait VarZeroVecFormat: 'static + Sized {
23/// The type to use for the indexing array
24 ///
25 /// Safety: must be a ULE for which all byte sequences are allowed
26#[doc(hidden)]
27type Index: IntegerULE;
28/// The type to use for the length segment
29 ///
30 /// Safety: must be a ULE for which all byte sequences are allowed
31#[doc(hidden)]
32type Len: IntegerULE;
33}
3435/// This trait represents various ULE types that can be used to represent an integer
36///
37/// Do not implement this trait, its internals may be changed in the future,
38/// and all of its associated items are hidden from the docs.
39#[doc(hidden)]
40pub unsafe trait IntegerULE: ULE {
41/// The error to show when unable to construct a vec
42#[doc(hidden)]
43const TOO_LARGE_ERROR: &'static str;
4445/// Safety: must be sizeof(self)
46#[doc(hidden)]
47const SIZE: usize;
4849/// Safety: must be maximum integral value represented here
50#[doc(hidden)]
51const MAX_VALUE: u32;
5253/// Safety: Must roundtrip with from_usize and represent the correct
54 /// integral value
55#[doc(hidden)]
56fn iule_to_usize(self) -> usize;
5758#[doc(hidden)]
59fn iule_from_usize(x: usize) -> Option<Self>;
6061/// Safety: Should always convert a buffer into an array of Self with the correct length
62#[doc(hidden)]
63 #[cfg(feature = "alloc")]
64fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self];
65}
6667/// This is a [`VarZeroVecFormat`] that stores u8s in the index array, and a u8 for a length.
68///
69/// Will have a smaller data size, but it's *extremely* likely for larger arrays
70/// to be unrepresentable (and error on construction). Should probably be used
71/// for known-small arrays, where all but the last field are known-small.
72#[derive(#[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::marker::Copy for Index8 { }Copy, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::clone::Clone for Index8 {
#[inline]
fn clone(&self) -> Index8 { *self }
}Clone, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::fmt::Debug for Index8 {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "Index8")
}
}Debug, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::hash::Hash for Index8 {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {}
}Hash, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::PartialEq for Index8 {
#[inline]
fn eq(&self, other: &Index8) -> bool { true }
}PartialEq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::Eq for Index8 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::PartialOrd for Index8 {
#[inline]
fn partial_cmp(&self, other: &Index8)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ordering::Equal)
}
}PartialOrd, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::Ord for Index8 {
#[inline]
fn cmp(&self, other: &Index8) -> ::core::cmp::Ordering {
::core::cmp::Ordering::Equal
}
}Ord)]
73#[allow(clippy::exhaustive_structs)] // marker
74pub struct Index8;
7576/// This is a [`VarZeroVecFormat`] that stores u16s in the index array, and a u16 for a length.
77///
78/// Will have a smaller data size, but it's more likely for larger arrays
79/// to be unrepresentable (and error on construction)
80///
81/// This is the default index size used by all [`VarZeroVec`](super::VarZeroVec) types.
82#[derive(#[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::marker::Copy for Index16 { }Copy, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::clone::Clone for Index16 {
#[inline]
fn clone(&self) -> Index16 { *self }
}Clone, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::fmt::Debug for Index16 {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "Index16")
}
}Debug, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::hash::Hash for Index16 {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {}
}Hash, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::PartialEq for Index16 {
#[inline]
fn eq(&self, other: &Index16) -> bool { true }
}PartialEq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::Eq for Index16 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::PartialOrd for Index16 {
#[inline]
fn partial_cmp(&self, other: &Index16)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ordering::Equal)
}
}PartialOrd, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::Ord for Index16 {
#[inline]
fn cmp(&self, other: &Index16) -> ::core::cmp::Ordering {
::core::cmp::Ordering::Equal
}
}Ord)]
83#[allow(clippy::exhaustive_structs)] // marker
84pub struct Index16;
8586/// This is a [`VarZeroVecFormat`] that stores u32s in the index array, and a u32 for a length.
87/// Will have a larger data size, but will support large arrays without
88/// problems.
89#[derive(#[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::marker::Copy for Index32 { }Copy, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::clone::Clone for Index32 {
#[inline]
fn clone(&self) -> Index32 { *self }
}Clone, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::fmt::Debug for Index32 {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::write_str(f, "Index32")
}
}Debug, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::hash::Hash for Index32 {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {}
}Hash, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::PartialEq for Index32 {
#[inline]
fn eq(&self, other: &Index32) -> bool { true }
}PartialEq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::Eq for Index32 {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) -> () {}
}Eq, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::PartialOrd for Index32 {
#[inline]
fn partial_cmp(&self, other: &Index32)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ordering::Equal)
}
}PartialOrd, #[automatically_derived]
#[allow(clippy::exhaustive_structs)]
impl ::core::cmp::Ord for Index32 {
#[inline]
fn cmp(&self, other: &Index32) -> ::core::cmp::Ordering {
::core::cmp::Ordering::Equal
}
}Ord)]
90#[allow(clippy::exhaustive_structs)] // marker
91pub struct Index32;
9293impl VarZeroVecFormatfor Index8 {
94type Index = u8;
95type Len = u8;
96}
9798impl VarZeroVecFormatfor Index16 {
99type Index = RawBytesULE<2>;
100type Len = RawBytesULE<2>;
101}
102103impl VarZeroVecFormatfor Index32 {
104type Index = RawBytesULE<4>;
105type Len = RawBytesULE<4>;
106}
107108unsafe impl IntegerULEfor u8 {
109const TOO_LARGE_ERROR: &'static str = "Attempted to build VarZeroVec out of elements that \
110 cumulatively are larger than a u8 in size";
111const SIZE: usize = mem::size_of::<Self>();
112const MAX_VALUE: u32 = u8::MAXas u32;
113#[inline]
114fn iule_to_usize(self) -> usize {
115selfas usize116 }
117#[inline]
118fn iule_from_usize(u: usize) -> Option<Self> {
119u8::try_from(u).ok()
120 }
121#[inline]
122 #[cfg(feature = "alloc")]
123fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] {
124 bytes
125 }
126}
127128unsafe impl IntegerULEfor RawBytesULE<2> {
129const TOO_LARGE_ERROR: &'static str = "Attempted to build VarZeroVec out of elements that \
130 cumulatively are larger than a u16 in size";
131const SIZE: usize = mem::size_of::<Self>();
132const MAX_VALUE: u32 = u16::MAXas u32;
133#[inline]
134fn iule_to_usize(self) -> usize {
135self.as_unsigned_int() as usize136 }
137#[inline]
138fn iule_from_usize(u: usize) -> Option<Self> {
139u16::try_from(u).ok().map(u16::to_unaligned)
140 }
141#[inline]
142 #[cfg(feature = "alloc")]
143fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] {
144Self::from_bytes_unchecked_mut(bytes)
145 }
146}
147148unsafe impl IntegerULEfor RawBytesULE<4> {
149const TOO_LARGE_ERROR: &'static str = "Attempted to build VarZeroVec out of elements that \
150 cumulatively are larger than a u32 in size";
151const SIZE: usize = mem::size_of::<Self>();
152const MAX_VALUE: u32 = u32::MAX;
153#[inline]
154fn iule_to_usize(self) -> usize {
155self.as_unsigned_int() as usize156 }
157#[inline]
158fn iule_from_usize(u: usize) -> Option<Self> {
159u32::try_from(u).ok().map(u32::to_unaligned)
160 }
161#[inline]
162 #[cfg(feature = "alloc")]
163fn iule_from_bytes_unchecked_mut(bytes: &mut [u8]) -> &mut [Self] {
164Self::from_bytes_unchecked_mut(bytes)
165 }
166}
167168/// A more parsed version of `VarZeroSlice`. This type is where most of the VarZeroVec
169/// internal representation code lies.
170///
171/// This is *basically* an `&'a [u8]` to a zero copy buffer, but split out into
172/// the buffer components. Logically this is capable of behaving as
173/// a `&'a [T::VarULE]`, but since `T::VarULE` is unsized that type does not actually
174/// exist.
175///
176/// See [`VarZeroVecComponents::parse_bytes()`] for information on the internal invariants involved
177#[derive(#[automatically_derived]
impl<'a, T: ::core::fmt::Debug + ?Sized, F: ::core::fmt::Debug>
::core::fmt::Debug for VarZeroVecComponents<'a, T, F> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f,
"VarZeroVecComponents", "len", &self.len, "indices",
&self.indices, "things", &self.things, "marker", &&self.marker)
}
}Debug)]
178pub struct VarZeroVecComponents<'a, T: ?Sized, F> {
179/// The number of elements
180len: u32,
181/// The list of indices into the `things` slice
182 /// Since the first element is always at things[0], the first element of the indices array is for the *second* element
183indices: &'a [u8],
184/// The contiguous list of `T::VarULE`s
185things: &'a [u8],
186 marker: PhantomData<(&'a T, F)>,
187}
188189// #[derive()] won't work here since we do not want it to be
190// bound on T: Copy
191impl<'a, T: ?Sized, F> Copyfor VarZeroVecComponents<'a, T, F> {}
192impl<'a, T: ?Sized, F> Clonefor VarZeroVecComponents<'a, T, F> {
193fn clone(&self) -> Self {
194*self195 }
196}
197198impl<'a, T: VarULE + ?Sized, F> Defaultfor VarZeroVecComponents<'a, T, F> {
199#[inline]
200fn default() -> Self {
201Self::new()
202 }
203}
204205impl<'a, T: VarULE + ?Sized, F> VarZeroVecComponents<'a, T, F> {
206#[inline]
207pub fn new() -> Self {
208Self {
209 len: 0,
210 indices: &[],
211 things: &[],
212 marker: PhantomData,
213 }
214 }
215}
216impl<'a, T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroVecComponents<'a, T, F> {
217/// Construct a new VarZeroVecComponents, checking invariants about the overall buffer size:
218 ///
219 /// - There must be either zero or at least four bytes (if four, this is the "length" parsed as a usize)
220 /// - There must be at least `4*(length - 1) + 4` bytes total, to form the array `indices` of indices
221 /// - `0..indices[0]` must index into a valid section of
222 /// `things` (the data after `indices`), such that it parses to a `T::VarULE`
223 /// - `indices[i - 1]..indices[i]` must index into a valid section of
224 /// `things` (the data after `indices`), such that it parses to a `T::VarULE`
225 /// - `indices[len - 2]..things.len()` must index into a valid section of
226 /// `things`, such that it parses to a `T::VarULE`
227#[inline]
228pub fn parse_bytes(slice: &'a [u8]) -> Result<Self, VarZeroVecFormatError> {
229// The empty VZV is special-cased to the empty slice
230if slice.is_empty() {
231return Ok(VarZeroVecComponents {
232 len: 0,
233 indices: &[],
234 things: &[],
235 marker: PhantomData,
236 });
237 }
238let len_bytes = slice239 .get(0..F::Len::SIZE)
240 .ok_or(VarZeroVecFormatError::Metadata)?;
241let len_ule =
242 F::Len::parse_bytes_to_slice(len_bytes).map_err(|_| VarZeroVecFormatError::Metadata)?;
243244let len = len_ule245 .first()
246 .ok_or(VarZeroVecFormatError::Metadata)?
247.iule_to_usize();
248249let rest = slice250 .get(F::Len::SIZE..)
251 .ok_or(VarZeroVecFormatError::Metadata)?;
252let len_u32 = u32::try_from(len).map_err(|_| VarZeroVecFormatError::Metadata);
253// We pass down the rest of the invariants
254Self::parse_bytes_with_length(len_u32?, rest)
255 }
256257/// Construct a new VarZeroVecComponents, checking invariants about the overall buffer size:
258 ///
259 /// - There must be at least `4*len` bytes total, to form the array `indices` of indices.
260 /// - `indices[i]..indices[i+1]` must index into a valid section of
261 /// `things` (the data after `indices`), such that it parses to a `T::VarULE`
262 /// - `indices[len - 1]..things.len()` must index into a valid section of
263 /// `things`, such that it parses to a `T::VarULE`
264#[inline]
265pub fn parse_bytes_with_length(
266 len: u32,
267 slice: &'a [u8],
268 ) -> Result<Self, VarZeroVecFormatError> {
269let len_minus_one = len.checked_sub(1);
270// The empty VZV is special-cased to the empty slice
271let Some(len_minus_one) = len_minus_oneelse {
272return Ok(VarZeroVecComponents {
273 len: 0,
274 indices: &[],
275 things: &[],
276 marker: PhantomData,
277 });
278 };
279// The indices array is one element shorter since the first index is always 0,
280 // so we use len_minus_one
281let indices_bytes = slice282 .get(..F::Index::SIZE * (len_minus_one as usize))
283 .ok_or(VarZeroVecFormatError::Metadata)?;
284let things = slice285 .get(F::Index::SIZE * (len_minus_one as usize)..)
286 .ok_or(VarZeroVecFormatError::Metadata)?;
287288let borrowed = VarZeroVecComponents {
289len,
290 indices: indices_bytes,
291things,
292 marker: PhantomData,
293 };
294295borrowed.check_indices_and_things()?;
296297Ok(borrowed)
298 }
299300/// Construct a [`VarZeroVecComponents`] from a byte slice that has previously
301 /// successfully returned a [`VarZeroVecComponents`] when passed to
302 /// [`VarZeroVecComponents::parse_bytes()`]. Will return the same
303 /// object as one would get from calling [`VarZeroVecComponents::parse_bytes()`].
304 ///
305 /// # Safety
306 /// The bytes must have previously successfully run through
307 /// [`VarZeroVecComponents::parse_bytes()`]
308pub unsafe fn from_bytes_unchecked(slice: &'a [u8]) -> Self {
309// The empty VZV is special-cased to the empty slice
310if slice.is_empty() {
311return VarZeroVecComponents {
312 len: 0,
313 indices: &[],
314 things: &[],
315 marker: PhantomData,
316 };
317 }
318let (len_bytes, data_bytes) = unsafe { slice.split_at_unchecked(F::Len::SIZE) };
319// Safety: F::Len allows all byte sequences
320let len_ule = F::Len::slice_from_bytes_unchecked(len_bytes);
321322let len = len_ule.get_unchecked(0).iule_to_usize();
323let len_u32 = lenas u32;
324325// Safety: This method requires the bytes to have passed through `parse_bytes()`
326 // whereas we're calling something that asks for `parse_bytes_with_length()`.
327 // The two methods perform similar validation, with parse_bytes() validating an additional
328 // 4-byte `length` header.
329Self::from_bytes_unchecked_with_length(len_u32, data_bytes)
330 }
331332/// Construct a [`VarZeroVecComponents`] from a byte slice that has previously
333 /// successfully returned a [`VarZeroVecComponents`] when passed to
334 /// [`VarZeroVecComponents::parse_bytes()`]. Will return the same
335 /// object as one would get from calling [`VarZeroVecComponents::parse_bytes()`].
336 ///
337 /// # Safety
338 /// The len,bytes must have previously successfully run through
339 /// [`VarZeroVecComponents::parse_bytes_with_length()`]
340pub unsafe fn from_bytes_unchecked_with_length(len: u32, slice: &'a [u8]) -> Self {
341let len_minus_one = len.checked_sub(1);
342// The empty VZV is special-cased to the empty slice
343let Some(len_minus_one) = len_minus_oneelse {
344return VarZeroVecComponents {
345 len: 0,
346 indices: &[],
347 things: &[],
348 marker: PhantomData,
349 };
350 };
351// The indices array is one element shorter since the first index is always 0,
352 // so we use len_minus_one
353let indices_bytes = slice.get_unchecked(..F::Index::SIZE * (len_minus_one as usize));
354let things = slice.get_unchecked(F::Index::SIZE * (len_minus_one as usize)..);
355356VarZeroVecComponents {
357len,
358 indices: indices_bytes,
359things,
360 marker: PhantomData,
361 }
362 }
363364/// Get the number of elements in this vector
365#[inline]
366pub fn len(self) -> usize {
367self.len as usize368 }
369370/// Returns `true` if the vector contains no elements.
371#[inline]
372pub fn is_empty(self) -> bool {
373self.len == 0
374}
375376/// Get the idx'th element out of this slice. Returns `None` if out of bounds.
377#[inline]
378pub fn get(self, idx: usize) -> Option<&'a T> {
379if idx >= self.len() {
380return None;
381 }
382Some(unsafe { self.get_unchecked(idx) })
383 }
384385/// Get the idx'th element out of this slice. Does not bounds check.
386 ///
387 /// Safety:
388 /// - `idx` must be in bounds (`idx < self.len()`)
389#[inline]
390pub(crate) unsafe fn get_unchecked(self, idx: usize) -> &'a T {
391let range = self.get_things_range(idx);
392let things_slice = self.things.get_unchecked(range);
393 T::from_bytes_unchecked(things_slice)
394 }
395396/// Get the range in `things` for the element at `idx`. Does not bounds check.
397 ///
398 /// Safety:
399 /// - `idx` must be in bounds (`idx < self.len()`)
400#[inline]
401pub(crate) unsafe fn get_things_range(self, idx: usize) -> Range<usize> {
402let start = if let Some(idx_minus_one) = idx.checked_sub(1) {
403self.indices_slice()
404 .get_unchecked(idx_minus_one)
405 .iule_to_usize()
406 } else {
4070
408};
409let end = if idx + 1 == self.len() {
410self.things.len()
411 } else {
412self.indices_slice().get_unchecked(idx).iule_to_usize()
413 };
414if true {
if !(start <= end) {
::core::panicking::panic("assertion failed: start <= end")
};
};debug_assert!(start <= end);
415 start..end
416 }
417418/// Get the size, in bytes, of the indices array
419pub(crate) unsafe fn get_indices_size(self) -> usize {
420self.indices.len()
421 }
422423/// Check the internal invariants of VarZeroVecComponents:
424 ///
425 /// - `indices[i]..indices[i+1]` must index into a valid section of
426 /// `things`, such that it parses to a `T::VarULE`
427 /// - `indices[len - 1]..things.len()` must index into a valid section of
428 /// `things`, such that it parses to a `T::VarULE`
429 /// - `indices` is monotonically increasing
430 ///
431 /// This method is NOT allowed to call any other methods on VarZeroVecComponents since all other methods
432 /// assume that the slice has been passed through check_indices_and_things
433#[inline]
434 #[expect(clippy::len_zero)] // more explicit to enforce safety invariants
435fn check_indices_and_things(self) -> Result<(), VarZeroVecFormatError> {
436if self.len() == 0 {
437if self.things.len() > 0 {
438return Err(VarZeroVecFormatError::Metadata);
439 } else {
440return Ok(());
441 }
442 }
443let indices_slice = self.indices_slice();
444match (&self.len(), &(indices_slice.len() + 1)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(self.len(), indices_slice.len() + 1);
445// Safety: i is in bounds (assertion above)
446let mut start = 0;
447for i in 0..self.len() {
448// The indices array is offset by 1: indices[0] is the end of the first
449 // element and the start of the next, since the start of the first element
450 // is always things[0]. So to get the end we get element `i`.
451let end = if let Some(end) = indices_slice.get(i) {
452 end.iule_to_usize()
453 } else {
454// This only happens at i = self.len() - 1 = indices_slice.len() + 1 - 1
455 // = indices_slice.len(). This is the last `end`, which is always the size of
456 // `things` and thus never stored in the array
457self.things.len()
458 };
459460if start > end {
461return Err(VarZeroVecFormatError::Metadata);
462 }
463if end > self.things.len() {
464return Err(VarZeroVecFormatError::Metadata);
465 }
466// Safety: start..end is a valid range in self.things
467let bytes = unsafe { self.things.get_unchecked(start..end) };
468 T::parse_bytes(bytes).map_err(VarZeroVecFormatError::Values)?;
469 start = end;
470 }
471Ok(())
472 }
473474/// Create an iterator over the Ts contained in VarZeroVecComponents
475#[inline]
476pub fn iter(self) -> VarZeroSliceIter<'a, T, F> {
477VarZeroSliceIter::new(self)
478 }
479480#[cfg(feature = "alloc")]
481pub fn to_vec(self) -> alloc::vec::Vec<alloc::boxed::Box<T>> {
482self.iter().map(T::to_boxed).collect()
483 }
484485#[inline]
486fn indices_slice(&self) -> &'a [F::Index] {
487unsafe { F::Index::slice_from_bytes_unchecked(self.indices) }
488 }
489490// Dump a debuggable representation of this type
491#[allow(unused)] // useful for debugging
492#[cfg(feature = "alloc")]
493pub(crate) fn dump(&self) -> alloc::string::String {
494let indices = self
495.indices_slice()
496 .iter()
497 .copied()
498 .map(IntegerULE::iule_to_usize)
499 .collect::<alloc::vec::Vec<_>>();
500alloc::format!("VarZeroVecComponents {{ indices: {indices:?} }}")
501 }
502}
503504/// An iterator over VarZeroSlice
505#[derive(#[automatically_derived]
impl<'a, T: ::core::fmt::Debug + ?Sized, F: ::core::fmt::Debug>
::core::fmt::Debug for VarZeroSliceIter<'a, T, F> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"VarZeroSliceIter", "components", &self.components, "index",
&self.index, "start_index", &&self.start_index)
}
}Debug)]
506pub struct VarZeroSliceIter<'a, T: ?Sized, F = Index16> {
507 components: VarZeroVecComponents<'a, T, F>,
508 index: usize,
509// Safety invariant: must be a valid index into the data segment of `components`, or an index at the end
510 // i.e. start_index <= components.things.len()
511 //
512 // It must be a valid index into the `things` array of components, coming from `components.indices_slice()`
513start_index: usize,
514}
515516impl<'a, T: VarULE + ?Sized, F: VarZeroVecFormat> VarZeroSliceIter<'a, T, F> {
517fn new(c: VarZeroVecComponents<'a, T, F>) -> Self {
518Self {
519 components: c,
520 index: 0,
521// Invariant upheld, 0 is always a valid index-or-end
522start_index: 0,
523 }
524 }
525}
526impl<'a, T: VarULE + ?Sized, F: VarZeroVecFormat> Iteratorfor VarZeroSliceIter<'a, T, F> {
527type Item = &'a T;
528529fn next(&mut self) -> Option<Self::Item> {
530// Note: the indices array doesn't contain 0 or len, we need to specially handle those edges. The 0 is handled
531 // by start_index, and the len is handled by the code for `end`.
532533if self.index >= self.components.len() {
534return None;
535 }
536537// Invariant established: self.index is in bounds for self.components.len(),
538 // which means it is in bounds for self.components.indices_slice() since that has the same length
539540let end = if self.index + 1 == self.components.len() {
541// We don't store the end index since it is computable, so the last element should use self.components.things.len()
542self.components.things.len()
543 } else {
544// Safety: self.index was known to be in bounds from the bounds check above.
545unsafe {
546self.components
547 .indices_slice()
548 .get_unchecked(self.index)
549 .iule_to_usize()
550 }
551 };
552// Invariant established: end has the same invariant as self.start_index since it comes from indices_slice, which is guaranteed
553 // to only contain valid indexes
554555let item = unsafe {
556// Safety: self.start_index and end both have in-range invariants, plus they are valid indices from indices_slice
557 // which means we can treat this data as a T
558T::from_bytes_unchecked(self.components.things.get_unchecked(self.start_index..end))
559 };
560self.index += 1;
561// Invariant upheld: end has the same invariant as self.start_index
562self.start_index = end;
563Some(item)
564 }
565566fn size_hint(&self) -> (usize, Option<usize>) {
567let remainder = self.components.len() - self.index;
568 (remainder, Some(remainder))
569 }
570}
571572impl<'a, T: VarULE + ?Sized, F: VarZeroVecFormat> ExactSizeIteratorfor VarZeroSliceIter<'a, T, F> {
573fn len(&self) -> usize {
574self.components.len() - self.index
575 }
576}
577578impl<'a, T, F> VarZeroVecComponents<'a, T, F>
579where
580T: VarULE,
581 T: ?Sized,
582 T: Ord,
583 F: VarZeroVecFormat,
584{
585/// Binary searches a sorted `VarZeroVecComponents<T>` for the given element. For more information, see
586 /// the primitive function [`binary_search`](slice::binary_search).
587pub fn binary_search(&self, needle: &T) -> Result<usize, usize> {
588self.binary_search_by(|probe| probe.cmp(needle))
589 }
590591pub fn binary_search_in_range(
592&self,
593 needle: &T,
594 range: Range<usize>,
595 ) -> Option<Result<usize, usize>> {
596self.binary_search_in_range_by(|probe| probe.cmp(needle), range)
597 }
598}
599600impl<'a, T, F> VarZeroVecComponents<'a, T, F>
601where
602T: VarULE,
603 T: ?Sized,
604 F: VarZeroVecFormat,
605{
606/// Binary searches a sorted `VarZeroVecComponents<T>` for the given predicate. For more information, see
607 /// the primitive function [`binary_search_by`](slice::binary_search_by).
608pub fn binary_search_by(&self, predicate: impl FnMut(&T) -> Ordering) -> Result<usize, usize> {
609// Safety: 0 and len are in range
610unsafe { self.binary_search_in_range_unchecked(predicate, 0..self.len()) }
611 }
612613// Binary search within a range.
614 // Values returned are relative to the range start!
615pub fn binary_search_in_range_by(
616&self,
617 predicate: impl FnMut(&T) -> Ordering,
618 range: Range<usize>,
619 ) -> Option<Result<usize, usize>> {
620if range.end > self.len() {
621return None;
622 }
623if range.end < range.start {
624return None;
625 }
626// Safety: We bounds checked above: end is in-bounds or len, and start is <= end
627let range_absolute =
628unsafe { self.binary_search_in_range_unchecked(predicate, range.clone()) };
629// The values returned are relative to the range start
630Some(
631range_absolute632 .map(|o| o - range.start)
633 .map_err(|e| e - range.start),
634 )
635 }
636637/// Safety: range must be in range for the slice (start <= len, end <= len, start <= end)
638unsafe fn binary_search_in_range_unchecked(
639&self,
640mut predicate: impl FnMut(&T) -> Ordering,
641 range: Range<usize>,
642 ) -> Result<usize, usize> {
643// Function invariant: size is always end - start
644let mut start = range.start;
645let mut end = range.end;
646let mut size;
647648// Loop invariant: 0 <= start < end <= len
649 // This invariant is initialized by the function safety invariants and the loop condition
650while start < end {
651 size = end - start;
652// This establishes mid < end (which implies mid < len)
653 // size is end - start. start + size is end (which is <= len).
654 // mid = start + size/2 will be less than end
655let mid = start + size / 2;
656657// Safety: mid is < end <= len, so in-range
658let cmp = predicate(self.get_unchecked(mid));
659660match cmp {
661 Ordering::Less => {
662// This retains the loop invariant since it
663 // increments start, and we already have 0 <= start
664 // start < end is enforced by the loop condition
665start = mid + 1;
666 }
667 Ordering::Greater => {
668// mid < end, so this decreases end.
669 // This means end <= len is still true, and
670 // end > start is enforced by the loop condition
671end = mid;
672 }
673 Ordering::Equal => return Ok(mid),
674 }
675 }
676Err(start)
677 }
678}
679680/// Collects the bytes for a VarZeroSlice into a Vec.
681#[cfg(feature = "alloc")]
682pub fn get_serializable_bytes_non_empty<T, A, F>(elements: &[A]) -> Option<alloc::vec::Vec<u8>>
683where
684T: VarULE + ?Sized,
685 A: EncodeAsVarULE<T>,
686 F: VarZeroVecFormat,
687{
688debug_assert!(!elements.is_empty());
689let len = compute_serializable_len::<T, A, F>(elements)?;
690debug_assert!(
691 len >= F::Len::SIZE as u32,
692"Must have at least F::Len::SIZE bytes to hold the length of the vector"
693);
694let mut output = alloc::vec![0u8; len as usize];
695 write_serializable_bytes::<T, A, F>(elements, &mut output);
696Some(output)
697}
698699/// Writes the bytes for a VarZeroLengthlessSlice into an output buffer.
700/// Usable for a VarZeroSlice if you first write the length bytes.
701///
702/// Every byte in the buffer will be initialized after calling this function.
703///
704/// # Panics
705///
706/// Panics if the buffer is not exactly the correct length.
707pub fn write_serializable_bytes_without_length<T, A, F>(elements: &[A], output: &mut [u8])
708where
709T: VarULE + ?Sized,
710 A: EncodeAsVarULE<T>,
711 F: VarZeroVecFormat,
712{
713if !(elements.len() <= F::Len::MAX_VALUE as usize) {
::core::panicking::panic("assertion failed: elements.len() <= F::Len::MAX_VALUE as usize")
};assert!(elements.len() <= F::Len::MAX_VALUE as usize);
714if elements.is_empty() {
715return;
716 }
717718// idx_offset = offset from the start of the buffer for the next index
719let mut idx_offset: usize = 0;
720// first_dat_offset = offset from the start of the buffer of the first data block
721let first_dat_offset: usize = idx_offset + (elements.len() - 1) * F::Index::SIZE;
722// dat_offset = offset from the start of the buffer of the next data block
723let mut dat_offset: usize = first_dat_offset;
724725for (i, element) in elements.iter().enumerate() {
726let element_len = element.encode_var_ule_len();
727728// The first index is always 0. We don't write it, or update the idx offset.
729if i != 0 {
730let idx_limit = idx_offset + F::Index::SIZE;
731#[expect(clippy::indexing_slicing)] // Function contract allows panicky behavior
732let idx_slice = &mut output[idx_offset..idx_limit];
733// VZV expects data offsets to be stored relative to the first data block
734let idx = dat_offset - first_dat_offset;
735if !(idx <= F::Index::MAX_VALUE as usize) {
::core::panicking::panic("assertion failed: idx <= F::Index::MAX_VALUE as usize")
};assert!(idx <= F::Index::MAX_VALUE as usize);
736#[expect(clippy::expect_used)] // this function is explicitly panicky
737let bytes_to_write = F::Index::iule_from_usize(idx).expect(F::Index::TOO_LARGE_ERROR);
738 idx_slice.copy_from_slice(ULE::slice_as_bytes(&[bytes_to_write]));
739740 idx_offset = idx_limit;
741 }
742743let dat_limit = dat_offset + element_len;
744#[expect(clippy::indexing_slicing)] // Function contract allows panicky behavior
745let dat_slice = &mut output[dat_offset..dat_limit];
746 element.encode_var_ule_write(dat_slice);
747if true {
match (&T::validate_bytes(dat_slice), &Ok(())) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(T::validate_bytes(dat_slice), Ok(()));
748 dat_offset = dat_limit;
749 }
750751if true {
match (&idx_offset, &(F::Index::SIZE * (elements.len() - 1))) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
};
};debug_assert_eq!(idx_offset, F::Index::SIZE * (elements.len() - 1));
752match (&dat_offset, &output.len()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(dat_offset, output.len());
753}
754755/// Writes the bytes for a VarZeroSlice into an output buffer.
756///
757/// Every byte in the buffer will be initialized after calling this function.
758///
759/// # Panics
760///
761/// Panics if the buffer is not exactly the correct length.
762pub fn write_serializable_bytes<T, A, F>(elements: &[A], output: &mut [u8])
763where
764T: VarULE + ?Sized,
765 A: EncodeAsVarULE<T>,
766 F: VarZeroVecFormat,
767{
768if elements.is_empty() {
769return;
770 }
771if !(elements.len() <= F::Len::MAX_VALUE as usize) {
::core::panicking::panic("assertion failed: elements.len() <= F::Len::MAX_VALUE as usize")
};assert!(elements.len() <= F::Len::MAX_VALUE as usize);
772#[expect(clippy::expect_used)] // This function is explicitly panicky
773let num_elements_ule = F::Len::iule_from_usize(elements.len()).expect(F::Len::TOO_LARGE_ERROR);
774#[expect(clippy::indexing_slicing)] // Function contract allows panicky behavior
775output[0..F::Len::SIZE].copy_from_slice(ULE::slice_as_bytes(&[num_elements_ule]));
776777#[expect(clippy::indexing_slicing)] // Function contract allows panicky behavior
778write_serializable_bytes_without_length::<T, A, F>(elements, &mut output[F::Len::SIZE..]);
779}
780781pub fn compute_serializable_len_without_length<T, A, F>(elements: &[A]) -> Option<u32>
782where
783T: VarULE + ?Sized,
784 A: EncodeAsVarULE<T>,
785 F: VarZeroVecFormat,
786{
787let elements_len = elements.len();
788let Some(elements_len_minus_one) = elements_len.checked_sub(1) else {
789// Empty vec is optimized to an empty byte representation
790return Some(0);
791 };
792let idx_len: u32 = u32::try_from(elements_len_minus_one)
793 .ok()?
794.checked_mul(F::Index::SIZEas u32)?;
795let data_len: u32 = elements796 .iter()
797 .map(|v| u32::try_from(v.encode_var_ule_len()).ok())
798 .try_fold(0u32, |s, v| s.checked_add(v?))?;
799let ret = idx_len.checked_add(data_len);
800if let Some(r) = ret {
801if r >= F::Index::MAX_VALUE {
802return None;
803 }
804 }
805ret806}
807808pub fn compute_serializable_len<T, A, F>(elements: &[A]) -> Option<u32>
809where
810T: VarULE + ?Sized,
811 A: EncodeAsVarULE<T>,
812 F: VarZeroVecFormat,
813{
814compute_serializable_len_without_length::<T, A, F>(elements).map(|x| x + F::Len::SIZEas u32)
815}