Merge pull request #121 from Arcterus/strtoul

stdlib: implement strtoul() and strtol() using a macro
This commit is contained in:
Jeremy Soller
2018-05-12 06:24:18 -06:00
committed by GitHub
3 changed files with 150 additions and 96 deletions
+119 -96
View File
@@ -388,7 +388,7 @@ pub extern "C" fn qsort(
width: size_t,
compar: Option<extern "C" fn(*const c_void, *const c_void) -> c_int>,
) {
unimplemented!();
unimplemented!()
}
#[no_mangle]
@@ -466,7 +466,7 @@ pub extern "C" fn srandom(seed: c_uint) {
#[no_mangle]
pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c_double {
//TODO: endptr
// TODO: endptr
use core::str::FromStr;
@@ -480,90 +480,6 @@ pub unsafe extern "C" fn strtod(s: *const c_char, endptr: *mut *mut c_char) -> c
}
}
#[no_mangle]
pub unsafe extern "C" fn strtol(
s: *const c_char,
endptr: *mut *const c_char,
base: c_int,
) -> c_long {
let set_endptr = |idx: isize| {
if !endptr.is_null() {
*endptr = s.offset(idx);
}
};
let invalid_input = || {
platform::errno = EINVAL;
set_endptr(0);
};
// only valid bases are 2 through 36
if base != 0 && (base < 2 || base > 36) {
invalid_input();
return 0;
}
let mut idx = 0;
// skip any whitespace at the beginning of the string
while ctype::isspace(*s.offset(idx) as c_int) != 0 {
idx += 1;
}
// check for +/-
let positive = match is_positive(*s.offset(idx)) {
Some((pos, i)) => {
idx += i;
pos
}
None => {
invalid_input();
return 0;
}
};
// convert the string to a number
let num_str = s.offset(idx);
let res = match base {
0 => detect_base(num_str).and_then(|(base, i)| convert_integer(num_str.offset(i), base)),
8 => convert_octal(num_str),
16 => convert_hex(num_str),
_ => convert_integer(num_str, base),
};
// check for error parsing octal/hex prefix
// also check to ensure a number was indeed parsed
let (num, i, _) = match res {
Some(res) => res,
None => {
invalid_input();
return 0;
}
};
idx += i;
// account for the sign
let mut num = num as c_long;
num = if num.is_negative() {
platform::errno = ERANGE;
if positive {
c_long::max_value()
} else {
c_long::min_value()
}
} else {
if positive {
num
} else {
-num
}
};
set_endptr(idx);
num
}
fn is_positive(ch: c_char) -> Option<(bool, isize)> {
match ch {
0 => None,
@@ -669,11 +585,125 @@ fn convert_integer(s: *const c_char, base: c_int) -> Option<(c_ulong, isize, boo
}
}
#[no_mangle]
pub extern "C" fn strtoul(s: *const c_char, endptr: *mut *mut c_char, base: c_int) -> c_ulong {
unimplemented!();
macro_rules! strto_impl {
($funcname:ident, $rettype:ty, $signed:expr, $maxval:expr, $minval:expr) => {
#[no_mangle]
pub unsafe extern "C" fn $funcname(s: *const c_char,
endptr: *mut *const c_char,
base: c_int)
-> $rettype {
// ensure these are constants
const CHECK_SIGN: bool = $signed;
const MAX_VAL: $rettype = $maxval;
const MIN_VAL: $rettype = $minval;
let set_endptr = |idx: isize| {
if !endptr.is_null() {
*endptr = s.offset(idx);
}
};
let invalid_input = || {
platform::errno = EINVAL;
set_endptr(0);
};
// only valid bases are 2 through 36
if base != 0 && (base < 2 || base > 36) {
invalid_input();
return 0;
}
let mut idx = 0;
// skip any whitespace at the beginning of the string
while ctype::isspace(*s.offset(idx) as c_int) != 0 {
idx += 1;
}
// check for +/-
let positive = match is_positive(*s.offset(idx)) {
Some((pos, i)) => {
idx += i;
pos
}
None => {
invalid_input();
return 0;
}
};
// convert the string to a number
let num_str = s.offset(idx);
let res = match base {
0 => detect_base(num_str).and_then(|(base, i)| {
convert_integer(num_str.offset(i), base)
}),
8 => convert_octal(num_str),
16 => convert_hex(num_str),
_ => convert_integer(num_str, base),
};
// check for error parsing octal/hex prefix
// also check to ensure a number was indeed parsed
let (num, i, overflow) = match res {
Some(res) => res,
None => {
invalid_input();
return 0;
}
};
idx += i;
let overflow = if CHECK_SIGN {
overflow || (num as c_long).is_negative()
} else {
overflow
};
// account for the sign
let num = num as $rettype;
let num = if overflow {
platform::errno = ERANGE;
if CHECK_SIGN {
if positive {
MAX_VAL
} else {
MIN_VAL
}
} else {
MAX_VAL
}
} else {
if positive {
num
} else {
// not using -num to keep the compiler happy
num.overflowing_neg().0
}
};
set_endptr(idx);
num
}
}
}
strto_impl!(
strtoul,
c_ulong,
false,
c_ulong::max_value(),
c_ulong::min_value()
);
strto_impl!(
strtol,
c_long,
true,
c_long::max_value(),
c_long::min_value()
);
#[no_mangle]
pub extern "C" fn system(command: *const c_char) -> c_int {
unimplemented!();
@@ -711,10 +741,3 @@ pub extern "C" fn wcstombs(s: *mut c_char, pwcs: *const wchar_t, n: size_t) -> s
pub extern "C" fn wctomb(s: *mut c_char, wchar: wchar_t) -> c_int {
unimplemented!();
}
/*
#[no_mangle]
pub extern "C" fn func(args) -> c_int {
unimplemented!();
}
*/
+1
View File
@@ -28,6 +28,7 @@ EXPECT_BINS=\
stdio/freopen \
stdlib/bsearch \
stdlib/strtol \
stdlib/strtoul \
stdlib/a64l \
stdlib/rand \
string/strncmp \
+30
View File
@@ -0,0 +1,30 @@
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
printf("%ld\n", strtoul(" -42", NULL, 0));
printf("%ld\n", strtoul(" +555", NULL, 0));
printf("%ld\n", strtoul(" 1234567890 ", NULL, 0));
printf("%ld\n", strtoul(" -42", NULL, 10));
printf("%ld\n", strtoul(" +555", NULL, 10));
printf("%ld\n", strtoul(" 1234567890 ", NULL, 10));
printf("%lx\n", strtoul(" 0x38Acfg", NULL, 0));
printf("%lx\n", strtoul("0Xabcdef12", NULL, 16));
printf("%lo\n", strtoul(" 073189", NULL, 0));
printf("%lo\n", strtoul(" 073189", NULL, 8));
printf("%lo\n", strtoul(" 0b", NULL, 8));
if(errno != 0) {
printf("errno is not 0 (%d), something went wrong\n", errno);
}
printf("%lo\n", strtoul(" 0b", NULL, 0));
if(errno != 0) {
printf("errno is not 0 (%d), something went wrong\n", errno);
}
return 0;
}