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