xref: /linux/rust/kernel/str/parse_int.rs (revision b0319c4642638bad4b36974055b1c0894b2c7aa9)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Integer parsing functions.
4 //!
5 //! Integer parsing functions for parsing signed and unsigned integers
6 //! potentially prefixed with `0x`, `0o`, or `0b`.
7 
8 use crate::prelude::*;
9 use crate::str::BStr;
10 use core::ops::Deref;
11 
12 // Make `FromStrRadix` a public type with a private name. This seals
13 // `ParseInt`, that is, prevents downstream users from implementing the
14 // trait.
15 mod private {
16     use crate::prelude::*;
17     use crate::str::BStr;
18 
19     /// Trait that allows parsing a [`&BStr`] to an integer with a radix.
20     pub trait FromStrRadix: Sized {
21         /// Parse `src` to [`Self`] using radix `radix`.
22         fn from_str_radix(src: &BStr, radix: u32) -> Result<Self>;
23 
24         /// Tries to convert `value` into [`Self`] and negates the resulting value.
25         fn from_u64_negated(value: u64) -> Result<Self>;
26     }
27 }
28 
29 /// Extract the radix from an integer literal optionally prefixed with
30 /// one of `0x`, `0X`, `0o`, `0O`, `0b`, `0B`, `0`.
31 fn strip_radix(src: &BStr) -> (u32, &BStr) {
32     match src.deref() {
33         [b'0', b'x' | b'X', rest @ ..] => (16, rest.as_ref()),
34         [b'0', b'o' | b'O', rest @ ..] => (8, rest.as_ref()),
35         [b'0', b'b' | b'B', rest @ ..] => (2, rest.as_ref()),
36         // NOTE: We are including the leading zero to be able to parse
37         // literal `0` here. If we removed it as a radix prefix, we would
38         // not be able to parse `0`.
39         [b'0', ..] => (8, src),
40         _ => (10, src),
41     }
42 }
43 
44 /// Trait for parsing string representations of integers.
45 ///
46 /// Strings beginning with `0x`, `0o`, or `0b` are parsed as hex, octal, or
47 /// binary respectively. Strings beginning with `0` otherwise are parsed as
48 /// octal. Anything else is parsed as decimal. A leading `+` or `-` is also
49 /// permitted. Any string parsed by [`kstrtol()`] or [`kstrtoul()`] will be
50 /// successfully parsed.
51 ///
52 /// [`kstrtol()`]: https://docs.kernel.org/core-api/kernel-api.html#c.kstrtol
53 /// [`kstrtoul()`]: https://docs.kernel.org/core-api/kernel-api.html#c.kstrtoul
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// # use kernel::str::parse_int::ParseInt;
59 /// # use kernel::b_str;
60 ///
61 /// assert_eq!(Ok(0u8), u8::from_str(b_str!("0")));
62 ///
63 /// assert_eq!(Ok(0xa2u8), u8::from_str(b_str!("0xa2")));
64 /// assert_eq!(Ok(-0xa2i32), i32::from_str(b_str!("-0xa2")));
65 ///
66 /// assert_eq!(Ok(-0o57i8), i8::from_str(b_str!("-0o57")));
67 /// assert_eq!(Ok(0o57i8), i8::from_str(b_str!("057")));
68 ///
69 /// assert_eq!(Ok(0b1001i16), i16::from_str(b_str!("0b1001")));
70 /// assert_eq!(Ok(-0b1001i16), i16::from_str(b_str!("-0b1001")));
71 ///
72 /// assert_eq!(Ok(127i8), i8::from_str(b_str!("127")));
73 /// assert!(i8::from_str(b_str!("128")).is_err());
74 /// assert_eq!(Ok(-128i8), i8::from_str(b_str!("-128")));
75 /// assert!(i8::from_str(b_str!("-129")).is_err());
76 /// assert_eq!(Ok(255u8), u8::from_str(b_str!("255")));
77 /// assert!(u8::from_str(b_str!("256")).is_err());
78 /// ```
79 pub trait ParseInt: private::FromStrRadix + TryFrom<u64> {
80     /// Parse a string according to the description in [`Self`].
81     fn from_str(src: &BStr) -> Result<Self> {
82         match src.deref() {
83             [b'-', rest @ ..] => {
84                 let (radix, digits) = strip_radix(rest.as_ref());
85                 // 2's complement values range from -2^(b-1) to 2^(b-1)-1.
86                 // So if we want to parse negative numbers as positive and
87                 // later multiply by -1, we have to parse into a larger
88                 // integer. We choose `u64` as sufficiently large.
89                 //
90                 // NOTE: 128 bit integers are not available on all
91                 // platforms, hence the choice of 64 bits.
92                 let val =
93                     u64::from_str_radix(core::str::from_utf8(digits).map_err(|_| EINVAL)?, radix)
94                         .map_err(|_| EINVAL)?;
95                 Self::from_u64_negated(val)
96             }
97             _ => {
98                 let (radix, digits) = strip_radix(src);
99                 Self::from_str_radix(digits, radix).map_err(|_| EINVAL)
100             }
101         }
102     }
103 }
104 
105 macro_rules! impl_parse_int {
106     ($($ty:ty),*) => {
107         $(
108             impl private::FromStrRadix for $ty {
109                 fn from_str_radix(src: &BStr, radix: u32) -> Result<Self> {
110                     <$ty>::from_str_radix(core::str::from_utf8(src).map_err(|_| EINVAL)?, radix)
111                         .map_err(|_| EINVAL)
112                 }
113 
114                 fn from_u64_negated(value: u64) -> Result<Self> {
115                     const ABS_MIN: u64 = {
116                         #[allow(unused_comparisons)]
117                         if <$ty>::MIN < 0 {
118                             1u64 << (<$ty>::BITS - 1)
119                         } else {
120                             0
121                         }
122                     };
123 
124                     if value > ABS_MIN {
125                         return Err(EINVAL);
126                     }
127 
128                     if value == ABS_MIN {
129                         return Ok(<$ty>::MIN);
130                     }
131 
132                     // SAFETY: The above checks guarantee that `value` fits into `Self`:
133                     // - if `Self` is unsigned, then `ABS_MIN == 0` and thus we have returned above
134                     //   (either `EINVAL` or `MIN`).
135                     // - if `Self` is signed, then we have that `0 <= value < ABS_MIN`. And since
136                     //   `ABS_MIN - 1` fits into `Self` by construction, `value` also does.
137                     let value: Self = unsafe { value.try_into().unwrap_unchecked() };
138 
139                     Ok((!value).wrapping_add(1))
140                 }
141             }
142 
143             impl ParseInt for $ty {}
144         )*
145     };
146 }
147 
148 impl_parse_int![i8, u8, i16, u16, i32, u32, i64, u64, isize, usize];
149