1use crate::{Approx, CastTo, ConvExact, ConvTo, Error, Exact, RangeError};
11use core::convert::Infallible;
12use core::mem::size_of;
13
14macro_rules! impl_via_as_neg_check {
15 ($x:ty: $y:ty) => {
16 impl ConvExact<$x> for $y {
17 type Error = RangeError;
18
19 #[inline]
20 fn conv_exact(x: $x) -> $y {
21 #[cfg(any(debug_assertions, feature = "assert_int"))]
22 assert!(
23 x >= 0,
24 "cast x: {} to {}: expected x >= 0, found x = {}",
25 stringify!($x), stringify!($y), x
26 );
27 x as $y
28 }
29 #[inline]
30 fn try_conv_exact(x: $x) -> Result<Self, RangeError> {
31 if x >= 0 {
32 Ok(x as $y)
33 } else {
34 Err(RangeError)
35 }
36 }
37 }
38 };
39 ($x:ty: $y:ty, $($yy:ty),+) => {
40 impl_via_as_neg_check!($x: $y);
41 impl_via_as_neg_check!($x: $($yy),+);
42 };
43}
44
45impl_via_as_neg_check!(i8: u8, u16, u32, u64, u128);
46impl_via_as_neg_check!(i16: u16, u32, u64, u128);
47impl_via_as_neg_check!(i32: u32, u64, u128);
48impl_via_as_neg_check!(i64: u64, u128);
49impl_via_as_neg_check!(i128: u128);
50
51macro_rules! impl_via_as_max_check {
53 ($x:ty: $y:tt) => {
54 impl ConvExact<$x> for $y {
55 type Error = RangeError;
56
57 #[inline]
58 fn conv_exact(x: $x) -> $y {
59 #[cfg(any(debug_assertions, feature = "assert_int"))]
60 assert!(
61 x <= $y::MAX as $x,
62 "cast x: {} to {}: expected x <= {}, found x = {}",
63 stringify!($x), stringify!($y), $y::MAX, x
64 );
65 x as $y
66 }
67 #[inline]
68 fn try_conv_exact(x: $x) -> Result<Self, RangeError> {
69 if x <= $y::MAX as $x {
70 Ok(x as $y)
71 } else {
72 Err(RangeError)
73 }
74 }
75 }
76 };
77 ($x:ty: $y:tt, $($yy:tt),+) => {
78 impl_via_as_max_check!($x: $y);
79 impl_via_as_max_check!($x: $($yy),+);
80 };
81}
82
83impl_via_as_max_check!(u8: i8);
84impl_via_as_max_check!(u16: i8, i16, u8);
85impl_via_as_max_check!(u32: i8, i16, i32, u8, u16);
86impl_via_as_max_check!(u64: i8, i16, i32, i64, u8, u16, u32);
87impl_via_as_max_check!(u128: i8, i16, i32, i64, i128);
88impl_via_as_max_check!(u128: u8, u16, u32, u64);
89
90macro_rules! impl_via_as_range_check {
92 ($x:ty: $y:tt) => {
93 impl ConvExact<$x> for $y {
94 type Error = RangeError;
95
96 #[inline]
97 fn conv_exact(x: $x) -> $y {
98 #[cfg(any(debug_assertions, feature = "assert_int"))]
99 assert!(
100 $y::MIN as $x <= x && x <= $y::MAX as $x,
101 "cast x: {} to {}: expected {} <= x <= {}, found x = {}",
102 stringify!($x), stringify!($y), $y::MIN, $y::MAX, x
103 );
104 x as $y
105 }
106 #[inline]
107 fn try_conv_exact(x: $x) -> Result<Self, RangeError> {
108 if $y::MIN as $x <= x && x <= $y::MAX as $x {
109 Ok(x as $y)
110 } else {
111 Err(RangeError)
112 }
113 }
114 }
115 };
116 ($x:ty: $y:tt, $($yy:tt),+) => {
117 impl_via_as_range_check!($x: $y);
118 impl_via_as_range_check!($x: $($yy),+);
119 };
120}
121
122impl_via_as_range_check!(i16: i8, u8);
123impl_via_as_range_check!(i32: i8, i16, u8, u16);
124impl_via_as_range_check!(i64: i8, i16, i32, u8, u16, u32);
125impl_via_as_range_check!(i128: i8, i16, i32, i64, u8, u16, u32, u64);
126
127macro_rules! impl_int_generic {
128 ($x:tt: $y:tt) => {
129 impl ConvExact<$x> for $y {
130 type Error = RangeError;
131
132 #[allow(unused_comparisons)]
133 #[inline]
134 fn conv_exact(x: $x) -> $y {
135 let src_is_signed = $x::MIN != 0;
136 let dst_is_signed = $y::MIN != 0;
137 if size_of::<$x>() < size_of::<$y>() {
138 if !dst_is_signed {
139 #[cfg(any(debug_assertions, feature = "assert_int"))]
140 assert!(
141 x >= 0,
142 "cast x: {} to {}: expected x >= 0, found x = {}",
143 stringify!($x), stringify!($y), x
144 );
145 }
146 } else if size_of::<$x>() == size_of::<$y>() {
147 if dst_is_signed {
148 #[cfg(any(debug_assertions, feature = "assert_int"))]
149 assert!(
150 x <= $y::MAX as $x,
151 "cast x: {} to {}: expected x <= {}, found x = {}",
152 stringify!($x), stringify!($y), $y::MAX, x
153 );
154 } else if src_is_signed {
155 #[cfg(any(debug_assertions, feature = "assert_int"))]
156 assert!(
157 x >= 0,
158 "cast x: {} to {}: expected x >= 0, found x = {}",
159 stringify!($x), stringify!($y), x
160 );
161 }
162 } else {
163 if src_is_signed {
165 #[cfg(any(debug_assertions, feature = "assert_int"))]
166 assert!(
167 $y::MIN as $x <= x && x <= $y::MAX as $x,
168 "cast x: {} to {}: expected {} <= x <= {}, found x = {}",
169 stringify!($x), stringify!($y), $y::MIN, $y::MAX, x
170 );
171 } else {
172 #[cfg(any(debug_assertions, feature = "assert_int"))]
173 assert!(
174 x <= $y::MAX as $x,
175 "cast x: {} to {}: expected x <= {}, found x = {}",
176 stringify!($x), stringify!($y), $y::MAX, x
177 );
178 }
179 }
180 x as $y
181 }
182 #[allow(unused_comparisons)]
183 #[inline]
184 fn try_conv_exact(x: $x) -> Result<Self, Self::Error> {
185 let src_is_signed = $x::MIN != 0;
186 let dst_is_signed = $y::MIN != 0;
187 if size_of::<$x>() < size_of::<$y>() {
188 if dst_is_signed || x >= 0 {
189 return Ok(x as $y);
190 }
191 } else if size_of::<$x>() == size_of::<$y>() {
192 if dst_is_signed {
193 if x <= $y::MAX as $x {
194 return Ok(x as $y);
195 }
196 } else if src_is_signed {
197 if x >= 0 {
198 return Ok(x as $y);
199 }
200 } else {
201 return Ok(x as $y);
203 }
204 } else {
205 if src_is_signed {
207 if $y::MIN as $x <= x && x <= $y::MAX as $x {
208 return Ok(x as $y);
209 }
210 } else {
211 if x <= $y::MAX as $x {
212 return Ok(x as $y);
213 }
214 }
215 }
216 Err(RangeError)
217 }
218 }
219 };
220 ($x:tt: $y:tt, $($yy:tt),+) => {
221 impl_int_generic!($x: $y);
222 impl_int_generic!($x: $($yy),+);
223 };
224}
225
226impl_int_generic!(i8: usize);
227impl_int_generic!(i16: usize);
228impl_int_generic!(i32: isize, usize);
229impl_int_generic!(i64: isize, usize);
230impl_int_generic!(i128: isize, usize);
231impl_int_generic!(u16: isize);
232impl_int_generic!(u32: isize, usize);
233impl_int_generic!(u64: isize, usize);
234impl_int_generic!(u128: isize, usize);
235impl_int_generic!(isize: i8, i16, i32, i64, i128);
236impl_int_generic!(usize: i8, i16, i32, i64, i128, isize);
237impl_int_generic!(isize: u8, u16, u32, u64, u128, usize);
238impl_int_generic!(usize: u8, u16, u32, u64, u128);
239
240macro_rules! impl_via_digits_check {
241 ($x:ty: $y:tt) => {
242 impl ConvTo<$x, Exact> for $y {
243 type Error = Error;
244
245 #[inline]
246 fn conv_to(_: Exact, x: $x) -> Self {
247 if cfg!(any(debug_assertions, feature = "assert_digits")) {
248 x.try_cast_to(Exact).unwrap_or_else(|_| {
249 panic!(
250 "cast x: {} to {}: inexact for x = {x}",
251 stringify!($x), stringify!($y)
252 )
253 })
254 } else {
255 x as $y
256 }
257 }
258 #[inline]
259 fn try_conv_to(_: Exact, x: $x) -> Result<Self, Error> {
260 let src_ty_bits = (size_of::<$x>() * 8) as u32;
261 let src_digits = src_ty_bits.saturating_sub(x.leading_zeros() + x.trailing_zeros());
262 let dst_digits = $y::MANTISSA_DIGITS;
263 if src_digits <= dst_digits {
264 Ok(x as $y)
265 } else {
266 Err(Error::Inexact)
267 }
268 }
269 }
270 };
271 ($x:ty: $y:tt, $($yy:tt),+) => {
272 impl_via_digits_check!($x: $y);
273 impl_via_digits_check!($x: $($yy),+);
274 };
275}
276
277macro_rules! impl_via_digits_check_signed {
278 ($x:ty: $y:tt) => {
279 impl ConvTo<$x, Exact> for $y {
280 type Error = Error;
281
282 #[inline]
283 fn conv_to(_: Exact, x: $x) -> Self {
284 if cfg!(any(debug_assertions, feature = "assert_digits")) {
285 x.try_cast_to(Exact).unwrap_or_else(|_| {
286 panic!(
287 "cast x: {} to {}: inexact for x = {x}",
288 stringify!($x), stringify!($y)
289 )
290 })
291 } else {
292 x as $y
293 }
294 }
295 #[inline]
296 fn try_conv_to(_: Exact, x: $x) -> Result<Self, Error> {
297 let src_ty_bits = (size_of::<$x>() * 8) as u32;
298 let src_digits = x.checked_abs()
299 .map(|y| src_ty_bits.saturating_sub(y.leading_zeros() + y.trailing_zeros()))
300 .unwrap_or(1 );
301 let dst_digits = $y::MANTISSA_DIGITS;
302 if src_digits <= dst_digits {
303 Ok(x as $y)
304 } else {
305 Err(Error::Inexact)
306 }
307 }
308 }
309 };
310 ($x:ty: $y:tt, $($yy:tt),+) => {
311 impl_via_digits_check_signed!($x: $y);
312 impl_via_digits_check_signed!($x: $($yy),+);
313 };
314}
315
316impl_via_digits_check!(u32: f32);
317impl_via_digits_check!(u64: f32, f64);
318impl_via_digits_check!(u128: f64);
319impl_via_digits_check!(usize: f32, f64);
320
321impl_via_digits_check_signed!(i32: f32);
322impl_via_digits_check_signed!(i64: f32, f64);
323impl_via_digits_check_signed!(i128: f32, f64);
324impl_via_digits_check_signed!(isize: f32, f64);
325
326impl ConvTo<u128, Exact> for f32 {
327 type Error = Error;
328
329 #[inline]
330 fn conv_to(_: Exact, x: u128) -> Self {
331 if cfg!(any(debug_assertions, feature = "assert_digits")) {
332 x.try_cast_to(Exact)
333 .unwrap_or_else(|_| panic!("cast x: u128 to f32: inexact for x = {x}"))
334 } else {
335 x as f32
336 }
337 }
338 #[inline]
339 fn try_conv_to(_: Exact, x: u128) -> Result<Self, Error> {
340 if x < 0xffff_ff80_0000_0000_0000_0000_0000_0000_u128 {
341 let src_digits = 128u32.saturating_sub(x.leading_zeros() + x.trailing_zeros());
342 if src_digits <= f32::MANTISSA_DIGITS {
343 Ok(x as f32)
344 } else {
345 Err(Error::Inexact)
346 }
347 } else {
348 Err(Error::Range)
349 }
350 }
351}
352
353macro_rules! impl_approx {
354 ($x:ty: $y:tt) => {
355 impl ConvTo<$x, Approx> for $y {
356 type Error = Infallible;
357
358 #[inline]
359 fn conv_to(_: Approx, x: $x) -> Self {
360 x as $y
361 }
362 #[inline]
363 fn try_conv_to(_: Approx, x: $x) -> Result<Self, Infallible> {
364 Ok(x as $y)
365 }
366 }
367
368 #[cfg(any(feature = "std", feature = "libm"))]
370 impl ConvTo<$x, crate::Nearest> for $y {
371 type Error = Infallible;
372
373 #[inline]
374 fn conv_to(_: crate::Nearest, x: $x) -> Self {
375 x as $y
376 }
377 #[inline]
378 fn try_conv_to(_: crate::Nearest, x: $x) -> Result<Self, Infallible> {
379 Ok(x as $y)
380 }
381 }
382 };
383 ($x:ty: $y:tt, $($yy:tt),+) => {
384 impl_approx!($x: $y);
385 impl_approx!($x: $($yy),+);
386 };
387}
388
389impl_approx!(u32: f32);
390impl_approx!(u64: f32, f64);
391impl_approx!(u128: f32, f64);
392impl_approx!(usize: f32, f64);
393
394impl_approx!(i32: f32);
395impl_approx!(i64: f32, f64);
396impl_approx!(i128: f32, f64);
397impl_approx!(isize: f32, f64);