1 //! Ergonomic, checked cast functions for primitive types
2 //!
3 //! This crate provides one checked cast function for each numeric primitive.
4 //! Use these functions to perform a cast from any other numeric primitive:
5 //!
6 //! ```
7 //! use cast::{u8, u16, Error};
8 //!
9 //! # fn main() {
10 //! // Infallible operations, like integer promotion, are equivalent to a normal
11 //! // cast with `as`
12 //! assert_eq!(u16(0u8), 0u16);
13 //!
14 //! // Everything else will return a `Result` depending on the success of the
15 //! // operation
16 //! assert_eq!(u8(0u16), Ok(0u8));
17 //! assert_eq!(u8(256u16), Err(Error::Overflow));
18 //! assert_eq!(u8(-1i8), Err(Error::Underflow));
19 //! assert_eq!(u8(1. / 0.), Err(Error::Infinite));
20 //! assert_eq!(u8(0. / 0.), Err(Error::NaN));
21 //! # }
22 //! ```
23 //!
24 //! There are no namespace problems between these functions, the "primitive
25 //! modules" in `core`/`std` and the built-in primitive types, so all them can
26 //! be in the same scope:
27 //!
28 //! ```
29 //! use std::u8;
30 //! use cast::{u8, u16};
31 //!
32 //! # fn main() {
33 //! // `u8` as a type
34 //! let x: u8 = 0;
35 //! // `u8` as a module
36 //! let y = u16(u8::MAX);
37 //! // `u8` as a function
38 //! let z = u8(y).unwrap();
39 //! # }
40 //! ```
41 //!
42 //! The checked cast functionality is also usable with type aliases via the
43 //! `cast` static method:
44 //!
45 //! ```
46 //! use std::os::raw::c_ulonglong;
47 //! // NOTE avoid shadowing `std::convert::From` - cf. rust-lang/rfcs#1311
48 //! use cast::From as _0;
49 //!
50 //! # fn main() {
51 //! assert_eq!(c_ulonglong::cast(0u8), 0u64);
52 //! # }
53 //! ```
54 //!
55 //! This crate also provides a `From` trait that can be used, for example,
56 //! to create a generic function that accepts any type that can be infallibly
57 //! casted to `u32`.
58 //!
59 //! ```
60 //! fn to_u32<T>(x: T) -> u32
61 //!     // reads as: "where u32 can be casted from T with output u32"
62 //!     where u32: cast::From<T, Output=u32>,
63 //! {
64 //!     cast::u32(x)
65 //! }
66 //!
67 //! # fn main() {
68 //! assert_eq!(to_u32(0u8), 0u32);
69 //! assert_eq!(to_u32(1u16), 1u32);
70 //! assert_eq!(to_u32(2u32), 2u32);
71 //!
72 //! // to_u32(-1i32);  // Compile error
73 //! # }
74 //! ```
75 //!
76 //! ## Minimal Supported Rust Version
77 //!
78 //! This crate is guaranteed to compile *as a dependency* on stable Rust 1.31 and up.
79 //! It's not guaranteed that `cargo test`-ing this crate follows the MSRV.
80 //! It *might* compile on older versions but that may change in any new patch release.
81 //!
82 //! ## Building without `std`
83 //!
84 //! This crate can be used without Rust's `std` crate by declaring it as
85 //! follows in your `Cargo.toml`:
86 //!
87 //! ``` toml
88 //! cast = { version = "*", default-features = false }
89 //! ```
90 
91 #![allow(const_err)]
92 #![cfg_attr(not(feature = "std"), no_std)]
93 #![deny(missing_docs)]
94 #![deny(unsafe_code)]
95 #![deny(warnings)]
96 
97 #[cfg(test)]
98 #[macro_use]
99 extern crate quickcheck;
100 
101 use core::fmt;
102 #[cfg(feature = "std")]
103 use std::error;
104 
105 #[cfg(test)]
106 mod test;
107 
108 #[cfg(android_dylib)]
109 extern crate std;
110 
111 /// Cast errors
112 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
113 pub enum Error {
114     /// Infinite value casted to a type that can only represent finite values
115     Infinite,
116     /// NaN value casted to a type that can't represent a NaN value
117     NaN,
118     /// Source value is greater than the maximum value that the destination type
119     /// can hold
120     Overflow,
121     /// Source value is smaller than the minimum value that the destination type
122     /// can hold
123     Underflow,
124 }
125 
126 impl Error {
127     /// A private helper function that implements `description`, because
128     /// `description` is only available when we have `std` enabled.
description_helper(&self) -> &str129     fn description_helper(&self) -> &str {
130         match *self {
131             Error::Infinite => "Cannot store infinite value in finite type",
132             Error::NaN => "Cannot store NaN in type which does not support it",
133             Error::Overflow => "Overflow during numeric conversion",
134             Error::Underflow => "Underflow during numeric conversion",
135         }
136     }
137 }
138 
139 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result140     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141         write!(f, "{}", self.description_helper())
142     }
143 }
144 
145 #[cfg(feature = "std")]
146 impl error::Error for Error {
description(&self) -> &str147     fn description(&self) -> &str {
148         self.description_helper()
149     }
150 }
151 
152 /// The "cast from" operation
153 pub trait From<Src> {
154     /// The result of the cast operation: either `Self` or `Result<Self, Error>`
155     type Output;
156 
157     /// Checked cast from `Src` to `Self`
cast(_: Src) -> Self::Output158     fn cast(_: Src) -> Self::Output;
159 }
160 
161 macro_rules! fns {
162     ($($ty:ident),+) => {
163         $(
164             /// Checked cast function
165             #[inline]
166             pub fn $ty<T>(x: T) -> <$ty as From<T>>::Output
167                 where $ty: From<T>
168             {
169                 <$ty as From<T>>::cast(x)
170             }
171          )+
172     }
173 }
174 
175 fns!(f32, f64, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
176 
177 fns!(i128, u128);
178 
179 /// `$dst` can hold any value of `$src`
180 macro_rules! promotion {
181     ($($src:ty => $($dst: ty),+);+;) => {
182         $(
183             $(
184                 impl From<$src> for $dst {
185                     type Output = $dst;
186 
187                     #[inline]
188                     fn cast(src: $src) -> $dst {
189                         src as $dst
190                     }
191                 }
192             )+
193         )+
194     }
195 }
196 
197 /// `$dst` can hold any positive value of `$src`
198 macro_rules! half_promotion {
199     ($($src:ty => $($dst:ty),+);+;) => {
200         $(
201             $(
202                 impl From<$src> for $dst {
203                     type Output = Result<$dst, Error>;
204 
205                     #[inline]
206                     fn cast(src: $src) -> Self::Output {
207                         if src < 0 {
208                             Err(Error::Underflow)
209                         } else {
210                             Ok(src as $dst)
211                         }
212                     }
213                 }
214             )+
215         )+
216     }
217 }
218 
219 /// From an unsigned `$src` to a smaller `$dst`
220 macro_rules! from_unsigned {
221     ($($src:ident => $($dst:ident),+);+;) => {
222         $(
223             $(
224                 impl From<$src> for $dst {
225                     type Output = Result<$dst, Error>;
226 
227                     #[inline]
228                     fn cast(src: $src) -> Self::Output {
229                         use core::$dst;
230 
231                         if src > $dst::MAX as $src {
232                             Err(Error::Overflow)
233                         } else {
234                             Ok(src as $dst)
235                         }
236                     }
237                 }
238             )+
239         )+
240     }
241 }
242 
243 /// From a signed `$src` to a smaller `$dst`
244 macro_rules! from_signed {
245     ($($src:ident => $($dst:ident),+);+;) => {
246         $(
247             $(
248                 impl From<$src> for $dst {
249                     type Output = Result<$dst, Error>;
250 
251                     #[inline]
252                     fn cast(src: $src) -> Self::Output {
253                         use core::$dst;
254 
255                         Err(if src < $dst::MIN as $src {
256                             Error::Underflow
257                         } else if src > $dst::MAX as $src {
258                             Error::Overflow
259                         } else {
260                             return Ok(src as $dst);
261                         })
262                     }
263                 }
264             )+
265         )+
266     }
267 }
268 
269 /// From a float `$src` to an integer `$dst`
270 macro_rules! from_float {
271     ($($src:ident => $($dst:ident),+);+;) => {
272         $(
273             $(
274                 impl From<$src> for $dst {
275                     type Output = Result<$dst, Error>;
276 
277                     #[inline]
278                     fn cast(src: $src) -> Self::Output {
279                         use core::{$dst, $src};
280 
281                         Err(if src != src {
282                             Error::NaN
283                         } else if src == $src::INFINITY ||
284                             src == $src::NEG_INFINITY {
285                             Error::Infinite
286                         } else if {
287                             // this '$dst::BITS' works on 1.31.0 (MSRV)
288                             let dst_bits = core::mem::size_of::<$dst>() as u32 * 8;
289                             let lossless = dst_bits < core::$src::MANTISSA_DIGITS;
290 
291                             let max = if lossless {
292                                 $dst::MAX as $src
293                             } else {
294                                 // we subtract 1 ULP (unit of least precision) here because some
295                                 // lossy conversions like `u64::MAX as f64` round *up* and we want
296                                 // to avoid the check below evaluating to false in that case
297                                 $src::from_bits(($dst::MAX as $src).to_bits() - 1)
298                             };
299 
300                             src > max
301                         } {
302                             Error::Overflow
303                         } else if $dst::MIN == 0 {
304                             // when casting to unsigned integer, negative values close to 0 but
305                             // larger than 1.0 should be truncated to 0; this behavior matches
306                             // casting from a float to a signed integer
307                             if src <= -1.0 {
308                                 Error::Underflow
309                             } else {
310                                 return Ok(src as $dst);
311                             }
312                         } else if src < $dst::MIN as $src {
313                             Error::Underflow
314                         } else  {
315                             return Ok(src as $dst);
316                         })
317                     }
318                 }
319             )+
320         )+
321     }
322 }
323 
324 /// From a float `$src` to an integer `$dst`, where $dst is large enough to contain
325 /// all values of `$src`. We can't ever overflow here
326 macro_rules! from_float_dst {
327     ($($src:ident => $($dst:ident),+);+;) => {
328         $(
329             $(
330                 impl From<$src> for $dst {
331                      type Output = Result<$dst, Error>;
332 
333                     #[inline]
334                     #[allow(unused_comparisons)]
335                     fn cast(src: $src) -> Self::Output {
336                         use core::{$dst, $src};
337 
338                         Err(if src != src {
339                             Error::NaN
340                         } else if src == $src::INFINITY ||
341                             src == $src::NEG_INFINITY {
342                             Error::Infinite
343                         } else if ($dst::MIN == 0) && src <= -1.0 {
344                             Error::Underflow
345                         } else {
346                             return Ok(src as $dst);
347                         })
348                     }
349                 }
350             )+
351         )+
352     }
353 }
354 
355 // PLAY TETRIS! ;-)
356 
357 #[cfg(target_pointer_width = "32")]
358 mod _32 {
359     use crate::{Error, From};
360 
361     // Signed
362     promotion! {
363         i8    => f32, f64, i8, i16, i32, isize, i64;
364         i16   => f32, f64,     i16, i32, isize, i64;
365         i32   => f32, f64,          i32, isize, i64;
366         isize => f32, f64,          i32, isize, i64;
367         i64   => f32, f64,                      i64;
368     }
369 
370     half_promotion! {
371         i8    =>                                     u8, u16, u32, usize, u64;
372         i16   =>                                         u16, u32, usize, u64;
373         i32   =>                                              u32, usize, u64;
374         isize =>                                              u32, usize, u64;
375         i64   =>                                                          u64;
376     }
377 
378     from_signed! {
379 
380         i16   =>           i8,                       u8;
381         i32   =>           i8, i16,                  u8, u16;
382         isize =>           i8, i16,                  u8, u16;
383         i64   =>           i8, i16, i32, isize,      u8, u16, u32, usize;
384     }
385 
386     // Unsigned
387     promotion! {
388         u8    => f32, f64,     i16, i32, isize, i64, u8, u16, u32, usize, u64;
389         u16   => f32, f64,          i32, isize, i64,     u16, u32, usize, u64;
390         u32   => f32, f64,                      i64,          u32, usize, u64;
391         usize => f32, f64,                      i64,          u32, usize, u64;
392         u64   => f32, f64,                                                u64;
393     }
394 
395     from_unsigned! {
396         u8    =>           i8;
397         u16   =>           i8, i16,                  u8;
398         u32   =>           i8, i16, i32, isize,      u8, u16;
399         usize =>           i8, i16, i32, isize,      u8, u16;
400         u64   =>           i8, i16, i32, isize, i64, u8, u16, u32, usize;
401     }
402 
403     // Float
404     promotion! {
405         f32   => f32, f64;
406         f64   =>      f64;
407     }
408 
409     from_float! {
410         f32 =>             i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
411         f64 =>             i8, i16, i32, isize, i64, u8, u16, u32, usize, u64;
412     }
413 }
414 
415 #[cfg(target_pointer_width = "64")]
416 mod _64 {
417     use crate::{Error, From};
418 
419     // Signed
420     promotion! {
421         i8    => f32, f64, i8, i16, i32, i64, isize;
422         i16   => f32, f64,     i16, i32, i64, isize;
423         i32   => f32, f64,          i32, i64, isize;
424         i64   => f32, f64,               i64, isize;
425         isize => f32, f64,               i64, isize;
426     }
427 
428     half_promotion! {
429         i8    =>                                     u8, u16, u32, u64, usize;
430         i16   =>                                         u16, u32, u64, usize;
431         i32   =>                                              u32, u64, usize;
432         i64   =>                                                   u64, usize;
433         isize =>                                                   u64, usize;
434     }
435 
436     from_signed! {
437 
438         i16   =>           i8,                       u8;
439         i32   =>           i8, i16,                  u8, u16;
440         i64   =>           i8, i16, i32,             u8, u16, u32;
441         isize =>           i8, i16, i32,             u8, u16, u32;
442     }
443 
444     // Unsigned
445     promotion! {
446         u8    => f32, f64,     i16, i32, i64, isize, u8, u16, u32, u64, usize;
447         u16   => f32, f64,          i32, i64, isize,     u16, u32, u64, usize;
448         u32   => f32, f64,               i64, isize,          u32, u64, usize;
449         u64   => f32, f64,                                         u64, usize;
450         usize => f32, f64,                                         u64, usize;
451     }
452 
453     from_unsigned! {
454         u8    =>           i8;
455         u16   =>           i8, i16,                  u8;
456         u32   =>           i8, i16, i32,             u8, u16;
457         u64   =>           i8, i16, i32, i64, isize, u8, u16, u32;
458         usize =>           i8, i16, i32, i64, isize, u8, u16, u32;
459     }
460 
461     // Float
462     promotion! {
463         f32  => f32, f64;
464         f64  =>      f64;
465     }
466 
467     from_float! {
468         f32 =>             i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
469         f64 =>             i8, i16, i32, i64, isize, u8, u16, u32, u64, usize;
470     }
471 }
472 
473 mod _x128 {
474     use crate::{Error, From};
475 
476     // Signed
477     promotion! {
478         i8    =>                              i128;
479         i16   =>                              i128;
480         i32   =>                              i128;
481         i64   =>                              i128;
482         isize =>                              i128;
483         i128  => f32, f64,                    i128;
484     }
485 
486     half_promotion! {
487         i8    =>                                                              u128;
488         i16   =>                                                              u128;
489         i32   =>                                                              u128;
490         i64   =>                                                              u128;
491         isize =>                                                              u128;
492         i128  =>                                                              u128;
493     }
494 
495     from_signed! {
496         i128  =>           i8, i16, i32, i64,       isize, u8, u16, u32, u64,       usize;
497     }
498 
499     // Unsigned
500     promotion! {
501         u8    =>                              i128,                           u128;
502         u16   =>                              i128,                           u128;
503         u32   =>                              i128,                           u128;
504         u64   =>                              i128,                           u128;
505         usize =>                              i128,                           u128;
506         u128  =>      f64,                                                    u128;
507     }
508 
509     from_unsigned! {
510         u128 => f32,       i8, i16, i32, i64, i128, isize, u8, u16, u32, u64,       usize;
511     }
512 
513     // Float
514     from_float_dst! {
515         f32 =>                                                                u128;
516     }
517 
518     from_float! {
519         f32 =>                                i128;
520         f64 =>                                i128,                           u128;
521     }
522 }
523 
524 // The missing piece
525 impl From<f64> for f32 {
526     type Output = Result<f32, Error>;
527 
528     #[inline]
cast(src: f64) -> Self::Output529     fn cast(src: f64) -> Self::Output {
530         use core::{f32, f64};
531 
532         if src != src || src == f64::INFINITY || src == f64::NEG_INFINITY {
533             Ok(src as f32)
534         } else if src < f32::MIN as f64 {
535             Err(Error::Underflow)
536         } else if src > f32::MAX as f64 {
537             Err(Error::Overflow)
538         } else {
539             Ok(src as f32)
540         }
541     }
542 }
543