| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| use std::num; |
| use thiserror::Error; |
| |
| use crate::util::bigint; |
| |
| #[derive(Error, Debug, Clone, Eq, PartialEq)] |
| pub enum ParseIntError { |
| #[error(transparent)] |
| ParseIntError(#[from] num::ParseIntError), |
| #[error(transparent)] |
| ParseByteArrayError(#[from] bigint::ParseBigIntError), |
| } |
| |
| /// Trait for parsing integers. |
| /// |
| /// Strings beginning with the common and classic prefixes `0x`, `0o`, `0b` or `0` are parsed |
| /// as hex, octal, binary, and octal (classic). Anything else is parsed as decimal. |
| /// A leading `+` or `-` is permitted. Any string parsed by `strtol(3)` or `strtoul(3)` |
| /// will be parsed successfully. |
| pub trait ParseInt: Sized { |
| type FromStrRadixErr: Into<ParseIntError>; |
| |
| fn from_str_radix(src: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>; |
| |
| fn from_str(src: &str) -> Result<Self, ParseIntError> { |
| let (val, negative) = if let Some(v) = src.strip_prefix('-') { |
| (v, true) |
| } else if let Some(v) = src.strip_prefix('+') { |
| (v, false) |
| } else { |
| (src, false) |
| }; |
| |
| let (radix, digits) = match val.get(0..2) { |
| Some("0x") | Some("0X") => (16, &val[2..]), |
| Some("0o") | Some("0O") => (8, &val[2..]), |
| Some("0b") | Some("0B") => (2, &val[2..]), |
| _ if val.starts_with('0') => (8, val), |
| _ => (10, val), |
| }; |
| |
| if negative { |
| let mut digits = digits.to_string(); |
| digits.insert(0, '-'); |
| Self::from_str_radix(&digits, radix).map_err(Self::FromStrRadixErr::into) |
| } else { |
| Self::from_str_radix(digits, radix).map_err(Self::FromStrRadixErr::into) |
| } |
| } |
| } |
| |
| macro_rules! impl_parse_int { |
| ($ty:ident) => { |
| impl ParseInt for $ty { |
| type FromStrRadixErr = num::ParseIntError; |
| |
| fn from_str_radix(src: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> { |
| $ty::from_str_radix(src, radix) |
| } |
| } |
| }; |
| } |
| |
| impl_parse_int!(i8); |
| impl_parse_int!(u8); |
| impl_parse_int!(i16); |
| impl_parse_int!(u16); |
| impl_parse_int!(i32); |
| impl_parse_int!(u32); |
| impl_parse_int!(i64); |
| impl_parse_int!(u64); |
| impl_parse_int!(i128); |
| impl_parse_int!(u128); |
| impl_parse_int!(isize); |
| impl_parse_int!(usize); |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn test_i8() { |
| assert_eq!(i8::from_str("0"), Ok(0)); |
| assert_eq!(i8::from_str("-1"), Ok(-1)); |
| assert_eq!(i8::from_str("+100"), Ok(100)); |
| assert_eq!(i8::from_str("-128"), Ok(-128)); |
| assert_eq!(i8::from_str("0x10"), Ok(16)); |
| assert_eq!(i8::from_str("-0x10"), Ok(-16)); |
| assert_eq!(i8::from_str("0o10"), Ok(8)); |
| assert_eq!(i8::from_str("-0o10"), Ok(-8)); |
| assert_eq!(i8::from_str("0b10"), Ok(2)); |
| assert_eq!(i8::from_str("-0b10"), Ok(-2)); |
| assert_eq!(i8::from_str("012"), Ok(10)); |
| assert!(i8::from_str("128").is_err()); |
| assert!(i8::from_str("-129").is_err()); |
| } |
| |
| #[test] |
| fn test_u8() { |
| assert_eq!(u8::from_str("0"), Ok(0)); |
| assert_eq!(u8::from_str("+100"), Ok(100)); |
| assert_eq!(u8::from_str("128"), Ok(128)); |
| assert_eq!(u8::from_str("255"), Ok(255)); |
| assert_eq!(u8::from_str("0x10"), Ok(16)); |
| assert_eq!(u8::from_str("0xFF"), Ok(255)); |
| assert_eq!(u8::from_str("0o10"), Ok(8)); |
| assert_eq!(u8::from_str("0b10000000"), Ok(128)); |
| assert_eq!(u8::from_str("012"), Ok(10)); |
| assert!(u8::from_str("-1").is_err()); |
| assert!(u8::from_str("256").is_err()); |
| } |
| |
| #[test] |
| fn test_i16() { |
| assert_eq!(i16::from_str("0"), Ok(0)); |
| assert_eq!(i16::from_str("-1"), Ok(-1)); |
| assert_eq!(i16::from_str("+32767"), Ok(32767)); |
| assert_eq!(i16::from_str("-32768"), Ok(-32768)); |
| assert_eq!(i16::from_str("0x3fff"), Ok(16383)); |
| assert_eq!(i16::from_str("-0x10"), Ok(-16)); |
| assert_eq!(i16::from_str("0o10"), Ok(8)); |
| assert_eq!(i16::from_str("-0o10"), Ok(-8)); |
| assert_eq!(i16::from_str("0b10"), Ok(2)); |
| assert_eq!(i16::from_str("-0b10"), Ok(-2)); |
| assert_eq!(i16::from_str("012"), Ok(10)); |
| assert!(i16::from_str("32768").is_err()); |
| assert!(i16::from_str("-32769").is_err()); |
| } |
| |
| #[test] |
| fn test_u16() { |
| assert_eq!(u16::from_str("0"), Ok(0)); |
| assert_eq!(u16::from_str("+100"), Ok(100)); |
| assert_eq!(u16::from_str("32768"), Ok(32768)); |
| assert_eq!(u16::from_str("65535"), Ok(65535)); |
| assert_eq!(u16::from_str("0x10"), Ok(16)); |
| assert_eq!(u16::from_str("0xFF"), Ok(255)); |
| assert_eq!(u16::from_str("0o10"), Ok(8)); |
| assert_eq!(u16::from_str("0b1000000000000000"), Ok(32768)); |
| assert_eq!(u16::from_str("012"), Ok(10)); |
| assert!(u16::from_str("-1").is_err()); |
| assert!(u16::from_str("65536").is_err()); |
| } |
| |
| #[test] |
| fn test_i32() { |
| assert_eq!(i32::from_str("0"), Ok(0)); |
| assert_eq!(i32::from_str("-1"), Ok(-1)); |
| assert_eq!(i32::from_str("+2147483647"), Ok(2147483647)); |
| assert_eq!(i32::from_str("-2147483648"), Ok(-2147483648)); |
| assert_eq!(i32::from_str("0x7fffffff"), Ok(2147483647)); |
| assert_eq!(i32::from_str("-0x10"), Ok(-16)); |
| assert_eq!(i32::from_str("0o10"), Ok(8)); |
| assert_eq!(i32::from_str("-0o10"), Ok(-8)); |
| assert_eq!(i32::from_str("0b10"), Ok(2)); |
| assert_eq!(i32::from_str("-0b10"), Ok(-2)); |
| assert_eq!(i32::from_str("012"), Ok(10)); |
| assert!(i32::from_str("2147483648").is_err()); |
| assert!(i32::from_str("-2147483649").is_err()); |
| } |
| |
| #[test] |
| fn test_u32() { |
| assert_eq!(u32::from_str("0"), Ok(0)); |
| assert_eq!(u32::from_str("+100"), Ok(100)); |
| assert_eq!(u32::from_str("2147483648"), Ok(2147483648)); |
| assert_eq!(u32::from_str("4294967295"), Ok(4294967295)); |
| assert_eq!(u32::from_str("0x10"), Ok(16)); |
| assert_eq!(u32::from_str("0xFF"), Ok(255)); |
| assert_eq!(u32::from_str("0o10"), Ok(8)); |
| assert_eq!(u32::from_str("012"), Ok(10)); |
| assert_eq!( |
| u32::from_str("0b10000000000000000000000000000000"), |
| Ok(2147483648) |
| ); |
| assert!(u32::from_str("-1").is_err()); |
| assert!(u32::from_str("4294967296").is_err()); |
| } |
| |
| #[test] |
| fn test_i64() { |
| assert_eq!(i64::from_str("0"), Ok(0)); |
| assert_eq!(i64::from_str("-1"), Ok(-1)); |
| assert_eq!( |
| i64::from_str("+9223372036854775807"), |
| Ok(9223372036854775807) |
| ); |
| assert_eq!( |
| i64::from_str("-9223372036854775808"), |
| Ok(-9223372036854775808) |
| ); |
| assert_eq!(i64::from_str("0x7fffffff"), Ok(2147483647)); |
| assert_eq!(i64::from_str("-0x10"), Ok(-16)); |
| assert_eq!(i64::from_str("0o10"), Ok(8)); |
| assert_eq!(i64::from_str("-0o10"), Ok(-8)); |
| assert_eq!(i64::from_str("0b10"), Ok(2)); |
| assert_eq!(i64::from_str("-0b10"), Ok(-2)); |
| assert_eq!(i64::from_str("012"), Ok(10)); |
| assert!(i64::from_str("9223372036854775808").is_err()); |
| assert!(i64::from_str("-9223372036854775809").is_err()); |
| } |
| |
| #[test] |
| fn test_u64() { |
| assert_eq!(u64::from_str("0"), Ok(0)); |
| assert_eq!(u64::from_str("+100"), Ok(100)); |
| assert_eq!( |
| u64::from_str("+9223372036854775808"), |
| Ok(9223372036854775808) |
| ); |
| assert_eq!( |
| u64::from_str("18446744073709551615"), |
| Ok(18446744073709551615) |
| ); |
| assert_eq!(u64::from_str("0x10"), Ok(16)); |
| assert_eq!(u64::from_str("0xFF"), Ok(255)); |
| assert_eq!(u64::from_str("0o10"), Ok(8)); |
| assert_eq!( |
| u64::from_str("0b10000000000000000000000000000000"), |
| Ok(2147483648) |
| ); |
| assert_eq!(u64::from_str("012"), Ok(10)); |
| assert!(u64::from_str("-1").is_err()); |
| assert!(u64::from_str("18446744073709551616").is_err()); |
| } |
| |
| #[test] |
| fn test_isize() { |
| // Since isize's size is target defined, we don't bother with |
| // trying to test large values and assume the tests for the |
| // other integer types will cover the values for isize. |
| assert_eq!(isize::from_str("0"), Ok(0)); |
| assert_eq!(isize::from_str("-1"), Ok(-1)); |
| assert_eq!(isize::from_str("0x7f"), Ok(127)); |
| assert_eq!(isize::from_str("-0x10"), Ok(-16)); |
| assert_eq!(isize::from_str("0o10"), Ok(8)); |
| assert_eq!(isize::from_str("-0o10"), Ok(-8)); |
| assert_eq!(isize::from_str("0b10"), Ok(2)); |
| assert_eq!(isize::from_str("-0b10"), Ok(-2)); |
| assert_eq!(isize::from_str("012"), Ok(10)); |
| } |
| |
| #[test] |
| fn test_usize() { |
| // Since usize's size is target defined, we don't bother with |
| // trying to test large values and assume the tests for the |
| // other integer types will cover the values for usize. |
| assert_eq!(usize::from_str("0"), Ok(0)); |
| assert_eq!(usize::from_str("+100"), Ok(100)); |
| assert_eq!(usize::from_str("0x10"), Ok(16)); |
| assert_eq!(usize::from_str("0xFF"), Ok(255)); |
| assert_eq!(usize::from_str("0o10"), Ok(8)); |
| assert_eq!(usize::from_str("012"), Ok(10)); |
| assert!(usize::from_str("-1").is_err()); |
| } |
| } |