Skip to content

Commit bdf8b86

Browse files
Andreas Hindborgjannau
authored andcommitted
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. Tested-by: Daniel Almeida <daniel.almeida@collabora.com> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Signed-off-by: Andreas Hindborg <a.hindborg@kernel.org>
1 parent bc1df78 commit bdf8b86

1 file changed

Lines changed: 155 additions & 0 deletions

File tree

rust/kernel/str.rs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,3 +945,158 @@ impl fmt::Debug for CString {
945945
macro_rules! fmt {
946946
($($f:tt)*) => ( core::format_args!($($f)*) )
947947
}
948+
949+
pub mod parse_int {
950+
//! Integer parsing functions for parsing signed and unsigned integers
951+
//! potentially prefixed with `0x`, `0o`, or `0b`.
952+
953+
use crate::prelude::*;
954+
use crate::str::BStr;
955+
use core::ops::Deref;
956+
957+
// Make `FromStrRadix` a public type with a private name. This seals
958+
// `ParseInt`, that is, prevents downstream users from implementing the
959+
// trait.
960+
mod private {
961+
use crate::str::BStr;
962+
963+
/// Trait that allows parsing a [`&BStr`] to an integer with a radix.
964+
///
965+
/// [`&BStr`]: kernel::str::BStr
966+
// This is required because the `from_str_radix` function on the primitive
967+
// integer types is not part of any trait.
968+
pub trait FromStrRadix: Sized {
969+
/// Parse `src` to `Self` using radix `radix`.
970+
fn from_str_radix(src: &BStr, radix: u32) -> Result<Self, crate::error::Error>;
971+
972+
/// Return the absolute value of Self::MIN.
973+
fn abs_min() -> u64;
974+
975+
/// Perform bitwise 2's complement on `self`.
976+
///
977+
/// Note: This function does not make sense for unsigned integers.
978+
fn complement(self) -> Self;
979+
}
980+
}
981+
982+
/// Extract the radix from an integer literal optionally prefixed with
983+
/// one of `0x`, `0X`, `0o`, `0O`, `0b`, `0B`, `0`.
984+
fn strip_radix(src: &BStr) -> (u32, &BStr) {
985+
match src.deref() {
986+
[b'0', b'x' | b'X', rest @ ..] => (16, rest.as_ref()),
987+
[b'0', b'o' | b'O', rest @ ..] => (8, rest.as_ref()),
988+
[b'0', b'b' | b'B', rest @ ..] => (2, rest.as_ref()),
989+
// NOTE: We are including the leading zero to be able to parse
990+
// literal 0 here. If we removed it as a radix prefix, we would not
991+
// be able to parse `0`.
992+
[b'0', ..] => (8, src),
993+
_ => (10, src),
994+
}
995+
}
996+
997+
/// Trait for parsing string representations of integers.
998+
///
999+
/// Strings beginning with `0x`, `0o`, or `0b` are parsed as hex, octal, or
1000+
/// binary respectively. Strings beginning with `0` otherwise are parsed as
1001+
/// octal. Anything else is parsed as decimal. A leading `+` or `-` is also
1002+
/// permitted. Any string parsed by [`kstrtol()`] or [`kstrtoul()`] will be
1003+
/// successfully parsed.
1004+
///
1005+
/// [`kstrtol()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtol
1006+
/// [`kstrtoul()`]: https://www.kernel.org/doc/html/latest/core-api/kernel-api.html#c.kstrtoul
1007+
///
1008+
/// # Example
1009+
/// ```
1010+
/// use kernel::str::parse_int::ParseInt;
1011+
/// use kernel::b_str;
1012+
///
1013+
/// assert_eq!(Ok(0), u8::from_str(b_str!("0")));
1014+
///
1015+
/// assert_eq!(Ok(0xa2u8), u8::from_str(b_str!("0xa2")));
1016+
/// assert_eq!(Ok(-0xa2i32), i32::from_str(b_str!("-0xa2")));
1017+
///
1018+
/// assert_eq!(Ok(-0o57i8), i8::from_str(b_str!("-0o57")));
1019+
/// assert_eq!(Ok(0o57i8), i8::from_str(b_str!("057")));
1020+
///
1021+
/// assert_eq!(Ok(0b1001i16), i16::from_str(b_str!("0b1001")));
1022+
/// assert_eq!(Ok(-0b1001i16), i16::from_str(b_str!("-0b1001")));
1023+
///
1024+
/// assert_eq!(Ok(127), i8::from_str(b_str!("127")));
1025+
/// assert!(i8::from_str(b_str!("128")).is_err());
1026+
/// assert_eq!(Ok(-128), i8::from_str(b_str!("-128")));
1027+
/// assert!(i8::from_str(b_str!("-129")).is_err());
1028+
/// assert_eq!(Ok(255), u8::from_str(b_str!("255")));
1029+
/// assert!(u8::from_str(b_str!("256")).is_err());
1030+
/// ```
1031+
pub trait ParseInt: private::FromStrRadix + TryFrom<u64> {
1032+
/// Parse a string according to the description in [`Self`].
1033+
fn from_str(src: &BStr) -> Result<Self> {
1034+
match src.deref() {
1035+
[b'-', rest @ ..] => {
1036+
let (radix, digits) = strip_radix(rest.as_ref());
1037+
// 2's complement values range from -2^(b-1) to 2^(b-1)-1.
1038+
// So if we want to parse negative numbers as positive and
1039+
// later multiply by -1, we have to parse into a larger
1040+
// integer. We choose u64 as sufficiently large. NOTE: 128
1041+
// bit integers are not available on all platforms, hence
1042+
// the choice of 64 bit.
1043+
let val = u64::from_str_radix(
1044+
core::str::from_utf8(digits).map_err(|_| EINVAL)?,
1045+
radix,
1046+
)
1047+
.map_err(|_| EINVAL)?;
1048+
1049+
if val > Self::abs_min() {
1050+
return Err(EINVAL);
1051+
}
1052+
1053+
// SAFETY: We checked that `val` will fit in `Self` above.
1054+
let val: Self = unsafe { val.try_into().unwrap_unchecked() };
1055+
1056+
Ok(val.complement())
1057+
}
1058+
_ => {
1059+
let (radix, digits) = strip_radix(src);
1060+
Self::from_str_radix(digits, radix).map_err(|_| EINVAL)
1061+
}
1062+
}
1063+
}
1064+
}
1065+
1066+
macro_rules! impl_parse_int {
1067+
($ty:ty) => {
1068+
impl private::FromStrRadix for $ty {
1069+
fn from_str_radix(src: &BStr, radix: u32) -> Result<Self, crate::error::Error> {
1070+
<$ty>::from_str_radix(core::str::from_utf8(src).map_err(|_| EINVAL)?, radix)
1071+
.map_err(|_| EINVAL)
1072+
}
1073+
1074+
fn abs_min() -> u64 {
1075+
#[allow(unused_comparisons)]
1076+
if Self::MIN < 0 {
1077+
1u64 << (Self::BITS - 1)
1078+
} else {
1079+
0
1080+
}
1081+
}
1082+
1083+
fn complement(self) -> Self {
1084+
(!self).wrapping_add((1 as $ty))
1085+
}
1086+
}
1087+
1088+
impl ParseInt for $ty {}
1089+
};
1090+
}
1091+
1092+
impl_parse_int!(i8);
1093+
impl_parse_int!(u8);
1094+
impl_parse_int!(i16);
1095+
impl_parse_int!(u16);
1096+
impl_parse_int!(i32);
1097+
impl_parse_int!(u32);
1098+
impl_parse_int!(i64);
1099+
impl_parse_int!(u64);
1100+
impl_parse_int!(isize);
1101+
impl_parse_int!(usize);
1102+
}

0 commit comments

Comments
 (0)