Skip to main content

primitives/types/heap_array/
array.rs

1use std::{
2    fmt::{Debug, Display},
3    marker::PhantomData,
4    mem::{ManuallyDrop, MaybeUninit},
5    ops::{Add, Mul, Sub},
6    vec,
7};
8
9use bytemuck::{box_bytes_of, from_box_bytes, BoxBytes, Pod};
10use derive_more::derive::{AsMut, AsRef, Deref, DerefMut, IntoIterator};
11use hybrid_array::{Array, ArraySize};
12use rayon::iter::IntoParallelIterator;
13use serde::{Deserialize, Serialize};
14use typenum::{Diff, Prod, Sum, U1, U2, U3};
15use wincode::{
16    io::{Reader, Writer},
17    ReadResult,
18    SchemaRead,
19    SchemaWrite,
20    TypeMeta,
21    WriteResult,
22};
23
24use crate::{errors::PrimitiveError, types::Positive};
25
26/// An array on the heap that encodes its length in the type system.
27#[derive(Deref, DerefMut, Clone, IntoIterator, AsRef, AsMut, Eq)]
28#[into_iterator(owned, ref, ref_mut)]
29pub struct HeapArray<T: Sized, M: Positive> {
30    #[deref]
31    #[deref_mut]
32    #[as_ref(forward)]
33    #[as_mut(forward)]
34    pub(super) data: Box<[T]>,
35    #[into_iterator(ignore)]
36    // `fn() -> M` is used instead of `M` so `HeapArray<T, M>` doesn't need `M` to implement `Send
37    // + Sync` to be `Send + Sync` itself. This would be the case if `M` was used directly.
38    pub(super) _len: PhantomData<fn() -> M>,
39}
40
41impl<T: Sized, M: Positive> HeapArray<T, M> {
42    pub(super) fn new(data: Box<[T]>) -> Self {
43        Self {
44            data,
45            _len: PhantomData,
46        }
47    }
48}
49
50// bytemuck::BoxBytes transformation for copy-less casting
51impl<T: Pod, M: Positive> HeapArray<T, M> {
52    pub fn into_box_bytes(self) -> BoxBytes {
53        box_bytes_of(self.data)
54    }
55
56    pub fn from_box_bytes(buf: BoxBytes) -> Self {
57        Self {
58            data: from_box_bytes(buf),
59            _len: PhantomData,
60        }
61    }
62}
63
64impl<T: Sized, M: Positive> HeapArray<T, M> {
65    pub fn map<F, U>(self, f: F) -> HeapArray<U, M>
66    where
67        F: FnMut(T) -> U,
68    {
69        self.into_iter().map(f).collect()
70    }
71}
72
73impl<T: Sized + Default, M: Positive> HeapArray<T, M> {
74    pub fn from_single_value(val: T) -> Self {
75        Self {
76            data: vec![val].into_boxed_slice(),
77            _len: PhantomData,
78        }
79    }
80}
81
82impl<T: Sized + Default, M: Positive> Default for HeapArray<T, M> {
83    fn default() -> Self {
84        Self {
85            data: (0..M::USIZE).map(|_| T::default()).collect(),
86            _len: PhantomData,
87        }
88    }
89}
90
91impl<T: Sized + Debug, M: Positive> Debug for HeapArray<T, M> {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        f.debug_struct(format!("HeapArray<{}>", M::USIZE).as_str())
94            .field("data", &self.data)
95            .finish()
96    }
97}
98
99impl<T: Sized + Serialize, M: Positive> Serialize for HeapArray<T, M> {
100    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
101        use serde::ser::SerializeTuple;
102        let mut tuple = serializer.serialize_tuple(M::USIZE)?;
103        for element in self.data.iter() {
104            tuple.serialize_element(element)?;
105        }
106        tuple.end()
107    }
108}
109
110impl<'de, T: Sized + Deserialize<'de>, M: Positive> Deserialize<'de> for HeapArray<T, M> {
111    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
112        struct HeapArrayVisitor<T, M: Positive> {
113            _phantom: PhantomData<(T, M)>,
114        }
115
116        impl<'de, T: Deserialize<'de>, M: Positive> serde::de::Visitor<'de> for HeapArrayVisitor<T, M> {
117            type Value = HeapArray<T, M>;
118
119            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
120                write!(formatter, "a tuple of {} elements", M::USIZE)
121            }
122
123            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
124            where
125                A: serde::de::SeqAccess<'de>,
126            {
127                let mut data = Vec::with_capacity(M::USIZE);
128                for i in 0..M::USIZE {
129                    let element = seq
130                        .next_element()?
131                        .ok_or_else(|| serde::de::Error::invalid_length(i, &self))?;
132                    data.push(element);
133                }
134
135                Ok(HeapArray {
136                    data: data.into_boxed_slice(),
137                    _len: PhantomData,
138                })
139            }
140        }
141
142        deserializer.deserialize_tuple(
143            M::USIZE,
144            HeapArrayVisitor {
145                _phantom: PhantomData,
146            },
147        )
148    }
149}
150
151impl<T: Sized + SchemaWrite<Src = T>, M: Positive> SchemaWrite for HeapArray<T, M> {
152    type Src = HeapArray<T, M>;
153
154    const TYPE_META: wincode::TypeMeta = match <T as SchemaWrite>::TYPE_META {
155        TypeMeta::Static { size, zero_copy } => TypeMeta::Static {
156            size: size * M::USIZE,
157            zero_copy,
158        },
159        TypeMeta::Dynamic => TypeMeta::Dynamic,
160    };
161
162    #[inline]
163    fn size_of(src: &Self::Src) -> WriteResult<usize> {
164        if let TypeMeta::Static { size, .. } = <Self as SchemaWrite>::TYPE_META {
165            return Ok(size);
166        }
167
168        // Extremely unlikely a type-in-memory's size will overflow usize::MAX.
169        src.iter()
170            .map(T::size_of)
171            .try_fold(0usize, |acc, x| x.map(|x| acc + x))
172    }
173
174    #[inline]
175    fn write(writer: &mut impl Writer, src: &Self::Src) -> WriteResult<()> {
176        if let TypeMeta::Static {
177            size,
178            zero_copy: true,
179        } = <Self as SchemaWrite>::TYPE_META
180        {
181            // SAFETY: `size` is the size of the encoded length. `writer.write(src)` will write
182            // `size` bytes, fully initializing the trusted window.
183            let writer = &mut unsafe { writer.as_trusted_for(size) }?;
184            // SAFETY: `T::Src` is zero-copy eligible (no invalid bit patterns, no layout
185            // requirements, no endianness checks, etc.).
186            unsafe { writer.write_slice_t(&src.data)? };
187            writer.finish()?;
188        } else if let TypeMeta::Static { size, .. } = <Self as SchemaWrite>::TYPE_META {
189            #[allow(clippy::arithmetic_side_effects)]
190            // SAFETY: `size` is the size of the encoded length.
191            // M writes of `T::Src` will write `size` bytes,
192            // fully initializing the trusted window.
193            let mut writer = unsafe { writer.as_trusted_for(size) }?;
194            for item in src {
195                T::write(&mut writer, item)?;
196            }
197            writer.finish()?;
198        } else {
199            for item in src {
200                T::write(writer, item)?;
201            }
202        }
203
204        Ok(())
205    }
206}
207
208pub(crate) struct SliceDropGuard<T> {
209    ptr: *mut MaybeUninit<T>,
210    initialized_len: usize,
211}
212
213impl<T> SliceDropGuard<T> {
214    pub(crate) fn new(ptr: *mut MaybeUninit<T>) -> Self {
215        Self {
216            ptr,
217            initialized_len: 0,
218        }
219    }
220
221    #[inline(always)]
222    #[allow(clippy::arithmetic_side_effects)]
223    pub(crate) fn inc_len(&mut self) {
224        self.initialized_len += 1;
225    }
226}
227
228impl<T> Drop for SliceDropGuard<T> {
229    #[inline(always)]
230    fn drop(&mut self) {
231        unsafe {
232            std::ptr::drop_in_place(std::ptr::slice_from_raw_parts_mut(
233                self.ptr.cast::<T>(),
234                self.initialized_len,
235            ));
236        }
237    }
238}
239
240impl<'de, T: Sized + SchemaRead<'de, Dst = T>, M: Positive> SchemaRead<'de> for HeapArray<T, M> {
241    type Dst = HeapArray<T::Dst, M>;
242
243    const TYPE_META: TypeMeta = const {
244        match T::TYPE_META {
245            TypeMeta::Static { size, zero_copy } => TypeMeta::Static {
246                size: M::USIZE * size,
247                zero_copy,
248            },
249            TypeMeta::Dynamic => TypeMeta::Dynamic,
250        }
251    };
252
253    #[inline]
254    fn read(reader: &mut impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
255        /// Drop guard for `TypeMeta::Static { zero_copy: true }` types.
256        ///
257        /// In this case we do not need to drop items individually, as
258        /// the container will be initialized by a single memcpy.
259        struct DropGuardRawCopy<T>(*mut [MaybeUninit<T>]);
260        impl<T> Drop for DropGuardRawCopy<T> {
261            #[inline]
262            fn drop(&mut self) {
263                let container = unsafe { Box::from_raw(self.0) };
264                drop(container);
265            }
266        }
267        /// Drop guard for `TypeMeta::Static { zero_copy: false } | TypeMeta::Dynamic` types.
268        ///
269        /// In this case we need to drop items individually, as
270        /// the container will be initialized by a series of reads.
271        struct DropGuardElemCopy<T> {
272            inner: ManuallyDrop<SliceDropGuard<T>>,
273            fat: *mut [MaybeUninit<T>],
274        }
275        impl<T> DropGuardElemCopy<T> {
276            #[inline(always)]
277            fn new(fat: *mut [MaybeUninit<T>], raw: *mut MaybeUninit<T>) -> Self {
278                Self {
279                    inner: ManuallyDrop::new(SliceDropGuard::new(raw)),
280                    fat,
281                }
282            }
283        }
284        impl<T> Drop for DropGuardElemCopy<T> {
285            #[inline]
286            fn drop(&mut self) {
287                unsafe {
288                    ManuallyDrop::drop(&mut self.inner);
289                }
290                let container = unsafe { Box::from_raw(self.fat) };
291                drop(container);
292            }
293        }
294        let mem = Box::<[T::Dst]>::new_uninit_slice(M::USIZE);
295        let fat = Box::into_raw(mem);
296        match T::TYPE_META {
297            TypeMeta::Static {
298                zero_copy: true, ..
299            } => {
300                let guard = DropGuardRawCopy(fat);
301                let dst = unsafe { &mut *fat };
302                unsafe { reader.copy_into_slice_t(dst)? };
303                std::mem::forget(guard);
304            }
305            TypeMeta::Static {
306                size,
307                zero_copy: false,
308            } => {
309                let raw_base = unsafe { (*fat).as_mut_ptr() };
310                let mut guard: DropGuardElemCopy<T::Dst> = DropGuardElemCopy::new(fat, raw_base);
311                #[allow(clippy::arithmetic_side_effects)]
312                let reader = &mut unsafe { reader.as_trusted_for(size * M::USIZE) }?;
313                for i in 0..M::USIZE {
314                    let slot = unsafe { &mut *raw_base.add(i) };
315                    T::read(reader, slot)?;
316                    guard.inner.inc_len();
317                }
318                std::mem::forget(guard);
319            }
320            TypeMeta::Dynamic => {
321                let raw_base = unsafe { (*fat).as_mut_ptr() };
322                let mut guard: DropGuardElemCopy<T::Dst> = DropGuardElemCopy::new(fat, raw_base);
323                for i in 0..M::USIZE {
324                    let slot = unsafe { &mut *raw_base.add(i) };
325                    T::read(reader, slot)?;
326                    guard.inner.inc_len();
327                }
328                std::mem::forget(guard);
329            }
330        }
331        let container = unsafe { Box::from_raw(fat) };
332        let container = unsafe { container.assume_init().try_into().unwrap() };
333        dst.write(container);
334        Ok(())
335    }
336}
337
338impl<T: Sized, M: Positive> FromIterator<T> for HeapArray<T, M> {
339    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
340        let data = iter.into_iter().collect::<Box<_>>();
341        assert_eq!(data.len(), M::USIZE,);
342        Self {
343            data,
344            _len: PhantomData,
345        }
346    }
347}
348
349impl<T: Sized + Send, M: Positive> IntoParallelIterator for HeapArray<T, M> {
350    type Item = T;
351    type Iter = rayon::vec::IntoIter<T>;
352
353    fn into_par_iter(self) -> Self::Iter {
354        self.data.into_par_iter()
355    }
356}
357
358// -----------------------
359// |   Split and Merge   |
360// -----------------------
361
362impl<T: Sized + Copy, M: Positive> HeapArray<T, M> {
363    pub fn split<M1, M2>(&self) -> (HeapArray<T, M1>, HeapArray<T, M2>)
364    where
365        M1: Positive,
366        M2: Positive + Add<M1, Output = M>,
367    {
368        let (m1, m2) = self.split_at(M1::USIZE);
369        (
370            HeapArray::<T, M1> {
371                data: m1.into(),
372                _len: PhantomData,
373            },
374            HeapArray::<T, M2> {
375                data: m2.into(),
376                _len: PhantomData,
377            },
378        )
379    }
380
381    pub fn split_halves<MDiv2>(&self) -> (HeapArray<T, MDiv2>, HeapArray<T, MDiv2>)
382    where
383        MDiv2: Positive + Mul<U2, Output = M>,
384    {
385        let (m1, m2) = self.split_at(MDiv2::USIZE);
386        (
387            HeapArray::<T, MDiv2> {
388                data: m1.into(),
389                _len: PhantomData,
390            },
391            HeapArray::<T, MDiv2> {
392                data: m2.into(),
393                _len: PhantomData,
394            },
395        )
396    }
397
398    pub fn merge_halves(this: Self, other: Self) -> HeapArray<T, Prod<M, U2>>
399    where
400        M: Mul<U2, Output: Positive>,
401    {
402        let mut vec = this.data.into_vec();
403        vec.extend(other.data.into_vec());
404        HeapArray::<T, Prod<M, U2>> {
405            data: vec.into_boxed_slice(),
406            _len: PhantomData,
407        }
408    }
409
410    pub fn split3<M1, M2, M3>(&self) -> (HeapArray<T, M1>, HeapArray<T, M2>, HeapArray<T, M3>)
411    where
412        M1: Positive,
413        M2: Positive + Add<M1>,
414        M3: Positive + Add<Sum<M2, M1>, Output = M>,
415    {
416        let (m1, m_rest) = self.split_at(M1::USIZE);
417        let (m2, m3) = m_rest.split_at(M2::USIZE);
418        (
419            HeapArray::<T, M1> {
420                data: m1.into(),
421                _len: PhantomData,
422            },
423            HeapArray::<T, M2> {
424                data: m2.into(),
425                _len: PhantomData,
426            },
427            HeapArray::<T, M3> {
428                data: m3.into(),
429                _len: PhantomData,
430            },
431        )
432    }
433
434    pub fn split_thirds<MDiv3>(
435        &self,
436    ) -> (
437        HeapArray<T, MDiv3>,
438        HeapArray<T, MDiv3>,
439        HeapArray<T, MDiv3>,
440    )
441    where
442        MDiv3: Positive + Mul<U3, Output = M>,
443    {
444        let (m1, m_rest) = self.split_at(MDiv3::USIZE);
445        let (m2, m3) = m_rest.split_at(MDiv3::USIZE);
446        (
447            HeapArray::<T, MDiv3> {
448                data: m1.into(),
449                _len: PhantomData,
450            },
451            HeapArray::<T, MDiv3> {
452                data: m2.into(),
453                _len: PhantomData,
454            },
455            HeapArray::<T, MDiv3> {
456                data: m3.into(),
457                _len: PhantomData,
458            },
459        )
460    }
461
462    pub fn merge_thirds(first: Self, second: Self, third: Self) -> HeapArray<T, Prod<M, U3>>
463    where
464        M: Mul<U3, Output: Positive>,
465    {
466        let mut vec = first.data.into_vec();
467        vec.extend(second.data.into_vec());
468        vec.extend(third.data.into_vec());
469        HeapArray::<T, Prod<M, U3>> {
470            data: vec.into_boxed_slice(),
471            _len: PhantomData,
472        }
473    }
474}
475
476pub struct HeapArrayTuple<T1: Sized, T2: Sized, M: Positive>(
477    pub HeapArray<T1, M>,
478    pub HeapArray<T2, M>,
479);
480
481impl<T1: Sized, T2: Sized, M: Positive> FromIterator<(T1, T2)> for HeapArrayTuple<T1, T2, M> {
482    fn from_iter<I: IntoIterator<Item = (T1, T2)>>(iter: I) -> Self {
483        let (data1, data2): (Vec<_>, Vec<_>) = iter.into_iter().unzip();
484
485        assert_eq!(data1.len(), M::USIZE);
486        assert_eq!(data2.len(), M::USIZE);
487        HeapArrayTuple(
488            HeapArray::<T1, M> {
489                data: data1.into_boxed_slice(),
490                _len: PhantomData,
491            },
492            HeapArray::<T2, M> {
493                data: data2.into_boxed_slice(),
494                _len: PhantomData,
495            },
496        )
497    }
498}
499
500impl<T: Sized, M: Positive> TryFrom<Vec<T>> for HeapArray<T, M> {
501    type Error = PrimitiveError;
502
503    fn try_from(data: Vec<T>) -> Result<Self, PrimitiveError> {
504        if data.len() == M::USIZE {
505            Ok(Self {
506                data: data.into_boxed_slice(),
507                _len: PhantomData,
508            })
509        } else {
510            Err(PrimitiveError::InvalidSize(M::USIZE, data.len()))
511        }
512    }
513}
514
515impl<T: Sized, M: Positive> TryFrom<Box<[T]>> for HeapArray<T, M> {
516    type Error = PrimitiveError;
517
518    fn try_from(data: Box<[T]>) -> Result<Self, PrimitiveError> {
519        if data.len() == M::USIZE {
520            Ok(Self {
521                data,
522                _len: PhantomData,
523            })
524        } else {
525            Err(PrimitiveError::InvalidSize(M::USIZE, data.len()))
526        }
527    }
528}
529
530impl<T: Sized> From<T> for HeapArray<T, U1> {
531    fn from(element: T) -> Self {
532        Self {
533            data: Box::new([element]),
534            _len: PhantomData,
535        }
536    }
537}
538
539impl<T: Sized, M: Positive> From<HeapArray<T, M>> for Vec<T> {
540    fn from(array: HeapArray<T, M>) -> Self {
541        array.data.into_vec()
542    }
543}
544
545impl<T: Sized + Clone, M: Positive + ArraySize> From<Array<T, M>> for HeapArray<T, M> {
546    fn from(array: Array<T, M>) -> Self {
547        Self {
548            data: array.to_vec().into_boxed_slice(),
549            _len: PhantomData,
550        }
551    }
552}
553
554impl<T: Sized, M: Positive> HeapArray<T, M> {
555    pub fn split_last<N: Positive>(self) -> (HeapArray<T, Diff<M, N>>, HeapArray<T, N>)
556    where
557        M: Sub<N, Output: Positive>,
558    {
559        let mut vec = self.data.into_vec();
560        let last_n = vec.split_off(M::USIZE - N::USIZE);
561
562        (
563            HeapArray {
564                data: vec.into_boxed_slice(),
565                _len: PhantomData,
566            },
567            HeapArray {
568                data: last_n.into_boxed_slice(),
569                _len: PhantomData,
570            },
571        )
572    }
573
574    pub fn from_fn(f: impl FnMut(usize) -> T) -> Self {
575        Self {
576            data: (0..M::USIZE).map(f).collect::<Box<_>>(),
577            _len: PhantomData,
578        }
579    }
580
581    pub fn from_constant(c: T) -> Self
582    where
583        T: Copy,
584    {
585        Self {
586            data: (0..M::USIZE).map(|_| c).collect::<Box<_>>(),
587            _len: PhantomData,
588        }
589    }
590}
591
592impl<T: Sized, M: Positive> Display for HeapArray<T, M>
593where
594    T: Display,
595{
596    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
597        write!(f, "[")?;
598        for (i, item) in self.data.iter().enumerate() {
599            if i != 0 {
600                write!(f, ", ")?;
601            }
602            write!(f, "{item}")?;
603        }
604        write!(f, "]")
605    }
606}
607
608#[cfg(test)]
609pub mod tests {
610    use hybrid_array::sizes::{U2, U3, U6};
611    use typenum::{U1, U4};
612
613    use super::*;
614
615    #[test]
616    fn test_heap_array() {
617        let array = HeapArray::<_, U3>::from_fn(|i| i);
618        assert_eq!(array.into_iter().collect::<Vec<_>>(), vec![0, 1, 2]);
619    }
620
621    #[test]
622    fn test_default() {
623        let array = HeapArray::<usize, U3>::default();
624        assert_eq!(array.len(), 3);
625    }
626
627    #[test]
628    fn test_heap_array_split_last() {
629        let array = HeapArray::<_, U6>::from_fn(|i| i);
630        let (first, last) = array.split_last::<U2>();
631        assert_eq!(first.into_iter().collect::<Vec<_>>(), vec![0, 1, 2, 3]);
632        assert_eq!(last.into_iter().collect::<Vec<_>>(), vec![4, 5]);
633    }
634
635    #[test]
636    fn test_heap_array_from_array() {
637        let array = Array::<_, U3>::from_fn(|i| i);
638        let heap_array = HeapArray::<_, U3>::from(array);
639        assert_eq!(heap_array.into_iter().collect::<Vec<_>>(), vec![0, 1, 2]);
640    }
641
642    #[test]
643    fn test_heap_array_from_vec() {
644        let vec = vec![0, 1, 2];
645        let heap_array = HeapArray::<_, U3>::try_from(vec).unwrap();
646        assert_eq!(heap_array.into_iter().collect::<Vec<_>>(), vec![0, 1, 2]);
647
648        let vec = vec![0, 1];
649        let heap_array = HeapArray::<_, U3>::try_from(vec);
650        assert!(heap_array.is_err());
651    }
652
653    #[test]
654    fn test_heap_array_from_iter() {
655        let heap_array = HeapArray::<_, U3>::from_fn(|i| i);
656        assert_eq!(heap_array.into_iter().collect::<Vec<_>>(), vec![0, 1, 2]);
657    }
658
659    #[test]
660    #[should_panic]
661    fn test_heap_array_from_iter_wrong_size() {
662        HeapArray::<_, U2>::from_iter(0..3);
663    }
664
665    #[test]
666    fn test_heap_array_deserialize() {
667        let array = HeapArray::<usize, U6>::from_fn(|i| i);
668        let serialized = bincode::serialize(&array).unwrap();
669        bincode::deserialize::<HeapArray<usize, U6>>(&serialized).unwrap();
670
671        // With the new tuple-based serialization (which doesn't include length),
672        // we can't detect length mismatches unless we use strict deserialization
673        // that rejects trailing bytes.
674        use bincode::Options;
675        let config = bincode::DefaultOptions::new()
676            .with_fixint_encoding()
677            .reject_trailing_bytes();
678
679        let wrong_deserialize = config.deserialize::<HeapArray<usize, U3>>(&serialized);
680        assert!(wrong_deserialize.is_err());
681    }
682
683    #[test]
684    fn test_heap_array_split() {
685        let array = HeapArray::<_, U6>::from_fn(|i| i);
686        let (first, second) = array.split::<U4, U2>();
687        assert_eq!(first.into_iter().collect::<Vec<_>>(), vec![0, 1, 2, 3]);
688        assert_eq!(second.into_iter().collect::<Vec<_>>(), vec![4, 5]);
689
690        let (first, second) = array.split_halves::<U3>();
691        assert_eq!(first.into_iter().collect::<Vec<_>>(), vec![0, 1, 2]);
692        assert_eq!(second.into_iter().collect::<Vec<_>>(), vec![3, 4, 5]);
693
694        // let (first, second) = array.split::<U4, U1>(); --> doesn't compile
695
696        // let (first, second) = array.split_halves::<U2>(); --> doesn't compile
697    }
698
699    #[test]
700    fn test_heap_array_split3() {
701        let array = HeapArray::<_, U6>::from_fn(|i| i);
702        let (first, second, third) = array.split3::<U3, U2, U1>();
703        assert_eq!(first.into_iter().collect::<Vec<_>>(), vec![0, 1, 2]);
704        assert_eq!(second.into_iter().collect::<Vec<_>>(), vec![3, 4]);
705        assert_eq!(third.into_iter().collect::<Vec<_>>(), vec![5]);
706
707        let (first, second, third) = array.split_thirds::<U2>();
708        assert_eq!(first.into_iter().collect::<Vec<_>>(), vec![0, 1]);
709        assert_eq!(second.into_iter().collect::<Vec<_>>(), vec![2, 3]);
710        assert_eq!(third.into_iter().collect::<Vec<_>>(), vec![4, 5]);
711
712        // let (a1, a2, a3) = array.split3::<U3, U2, U2>(); // doesn't compile
713
714        // let (a1, a2, a3) = array.split_thirds::<U2>(); // doesn't compile
715    }
716
717    #[test]
718    fn test_heap_array_wincode_roundtrip() {
719        // Test static zero-copy type
720        let array = HeapArray::<u32, U4>::from_fn(|i| (i * 10) as u32);
721        let ser = wincode::serialize(&array).unwrap();
722        let bin_ser = bincode::serialize(&array).unwrap();
723        let deserialized: HeapArray<u32, U4> = wincode::deserialize(&ser).unwrap();
724
725        assert_eq!(ser, bin_ser);
726        assert_eq!(deserialized, array);
727
728        // Test static non-zero-copy type
729        #[derive(
730            Debug, Copy, Clone, PartialEq, SchemaRead, SchemaWrite, Serialize, Deserialize,
731        )]
732        struct NonZeroCopy {
733            a: u8,
734            b: u16,
735        }
736        let array = HeapArray::<NonZeroCopy, U3>::from_fn(|i| NonZeroCopy {
737            a: i as u8,
738            b: (i * 100) as u16,
739        });
740        let ser = wincode::serialize(&array).unwrap();
741        let bin_ser = bincode::serialize(&array).unwrap();
742        let deserialized: HeapArray<NonZeroCopy, U3> = wincode::deserialize(&ser).unwrap();
743        assert_eq!(ser, bin_ser);
744        assert_eq!(deserialized, array);
745
746        // Test dynamically-sized type
747        let array = HeapArray::<String, U2>::from_fn(|i| format!("String {i}"));
748        let ser = wincode::serialize(&array).unwrap();
749        let bin_ser = bincode::serialize(&array).unwrap();
750        let deserialized: HeapArray<String, U2> = wincode::deserialize(&ser).unwrap();
751        assert_eq!(ser, bin_ser);
752        assert_eq!(deserialized, array);
753    }
754}