Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floating point parsing support for scanf family #924

Merged
merged 20 commits into from
Nov 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
64c883a
vcscanf: move buffer variables to top of function
matheusmoreira Nov 2, 2023
3403b6c
vcscanf: change buf type to unsigned char *
matheusmoreira Nov 2, 2023
44cb305
vcscanf: add buffer cursor variable
matheusmoreira Nov 2, 2023
c161713
vcscanf: define BUFFER macro
matheusmoreira Nov 2, 2023
0e7c6aa
vcscanf: free buf before returning if not NULL
matheusmoreira Nov 2, 2023
c8e941b
vcscanf: define floating point result variable
matheusmoreira Nov 2, 2023
ec2b272
vcscanf: add floating point number scanner case
matheusmoreira Nov 2, 2023
a2225a2
vcscanf: include the gdtoa library
matheusmoreira Nov 2, 2023
99c5006
vcscanf: parse floating point numbers
matheusmoreira Nov 2, 2023
758cdb7
examples: add a parsefloat example
matheusmoreira Nov 2, 2023
90f19ed
vcscanf: ensure buf is NULL after string decoding
matheusmoreira Nov 2, 2023
bb0d4c2
vcscanf: fix formatting errors
matheusmoreira Nov 2, 2023
cc79f62
vcscanf: add curly braces around else clause
matheusmoreira Nov 2, 2023
c38aec9
test: add sscanf floating point parser tests
matheusmoreira Nov 2, 2023
ded97bf
vcscanf: add curly braces around another else
matheusmoreira Nov 2, 2023
c78ae96
vcscanf: buffer the last 'y' of "infinity"
matheusmoreira Nov 2, 2023
2daad6e
vcscanf: buffer the ')' of "nan(whatever)"
matheusmoreira Nov 2, 2023
64d2b12
test: parse more floats at once
matheusmoreira Nov 2, 2023
8f12a06
vcscanf: remove strtod call repetition
matheusmoreira Nov 4, 2023
850ab95
vcscanf: refactor buffer usage
matheusmoreira Nov 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions examples/parsefloat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <stdio.h>

#define PARSE_AND_PRINT(type, scan_fmt, print_fmt, str) \
do { \
type val; int ret; \
ret = sscanf(str, scan_fmt, &val); \
printf("\"%s\" => " print_fmt " = %d\n", str, val, ret); \
} while (0)

int main()
{
PARSE_AND_PRINT(float, "%f", "%f", "0.3715");
matheusmoreira marked this conversation as resolved.
Show resolved Hide resolved
PARSE_AND_PRINT(float, "%f", "%f", ".3715");
PARSE_AND_PRINT(float, "%f", "%f", "3715");
PARSE_AND_PRINT(float, "%f", "%f", "111.11");
PARSE_AND_PRINT(float, "%f", "%f", "-2.22");
PARSE_AND_PRINT(float, "%f", "%f", "Nan");
PARSE_AND_PRINT(float, "%f", "%f", "nAn(2)");
PARSE_AND_PRINT(float, "%f", "%f", "-NAN(_asdfZXCV1234_)");
PARSE_AND_PRINT(float, "%f", "%f", "-nan");
PARSE_AND_PRINT(float, "%f", "%f", "+nan");
PARSE_AND_PRINT(float, "%f", "%f", "inF");
PARSE_AND_PRINT(float, "%f", "%f", "iNfINiTy");
PARSE_AND_PRINT(float, "%f", "%f", "+inf");
PARSE_AND_PRINT(float, "%f", "%f", "-inf");
PARSE_AND_PRINT(float, "%f", "%f", "0X1.BC70A3D70A3D7P+6");
PARSE_AND_PRINT(float, "%f", "%f", "1.18973e+4932zzz");
PARSE_AND_PRINT(float, "%f", "%.10f", " -0.0000000123junk");
PARSE_AND_PRINT(float, "%f", "%f", "junk");
return 0;
}
199 changes: 195 additions & 4 deletions libc/stdio/vcscanf.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "libc/str/tpdecodecb.internal.h"
#include "libc/str/utf16.h"
#include "libc/sysv/errfuns.h"
#include "third_party/gdtoa/gdtoa.h"

#define READ \
({ \
Expand All @@ -34,6 +35,21 @@
c; \
})

#define FP_BUFFER_GROW 48
#define BUFFER \
({ \
int c = READ; \
if (fpbufcur >= fpbufsize - 1) { \
fpbufsize = fpbufsize + FP_BUFFER_GROW; \
fpbuf = realloc(fpbuf, fpbufsize); \
} \
if (c != -1) { \
fpbuf[fpbufcur++] = c; \
fpbuf[fpbufcur] = '\0'; \
} \
c; \
})

