mirror of https://github.com/flysand7/ciabatta.git
Complete implementation of strto* and ato* functions
This commit is contained in:
parent
04555ac1db
commit
e18e9a988e
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
int errno;
|
|
@ -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) {
|
||||||
coef = -1;
|
if(*str == '-') {
|
||||||
++nptr;
|
coef = -1;
|
||||||
|
++str;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(*nptr == '+') {
|
if(*str == '+') {
|
||||||
++nptr;
|
++str;
|
||||||
}
|
}
|
||||||
unsigned long base = (unsigned long)inbase;
|
|
||||||
unsigned long value = 0;
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
unsigned long max_modulo = (unsigned long)LONG_MAX+1;
|
// We only allow the modulo of value equal to abs(int_min) if it is
|
||||||
if(value > max_modulo) {
|
// preceeded by the minus sign.
|
||||||
errno = ERANGE;
|
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;
|
||||||
|
}
|
||||||
|
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(endptr != NULL) {
|
||||||
if(coef == 1) {
|
if(!conv_performed) {
|
||||||
errno = ERANGE;
|
*endptr = (char *)nptr;
|
||||||
value = 0;
|
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
value = LONG_MIN;
|
*endptr = (char *)str;
|
||||||
coef = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end:
|
*coefptr = coef;
|
||||||
if(endptr != NULL) {
|
return value;
|
||||||
*endptr = (char *)nptr;
|
}
|
||||||
|
|
||||||
|
intl strtol(const char *restrict nptr, char **restrict endptr, int base) {
|
||||||
|
intull int_max = (intull)LONG_MAX;
|
||||||
|
intl coef;
|
||||||
|
intull modulo = strto_generic(nptr, endptr, base, &coef, int_max);
|
||||||
|
intl value;
|
||||||
|
if(modulo == int_max) {
|
||||||
|
value = LONG_MIN;
|
||||||
}
|
}
|
||||||
return coef*(long)value;
|
else {
|
||||||
|
value = coef * (intl)modulo;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long
|
intll strtoll(const char *restrict nptr, char **restrict endptr, int base) {
|
||||||
strtoll(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
|
intul strtoul(const char *restrict nptr, char **restrict endptr, int base) {
|
||||||
strtoul(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long
|
intull strtoull(const char *restrict nptr, char **restrict endptr, int base) {
|
||||||
strtoull(const char *restrict nptr, char **restrict endptr, int base) {
|
intull int_max = (intull)ULLONG_MAX;
|
||||||
return 0;
|
return strto_generic(nptr, endptr, base, NULL, int_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
double atof(const char *nptr) {
|
double atof(const char *nptr) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
41
test/test.c
41
test/test.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue