use core::ffi::c_int;
use axerrno::LinuxError;
use kmod::capi_fn;
const KSTRTOX_OVERFLOW: u32 = 1 << 31;
const ULLONG_MAX: u64 = u64::MAX;
const INT_MAX: usize = i32::MAX as usize;
#[inline]
fn to_lower(c: u8) -> u8 {
if c.is_ascii_uppercase() { c + 32 } else { c }
}
#[inline]
fn is_xdigit(c: u8) -> bool {
c.is_ascii_digit() || (b'a'..=b'f').contains(&c) || (b'A'..=b'F').contains(&c)
}
#[inline(never)]
pub unsafe extern "C" fn _parse_integer_fixup_radix(
mut s: *const core::ffi::c_char,
base: *mut u32,
) -> *const core::ffi::c_char {
if *base == 0 {
let first = *s as u8;
if first == b'0' {
let second = *s.add(1) as u8;
if to_lower(second) == b'x' && is_xdigit(*s.add(2) as u8) {
*base = 16;
} else {
*base = 8;
}
} else {
*base = 10;
}
}
if *base == 16 && *s as u8 == b'0' && to_lower(*s.add(1) as u8) == b'x' {
s = s.add(2);
}
s
}
#[inline(never)]
pub unsafe extern "C" fn _parse_integer_limit(
mut s: *const core::ffi::c_char,
base: u32,
p: *mut u64,
mut max_chars: usize,
) -> u32 {
let mut res: u64 = 0;
let mut rv: u32 = 0;
while max_chars > 0 {
let c = *s as u8;
let lc = to_lower(c);
let val: u32;
if c.is_ascii_digit() {
val = (c - b'0') as u32;
} else if (b'a'..=b'f').contains(&lc) {
val = (lc - b'a' + 10) as u32;
} else {
break;
}
if val >= base {
break;
}
if res & (!0u64 << 60) != 0 && res > (ULLONG_MAX - val as u64) / base as u64 {
rv |= KSTRTOX_OVERFLOW;
}
res = res.wrapping_mul(base as u64).wrapping_add(val as u64);
rv += 1;
s = s.add(1);
max_chars -= 1;
}
*p = res;
rv
}
#[inline(never)]
pub unsafe extern "C" fn _parse_integer(
s: *const core::ffi::c_char,
base: u32,
p: *mut u64,
) -> u32 {
_parse_integer_limit(s, base, p, INT_MAX)
}
fn kstrtoull_internal(s: *const core::ffi::c_char, base: u32, res: *mut u64) -> c_int {
let mut s = s;
let mut _base = base;
let mut _res: u64 = 0;
unsafe {
s = _parse_integer_fixup_radix(s, &mut _base);
let rv = _parse_integer(s, _base, &mut _res);
if rv & KSTRTOX_OVERFLOW != 0 {
return -(LinuxError::ERANGE as c_int);
}
if rv == 0 {
return -(LinuxError::EINVAL as c_int);
}
s = s.add(rv as usize);
if *s as u8 == b'\n' {
s = s.add(1);
}
if *s != 0 {
return -(LinuxError::EINVAL as c_int);
}
*res = _res;
}
0
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtoull(s: *const core::ffi::c_char, base: u32, res: *mut u64) -> c_int {
let s = if !s.is_null() && *s as u8 == b'+' {
s.add(1)
} else {
s
};
kstrtoull_internal(s, base, res)
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtoll(s: *const core::ffi::c_char, base: u32, res: *mut i64) -> c_int {
if s.is_null() {
return -(LinuxError::EINVAL as c_int);
}
if *s as u8 == b'-' {
let mut tmp: u64 = 0;
let rv = kstrtoull_internal(s.add(1), base, &mut tmp);
if rv < 0 {
return rv;
}
let tmp_signed = -(tmp as i64);
if tmp_signed > 0 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp_signed;
}
} else {
let mut tmp: u64 = 0;
let rv = kstrtoull(s, base, &mut tmp);
if rv < 0 {
return rv;
}
if (tmp as i64) < 0 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp as i64;
}
}
0
}
fn _kstrtoul_internal(s: *const core::ffi::c_char, base: u32, res: *mut u32) -> c_int {
let mut tmp: u64 = 0;
let rv = unsafe { kstrtoull(s, base, &mut tmp) };
if rv < 0 {
return rv;
}
if tmp != tmp as u32 as u64 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp as u32;
}
0
}
fn _kstrtol_internal(s: *const core::ffi::c_char, base: u32, res: *mut i32) -> c_int {
let mut tmp: i64 = 0;
let rv = unsafe { kstrtoll(s, base, &mut tmp) };
if rv < 0 {
return rv;
}
if tmp != tmp as i32 as i64 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp as i32;
}
0
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtouint(
s: *const core::ffi::c_char,
base: u32,
res: *mut u32,
) -> c_int {
_kstrtoul_internal(s, base, res)
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtoint(s: *const core::ffi::c_char, base: u32, res: *mut i32) -> c_int {
_kstrtol_internal(s, base, res)
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtou16(s: *const core::ffi::c_char, base: u32, res: *mut u16) -> c_int {
let mut tmp: u64 = 0;
let rv = unsafe { kstrtoull(s, base, &mut tmp) };
if rv < 0 {
return rv;
}
if tmp != tmp as u16 as u64 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp as u16;
}
0
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtos16(s: *const core::ffi::c_char, base: u32, res: *mut i16) -> c_int {
let mut tmp: i64 = 0;
let rv = unsafe { kstrtoll(s, base, &mut tmp) };
if rv < 0 {
return rv;
}
if tmp != tmp as i16 as i64 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp as i16;
}
0
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtou8(s: *const core::ffi::c_char, base: u32, res: *mut u8) -> c_int {
let mut tmp: u64 = 0;
let rv = unsafe { kstrtoull(s, base, &mut tmp) };
if rv < 0 {
return rv;
}
if tmp != tmp as u8 as u64 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp as u8;
}
0
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtos8(s: *const core::ffi::c_char, base: u32, res: *mut i8) -> c_int {
let mut tmp: i64 = 0;
let rv = unsafe { kstrtoll(s, base, &mut tmp) };
if rv < 0 {
return rv;
}
if tmp != tmp as i8 as i64 {
return -(LinuxError::ERANGE as c_int);
}
unsafe {
*res = tmp as i8;
}
0
}
#[capi_fn]
#[inline(never)]
pub unsafe extern "C" fn kstrtobool(s: *const core::ffi::c_char, res: *mut bool) -> c_int {
if s.is_null() || res.is_null() {
return -(LinuxError::EINVAL as c_int);
}
let first_char = *s as u8;
match first_char {
b'y' | b'Y' | b't' | b'T' | b'1' => {
*res = true;
0
}
b'n' | b'N' | b'f' | b'F' | b'0' => {
*res = false;
0
}
b'o' | b'O' => {
let second_char = *s.add(1) as u8;
match second_char {
b'n' | b'N' => {
*res = true;
0
}
b'f' | b'F' => {
*res = false;
0
}
_ => -(LinuxError::EINVAL as c_int),
}
}
_ => -(LinuxError::EINVAL as c_int),
}
}
#[cfg(test)]
mod tests {
use core::ffi::c_int;
#[test]
fn test_kstrtobool() {
use super::kstrtobool;
let test_cases = [
(c"y", true),
(c"Y", true),
(c"t", true),
(c"T", true),
(c"1", true),
(c"n", false),
(c"N", false),
(c"f", false),
(c"F", false),
(c"0", false),
(c"on", true),
(c"ON", true),
(c"off", false),
(c"OFF", false),
];
for (input, expected) in test_cases.iter() {
let mut result: bool = false;
let ret_code = unsafe { kstrtobool(input.as_ptr(), &mut result as *mut bool) };
assert_eq!(ret_code, 0, "Input: {:?}", input);
assert_eq!(result, *expected, "Input: {:?}", input);
}
let invalid_inputs = [c"", c"maybe", c"2", c"o"];
for input in invalid_inputs.iter() {
let mut result: bool = false;
let ret_code = unsafe { kstrtobool(input.as_ptr(), &mut result as *mut bool) };
assert_eq!(
ret_code,
-(super::LinuxError::EINVAL as c_int),
"Input: {:?}",
input
);
}
let mut result: bool = false;
let ret_code = unsafe { kstrtobool(core::ptr::null(), &mut result as *mut bool) };
assert_eq!(
ret_code,
-(super::LinuxError::EINVAL as c_int),
"Null string pointer"
);
}
#[test]
fn test_kstrtoull() {
use super::kstrtoull;
let mut result: u64 = 0;
let ret = unsafe { kstrtoull(c"123".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 123);
let ret = unsafe { kstrtoull(c"0x1a".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 26);
let ret = unsafe { kstrtoull(c"0777".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 511);
let ret = unsafe { kstrtoull(c"+456".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 456);
let ret = unsafe { kstrtoull(c"789\n".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 789);
let ret = unsafe { kstrtoull(c"abc".as_ptr(), 10, &mut result) };
assert!(ret < 0);
let ret = unsafe { kstrtoull(c"18446744073709551616".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
#[test]
fn test_kstrtoll() {
use super::kstrtoll;
let mut result: i64 = 0;
let ret = unsafe { kstrtoll(c"123".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 123);
let ret = unsafe { kstrtoll(c"-456".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, -456);
let ret = unsafe { kstrtoll(c"+789".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 789);
let ret = unsafe { kstrtoll(c"-0x10".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, -16);
let ret = unsafe { kstrtoll(c"xyz".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
#[test]
fn test_kstrtouint() {
use super::kstrtouint;
let mut result: u32 = 0;
let ret = unsafe { kstrtouint(c"123".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 123);
let ret = unsafe { kstrtouint(c"0xff".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 255);
let ret = unsafe { kstrtouint(c"456\n".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 456);
let ret = unsafe { kstrtouint(c"invalid".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
#[test]
fn test_kstrtoint() {
use super::kstrtoint;
let mut result: i32 = 0;
let ret = unsafe { kstrtoint(c"789".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 789);
let ret = unsafe { kstrtoint(c"-123".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, -123);
let ret = unsafe { kstrtoint(c"0x20".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 32);
let ret = unsafe { kstrtoint(c"notint".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
#[test]
fn test_kstrtou16() {
use super::kstrtou16;
let mut result: u16 = 0;
let ret = unsafe { kstrtou16(c"65535".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 65535);
let ret = unsafe { kstrtou16(c"0xffff".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 65535);
let ret = unsafe { kstrtou16(c"65536".as_ptr(), 10, &mut result) };
assert!(ret < 0);
let ret = unsafe { kstrtou16(c"notu16".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
#[test]
fn test_kstrtos16() {
use super::kstrtos16;
let mut result: i16 = 0;
let ret = unsafe { kstrtos16(c"32767".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 32767);
let ret = unsafe { kstrtos16(c"-32768".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, -32768);
let ret = unsafe { kstrtos16(c"0x100".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 256);
let ret = unsafe { kstrtos16(c"32768".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
#[test]
fn test_kstrtou8() {
use super::kstrtou8;
let mut result: u8 = 0;
let ret = unsafe { kstrtou8(c"255".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 255);
let ret = unsafe { kstrtou8(c"0xff".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 255);
let ret = unsafe { kstrtou8(c"256".as_ptr(), 10, &mut result) };
assert!(ret < 0);
let ret = unsafe { kstrtou8(c"notu8".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
#[test]
fn test_kstrtos8() {
use super::kstrtos8;
let mut result: i8 = 0;
let ret = unsafe { kstrtos8(c"127".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 127);
let ret = unsafe { kstrtos8(c"-128".as_ptr(), 10, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, -128);
let ret = unsafe { kstrtos8(c"0x10".as_ptr(), 0, &mut result) };
assert_eq!(ret, 0);
assert_eq!(result, 16);
let ret = unsafe { kstrtos8(c"128".as_ptr(), 10, &mut result) };
assert!(ret < 0);
}
}