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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
mod sealed {
    pub trait Sealed {}
    impl Sealed for u8 {}
    impl Sealed for u16 {}
    #[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))]
    impl Sealed for u32 {}
}

#[derive(Debug)]
pub struct InvalidLength<T> {
    pub(crate) backtrace: std::backtrace::Backtrace,
    type_name: &'static str,
    original: Box<[T]>,
}

impl<T> InvalidLength<T> {
    #[track_caller]
    fn new(type_name: &'static str, original: Box<[T]>) -> Self {
        Self {
            backtrace: std::backtrace::Backtrace::capture(),
            type_name,
            original,
        }
    }

    /// Returns the original Box<[T]> that could not be converted from.
    pub fn get_inner(self) -> Box<[T]> {
        self.original
    }
}

impl<T> std::fmt::Display for InvalidLength<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Cannot fit {} into {}:\n\n{}",
            self.original.len(),
            self.type_name,
            self.backtrace
        )
    }
}

/// A sealed trait to represent valid lengths for a [`FixedArray`].
///
/// This is implemented on `u32` for non-16 bit platforms, and `u16` on all platforms.
///
/// [`FixedArray`]: `crate::array::FixedArray`
pub trait ValidLength: sealed::Sealed + Default + Copy + TryFrom<usize> + Into<u32> {
    const ZERO: Self;
    const MAX: usize;

    /// # Errors
    ///
    /// Errors if the val's length cannot fit into Self.
    #[allow(clippy::type_complexity)]
    fn from_usize<T>(val: Box<[T]>) -> Result<(Self, Box<[T]>), InvalidLength<T>> {
        match val.len().try_into() {
            Ok(len) => Ok((len, val)),
            Err(_) => Err(InvalidLength::new(std::any::type_name::<Self>(), val)),
        }
    }

    fn to_usize(self) -> usize;
}

impl ValidLength for u8 {
    const ZERO: Self = 0;
    #[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
    const MAX: usize = u8::MAX as usize;

    fn to_usize(self) -> usize {
        self.into()
    }
}

impl ValidLength for u16 {
    const ZERO: Self = 0;
    #[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
    const MAX: usize = u16::MAX as usize;

    fn to_usize(self) -> usize {
        self.into()
    }
}

#[cfg(any(target_pointer_width = "64", target_pointer_width = "32"))]
impl ValidLength for u32 {
    const ZERO: Self = 0;
    #[allow(clippy::as_conversions)] // Cannot use `.into()` in const.
    const MAX: usize = u32::MAX as usize;

    fn to_usize(self) -> usize {
        self.try_into()
            .expect("u32 can fit into usize on platforms with pointer lengths of 32 and 64")
    }
}

#[cfg(target_pointer_width = "16")]
pub type SmallLen = u16;
#[cfg(not(target_pointer_width = "16"))]
pub type SmallLen = u32;