mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
rust: str: add radix prefixed integer parsing functions
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. Suggested-by: Benno Lossin <benno.lossin@proton.me> Tested-by: Daniel Gomez <da.gomez@samsung.com> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: Benno Lossin <lossin@kernel.org> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org> Signed-off-by: Daniel Gomez <da.gomez@kernel.org>
This commit is contained in:
committed by
Daniel Gomez
parent
821fe7bf16
commit
51d9ee90ea
@@ -13,6 +13,8 @@ use core::{
|
|||||||
ops::{self, Deref, DerefMut, Index},
|
ops::{self, Deref, DerefMut, Index},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod parse_int;
|
||||||
|
|
||||||
/// Byte string without UTF-8 validity guarantee.
|
/// Byte string without UTF-8 validity guarantee.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct BStr([u8]);
|
pub struct BStr([u8]);
|
||||||
|
|||||||
148
rust/kernel/str/parse_int.rs
Normal file
148
rust/kernel/str/parse_int.rs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
//! Integer parsing functions.
|
||||||
|
//!
|
||||||
|
//! 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;
|
||||||
|
|
||||||
|
// Make `FromStrRadix` a public type with a private name. This seals
|
||||||
|
// `ParseInt`, that is, prevents downstream users from implementing the
|
||||||
|
// trait.
|
||||||
|
mod private {
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::str::BStr;
|
||||||
|
|
||||||
|
/// Trait that allows parsing a [`&BStr`] to an integer with a radix.
|
||||||
|
pub trait FromStrRadix: Sized {
|
||||||
|
/// Parse `src` to [`Self`] using radix `radix`.
|
||||||
|
fn from_str_radix(src: &BStr, radix: u32) -> Result<Self>;
|
||||||
|
|
||||||
|
/// Tries to convert `value` into [`Self`] and negates the resulting value.
|
||||||
|
fn from_u64_negated(value: u64) -> Result<Self>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the radix from an integer literal optionally prefixed with
|
||||||
|
/// one of `0x`, `0X`, `0o`, `0O`, `0b`, `0B`, `0`.
|
||||||
|
fn strip_radix(src: &BStr) -> (u32, &BStr) {
|
||||||
|
match src.deref() {
|
||||||
|
[b'0', b'x' | b'X', rest @ ..] => (16, rest.as_ref()),
|
||||||
|
[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 be able to parse `0`.
|
||||||
|
[b'0', ..] => (8, src),
|
||||||
|
_ => (10, src),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for parsing string representations of integers.
|
||||||
|
///
|
||||||
|
/// Strings beginning with `0x`, `0o`, or `0b` are parsed as hex, octal, or
|
||||||
|
/// binary respectively. Strings beginning with `0` otherwise are parsed as
|
||||||
|
/// octal. Anything else is parsed as decimal. A leading `+` or `-` is also
|
||||||
|
/// permitted. Any string parsed by [`kstrtol()`] or [`kstrtoul()`] will be
|
||||||
|
/// successfully parsed.
|
||||||
|
///
|
||||||
|
/// [`kstrtol()`]: https://docs.kernel.org/core-api/kernel-api.html#c.kstrtol
|
||||||
|
/// [`kstrtoul()`]: https://docs.kernel.org/core-api/kernel-api.html#c.kstrtoul
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # 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")));
|
||||||
|
///
|
||||||
|
/// assert_eq!(Ok(-0o57i8), i8::from_str(b_str!("-0o57")));
|
||||||
|
/// assert_eq!(Ok(0o57i8), i8::from_str(b_str!("057")));
|
||||||
|
///
|
||||||
|
/// assert_eq!(Ok(0b1001i16), i16::from_str(b_str!("0b1001")));
|
||||||
|
/// assert_eq!(Ok(-0b1001i16), i16::from_str(b_str!("-0b1001")));
|
||||||
|
///
|
||||||
|
/// assert_eq!(Ok(127i8), i8::from_str(b_str!("127")));
|
||||||
|
/// assert!(i8::from_str(b_str!("128")).is_err());
|
||||||
|
/// assert_eq!(Ok(-128i8), i8::from_str(b_str!("-128")));
|
||||||
|
/// assert!(i8::from_str(b_str!("-129")).is_err());
|
||||||
|
/// 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> {
|
||||||
|
/// Parse a string according to the description in [`Self`].
|
||||||
|
fn from_str(src: &BStr) -> Result<Self> {
|
||||||
|
match src.deref() {
|
||||||
|
[b'-', rest @ ..] => {
|
||||||
|
let (radix, digits) = strip_radix(rest.as_ref());
|
||||||
|
// 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 bits.
|
||||||
|
let val =
|
||||||
|
u64::from_str_radix(core::str::from_utf8(digits).map_err(|_| EINVAL)?, radix)
|
||||||
|
.map_err(|_| EINVAL)?;
|
||||||
|
Self::from_u64_negated(val)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let (radix, digits) = strip_radix(src);
|
||||||
|
Self::from_str_radix(digits, radix).map_err(|_| EINVAL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_parse_int {
|
||||||
|
($($ty:ty),*) => {
|
||||||
|
$(
|
||||||
|
impl private::FromStrRadix for $ty {
|
||||||
|
fn from_str_radix(src: &BStr, radix: u32) -> Result<Self> {
|
||||||
|
<$ty>::from_str_radix(core::str::from_utf8(src).map_err(|_| EINVAL)?, radix)
|
||||||
|
.map_err(|_| EINVAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_u64_negated(value: u64) -> Result<Self> {
|
||||||
|
const ABS_MIN: u64 = {
|
||||||
|
#[allow(unused_comparisons)]
|
||||||
|
if <$ty>::MIN < 0 {
|
||||||
|
1u64 << (<$ty>::BITS - 1)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if value > ABS_MIN {
|
||||||
|
return Err(EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == ABS_MIN {
|
||||||
|
return Ok(<$ty>::MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: The above checks guarantee that `value` fits into `Self`:
|
||||||
|
// - if `Self` is unsigned, then `ABS_MIN == 0` and thus we have returned above
|
||||||
|
// (either `EINVAL` or `MIN`).
|
||||||
|
// - if `Self` is signed, then we have that `0 <= value < ABS_MIN`. And since
|
||||||
|
// `ABS_MIN - 1` fits into `Self` by construction, `value` also does.
|
||||||
|
let value: Self = unsafe { value.try_into().unwrap_unchecked() };
|
||||||
|
|
||||||
|
Ok((!value).wrapping_add(1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParseInt for $ty {}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_parse_int![i8, u8, i16, u16, i32, u32, i64, u64, isize, usize];
|
||||||
Reference in New Issue
Block a user