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