Skip to main content

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