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
//! Common utility functions and types.

use error::{ Error, Result };

/// Converts an `i8`, `i16`, `i32` or `i64` to a `usize` if the range and
/// the value permits. Constructs an error message based on `msg` otherwise.
#[cfg_attr(feature = "cargo-clippy", allow(cast_possible_wrap, cast_possible_truncation, if_same_then_else))]
pub fn int_to_usize_with_msg<T: Into<i64>>(x: T, msg: &str) -> Result<usize> {
    use std::usize;
    use std::mem::size_of;

    let n: i64 = x.into();

    // XXX: the correctness of this usize -> i64 cast relies on the following:
    // 1. if `sizeof(usize) >= sizeof(i64)`, i.e. 64-bit and wider word size
    //    platforms (the typical), then `i64::MAX` always fits into a `usize`,
    //    therefore the cast `n as usize` is safe as long as `n >= 0`.
    // 2. Otherwise, if `sizeof(usize) < sizeof(i64)`, eg. 32-bit architectures,
    //    then we can safely cast `usize::MAX` to `i64` in order to find out
    //    via comparison whether the actual `i64` value fits dynamically.
    if n < 0 {
        Err(Error::new(format!("{} ({}) is negative", msg, n)))
    } else if size_of::<usize>() >= size_of::<i64>() {
        Ok(n as usize)
    } else if n <= usize::MAX as i64 {
        Ok(n as usize)
    } else {
        Err(Error::new(format!("{} ({}) overflows `usize`", msg, n)))
    }
}