File Index
Files and directories
Sign in
FILE

parse_int.rs

File preview with syntax highlighting.

linux-6.19.14/rust/kernel/str/parse_int.rs RUST
  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
  8use crate::prelude::*;
  9use crate::str::BStr;
 10use 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.
 15mod 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`.
 31fn 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/// ```
 79pub 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
105macro_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
148impl_parse_int![i8, u8, i16, u16, i32, u32, i64, u64, isize, usize];
~/ Read-only file preview.