/**
* String / file / stream decoder.
*
Expand All @@ -60,6 +76,9 @@ int __vcscanf(int callback(void *), //
struct FreeMe *next;
void *ptr;
} *freeme = NULL;
unsigned char *fpbuf = NULL;
size_t fpbufsize;
size_t fpbufcur;
const unsigned char *p = (const unsigned char *)fmt;
int *n_ptr;
int items = 0;
Expand All @@ -84,8 +103,9 @@ int __vcscanf(int callback(void *), //
break;
case '%': {
uint128_t number;
void *buf;
unsigned char *buf;
size_t bufsize;
double fp;
unsigned width = 0;
unsigned char bits = 32;
unsigned char charbytes = sizeof(char);
Expand Down Expand Up @@ -208,6 +228,27 @@ int __vcscanf(int callback(void *), //
base = 10;
}
goto DecodeNumber;
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G': // floating point number
if (!(charbytes == sizeof(char) || charbytes == sizeof(wchar_t))) {
items = -1;
goto Done;
}
while (isspace(c)) {
c = READ;
}
fpbufsize = FP_BUFFER_GROW;
fpbuf = malloc(fpbufsize);
fpbufcur = 0;
fpbuf[fpbufcur++] = c;
fpbuf[fpbufcur] = '\0';
goto ConsumeFloatingPointNumber;
default:
items = einval();
goto Done;
Expand Down Expand Up @@ -293,6 +334,154 @@ int __vcscanf(int callback(void *), //
goto Done;
}
continue;
ConsumeFloatingPointNumber:
if (c == '+' || c == '-') {
c = BUFFER;
}
bool hexadecimal = false;
if (c == '0') {
c = BUFFER;
if (c == 'x' || c == 'X') {
c = BUFFER;
hexadecimal = true;
goto BufferFloatingPointNumber;
} else if (c == -1) {
goto GotFloatingPointNumber;
} else {
goto BufferFloatingPointNumber;
}
} else if (c == 'n' || c == 'N') {
c = BUFFER;
if (c == 'a' || c == 'A') {
c = BUFFER;
if (c == 'n' || c == 'N') {
c = BUFFER;
if (c == '(') {
c = BUFFER;
do {
bool isdigit = c >= '0' && c <= '9';
bool isletter = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
if (!(c == '_' || isdigit || isletter)) {
goto Done;
}
} while ((c = BUFFER) != -1 && c != ')');
if (c == ')') {
c = BUFFER;
}
goto GotFloatingPointNumber;
} else {
goto GotFloatingPointNumber;
}
} else {
goto Done;
}
} else {
goto Done;
}
} else if (c == 'i' || c == 'I') {
c = BUFFER;
if (c == 'n' || c == 'N') {
c = BUFFER;
if (c == 'f' || c == 'F') {
c = BUFFER;
if (c == 'i' || c == 'I') {
c = BUFFER;
if (c == 'n' || c == 'N') {
c = BUFFER;
if (c == 'i' || c == 'I') {
c = BUFFER;
if (c == 't' || c == 'T') {
c = BUFFER;
if (c == 'y' || c == 'Y') {
c = BUFFER;
} else {
goto Done;
}
} else {
goto Done;
}
} else {
goto Done;
}
} else {
goto Done;
}
} else {
if (c != -1 && unget) {
unget(c, arg);
}
goto GotFloatingPointNumber;
}
} else {
goto Done;
}
} else {
goto Done;
}
}
BufferFloatingPointNumber:
enum { INTEGER, FRACTIONAL, SIGN, EXPONENT } state = INTEGER;
do {
bool isdecdigit = c >= '0' && c <= '9';
bool ishexdigit = (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
bool ispoint = c == '.' || c == ',';
bool isdecexp = c == 'e' || c == 'E';
bool ishexp = c == 'p' || c == 'P';
bool issign = c == '+' || c == '-';

switch (state) {
case INTEGER:
case FRACTIONAL:
if (isdecdigit || (hexadecimal && ishexdigit)) {
goto Continue;
} else if (state == INTEGER && ispoint) {
state = FRACTIONAL;
goto Continue;
} else if (isdecexp || (hexadecimal && ishexp)) {
state = SIGN;
goto Continue;
} else {
goto Break;
}
case SIGN:
if (issign) {
state = EXPONENT;
goto Continue;
}
state = EXPONENT;
// fallthrough
case EXPONENT:
if (isdecdigit) {
goto Continue;
} else {
goto Break;
}
default:
goto Break;
}
Continue:
continue;
Break:
if (c != -1 && unget) {
unget(c, arg);
}
break;
} while ((c = BUFFER) != -1);
GotFloatingPointNumber:
fp = strtod((char *)fpbuf, NULL);
if (!discard) {
++items;
void *out = va_arg(va, void *);
if (charbytes == sizeof(char)) {
*(float *)out = (float)fp;
} else {
*(double *)out = (double)fp;
}
}
free(fpbuf);
fpbuf = NULL;
fpbufcur = fpbufsize = 0;
continue;
ReportConsumed:
n_ptr = va_arg(va, int *);
*n_ptr = consumed - 1; // minus lookahead
Expand Down Expand Up @@ -321,7 +510,7 @@ int __vcscanf(int callback(void *), //
}
if (c != -1 && j + !rawmode < bufsize && (rawmode || !isspace(c))) {
if (charbytes == 1) {
((unsigned char *)buf)[j++] = (unsigned char)c;
buf[j++] = (unsigned char)c;
c = READ;
} else if (tpdecodecb((wint_t *)&c, c, (void *)callback, arg) !=
-1) {
Expand All @@ -343,7 +532,7 @@ int __vcscanf(int callback(void *), //
goto Done;
} else if (!rawmode && j < bufsize) {
if (charbytes == sizeof(char)) {
((unsigned char *)buf)[j] = '\0';
buf[j] = '\0';
} else if (charbytes == sizeof(char16_t)) {
((char16_t *)buf)[j] = u'\0';
} else if (charbytes == sizeof(wchar_t)) {
Expand All @@ -355,8 +544,9 @@ int __vcscanf(int callback(void *), //
}
++items;
if (ismalloc) {
*va_arg(va, char **) = buf;
*va_arg(va, char **) = (void *) buf;
}
buf = NULL;
} else {
do {
if (isspace(c)) break;
Expand All @@ -377,5 +567,6 @@ int __vcscanf(int callback(void *), //
if (items == -1) free(entry->ptr);
free(entry);
}
if (fpbuf) free(fpbuf);
return items;
}
Loading