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