From c9a0ac5886ccdb79457d89e5d197fa041195df04 Mon Sep 17 00:00:00 2001 From: Ramiro Polla Date: Sat, 8 Dec 2018 19:28:46 +0100 Subject: [PATCH] json_tokener: optimize parsing of integer values speedup for 32-bit: ~8% speedup for 64-bit: ~9% --- json_tokener.c | 2 +- json_util.c | 52 ++++++++++++++++++++++++++++++++++++++------------ json_util.h | 1 + 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/json_tokener.c b/json_tokener.c index b5fb210..6fc4937 100644 --- a/json_tokener.c +++ b/json_tokener.c @@ -824,7 +824,7 @@ struct json_object* json_tokener_parse_ex(struct json_tokener *tok, { int64_t num64; double numd; - if (!tok->is_double && json_parse_int64(tok->pb->buf, &num64) == 0) { + if (!tok->is_double && json_parse_sanitized_int64(tok->pb->buf, tok->pb->bpos, &num64) == 0) { if (num64 && tok->pb->buf[0]=='0' && (tok->flags & JSON_TOKENER_STRICT)) { /* in strict mode, number must not start with 0 */ diff --git a/json_util.c b/json_util.c index ad7704a..d36ef6f 100644 --- a/json_util.c +++ b/json_util.c @@ -39,10 +39,6 @@ #endif /* HAVE_UNISTD_H */ #ifdef WIN32 -# if MSC_VER < 1800 -/* strtoll is available only since Visual Studio 2013 */ -# define strtoll _strtoi64 -# endif # define WIN32_LEAN_AND_MEAN # include # include @@ -195,16 +191,48 @@ int json_parse_double(const char *buf, double *retval) return end == buf ? 1 : 0; } +// The input buffer 'buf' must contain only digits (0 to 9), except +// for the first character, which may be a negative sign '-'. +int json_parse_sanitized_int64(const char *buf, size_t len, int64_t *retval) +{ + uint64_t uval = 0; + int is_negative = (*buf == '-'); + size_t ii = is_negative ? 1 : 0; + + if (ii == len || buf[ii] == '\0') + return 1; + + while (ii < len) + { + uint64_t tmp = (uval * 10) + buf[ii++] - '0'; + // Check for overflow. + if ((int64_t) uval > (int64_t) tmp) + { + *retval = is_negative ? INT64_MIN : INT64_MAX; + return 0; + } + uval = tmp; + } + + *retval = is_negative ? -uval : uval; + + return 0; +} + int json_parse_int64(const char *buf, int64_t *retval) { - char *end = NULL; - int64_t val; - - errno = 0; - val = strtoll(buf, &end, 10); - if (end != buf) - *retval = val; - return ((val == 0 && errno != 0) || (end == buf)) ? 1 : 0; + size_t len = 0; + // Skip leading white spaces. + while (isspace(*buf)) + buf++; + // Calculate length of valid input. + if (buf[len] == '-') + len++; + while (buf[len] >= '0' && buf[len] <= '9') + len++; + if (len == 0) + return 1; + return json_parse_sanitized_int64(buf, len, retval); } #ifndef HAVE_REALLOC diff --git a/json_util.h b/json_util.h index 3e1b294..bd7f970 100644 --- a/json_util.h +++ b/json_util.h @@ -90,6 +90,7 @@ extern int json_object_to_fd(int fd, struct json_object *obj, int flags); const char *json_util_get_last_err(void); +extern int json_parse_sanitized_int64(const char *buf, size_t len, int64_t *retval); extern int json_parse_int64(const char *buf, int64_t *retval); extern int json_parse_double(const char *buf, double *retval); -- 2.50.1