Skip to content

Commit efbf0f2

Browse files
author
Andreas Hindborg
committed
LIST: [PATCH v17 2/7] 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 <[email protected]> Tested-by: Daniel Gomez <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Reviewed-by: Benno Lossin <[email protected]> Link: Rust-for-Linux#7 [1] Link: Rust-for-Linux#82 [2] Link: Rust-for-Linux#87 [3] Link: Rust-for-Linux#11 [4] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Andreas Hindborg <[email protected]>
1 parent 0156422 commit efbf0f2

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

rust/kernel/str.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use core::{
1313
ops::{self, Deref, DerefMut, Index},
1414
};
1515

16+
pub mod parse_int;
17+
1618
/// Byte string without UTF-8 validity guarantee.
1719
#[repr(transparent)]
1820
pub struct BStr([u8]);

rust/kernel/str/parse_int.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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

Comments
 (0)