zerovec/zerovec/
slice.rs

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 ).
4
5use super::*;
6use core::cmp::Ordering;
7use core::ops::Range;
8
9/// A zero-copy "slice", i.e. the zero-copy version of `[T]`.
10///
11/// This behaves
12/// similarly to [`ZeroVec<T>`], however [`ZeroVec<T>`] is allowed to contain
13/// owned data and as such is ideal for deserialization since most human readable
14/// serialization formats cannot unconditionally deserialize zero-copy.
15///
16/// This type can be used inside [`VarZeroVec<T>`](crate::VarZeroVec) and [`ZeroMap`](crate::ZeroMap):
17/// This essentially allows for the construction of zero-copy types isomorphic to `Vec<Vec<T>>` by instead
18/// using `VarZeroVec<ZeroSlice<T>>`. See the [`VarZeroVec`](crate::VarZeroVec) docs for an example.
19///
20/// # Examples
21///
22/// Const-construct a ZeroSlice of u16:
23///
24/// ```
25/// use zerovec::ule::AsULE;
26/// use zerovec::ZeroSlice;
27///
28/// const DATA: &ZeroSlice<u16> =
29///     ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([
30///         211, 281, 421, 32973,
31///     ]));
32///
33/// assert_eq!(DATA.get(1), Some(281));
34/// ```
35#[repr(transparent)]
36pub struct ZeroSlice<T: AsULE>([T::ULE]);
37
38impl<T> ZeroSlice<T>
39where
40    T: AsULE,
41{
42    /// Returns an empty slice.
43    pub const fn new_empty() -> &'static Self {
44        Self::from_ule_slice(&[])
45    }
46
47    /// Get this [`ZeroSlice`] as a borrowed [`ZeroVec`]
48    ///
49    /// [`ZeroSlice`] does not have most of the methods that [`ZeroVec`] does,
50    /// so it is recommended to convert it to a [`ZeroVec`] before doing anything.
51    #[inline]
52    pub const fn as_zerovec(&self) -> ZeroVec<'_, T> {
53        ZeroVec::new_borrowed(&self.0)
54    }
55
56    /// Attempt to construct a `&ZeroSlice<T>` from a byte slice, returning an error
57    /// if it's not a valid byte sequence
58    pub fn parse_bytes(bytes: &[u8]) -> Result<&Self, UleError> {
59        T::ULE::parse_bytes_to_slice(bytes).map(Self::from_ule_slice)
60    }
61
62    /// Uses a `&[u8]` buffer as a `ZeroVec<T>` without any verification.
63    ///
64    /// # Safety
65    ///
66    /// `bytes` need to be an output from [`ZeroSlice::as_bytes()`].
67    pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
68        // &[u8] and &[T::ULE] are the same slice with different length metadata.
69        Self::from_ule_slice(core::slice::from_raw_parts(
70            bytes.as_ptr() as *const T::ULE,
71            bytes.len() / core::mem::size_of::<T::ULE>(),
72        ))
73    }
74
75    /// Construct a `&ZeroSlice<T>` from a slice of ULEs.
76    ///
77    /// This function can be used for constructing ZeroVecs in a const context, avoiding
78    /// parsing checks.
79    ///
80    /// See [`ZeroSlice`] for an example.
81    #[inline]
82    pub const fn from_ule_slice(slice: &[T::ULE]) -> &Self {
83        // This is safe because ZeroSlice is transparent over [T::ULE]
84        // so &ZeroSlice<T> can be safely cast from &[T::ULE]
85        unsafe { &*(slice as *const _ as *const Self) }
86    }
87
88    /// Construct a `Box<ZeroSlice<T>>` from a boxed slice of ULEs
89    #[inline]
90    #[cfg(feature = "alloc")]
91    pub fn from_boxed_slice(slice: alloc::boxed::Box<[T::ULE]>) -> alloc::boxed::Box<Self> {
92        // This is safe because ZeroSlice is transparent over [T::ULE]
93        // so Box<ZeroSlice<T>> can be safely cast from Box<[T::ULE]>
94        unsafe { alloc::boxed::Box::from_raw(alloc::boxed::Box::into_raw(slice) as *mut Self) }
95    }
96
97    /// Returns this slice as its underlying `&[u8]` byte buffer representation.
98    ///
99    /// Useful for serialization.
100    ///
101    /// # Example
102    ///
103    /// ```
104    /// use zerovec::ZeroVec;
105    ///
106    /// // The little-endian bytes correspond to the numbers on the following line.
107    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
108    /// let nums: &[u16] = &[211, 281, 421, 32973];
109    ///
110    /// let zerovec = ZeroVec::alloc_from_slice(nums);
111    ///
112    /// assert_eq!(bytes, zerovec.as_bytes());
113    /// ```
114    #[inline]
115    pub fn as_bytes(&self) -> &[u8] {
116        T::ULE::slice_as_bytes(self.as_ule_slice())
117    }
118
119    /// Dereferences this slice as `&[T::ULE]`.
120    #[inline]
121    pub const fn as_ule_slice(&self) -> &[T::ULE] {
122        &self.0
123    }
124
125    /// Returns the number of elements in this slice.
126    ///
127    /// # Example
128    ///
129    /// ```
130    /// use zerovec::ule::AsULE;
131    /// use zerovec::ZeroVec;
132    ///
133    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
134    /// let zerovec: ZeroVec<u16> =
135    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
136    ///
137    /// assert_eq!(4, zerovec.len());
138    /// assert_eq!(
139    ///     bytes.len(),
140    ///     zerovec.len() * std::mem::size_of::<<u16 as AsULE>::ULE>()
141    /// );
142    /// ```
143    #[inline]
144    pub const fn len(&self) -> usize {
145        self.as_ule_slice().len()
146    }
147
148    /// Returns whether this slice is empty.
149    ///
150    /// # Example
151    ///
152    /// ```
153    /// use zerovec::ZeroVec;
154    ///
155    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
156    /// let zerovec: ZeroVec<u16> =
157    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
158    /// assert!(!zerovec.is_empty());
159    ///
160    /// let emptyvec: ZeroVec<u16> = ZeroVec::parse_bytes(&[]).expect("infallible");
161    /// assert!(emptyvec.is_empty());
162    /// ```
163    #[inline]
164    pub const fn is_empty(&self) -> bool {
165        self.as_ule_slice().is_empty()
166    }
167}
168
169impl<T> ZeroSlice<T>
170where
171    T: AsULE,
172{
173    /// Gets the element at the specified index. Returns `None` if out of range.
174    ///
175    /// # Example
176    ///
177    /// ```
178    /// use zerovec::ZeroVec;
179    ///
180    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
181    /// let zerovec: ZeroVec<u16> =
182    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
183    ///
184    /// assert_eq!(zerovec.get(2), Some(421));
185    /// assert_eq!(zerovec.get(4), None);
186    /// ```
187    #[inline]
188    pub fn get(&self, index: usize) -> Option<T> {
189        self.as_ule_slice()
190            .get(index)
191            .copied()
192            .map(T::from_unaligned)
193    }
194
195    /// Gets the entire slice as an array of length `N`. Returns `None` if the slice
196    /// does not have exactly `N` elements.
197    ///
198    /// # Example
199    ///
200    /// ```
201    /// use zerovec::ZeroVec;
202    ///
203    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
204    /// let zerovec: ZeroVec<u16> =
205    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
206    /// let array: [u16; 4] =
207    ///     zerovec.get_as_array().expect("should be 4 items in array");
208    ///
209    /// assert_eq!(array[2], 421);
210    /// ```
211    pub fn get_as_array<const N: usize>(&self) -> Option<[T; N]> {
212        let ule_array = <&[T::ULE; N]>::try_from(self.as_ule_slice()).ok()?;
213        Some(ule_array.map(|u| T::from_unaligned(u)))
214    }
215
216    /// Gets a subslice of elements within a certain range. Returns `None` if the range
217    /// is out of bounds of this `ZeroSlice`.
218    ///
219    /// # Example
220    ///
221    /// ```
222    /// use zerovec::ZeroVec;
223    ///
224    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
225    /// let zerovec: ZeroVec<u16> =
226    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
227    ///
228    /// assert_eq!(
229    ///     zerovec.get_subslice(1..3),
230    ///     Some(&*ZeroVec::from_slice_or_alloc(&[0x0119, 0x01A5]))
231    /// );
232    /// assert_eq!(zerovec.get_subslice(3..5), None);
233    /// ```
234    #[inline]
235    pub fn get_subslice(&self, range: Range<usize>) -> Option<&ZeroSlice<T>> {
236        self.0.get(range).map(ZeroSlice::from_ule_slice)
237    }
238
239    /// Get a borrowed reference to the underlying ULE type at a specified index.
240    ///
241    /// Prefer [`Self::get()`] over this method where possible since working
242    /// directly with `ULE` types is less ergonomic
243    pub fn get_ule_ref(&self, index: usize) -> Option<&T::ULE> {
244        self.as_ule_slice().get(index)
245    }
246
247    /// Casts a `ZeroSlice<T>` to a compatible `ZeroSlice<P>`.
248    ///
249    /// `T` and `P` are compatible if they have the same `ULE` representation.
250    ///
251    /// If the `ULE`s of `T` and `P` are different, use [`Self::try_as_converted()`].
252    ///
253    /// # Examples
254    ///
255    /// ```
256    /// use zerovec::ZeroSlice;
257    ///
258    /// const BYTES: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
259    /// const ZS_U16: &ZeroSlice<u16> = {
260    ///     match ZeroSlice::<u16>::try_from_bytes(BYTES) {
261    ///         Ok(s) => s,
262    ///         Err(_) => unreachable!(),
263    ///     }
264    /// };
265    ///
266    /// let zs_i16: &ZeroSlice<i16> = ZS_U16.cast();
267    ///
268    /// assert_eq!(ZS_U16.get(3), Some(32973));
269    /// assert_eq!(zs_i16.get(3), Some(-32563));
270    /// ```
271    #[inline]
272    pub const fn cast<P>(&self) -> &ZeroSlice<P>
273    where
274        P: AsULE<ULE = T::ULE>,
275    {
276        ZeroSlice::<P>::from_ule_slice(self.as_ule_slice())
277    }
278
279    /// Converts a `&ZeroSlice<T>` into a `&ZeroSlice<P>`.
280    ///
281    /// The resulting slice will have the same length as the original slice
282    /// if and only if `T::ULE` and `P::ULE` are the same size.
283    ///
284    /// If `T` and `P` have the exact same `ULE`, use [`Self::cast()`].
285    ///
286    /// # Examples
287    ///
288    /// ```
289    /// use zerovec::ZeroSlice;
290    ///
291    /// const BYTES: &[u8] = &[0x7F, 0xF3, 0x01, 0x00, 0x49, 0xF6, 0x01, 0x00];
292    /// const ZS_U32: &ZeroSlice<u32> = {
293    ///     match ZeroSlice::<u32>::try_from_bytes(BYTES) {
294    ///         Ok(s) => s,
295    ///         Err(_) => unreachable!(),
296    ///     }
297    /// };
298    ///
299    /// let zs_u8_4: &ZeroSlice<[u8; 4]> =
300    ///     ZS_U32.try_as_converted().expect("valid code points");
301    ///
302    /// assert_eq!(ZS_U32.get(0), Some(127871));
303    /// assert_eq!(zs_u8_4.get(0), Some([0x7F, 0xF3, 0x01, 0x00]));
304    /// ```
305    #[inline]
306    pub fn try_as_converted<P: AsULE>(&self) -> Result<&ZeroSlice<P>, UleError> {
307        let new_slice = P::ULE::parse_bytes_to_slice(self.as_bytes())?;
308        Ok(ZeroSlice::from_ule_slice(new_slice))
309    }
310
311    /// Gets the first element. Returns `None` if empty.
312    ///
313    /// # Example
314    ///
315    /// ```
316    /// use zerovec::ZeroVec;
317    ///
318    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
319    /// let zerovec: ZeroVec<u16> =
320    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
321    ///
322    /// assert_eq!(zerovec.first(), Some(211));
323    /// ```
324    #[inline]
325    pub fn first(&self) -> Option<T> {
326        self.as_ule_slice().first().copied().map(T::from_unaligned)
327    }
328
329    /// Gets the last element. Returns `None` if empty.
330    ///
331    /// # Example
332    ///
333    /// ```
334    /// use zerovec::ZeroVec;
335    ///
336    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
337    /// let zerovec: ZeroVec<u16> =
338    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
339    ///
340    /// assert_eq!(zerovec.last(), Some(32973));
341    /// ```
342    #[inline]
343    pub fn last(&self) -> Option<T> {
344        self.as_ule_slice().last().copied().map(T::from_unaligned)
345    }
346
347    /// Gets an iterator over the elements.
348    ///
349    /// # Example
350    ///
351    /// ```
352    /// use zerovec::ZeroVec;
353    ///
354    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
355    /// let zerovec: ZeroVec<u16> =
356    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
357    /// let mut it = zerovec.iter();
358    ///
359    /// assert_eq!(it.next(), Some(211));
360    /// assert_eq!(it.next(), Some(281));
361    /// assert_eq!(it.next(), Some(421));
362    /// assert_eq!(it.next(), Some(32973));
363    /// assert_eq!(it.next(), None);
364    /// ```
365    #[inline]
366    pub fn iter<'a>(&'a self) -> ZeroSliceIter<'a, T> {
367        ZeroSliceIter(self.as_ule_slice().iter())
368    }
369
370    /// Returns a tuple with the first element and a subslice of the remaining elements.
371    ///
372    /// # Example
373    ///
374    /// ```
375    /// use zerovec::ule::AsULE;
376    /// use zerovec::ZeroSlice;
377    ///
378    /// const DATA: &ZeroSlice<u16> =
379    ///     ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([
380    ///         211, 281, 421, 32973,
381    ///     ]));
382    /// const EXPECTED_VALUE: (u16, &ZeroSlice<u16>) = (
383    ///     211,
384    ///     ZeroSlice::<u16>::from_ule_slice(&<u16 as AsULE>::ULE::from_array([
385    ///         281, 421, 32973,
386    ///     ])),
387    /// );
388    /// assert_eq!(EXPECTED_VALUE, DATA.split_first().unwrap());
389    /// ```
390    #[inline]
391    pub fn split_first(&self) -> Option<(T, &ZeroSlice<T>)> {
392        if let Some(first) = self.first() {
393            return Some((
394                first,
395                // `unwrap()` must succeed, because `first()` returned `Some`.
396                #[allow(clippy::unwrap_used)]
397                self.get_subslice(1..self.len()).unwrap(),
398            ));
399        }
400        None
401    }
402}
403
404/// An iterator over elements in a VarZeroVec
405#[derive(Debug)]
406pub struct ZeroSliceIter<'a, T: AsULE>(core::slice::Iter<'a, T::ULE>);
407
408impl<'a, T: AsULE> Iterator for ZeroSliceIter<'a, T> {
409    type Item = T;
410    fn next(&mut self) -> Option<T> {
411        self.0.next().copied().map(T::from_unaligned)
412    }
413}
414
415impl<'a, T: AsULE> ExactSizeIterator for ZeroSliceIter<'a, T> {
416    fn len(&self) -> usize {
417        self.0.len()
418    }
419}
420
421impl<'a, T: AsULE> DoubleEndedIterator for ZeroSliceIter<'a, T> {
422    fn next_back(&mut self) -> Option<T> {
423        self.0.next_back().copied().map(T::from_unaligned)
424    }
425}
426
427impl<T> ZeroSlice<T>
428where
429    T: AsULE + Ord,
430{
431    /// Binary searches a sorted `ZeroVec<T>` for the given element. For more information, see
432    /// the primitive function [`binary_search`].
433    ///
434    /// # Example
435    ///
436    /// ```
437    /// use zerovec::ZeroVec;
438    ///
439    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
440    /// let zerovec: ZeroVec<u16> =
441    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
442    ///
443    /// assert_eq!(zerovec.binary_search(&281), Ok(1));
444    /// assert_eq!(zerovec.binary_search(&282), Err(2));
445    /// ```
446    ///
447    /// [`binary_search`]: https://doc.rust-lang.org/std/primitive.slice.html#method.binary_search
448    #[inline]
449    pub fn binary_search(&self, x: &T) -> Result<usize, usize> {
450        self.as_ule_slice()
451            .binary_search_by(|probe| T::from_unaligned(*probe).cmp(x))
452    }
453}
454
455impl<T> ZeroSlice<T>
456where
457    T: AsULE,
458{
459    /// Binary searches a sorted `ZeroVec<T>` based on a given predicate. For more information, see
460    /// the primitive function [`binary_search_by`].
461    ///
462    /// # Example
463    ///
464    /// ```
465    /// use zerovec::ZeroVec;
466    ///
467    /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
468    /// let zerovec: ZeroVec<u16> =
469    ///     ZeroVec::parse_bytes(bytes).expect("infallible");
470    ///
471    /// assert_eq!(zerovec.binary_search_by(|x| x.cmp(&281)), Ok(1));
472    /// assert_eq!(zerovec.binary_search_by(|x| x.cmp(&282)), Err(2));
473    /// ```
474    ///
475    /// [`binary_search_by`]: https://doc.rust-lang.org/std/primitive.slice.html#method.binary_search_by
476    #[inline]
477    pub fn binary_search_by(
478        &self,
479        mut predicate: impl FnMut(T) -> Ordering,
480    ) -> Result<usize, usize> {
481        self.as_ule_slice()
482            .binary_search_by(|probe| predicate(T::from_unaligned(*probe)))
483    }
484}
485
486// Safety (based on the safety checklist on the VarULE trait):
487// (`ZeroSlice<T>` is a transparent wrapper around [T::ULE])
488//  1. [T::ULE] does not include any uninitialized or padding bytes (achieved by being a slice of a ULE type)
489//  2. [T::ULE] is aligned to 1 byte (achieved by being a slice of a ULE type)
490//  3. The impl of `validate_bytes()` returns an error if any byte is not valid.
491//  4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety
492//  5. The impl of `from_bytes_unchecked()` returns a reference to the same data.
493//  6. `as_bytes()` and `parse_bytes()` are defaulted
494//  7. `[T::ULE]` byte equality is semantic equality (relying on the guideline of the underlying `ULE` type)
495unsafe impl<T: AsULE + 'static> VarULE for ZeroSlice<T> {
496    #[inline]
497    fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
498        T::ULE::validate_bytes(bytes)
499    }
500
501    #[inline]
502    unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
503        Self::from_ule_slice(T::ULE::slice_from_bytes_unchecked(bytes))
504    }
505}
506
507impl<T> Eq for ZeroSlice<T> where T: AsULE + Eq {}
508
509impl<T> PartialEq<ZeroSlice<T>> for ZeroSlice<T>
510where
511    T: AsULE + PartialEq,
512{
513    #[inline]
514    fn eq(&self, other: &ZeroSlice<T>) -> bool {
515        self.as_zerovec().eq(&other.as_zerovec())
516    }
517}
518
519impl<T> PartialEq<[T]> for ZeroSlice<T>
520where
521    T: AsULE + PartialEq,
522{
523    #[inline]
524    fn eq(&self, other: &[T]) -> bool {
525        self.iter().eq(other.iter().copied())
526    }
527}
528
529impl<'a, T> PartialEq<ZeroVec<'a, T>> for ZeroSlice<T>
530where
531    T: AsULE + PartialEq,
532{
533    #[inline]
534    fn eq(&self, other: &ZeroVec<'a, T>) -> bool {
535        self.as_zerovec().eq(other)
536    }
537}
538
539impl<'a, T> PartialEq<ZeroSlice<T>> for ZeroVec<'a, T>
540where
541    T: AsULE + PartialEq,
542{
543    #[inline]
544    fn eq(&self, other: &ZeroSlice<T>) -> bool {
545        self.eq(&other.as_zerovec())
546    }
547}
548
549impl<T> fmt::Debug for ZeroSlice<T>
550where
551    T: AsULE + fmt::Debug,
552{
553    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554        self.as_zerovec().fmt(f)
555    }
556}
557
558impl<T: AsULE + PartialOrd> PartialOrd for ZeroSlice<T> {
559    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
560        self.iter().partial_cmp(other.iter())
561    }
562}
563
564impl<T: AsULE + Ord> Ord for ZeroSlice<T> {
565    fn cmp(&self, other: &Self) -> Ordering {
566        self.iter().cmp(other.iter())
567    }
568}
569
570#[cfg(feature = "alloc")]
571impl<T: AsULE> AsRef<ZeroSlice<T>> for alloc::vec::Vec<T::ULE> {
572    fn as_ref(&self) -> &ZeroSlice<T> {
573        ZeroSlice::<T>::from_ule_slice(self)
574    }
575}
576
577impl<T: AsULE> AsRef<ZeroSlice<T>> for &[T::ULE] {
578    fn as_ref(&self) -> &ZeroSlice<T> {
579        ZeroSlice::<T>::from_ule_slice(self)
580    }
581}
582
583impl<T> Default for &ZeroSlice<T>
584where
585    T: AsULE,
586{
587    fn default() -> Self {
588        ZeroSlice::from_ule_slice(&[])
589    }
590}
591
592#[cfg(test)]
593mod test {
594    use super::*;
595    use crate::zeroslice;
596
597    #[test]
598    fn test_split_first() {
599        {
600            // empty slice.
601            assert_eq!(None, ZeroSlice::<u16>::new_empty().split_first());
602        }
603        {
604            // single element slice
605            const DATA: &ZeroSlice<u16> =
606                zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [211]);
607            assert_eq!((211, zeroslice![]), DATA.split_first().unwrap());
608        }
609        {
610            // slice with many elements.
611            const DATA: &ZeroSlice<u16> =
612                zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [211, 281, 421, 32973]);
613            const EXPECTED_VALUE: (u16, &ZeroSlice<u16>) = (
614                211,
615                zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [281, 421, 32973]),
616            );
617
618            assert_eq!(EXPECTED_VALUE, DATA.split_first().unwrap());
619        }
620    }
621}