blob: 6da3d4806cd0e814b660aaf8707415294d74b3d5 [file] [log] [blame]
// 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());
}
}