Skip to main content

nbyte_int/
conversions.rs

1use super::types::*;
2use core::num::TryFromIntError;
3
4#[cold]
5#[inline(always)]
6fn neg_overflow() -> TryFromIntError {
7    match u8::try_from(-1_i8) {
8        Err(err) => err,
9        // this runs at runtime, don't panic
10        Ok(_) => unsafe { core::hint::unreachable_unchecked() },
11    }
12}
13
14#[cold]
15#[inline(always)]
16fn pos_overflow() -> TryFromIntError {
17    match u8::try_from(256_u16) {
18        Err(err) => err,
19        // this runs at runtime, don't panic
20        Ok(_) => unsafe { core::hint::unreachable_unchecked() },
21    }
22}
23
24// `P` = primitive, `C` = custom; the value of a custom int is its backing type
25macro_rules! to_val {
26    (P $x:expr) => {
27        $x
28    };
29    (C $x:expr) => {
30        $x.get()
31    };
32}
33
34macro_rules! lossless {
35    // primitive <-> primitive already exists in core
36    (P $s:ident $sb:ident => P $d:ident $db:ident) => {};
37
38    ($sk:ident $s:ident $sb:ident => P $d:ident $db:ident) => {
39        impl From<$s> for $d {
40            #[inline(always)]
41            fn from(x: $s) -> Self {
42                to_val!($sk x) as $d
43            }
44        }
45    };
46
47    ($sk:ident $s:ident $sb:ident => C $d:ident $db:ident) => {
48        impl From<$s> for $d {
49            #[inline(always)]
50            fn from(x: $s) -> Self {
51                // `as` sign/zero extends, and the value is known to fit
52                unsafe { <$d>::new_unchecked(to_val!($sk x) as $db) }
53            }
54        }
55    };
56}
57
58macro_rules! fallible {
59    (P $s:ident $sb:ident => P $d:ident $db:ident) => {};
60
61    ($sk:ident $s:ident $sb:ident => $dk:ident $d:ident $db:ident) => {
62        impl TryFrom<$s> for $d {
63            type Error = TryFromIntError;
64
65            #[inline]
66            fn try_from(x: $s) -> Result<Self, Self::Error> {
67                // step 1: source value -> destination's backing primitive
68                // (the error is `Infallible` when that step is lossless)
69                match <$db as TryFrom<$sb>>::try_from(to_val!($sk x)) {
70                    Ok(v) => fallible!(@finish $dk $d v; $db),
71                    Err(err) => Err(TryFromIntError::from(err)),
72                }
73            }
74        }
75    };
76    // step 2: backing primitive -> destination
77    (@finish P $d:ident $v:ident; $db: ident) => { Ok($v) };
78
79    (@finish C $d:ident $v:ident; $db: ident) => {{
80        const MIN: $db = $d::MIN.get();
81        const MAX: $db = $d::MAX.get();
82
83        // Note: we can't use a match stmt in a neat way
84        // because of unsigned values since
85        // ..MIN is an invalid range (its empty) and emits a compile error
86
87        let value = $v;
88        if value < MIN {
89            Err(neg_overflow())
90        } else if value > MAX {
91            Err(pos_overflow())
92        } else {
93            Ok(unsafe { $d::new_unchecked(value) })
94        }
95    }};
96}
97
98macro_rules! conv_pair {
99    // `s` is strictly narrower than `b`
100    (
101        ($sk:ident $si:ident $su:ident as $sib:ident $sub:ident)
102        ($bk:ident $bi:ident $bu:ident as $bib:ident $bub:ident)
103    ) => {
104        // widening
105        lossless! { $sk $si $sib => $bk $bi $bib } // iN -> iM
106        lossless! { $sk $su $sub => $bk $bu $bub } // uN -> uM
107        lossless! { $sk $su $sub => $bk $bi $bib } // uN -> iM
108        fallible! { $sk $si $sib => $bk $bu $bub } // iN -> uM (negatives)
109
110        // narrowing
111        fallible! { $bk $bi $bib => $sk $si $sib }
112        fallible! { $bk $bi $bib => $sk $su $sub }
113        fallible! { $bk $bu $bub => $sk $si $sib }
114        fallible! { $bk $bu $bub => $sk $su $sub }
115    };
116}
117
118macro_rules! conversions {
119    () => {};
120    (
121        ($k:ident $i:ident $u:ident as $ib:ident $ub:ident)
122        $($rest:tt)*
123    ) => {
124        // same width, different sign
125        fallible! { $k $i $ib => $k $u $ub }
126        fallible! { $k $u $ub => $k $i $ib }
127
128        $(conv_pair! { ($k $i $u as $ib $ub) $rest })*
129
130        conversions! { $($rest)* }
131    };
132}
133
134// MUST be sorted by ascending bit-width
135conversions! {
136    (P i8   u8   as i8   u8)
137    (P i16  u16  as i16  u16)
138    (C i24  u24  as i32  u32)
139    (P i32  u32  as i32  u32)
140    (C i40  u40  as i64  u64)
141    (C i48  u48  as i64  u64)
142    (C i56  u56  as i64  u64)
143    (P i64  u64  as i64  u64)
144    (C i72  u72  as i128 u128)
145    (C i80  u80  as i128 u128)
146    (C i88  u88  as i128 u128)
147    (C i96  u96  as i128 u128)
148    (C i104 u104 as i128 u128)
149    (C i112 u112 as i128 u128)
150    (C i120 u120 as i128 u128)
151    (P i128 u128 as i128 u128)
152}