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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#[cfg(test)]
mod tests;
/// SI unit constants: K, M, G, KB...
pub mod units;
type Result<T> = ::std::result::Result<T, ::std::num::ParseIntError>;

/// Parse a numeric string with an optional one-or-two character multiplicative
/// suffix (K, KB, M, MB, ...)
/// into an integer. Numeric strings may be decimal, octal, or hexadecimal.
/// Multiple numeric strings may be multipled together by separating them with
/// the 'X' character. See `number/units` for a list of the SI suffixes.
///
/// ```
/// #use dd::opts::number::parse;
///    vec![
///        ("base case", "2", Some(2)),
///        ("two ok", "0x18X21", Some(0x18 * 21)),
///        ("mixed types", "0x11X0o22X2", Some(0x11 * 0o22 * 2)),
///        ("multiple suffixes", "0x13MX9K", Some(0x13 * M * 9 * K)),
///        ("bad string", "asjopdajsdomasd", None),
///    ]
///    .into_iter()
///    .for_each(|(name, s, want)| {
///        let got = parse(s).ok();
///        assert_eq!(
///            want, got,
///            r#"{}: want number("{}") = {:?}, but got {:?}"#,
///            name, s, want, got
///        );
///    })
/// ```
pub fn parse(s: &str) -> Result<usize> {
    let res: Result<Vec<usize>> = s.split('X').map(|s| scaled_part(s.trim())).collect();
    Ok(res?.into_iter().product())
}

/// Wrapper around [`parse`] that wraps the usize in an [Option]
pub fn parse_opt(s: &str) -> Result<Option<usize>> { Ok(Some(parse(s)?)) }

/// parse a number in the form [NUM][SCALE] to a usize,
/// where NUM is a hexidecimal, octal, or decimal number,
/// and scale is a one-or two byte suffix. see the documentation of parse for
/// more.
fn scaled_part(s: &str) -> Result<usize> {
    let (scale_unit, offset) = scaling_factor(s);
    let end = s.len() - offset;
    Ok(scale_unit * numeric_part(&s[..end])?)
}

/// parse a string to a number, using the starting two characters to determine
/// whether it is hexidecimal, octal, or decimal
fn numeric_part(s: &str) -> Result<usize> {
    let mut bytes = s.bytes();
    match (bytes.next(), bytes.next()) {
        // hexadecimal
        (Some(b'0'), Some(b'x')) | (Some(b'0'), Some(b'X')) => usize::from_str_radix(&s[2..], 16),
        // octal
        (Some(b'0'), Some(b'o')) => usize::from_str_radix(&s[2..], 8),
        // assume deciaml
        _ => usize::from_str_radix(s, 10),
    }
}
/// parse the suffix of the string to find a scaling factor, if any
/// and return the length of the suffix
fn scaling_factor(s: &str) -> (usize, usize) {
    let (scaling, offset) = if let Some(x) = two_byte_scaling_factor(s) {
        (x, 2)
    } else if let Some(x) = one_byte_scaling_factor(s) {
        (x, 1)
    } else {
        (1, 0)
    };
    (scaling, offset)
}

fn two_byte_scaling_factor(s: &str) -> Option<usize> {
    use self::units::*;
    let mut bytes = s.bytes();
    if Some(b'B') != bytes.next_back() {
        return None;
    }
    match bytes.next_back()? {
        b'K' => Some(KB),
        b'M' => Some(MB),
        b'G' => Some(GB),
        b'T' => Some(TB),
        b'P' => Some(PB),
        b'Z' => Some(ZB),
        _ => None,
    }
}
fn one_byte_scaling_factor(s: &str) -> Option<usize> {
    use self::units::*;
    Some(match s.bytes().next_back()? {
        b'C' => 1,
        b'W' => BITSIZE,
        b'B' => B,
        b'K' => K,
        b'M' => M,
        b'G' => G,
        b'T' => T,
        b'P' => P,
        b'Z' => Z,
        //  (_, b'y') => Y, // would have to use u128
        _ => return None,
    })
}