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 #[expect(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 fn size_hint(&self) -> (usize, Option<usize>) {
415 self.0.size_hint()
416 }
417}
418
419impl<'a, T: AsULE> ExactSizeIterator for ZeroSliceIter<'a, T> {
420 fn len(&self) -> usize {
421 self.0.len()
422 }
423}
424
425impl<'a, T: AsULE> DoubleEndedIterator for ZeroSliceIter<'a, T> {
426 fn next_back(&mut self) -> Option<T> {
427 self.0.next_back().copied().map(T::from_unaligned)
428 }
429}
430
431impl<T> ZeroSlice<T>
432where
433 T: AsULE + Ord,
434{
435 /// Binary searches a sorted `ZeroVec<T>` for the given element. For more information, see
436 /// the primitive function [`binary_search`].
437 ///
438 /// # Example
439 ///
440 /// ```
441 /// use zerovec::ZeroVec;
442 ///
443 /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
444 /// let zerovec: ZeroVec<u16> =
445 /// ZeroVec::parse_bytes(bytes).expect("infallible");
446 ///
447 /// assert_eq!(zerovec.binary_search(&281), Ok(1));
448 /// assert_eq!(zerovec.binary_search(&282), Err(2));
449 /// ```
450 ///
451 /// [`binary_search`]: https://doc.rust-lang.org/std/primitive.slice.html#method.binary_search
452 #[inline]
453 pub fn binary_search(&self, x: &T) -> Result<usize, usize> {
454 self.as_ule_slice()
455 .binary_search_by(|probe| T::from_unaligned(*probe).cmp(x))
456 }
457}
458
459impl<T> ZeroSlice<T>
460where
461 T: AsULE,
462{
463 /// Binary searches a sorted `ZeroVec<T>` based on a given predicate. For more information, see
464 /// the primitive function [`binary_search_by`].
465 ///
466 /// # Example
467 ///
468 /// ```
469 /// use zerovec::ZeroVec;
470 ///
471 /// let bytes: &[u8] = &[0xD3, 0x00, 0x19, 0x01, 0xA5, 0x01, 0xCD, 0x80];
472 /// let zerovec: ZeroVec<u16> =
473 /// ZeroVec::parse_bytes(bytes).expect("infallible");
474 ///
475 /// assert_eq!(zerovec.binary_search_by(|x| x.cmp(&281)), Ok(1));
476 /// assert_eq!(zerovec.binary_search_by(|x| x.cmp(&282)), Err(2));
477 /// ```
478 ///
479 /// [`binary_search_by`]: https://doc.rust-lang.org/std/primitive.slice.html#method.binary_search_by
480 #[inline]
481 pub fn binary_search_by(
482 &self,
483 mut predicate: impl FnMut(T) -> Ordering,
484 ) -> Result<usize, usize> {
485 self.as_ule_slice()
486 .binary_search_by(|probe| predicate(T::from_unaligned(*probe)))
487 }
488}
489
490// Safety (based on the safety checklist on the VarULE trait):
491// (`ZeroSlice<T>` is a transparent wrapper around [T::ULE])
492// 1. [T::ULE] does not include any uninitialized or padding bytes (achieved by being a slice of a ULE type)
493// 2. [T::ULE] is aligned to 1 byte (achieved by being a slice of a ULE type)
494// 3. The impl of `validate_bytes()` returns an error if any byte is not valid.
495// 4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety
496// 5. The impl of `from_bytes_unchecked()` returns a reference to the same data.
497// 6. `as_bytes()` and `parse_bytes()` are defaulted
498// 7. `[T::ULE]` byte equality is semantic equality (relying on the guideline of the underlying `ULE` type)
499unsafe impl<T: AsULE + 'static> VarULE for ZeroSlice<T> {
500 #[inline]
501 fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
502 T::ULE::validate_bytes(bytes)
503 }
504
505 #[inline]
506 unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
507 Self::from_ule_slice(T::ULE::slice_from_bytes_unchecked(bytes))
508 }
509}
510
511impl<T> Eq for ZeroSlice<T> where T: AsULE + Eq {}
512
513impl<T> PartialEq<ZeroSlice<T>> for ZeroSlice<T>
514where
515 T: AsULE + PartialEq,
516{
517 #[inline]
518 fn eq(&self, other: &ZeroSlice<T>) -> bool {
519 self.as_zerovec().eq(&other.as_zerovec())
520 }
521}
522
523impl<T> PartialEq<[T]> for ZeroSlice<T>
524where
525 T: AsULE + PartialEq,
526{
527 #[inline]
528 fn eq(&self, other: &[T]) -> bool {
529 self.iter().eq(other.iter().copied())
530 }
531}
532
533impl<'a, T> PartialEq<ZeroVec<'a, T>> for ZeroSlice<T>
534where
535 T: AsULE + PartialEq,
536{
537 #[inline]
538 fn eq(&self, other: &ZeroVec<'a, T>) -> bool {
539 self.as_zerovec().eq(other)
540 }
541}
542
543impl<'a, T> PartialEq<ZeroSlice<T>> for ZeroVec<'a, T>
544where
545 T: AsULE + PartialEq,
546{
547 #[inline]
548 fn eq(&self, other: &ZeroSlice<T>) -> bool {
549 self.eq(&other.as_zerovec())
550 }
551}
552
553impl<T> fmt::Debug for ZeroSlice<T>
554where
555 T: AsULE + fmt::Debug,
556{
557 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
558 self.as_zerovec().fmt(f)
559 }
560}
561
562impl<T: AsULE + PartialOrd> PartialOrd for ZeroSlice<T> {
563 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
564 self.iter().partial_cmp(other.iter())
565 }
566}
567
568impl<T: AsULE + Ord> Ord for ZeroSlice<T> {
569 fn cmp(&self, other: &Self) -> Ordering {
570 self.iter().cmp(other.iter())
571 }
572}
573
574#[cfg(feature = "alloc")]
575impl<T: AsULE> AsRef<ZeroSlice<T>> for alloc::vec::Vec<T::ULE> {
576 fn as_ref(&self) -> &ZeroSlice<T> {
577 ZeroSlice::<T>::from_ule_slice(self)
578 }
579}
580
581impl<T: AsULE> AsRef<ZeroSlice<T>> for &[T::ULE] {
582 fn as_ref(&self) -> &ZeroSlice<T> {
583 ZeroSlice::<T>::from_ule_slice(self)
584 }
585}
586
587impl<T> Default for &ZeroSlice<T>
588where
589 T: AsULE,
590{
591 fn default() -> Self {
592 ZeroSlice::from_ule_slice(&[])
593 }
594}
595
596#[cfg(test)]
597mod test {
598 use super::*;
599 use crate::zeroslice;
600
601 #[test]
602 fn test_split_first() {
603 {
604 // empty slice.
605 assert_eq!(None, ZeroSlice::<u16>::new_empty().split_first());
606 }
607 {
608 // single element slice
609 const DATA: &ZeroSlice<u16> =
610 zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [211]);
611 assert_eq!((211, zeroslice![]), DATA.split_first().unwrap());
612 }
613 {
614 // slice with many elements.
615 const DATA: &ZeroSlice<u16> =
616 zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [211, 281, 421, 32973]);
617 const EXPECTED_VALUE: (u16, &ZeroSlice<u16>) = (
618 211,
619 zeroslice!(u16; <u16 as AsULE>::ULE::from_unsigned; [281, 421, 32973]),
620 );
621
622 assert_eq!(EXPECTED_VALUE, DATA.split_first().unwrap());
623 }
624 }
625}