1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// Copyright 2021-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use core::{any::TypeId, mem::MaybeUninit};

use crate::{error::UnpackError, packer::Packer, unpacker::Unpacker, Packable};

impl<T: Packable, const N: usize> Packable for [T; N] {
    type UnpackError = T::UnpackError;

    #[inline]
    fn pack<P: Packer>(&self, packer: &mut P) -> Result<(), P::Error> {
        if TypeId::of::<T>() == TypeId::of::<u8>() {
            // Safety: `Self` is identical to `[u8; N]`.
            let bytes = unsafe { core::mem::transmute::<&Self, &[u8; N]>(self) };
            packer.pack_bytes(bytes)?;
        } else {
            for item in self.iter() {
                item.pack(packer)?;
            }
        }

        Ok(())
    }

    #[inline]
    fn unpack<U: Unpacker, const VERIFY: bool>(
        unpacker: &mut U,
    ) -> Result<Self, UnpackError<Self::UnpackError, U::Error>> {
        if TypeId::of::<T>() == TypeId::of::<u8>() {
            let mut bytes = [0u8; N];
            unpacker.unpack_bytes(&mut bytes)?;
            // Safety: `Self` is identical to `[u8; N]`.
            Ok(unsafe { (&bytes as *const [u8; N] as *const Self).read() })
        } else {
            // Safety: an uninitialized array of [`MaybeUninit`]s is safe to be considered initialized.
            // FIXME: replace with [`MaybeUninit::uninit_array`] when stabilized.
            let mut array = unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() };

            for item in array.iter_mut() {
                let unpacked = T::unpack::<_, VERIFY>(unpacker)?;

                // Safety: each `item` is only visited once so we are never overwriting nor dropping values that are
                // already initialized.
                unsafe {
                    item.as_mut_ptr().write(unpacked);
                }
            }

            // Safety: We traversed the whole array and initialized every item.
            // FIXME: replace with [`MaybeUninit::array_assume_init`] when stabilized.
            Ok(unsafe { (&array as *const [MaybeUninit<T>; N] as *const Self).read() })
        }
    }
}