|
| 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]; |
0 commit comments