Re: [PATCH v9 1/3] rust: str: add radix prefixed integer parsing functions

From: Miguel Ojeda
Date: Mon Mar 31 2025 - 06:34:23 EST


On Fri, Mar 21, 2025 at 10:18 AM Andreas Hindborg <a.hindborg@xxxxxxxxxx> wrote:
>
> Add the trait `ParseInt` for parsing string representations of integers
> where the string representations are optionally prefixed by a radix
> specifier. Implement the trait for the primitive integer types.
>
> Tested-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
> Reviewed-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
> Signed-off-by: Andreas Hindborg <a.hindborg@xxxxxxxxxx>

I had applied the following comments in v8 when I originally took it
before discovering the UB -- since this patch may finally go through
modules (or not), Andreas asked me to put them here:

[ Added integer type suffixes to `assert!`s for consistency with the
others. Changed links to docs.kernel.org. Applied Markdown and
intra-doc links where possible. Changed to `///` for `mod` docs.
Slightly reworded comment. Pluralized section name. Hid
`use`s. Removed `#[expect]` for the `rusttest` target. - Miguel ]

Attached range diff of what I did.

I hope that helps!

Cheers,
Miguel
+@@ rust/kernel/str.rs: macro_rules! c_str {
+ }
+
+ #[cfg(test)]
+-#[expect(clippy::items_after_test_module)]
+ mod tests {
+ use super::*;
+
@@ rust/kernel/str.rs: fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
macro_rules! fmt {
($($f:tt)*) => ( core::format_args!($($f)*) )
}
+
++/// Integer parsing functions for parsing signed and unsigned integers
++/// potentially prefixed with `0x`, `0o`, or `0b`.
+pub mod parse_int {
-+ //! Integer parsing functions for parsing signed and unsigned integers
-+ //! potentially prefixed with `0x`, `0o`, or `0b`.
-+
+ use crate::prelude::*;
+ use crate::str::BStr;
+ use core::ops::Deref;
@@ rust/kernel/str.rs: fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // This is required because the `from_str_radix` function on the primitive
+ // integer types is not part of any trait.
+ pub trait FromStrRadix: Sized {
-+ /// Parse `src` to `Self` using radix `radix`.
++ /// Parse `src` to [`Self`] using radix `radix`.
+ fn from_str_radix(src: &BStr, radix: u32) -> Result<Self, crate::error::Error>;
+
-+ /// Return the absolute value of Self::MIN.
++ /// Return the absolute value of [`Self::MIN`].
+ fn abs_min() -> u64;
+
+ /// Perform bitwise 2's complement on `self`.
@@ rust/kernel/str.rs: fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ [b'0', b'o' | b'O', rest @ ..] => (8, rest.as_ref()),
+ [b'0', b'b' | b'B', rest @ ..] => (2, rest.as_ref()),
+ // NOTE: We are including the leading zero to be able to parse
-+ // literal 0 here. If we removed it as a radix prefix, we would not
++ // literal `0` here. If we removed it as a radix prefix, we would not
+ // be able to parse `0`.
+ [b'0', ..] => (8, src),
+ _ => (10, src),
@@ rust/kernel/str.rs: fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// permitted. Any string parsed by [`kstrtol()`] or [`kstrtoul()`] will be
+ /// successfully parsed.
+ ///
-+ /// [`kstrtol()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtol
-+ /// [`kstrtoul()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtoul
++ /// [`kstrtol()`]: https://docs.kernel.org/core-api/kernel-api.html#c.kstrtol
++ /// [`kstrtoul()`]: https://docs.kernel.org/core-api/kernel-api.html#c.kstrtoul
+ ///
-+ /// # Example
-+ /// ```
-+ /// use kernel::str::parse_int::ParseInt;
-+ /// use kernel::b_str;
++ /// # Examples
+ ///
-+ /// assert_eq!(Ok(0), u8::from_str(b_str!("0")));
++ /// ```
++ /// # use kernel::str::parse_int::ParseInt;
++ /// # use kernel::b_str;
++ /// assert_eq!(Ok(0u8), u8::from_str(b_str!("0")));
+ ///
+ /// assert_eq!(Ok(0xa2u8), u8::from_str(b_str!("0xa2")));
+ /// assert_eq!(Ok(-0xa2i32), i32::from_str(b_str!("-0xa2")));
@@ rust/kernel/str.rs: fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ /// assert_eq!(Ok(0b1001i16), i16::from_str(b_str!("0b1001")));
+ /// assert_eq!(Ok(-0b1001i16), i16::from_str(b_str!("-0b1001")));
+ ///
-+ /// assert_eq!(Ok(127), i8::from_str(b_str!("127")));
++ /// assert_eq!(Ok(127i8), i8::from_str(b_str!("127")));
+ /// assert!(i8::from_str(b_str!("128")).is_err());
-+ /// assert_eq!(Ok(-128), i8::from_str(b_str!("-128")));
++ /// assert_eq!(Ok(-128i8), i8::from_str(b_str!("-128")));
+ /// assert!(i8::from_str(b_str!("-129")).is_err());
-+ /// assert_eq!(Ok(255), u8::from_str(b_str!("255")));
++ /// assert_eq!(Ok(255u8), u8::from_str(b_str!("255")));
+ /// assert!(u8::from_str(b_str!("256")).is_err());
+ /// ```
+ pub trait ParseInt: private::FromStrRadix + TryFrom<u64> {
@@ rust/kernel/str.rs: fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // 2's complement values range from -2^(b-1) to 2^(b-1)-1.
+ // So if we want to parse negative numbers as positive and
+ // later multiply by -1, we have to parse into a larger
-+ // integer. We choose u64 as sufficiently large. NOTE: 128
-+ // bit integers are not available on all platforms, hence
-+ // the choice of 64 bit.
++ // integer. We choose `u64` as sufficiently large.
++ //
++ // NOTE: 128-bit integers are not available on all
++ // platforms, hence the choice of 64-bit.
+ let val = u64::from_str_radix(
+ core::str::from_utf8(digits).map_err(|_| EINVAL)?,
+ radix,