easy_cast/
impl_int.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Integer impls for Conv.
7//!
8//! See also `impl_basic` which inherits integer impls from From.
9
10use super::*;
11use core::mem::size_of;
12
13macro_rules! impl_via_as_neg_check {
14    ($x:ty: $y:ty) => {
15        impl Conv<$x> for $y {
16            #[inline]
17            fn conv(x: $x) -> $y {
18                #[cfg(any(debug_assertions, feature = "assert_int"))]
19                assert!(
20                    x >= 0,
21                    "cast x: {} to {}: expected x >= 0, found x = {}",
22                    stringify!($x), stringify!($y), x
23                );
24                x as $y
25            }
26            #[inline]
27            fn try_conv(x: $x) -> Result<Self> {
28                if x >= 0 {
29                    Ok(x as $y)
30                } else {
31                    Err(Error::Range)
32                }
33            }
34        }
35    };
36    ($x:ty: $y:ty, $($yy:ty),+) => {
37        impl_via_as_neg_check!($x: $y);
38        impl_via_as_neg_check!($x: $($yy),+);
39    };
40}
41
42impl_via_as_neg_check!(i8: u8, u16, u32, u64, u128);
43impl_via_as_neg_check!(i16: u16, u32, u64, u128);
44impl_via_as_neg_check!(i32: u32, u64, u128);
45impl_via_as_neg_check!(i64: u64, u128);
46impl_via_as_neg_check!(i128: u128);
47
48// Assumption: $y::MAX is representable as $x
49macro_rules! impl_via_as_max_check {
50    ($x:ty: $y:tt) => {
51        impl Conv<$x> for $y {
52            #[inline]
53            fn conv(x: $x) -> $y {
54                #[cfg(any(debug_assertions, feature = "assert_int"))]
55                assert!(
56                    x <= $y::MAX as $x,
57                    "cast x: {} to {}: expected x <= {}, found x = {}",
58                    stringify!($x), stringify!($y), $y::MAX, x
59                );
60                x as $y
61            }
62            #[inline]
63            fn try_conv(x: $x) -> Result<Self> {
64                if x <= $y::MAX as $x {
65                    Ok(x as $y)
66                } else {
67                    Err(Error::Range)
68                }
69            }
70        }
71    };
72    ($x:ty: $y:tt, $($yy:tt),+) => {
73        impl_via_as_max_check!($x: $y);
74        impl_via_as_max_check!($x: $($yy),+);
75    };
76}
77
78impl_via_as_max_check!(u8: i8);
79impl_via_as_max_check!(u16: i8, i16, u8);
80impl_via_as_max_check!(u32: i8, i16, i32, u8, u16);
81impl_via_as_max_check!(u64: i8, i16, i32, i64, u8, u16, u32);
82impl_via_as_max_check!(u128: i8, i16, i32, i64, i128);
83impl_via_as_max_check!(u128: u8, u16, u32, u64);
84
85// Assumption: $y::MAX and $y::MIN are representable as $x
86macro_rules! impl_via_as_range_check {
87    ($x:ty: $y:tt) => {
88        impl Conv<$x> for $y {
89            #[inline]
90            fn conv(x: $x) -> $y {
91                #[cfg(any(debug_assertions, feature = "assert_int"))]
92                assert!(
93                    $y::MIN as $x <= x && x <= $y::MAX as $x,
94                    "cast x: {} to {}: expected {} <= x <= {}, found x = {}",
95                    stringify!($x), stringify!($y), $y::MIN, $y::MAX, x
96                );
97                x as $y
98            }
99            #[inline]
100            fn try_conv(x: $x) -> Result<Self> {
101                if $y::MIN as $x <= x && x <= $y::MAX as $x {
102                    Ok(x as $y)
103                } else {
104                    Err(Error::Range)
105                }
106            }
107        }
108    };
109    ($x:ty: $y:tt, $($yy:tt),+) => {
110        impl_via_as_range_check!($x: $y);
111        impl_via_as_range_check!($x: $($yy),+);
112    };
113}
114
115impl_via_as_range_check!(i16: i8, u8);
116impl_via_as_range_check!(i32: i8, i16, u8, u16);
117impl_via_as_range_check!(i64: i8, i16, i32, u8, u16, u32);
118impl_via_as_range_check!(i128: i8, i16, i32, i64, u8, u16, u32, u64);
119
120macro_rules! impl_int_generic {
121    ($x:tt: $y:tt) => {
122        impl Conv<$x> for $y {
123            #[allow(unused_comparisons)]
124            #[inline]
125            fn conv(x: $x) -> $y {
126                let src_is_signed = $x::MIN != 0;
127                let dst_is_signed = $y::MIN != 0;
128                if size_of::<$x>() < size_of::<$y>() {
129                    if !dst_is_signed {
130                        #[cfg(any(debug_assertions, feature = "assert_int"))]
131                        assert!(
132                            x >= 0,
133                            "cast x: {} to {}: expected x >= 0, found x = {}",
134                            stringify!($x), stringify!($y), x
135                        );
136                    }
137                } else if size_of::<$x>() == size_of::<$y>() {
138                    if dst_is_signed {
139                        #[cfg(any(debug_assertions, feature = "assert_int"))]
140                        assert!(
141                            x <= $y::MAX as $x,
142                            "cast x: {} to {}: expected x <= {}, found x = {}",
143                            stringify!($x), stringify!($y), $y::MAX, x
144                        );
145                    } else if src_is_signed {
146                        #[cfg(any(debug_assertions, feature = "assert_int"))]
147                        assert!(
148                            x >= 0,
149                            "cast x: {} to {}: expected x >= 0, found x = {}",
150                            stringify!($x), stringify!($y), x
151                        );
152                    }
153                } else {
154                    // src size > dst size
155                    if src_is_signed {
156                        #[cfg(any(debug_assertions, feature = "assert_int"))]
157                        assert!(
158                            $y::MIN as $x <= x && x <= $y::MAX as $x,
159                            "cast x: {} to {}: expected {} <= x <= {}, found x = {}",
160                            stringify!($x), stringify!($y), $y::MIN, $y::MAX, x
161                        );
162                    } else {
163                        #[cfg(any(debug_assertions, feature = "assert_int"))]
164                        assert!(
165                            x <= $y::MAX as $x,
166                            "cast x: {} to {}: expected x <= {}, found x = {}",
167                            stringify!($x), stringify!($y), $y::MAX, x
168                        );
169                    }
170                }
171                x as $y
172            }
173            #[allow(unused_comparisons)]
174            #[inline]
175            fn try_conv(x: $x) -> Result<Self> {
176                let src_is_signed = $x::MIN != 0;
177                let dst_is_signed = $y::MIN != 0;
178                if size_of::<$x>() < size_of::<$y>() {
179                    if dst_is_signed || x >= 0 {
180                        return Ok(x as $y);
181                    }
182                } else if size_of::<$x>() == size_of::<$y>() {
183                    if dst_is_signed {
184                        if x <= $y::MAX as $x {
185                            return Ok(x as $y);
186                        }
187                    } else if src_is_signed {
188                        if x >= 0 {
189                            return Ok(x as $y);
190                        }
191                    } else {
192                        // types are identical (e.g. usize == u64)
193                        return Ok(x as $y);
194                    }
195                } else {
196                    // src size > dst size
197                    if src_is_signed {
198                        if $y::MIN as $x <= x && x <= $y::MAX as $x {
199                            return Ok(x as $y);
200                        }
201                    } else {
202                        if x <= $y::MAX as $x {
203                            return Ok(x as $y);
204                        }
205                    }
206                }
207                Err(Error::Range)
208            }
209        }
210    };
211    ($x:tt: $y:tt, $($yy:tt),+) => {
212        impl_int_generic!($x: $y);
213        impl_int_generic!($x: $($yy),+);
214    };
215}
216
217impl_int_generic!(i8: isize, usize);
218impl_int_generic!(i16: isize, usize);
219impl_int_generic!(i32: isize, usize);
220impl_int_generic!(i64: isize, usize);
221impl_int_generic!(i128: isize, usize);
222impl_int_generic!(u8: isize, usize);
223impl_int_generic!(u16: isize, usize);
224impl_int_generic!(u32: isize, usize);
225impl_int_generic!(u64: isize, usize);
226impl_int_generic!(u128: isize, usize);
227impl_int_generic!(isize: i8, i16, i32, i64, i128);
228impl_int_generic!(usize: i8, i16, i32, i64, i128, isize);
229impl_int_generic!(isize: u8, u16, u32, u64, u128, usize);
230impl_int_generic!(usize: u8, u16, u32, u64, u128);
231
232macro_rules! impl_via_digits_check {
233    ($x:ty: $y:tt) => {
234        impl Conv<$x> for $y {
235            #[inline]
236            fn conv(x: $x) -> Self {
237                if cfg!(any(debug_assertions, feature = "assert_digits")) {
238                    Self::try_conv(x).unwrap_or_else(|_| {
239                        panic!(
240                            "cast x: {} to {}: inexact for x = {}",
241                            stringify!($x), stringify!($y), x
242                        )
243                    })
244                } else {
245                    x as $y
246                }
247            }
248            #[inline]
249            fn try_conv(x: $x) -> Result<Self> {
250                let src_ty_bits = (size_of::<$x>() * 8) as u32;
251                let src_digits = src_ty_bits.saturating_sub(x.leading_zeros() + x.trailing_zeros());
252                let dst_digits = $y::MANTISSA_DIGITS;
253                if src_digits <= dst_digits {
254                    Ok(x as $y)
255                } else {
256                    Err(Error::Inexact)
257                }
258            }
259        }
260    };
261    ($x:ty: $y:tt, $($yy:tt),+) => {
262        impl_via_digits_check!($x: $y);
263        impl_via_digits_check!($x: $($yy),+);
264    };
265}
266
267macro_rules! impl_via_digits_check_signed {
268    ($x:ty: $y:tt) => {
269        impl Conv<$x> for $y {
270            #[inline]
271            fn conv(x: $x) -> Self {
272                if cfg!(any(debug_assertions, feature = "assert_digits")) {
273                    Self::try_conv(x).unwrap_or_else(|_| {
274                        panic!(
275                            "cast x: {} to {}: inexact for x = {}",
276                            stringify!($x), stringify!($y), x
277                        )
278                    })
279                } else {
280                    x as $y
281                }
282            }
283            #[inline]
284            fn try_conv(x: $x) -> Result<Self> {
285                let src_ty_bits = (size_of::<$x>() * 8) as u32;
286                let src_digits = x.checked_abs()
287                    .map(|y| src_ty_bits.saturating_sub(y.leading_zeros() + y.trailing_zeros()))
288                    .unwrap_or(1 /*MIN has one binary digit in float repr*/);
289                let dst_digits = $y::MANTISSA_DIGITS;
290                if src_digits <= dst_digits {
291                    Ok(x as $y)
292                } else {
293                    Err(Error::Inexact)
294                }
295            }
296        }
297    };
298    ($x:ty: $y:tt, $($yy:tt),+) => {
299        impl_via_digits_check_signed!($x: $y);
300        impl_via_digits_check_signed!($x: $($yy),+);
301    };
302}
303
304impl_via_digits_check!(u32: f32);
305impl_via_digits_check!(u64: f32, f64);
306impl_via_digits_check!(u128: f32, f64);
307impl_via_digits_check!(usize: f32, f64);
308
309impl_via_digits_check_signed!(i32: f32);
310impl_via_digits_check_signed!(i64: f32, f64);
311impl_via_digits_check_signed!(i128: f32, f64);
312impl_via_digits_check_signed!(isize: f32, f64);