From b1dc3e77de048d259d526e1d4d561a54454cc907 Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Wed, 19 May 2021 21:39:33 +0200 Subject: [PATCH] stdlib: add and test strtoull (DONE) --- CMakeLists.txt | 1 + STATUS | 8 ++++- include/stdlib.h | 41 +++++++++++++++++++++++++ src/libc/stdlib/strtoull.c | 62 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 src/libc/stdlib/strtoull.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 64155cb..41cf356 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,7 @@ set(SOURCES src/libc/stdlib/llabs.c src/libc/stdlib/lldiv.c src/libc/stdlib/reallocarray.c + src/libc/stdlib/strtoull.c # string src/libc/string/strchr.c src/libc/string/strcpy.c diff --git a/STATUS b/STATUS index 55b2090..148bbbd 100644 --- a/STATUS +++ b/STATUS @@ -67,6 +67,8 @@ DONE: Function/symbol/macro is defined, builds, links, and is tested files in /usr/share/i18n/locales -> Implement setlocale() and localeconv() properly (not hard) -> Probably support nl_langinfo(), which is much better than localeconv() + -> Fix the "TODO: locale: ..." messages wherever assumptions on the locale + are made in the code 7.12 Provided by Lephenixnoir's port of OpenLibm. @@ -95,7 +97,11 @@ DONE: Function/symbol/macro is defined, builds, links, and is tested TODO (will give the full list later on) 7.20 - 7.20.1 Numeric conversion functions: TODO + 7.20.1.1 atof: TODO + 7.20.1.2 atoi, atol, atoll: TODO + 7.20.1.3 strtod, strtof, strtold: TODO + 7.20.1.4 strtol, strtoul, strtoll: TODO + strtoull: DONE 7.20.2 Pseudo-random sequence generation functions: TODO 7.20.3 Memory management functions: TODO (check existing code first) 7.20.4 Communication with the environment: TODO diff --git a/include/stdlib.h b/include/stdlib.h index cab010d..db5cc60 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -63,4 +63,45 @@ div_t div(int __num, int __denom); ldiv_t ldiv(long int __num, long int __denom); lldiv_t lldiv(long long int __num, long long int __denom); +/* Numeric conversion functions. */ + +/* Parse a long int from a string. */ +extern long int strtol( + char const * restrict __ptr, + char ** restrict __endptr, + int __base); + +/* Parse a long unsigned int from a string. */ +extern unsigned long int strtoul( + char const * restrict __ptr, + char ** restrict __endptr, + int __base); + +/* Parse a long long int from a string. */ +extern long long int strtoll( + char const * restrict __ptr, + char ** restrict __endptr, + int __base); + +/* Parse a long long unsigned int from a string. */ +extern unsigned long long int strtoull( + char const * restrict __ptr, + char ** restrict __endptr, + int __base); + +/* Parse a double from a string. */ +extern double strtod( + char const * restrict __ptr, + char ** restrict __endptr); + +/* Parse a float from a string. */ +extern float strtof( + char const * restrict __ptr, + char ** restrict __endptr); + +/* Parse a long double from a string. */ +extern long double strtold( + char const * restrict __ptr, + char ** restrict __endptr); + #endif /*__STDLIB_H__*/ diff --git a/src/libc/stdlib/strtoull.c b/src/libc/stdlib/strtoull.c new file mode 100644 index 0000000..dcf4ee5 --- /dev/null +++ b/src/libc/stdlib/strtoull.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include + +unsigned long long int strtoull(char const * restrict ptr, + char ** restrict endptr, int base) +{ + /* Save the value of ptr in endptr now in case the format is invalid */ + if(endptr) *endptr = (char *)ptr; + + /* Skip initial whitespace */ + while(isspace(*ptr)) ptr++; + + /* Accept a sign character */ + bool negative = false; + if(*ptr == '-') negative = true; + if(*ptr == '-' || *ptr == '+') ptr++; + + unsigned long long x = 0; + bool valid = false; + + /* Read prefixes and determine base */ + if((base == 0 || base == 16) && ptr[0]=='0' && tolower(ptr[1])=='x') { + ptr += 2; + base = 16; + } + else if(base == 0 && ptr[0] == '0') { + ptr++; + base = 8; + } + else if(base == 0) { + base = 10; + } + + /* Read digits */ + while(1) { + int v = -1; + if(isdigit(*ptr)) v = *ptr - '0'; + if(islower(*ptr)) v = *ptr - 'a' + 10; + if(v == -1 || v >= base) break; + + /* The value is valid as long as there is at least one digit */ + valid = true; + + /* (x = base*x + v) but with overflow checks */ + if(__builtin_umulll_overflow(x, base, &x)) + errno = ERANGE; + if(__builtin_uaddll_overflow(x, v, &x)) + errno = ERANGE; + + ptr++; + } + + if(negative) { + if(x != 0) errno = ERANGE; + x = -x; + } + + if(endptr && valid) *endptr = (char *)ptr; + return x; +}