Complete implementation of strto* and ato* functions

This commit is contained in:
bumbread 2022-06-05 14:13:27 +11:00
parent 04555ac1db
commit e18e9a988e
4 changed files with 155 additions and 60 deletions

4
code/errno.c Normal file
View File

@ -0,0 +1,4 @@
#include <errno.h>
int errno;

View File

@ -9,6 +9,14 @@
// TODO: i made a function that parses longs and i'm fucken // TODO: i made a function that parses longs and i'm fucken
// tired. someone make float parsing :kekw: // tired. someone make float parsing :kekw:
// Call me weak if you want but I'm actually too lazy to type
// them out every time, also they take up a lot of horiz space.
typedef long long int intll;
typedef long int intl;
typedef unsigned long long int intull;
typedef unsigned long int intul;
typedef unsigned int intu;
#define inrange(start, c, end) ((start) <= (c) && (c) <= (end)) #define inrange(start, c, end) ((start) <= (c) && (c) <= (end))
static bool isbase(int c, int base) { static bool isbase(int c, int base) {
@ -43,92 +51,144 @@ static long todigitl(int c) {
return val; return val;
} }
long static intull strto_generic(
strtol(const char *restrict nptr, char **restrict endptr, int inbase) { const char *restrict nptr,
if(!inrange(0, inbase, 36)) { char **restrict endptr,
*endptr = NULL; int inbase,
return 0; intl *coefptr,
intull int_max
) {
const char *restrict str = nptr;
intull value = 0;
int digits_read = 0;
bool is_signed = (coefptr != NULL);
// Find max{abs(int)}. Signed integers have negative,
// whose absolute value is 1 bigger than int_max.
intull int_abs_max = int_max;
if(is_signed) {
++int_abs_max;
} }
if(!inrange(0, inbase, 36)) {
goto finish;
}
intull base = (intull)inbase;
// Skip space on the beginning // Skip space on the beginning
while(isspace(*nptr)) { while(isspace(*str)) {
++nptr; ++str;
} }
// Parse sign // Parse sign
long coef = 1; intl coef = 1;
if(*nptr == '-') { if(is_signed) {
if(*str == '-') {
coef = -1; coef = -1;
++nptr; ++str;
} }
if(*nptr == '+') {
++nptr;
} }
unsigned long base = (unsigned long)inbase; if(*str == '+') {
unsigned long value = 0; ++str;
}
// See if we need to parse base in C-like format // See if we need to parse base in C-like format
if(*nptr == '0' && *(nptr+1) == 'x') { // then set the base accordingly
++nptr; if(*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) {
++str;
if(base == 16 || base == 0) { if(base == 16 || base == 0) {
++nptr; ++str;
base = 16; base = 16;
} }
else { else {
value = 0; goto finish;
goto end;
} }
} }
else if(*nptr == '0') { else if(*str == '0') {
++nptr; ++str;
++digits_read;
if(base == 8 || base == 0) { if(base == 8 || base == 0) {
base = 8; base = 8;
} }
} }
while(isbase(*nptr, (int)base)) { // Parse the string of digits in the given base. If the value
unsigned long digit = (unsigned long)todigitl(*nptr); // exceeds abs(int_min) we exit with range error.
if(value > (ULONG_MAX - digit)/base) { while(isbase(*str, (int)base)) {
errno = ERANGE; intull digit = (intull)todigitl(*str);
value = 0; if(value > (int_abs_max - digit)/base) {
goto end; goto error_out_of_range;
} }
value = base*value + digit; value = base*value + digit;
++nptr; ++str;
++digits_read;
}
// We only allow the modulo of value equal to abs(int_min) if it is
// preceeded by the minus sign.
if(is_signed) {
if(value == int_abs_max && coef != -1) {
goto error_out_of_range;
}
}
goto finish;
error_out_of_range:
// Skip the remainder of the subject string
while(isbase(*str, (int)base)) {
++str;
} }
unsigned long max_modulo = (unsigned long)LONG_MAX+1;
if(value > max_modulo) {
errno = ERANGE; errno = ERANGE;
value = int_max;
goto finish;
finish:
// If no conversion is performed we return the value of 0 and *endptr
// is set to the nptr.
bool conv_performed = (digits_read > 0);
if(!conv_performed) {
value = 0; value = 0;
goto end;
} }
if(value == max_modulo) {
if(coef == 1) {
errno = ERANGE;
value = 0;
goto end;
}
else {
value = LONG_MIN;
coef = 1;
}
}
end:
if(endptr != NULL) { if(endptr != NULL) {
if(!conv_performed) {
*endptr = (char *)nptr; *endptr = (char *)nptr;
} }
return coef*(long)value; else {
*endptr = (char *)str;
}
}
*coefptr = coef;
return value;
} }
long long intl strtol(const char *restrict nptr, char **restrict endptr, int base) {
strtoll(const char *restrict nptr, char **restrict endptr, int base) { intull int_max = (intull)LONG_MAX;
return 0; intl coef;
intull modulo = strto_generic(nptr, endptr, base, &coef, int_max);
intl value;
if(modulo == int_max) {
value = LONG_MIN;
}
else {
value = coef * (intl)modulo;
}
return value;
} }
unsigned long intll strtoll(const char *restrict nptr, char **restrict endptr, int base) {
strtoul(const char *restrict nptr, char **restrict endptr, int base) { intull int_max = (intull)LLONG_MAX;
return 0; intl coef;
intull modulo = strto_generic(nptr, endptr, base, &coef, int_max);
intll value;
if(modulo == int_max) {
value = LLONG_MIN;
}
else {
value = (intll)coef * (intll)modulo;
}
return value;
} }
unsigned long long intul strtoul(const char *restrict nptr, char **restrict endptr, int base) {
strtoull(const char *restrict nptr, char **restrict endptr, int base) { intull int_max = (intull)ULONG_MAX;
return 0; intull value = strto_generic(nptr, endptr, base, NULL, int_max);
return (intul)value;
}
intull strtoull(const char *restrict nptr, char **restrict endptr, int base) {
intull int_max = (intull)ULLONG_MAX;
return strto_generic(nptr, endptr, base, NULL, int_max);
} }
double atof(const char *nptr) { double atof(const char *nptr) {

View File

@ -5,4 +5,4 @@
#define ERANGE 3 #define ERANGE 3
// TODO: make it thread-local // TODO: make it thread-local
int errno; extern int errno;

View File

@ -1,13 +1,44 @@
#include <assert.h> #include <assert.h>
#include <ctype.h> #include <ctype.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <errno.h>
int main(int argc, char** argv) { int main(int argc, char** argv) {
/*for (int i = 0; i < argv; i++) { const char *p = "10 200000000000000000000000000000 30 -40 junk";
printf("[%d] = %s\n", argv[i]); // printf("Parsing '%s':\n", p);
}*/
for (char c = 'a'; c != 'z'; ++c) { for (;;)
assert(isupper(toupper(c))); {
// errno can be set to any non-zero value by a library function call
// regardless of whether there was an error, so it needs to be cleared
// in order to check the error set by strtol
errno = 0;
char *end;
const long i = strtol(p, &end, 10);
if (p == end)
break;
const bool range_error = errno == ERANGE;
// printf("Extracted '%.*s', strtol returned %ld.", (int)(end-p), p, i);
p = end;
// if (range_error)
// printf(" Range error occurred.");
// putchar('\n');
} }
// printf("Unextracted leftover: '%s'\n\n", p);
// parsing without error handling
long v1 = strtol("1010", NULL, 2);
long v2 = strtol("12", NULL, 8);
long v3 = strtol("A", NULL, 16);
long v4 = strtol("junk", NULL, 36);
long v5 = strtol("012", NULL, 0);
long v6 = strtol("0xA", NULL, 0);
long v7 = strtol("junk", NULL, 0);
return 0; return 0;
} }