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}