opentitanlib/util/
parse_int.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use std::num;
6use thiserror::Error;
7
8use crate::util::bigint;
9
10#[derive(Error, Debug, Clone, Eq, PartialEq)]
11pub enum ParseIntError {
12    #[error(transparent)]
13    ParseIntError(#[from] num::ParseIntError),
14    #[error(transparent)]
15    ParseByteArrayError(#[from] bigint::ParseBigIntError),
16}
17
18/// Trait for parsing integers.
19///
20/// Strings beginning with the common and classic prefixes `0x`, `0o`, `0b` or `0` are parsed
21/// as hex, octal, binary, and octal (classic).  Anything else is parsed as decimal.
22/// A leading `+` or `-` is permitted.  Any string parsed by `strtol(3)` or `strtoul(3)`
23/// will be parsed successfully.
24pub trait ParseInt: Sized {
25    type FromStrRadixErr: Into<ParseIntError>;
26
27    fn from_str_radix(src: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>;
28
29    fn from_str(src: &str) -> Result<Self, ParseIntError> {
30        let (val, negative) = if let Some(v) = src.strip_prefix('-') {
31            (v, true)
32        } else if let Some(v) = src.strip_prefix('+') {
33            (v, false)
34        } else {
35            (src, false)
36        };
37
38        let (radix, digits) = match val.get(0..2) {
39            Some("0x") | Some("0X") => (16, &val[2..]),
40            Some("0o") | Some("0O") => (8, &val[2..]),
41            Some("0b") | Some("0B") => (2, &val[2..]),
42            _ if val.starts_with('0') => (8, val),
43            _ => (10, val),
44        };
45
46        if negative {
47            let mut digits = digits.to_string();
48            digits.insert(0, '-');
49            Self::from_str_radix(&digits, radix).map_err(Self::FromStrRadixErr::into)
50        } else {
51            Self::from_str_radix(digits, radix).map_err(Self::FromStrRadixErr::into)
52        }
53    }
54}
55
56macro_rules! impl_parse_int {
57    ($ty:ident) => {
58        impl ParseInt for $ty {
59            type FromStrRadixErr = num::ParseIntError;
60
61            fn from_str_radix(src: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
62                $ty::from_str_radix(src, radix)
63            }
64        }
65    };
66}
67
68impl_parse_int!(i8);
69impl_parse_int!(u8);
70impl_parse_int!(i16);
71impl_parse_int!(u16);
72impl_parse_int!(i32);
73impl_parse_int!(u32);
74impl_parse_int!(i64);
75impl_parse_int!(u64);
76impl_parse_int!(i128);
77impl_parse_int!(u128);
78impl_parse_int!(isize);
79impl_parse_int!(usize);
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_i8() {
87        assert_eq!(i8::from_str("0"), Ok(0));
88        assert_eq!(i8::from_str("-1"), Ok(-1));
89        assert_eq!(i8::from_str("+100"), Ok(100));
90        assert_eq!(i8::from_str("-128"), Ok(-128));
91        assert_eq!(i8::from_str("0x10"), Ok(16));
92        assert_eq!(i8::from_str("-0x10"), Ok(-16));
93        assert_eq!(i8::from_str("0o10"), Ok(8));
94        assert_eq!(i8::from_str("-0o10"), Ok(-8));
95        assert_eq!(i8::from_str("0b10"), Ok(2));
96        assert_eq!(i8::from_str("-0b10"), Ok(-2));
97        assert_eq!(i8::from_str("012"), Ok(10));
98        assert!(i8::from_str("128").is_err());
99        assert!(i8::from_str("-129").is_err());
100    }
101
102    #[test]
103    fn test_u8() {
104        assert_eq!(u8::from_str("0"), Ok(0));
105        assert_eq!(u8::from_str("+100"), Ok(100));
106        assert_eq!(u8::from_str("128"), Ok(128));
107        assert_eq!(u8::from_str("255"), Ok(255));
108        assert_eq!(u8::from_str("0x10"), Ok(16));
109        assert_eq!(u8::from_str("0xFF"), Ok(255));
110        assert_eq!(u8::from_str("0o10"), Ok(8));
111        assert_eq!(u8::from_str("0b10000000"), Ok(128));
112        assert_eq!(u8::from_str("012"), Ok(10));
113        assert!(u8::from_str("-1").is_err());
114        assert!(u8::from_str("256").is_err());
115    }
116
117    #[test]
118    fn test_i16() {
119        assert_eq!(i16::from_str("0"), Ok(0));
120        assert_eq!(i16::from_str("-1"), Ok(-1));
121        assert_eq!(i16::from_str("+32767"), Ok(32767));
122        assert_eq!(i16::from_str("-32768"), Ok(-32768));
123        assert_eq!(i16::from_str("0x3fff"), Ok(16383));
124        assert_eq!(i16::from_str("-0x10"), Ok(-16));
125        assert_eq!(i16::from_str("0o10"), Ok(8));
126        assert_eq!(i16::from_str("-0o10"), Ok(-8));
127        assert_eq!(i16::from_str("0b10"), Ok(2));
128        assert_eq!(i16::from_str("-0b10"), Ok(-2));
129        assert_eq!(i16::from_str("012"), Ok(10));
130        assert!(i16::from_str("32768").is_err());
131        assert!(i16::from_str("-32769").is_err());
132    }
133
134    #[test]
135    fn test_u16() {
136        assert_eq!(u16::from_str("0"), Ok(0));
137        assert_eq!(u16::from_str("+100"), Ok(100));
138        assert_eq!(u16::from_str("32768"), Ok(32768));
139        assert_eq!(u16::from_str("65535"), Ok(65535));
140        assert_eq!(u16::from_str("0x10"), Ok(16));
141        assert_eq!(u16::from_str("0xFF"), Ok(255));
142        assert_eq!(u16::from_str("0o10"), Ok(8));
143        assert_eq!(u16::from_str("0b1000000000000000"), Ok(32768));
144        assert_eq!(u16::from_str("012"), Ok(10));
145        assert!(u16::from_str("-1").is_err());
146        assert!(u16::from_str("65536").is_err());
147    }
148
149    #[test]
150    fn test_i32() {
151        assert_eq!(i32::from_str("0"), Ok(0));
152        assert_eq!(i32::from_str("-1"), Ok(-1));
153        assert_eq!(i32::from_str("+2147483647"), Ok(2147483647));
154        assert_eq!(i32::from_str("-2147483648"), Ok(-2147483648));
155        assert_eq!(i32::from_str("0x7fffffff"), Ok(2147483647));
156        assert_eq!(i32::from_str("-0x10"), Ok(-16));
157        assert_eq!(i32::from_str("0o10"), Ok(8));
158        assert_eq!(i32::from_str("-0o10"), Ok(-8));
159        assert_eq!(i32::from_str("0b10"), Ok(2));
160        assert_eq!(i32::from_str("-0b10"), Ok(-2));
161        assert_eq!(i32::from_str("012"), Ok(10));
162        assert!(i32::from_str("2147483648").is_err());
163        assert!(i32::from_str("-2147483649").is_err());
164    }
165
166    #[test]
167    fn test_u32() {
168        assert_eq!(u32::from_str("0"), Ok(0));
169        assert_eq!(u32::from_str("+100"), Ok(100));
170        assert_eq!(u32::from_str("2147483648"), Ok(2147483648));
171        assert_eq!(u32::from_str("4294967295"), Ok(4294967295));
172        assert_eq!(u32::from_str("0x10"), Ok(16));
173        assert_eq!(u32::from_str("0xFF"), Ok(255));
174        assert_eq!(u32::from_str("0o10"), Ok(8));
175        assert_eq!(u32::from_str("012"), Ok(10));
176        assert_eq!(
177            u32::from_str("0b10000000000000000000000000000000"),
178            Ok(2147483648)
179        );
180        assert!(u32::from_str("-1").is_err());
181        assert!(u32::from_str("4294967296").is_err());
182    }
183
184    #[test]
185    fn test_i64() {
186        assert_eq!(i64::from_str("0"), Ok(0));
187        assert_eq!(i64::from_str("-1"), Ok(-1));
188        assert_eq!(
189            i64::from_str("+9223372036854775807"),
190            Ok(9223372036854775807)
191        );
192        assert_eq!(
193            i64::from_str("-9223372036854775808"),
194            Ok(-9223372036854775808)
195        );
196        assert_eq!(i64::from_str("0x7fffffff"), Ok(2147483647));
197        assert_eq!(i64::from_str("-0x10"), Ok(-16));
198        assert_eq!(i64::from_str("0o10"), Ok(8));
199        assert_eq!(i64::from_str("-0o10"), Ok(-8));
200        assert_eq!(i64::from_str("0b10"), Ok(2));
201        assert_eq!(i64::from_str("-0b10"), Ok(-2));
202        assert_eq!(i64::from_str("012"), Ok(10));
203        assert!(i64::from_str("9223372036854775808").is_err());
204        assert!(i64::from_str("-9223372036854775809").is_err());
205    }
206
207    #[test]
208    fn test_u64() {
209        assert_eq!(u64::from_str("0"), Ok(0));
210        assert_eq!(u64::from_str("+100"), Ok(100));
211        assert_eq!(
212            u64::from_str("+9223372036854775808"),
213            Ok(9223372036854775808)
214        );
215        assert_eq!(
216            u64::from_str("18446744073709551615"),
217            Ok(18446744073709551615)
218        );
219        assert_eq!(u64::from_str("0x10"), Ok(16));
220        assert_eq!(u64::from_str("0xFF"), Ok(255));
221        assert_eq!(u64::from_str("0o10"), Ok(8));
222        assert_eq!(
223            u64::from_str("0b10000000000000000000000000000000"),
224            Ok(2147483648)
225        );
226        assert_eq!(u64::from_str("012"), Ok(10));
227        assert!(u64::from_str("-1").is_err());
228        assert!(u64::from_str("18446744073709551616").is_err());
229    }
230
231    #[test]
232    fn test_isize() {
233        // Since isize's size is target defined, we don't bother with
234        // trying to test large values and assume the tests for the
235        // other integer types will cover the values for isize.
236        assert_eq!(isize::from_str("0"), Ok(0));
237        assert_eq!(isize::from_str("-1"), Ok(-1));
238        assert_eq!(isize::from_str("0x7f"), Ok(127));
239        assert_eq!(isize::from_str("-0x10"), Ok(-16));
240        assert_eq!(isize::from_str("0o10"), Ok(8));
241        assert_eq!(isize::from_str("-0o10"), Ok(-8));
242        assert_eq!(isize::from_str("0b10"), Ok(2));
243        assert_eq!(isize::from_str("-0b10"), Ok(-2));
244        assert_eq!(isize::from_str("012"), Ok(10));
245    }
246
247    #[test]
248    fn test_usize() {
249        // Since usize's size is target defined, we don't bother with
250        // trying to test large values and assume the tests for the
251        // other integer types will cover the values for usize.
252        assert_eq!(usize::from_str("0"), Ok(0));
253        assert_eq!(usize::from_str("+100"), Ok(100));
254        assert_eq!(usize::from_str("0x10"), Ok(16));
255        assert_eq!(usize::from_str("0xFF"), Ok(255));
256        assert_eq!(usize::from_str("0o10"), Ok(8));
257        assert_eq!(usize::from_str("012"), Ok(10));
258        assert!(usize::from_str("-1").is_err());
259    }
260}