json_util: parse integer numbers directly

Avoid a trip through floating-point numbers when parsing JSON numbers as
integers.

This will allow parsing 64-bit integers that cannot be represented in
double precision floating point.

Change-Id: Ic428c9f12e44e6dbee72f39a91ecb56ab30b365f
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
Reviewed-on: https://review.gerrithub.io/364132
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Tested-by: SPDK Automated Test System <sys_sgsw@intel.com>
This commit is contained in:
Daniel Verkamp 2017-06-02 12:47:45 -07:00
parent d7134439f6
commit 7089d58244
2 changed files with 145 additions and 16 deletions

View File

@ -92,6 +92,113 @@ spdk_json_strdup(const struct spdk_json_val *val)
return s;
}
struct spdk_json_num {
bool negative;
uint64_t significand;
int64_t exponent;
};
static int
spdk_json_number_split(const struct spdk_json_val *val, struct spdk_json_num *num)
{
const char *iter;
size_t remaining;
uint64_t *pval;
uint64_t frac_digits = 0;
uint64_t exponent_u64 = 0;
bool exponent_negative = false;
enum {
NUM_STATE_INT,
NUM_STATE_FRAC,
NUM_STATE_EXP,
} state;
memset(num, 0, sizeof(*num));
if (val->type != SPDK_JSON_VAL_NUMBER) {
return -EINVAL;
}
remaining = val->len;
if (remaining == 0) {
return -EINVAL;
}
iter = val->start;
if (*iter == '-') {
num->negative = true;
iter++;
remaining--;
}
state = NUM_STATE_INT;
pval = &num->significand;
while (remaining--) {
char c = *iter++;
if (c == '.') {
state = NUM_STATE_FRAC;
} else if (c == 'e' || c == 'E') {
state = NUM_STATE_EXP;
pval = &exponent_u64;
} else if (c == '-') {
assert(state == NUM_STATE_EXP);
exponent_negative = true;
} else if (c == '+') {
assert(state == NUM_STATE_EXP);
/* exp_negative = false; */ /* already false by default */
} else {
uint64_t new_val;
assert(c >= '0' && c <= '9');
new_val = *pval * 10 + c - '0';
if (new_val < *pval) {
return -ERANGE;
}
if (state == NUM_STATE_FRAC) {
frac_digits++;
}
*pval = new_val;
}
}
if (exponent_negative) {
if (exponent_u64 > 9223372036854775808ULL) { /* abs(INT64_MIN) */
return -ERANGE;
}
num->exponent = (int64_t) - exponent_u64;
} else {
if (exponent_u64 > INT64_MAX) {
return -ERANGE;
}
num->exponent = exponent_u64;
}
num->exponent -= frac_digits;
/* Apply as much of the exponent as possible without overflow or truncation */
if (num->exponent < 0) {
while (num->exponent && num->significand >= 10 && num->significand % 10 == 0) {
num->significand /= 10;
num->exponent++;
}
} else { /* positive exponent */
while (num->exponent) {
uint64_t new_val = num->significand * 10;
if (new_val < num->significand) {
break;
}
num->significand = new_val;
num->exponent--;
}
}
return 0;
}
int
spdk_json_number_to_double(const struct spdk_json_val *val, double *num)
{
@ -119,38 +226,53 @@ spdk_json_number_to_double(const struct spdk_json_val *val, double *num)
int
spdk_json_number_to_int32(const struct spdk_json_val *val, int32_t *num)
{
double dbl;
struct spdk_json_num split_num;
int rc;
if (spdk_json_number_to_double(val, &dbl)) {
return -1;
rc = spdk_json_number_split(val, &split_num);
if (rc) {
return rc;
}
*num = (int32_t)dbl;
if (dbl != (double)*num) {
return -1;
if (split_num.exponent) {
return -ERANGE;
}
if (split_num.negative) {
if (split_num.significand > 2147483648) { /* abs(INT32_MIN) */
return -ERANGE;
}
*num = (int32_t) - (int64_t)split_num.significand;
return 0;
}
/* positive */
if (split_num.significand > INT32_MAX) {
return -ERANGE;
}
*num = (int32_t)split_num.significand;
return 0;
}
int
spdk_json_number_to_uint32(const struct spdk_json_val *val, uint32_t *num)
{
double dbl;
struct spdk_json_num split_num;
int rc;
if (spdk_json_number_to_double(val, &dbl)) {
return -1;
rc = spdk_json_number_split(val, &split_num);
if (rc) {
return rc;
}
if (dbl < 0) {
return -1;
if (split_num.exponent || split_num.negative) {
return -ERANGE;
}
*num = (uint32_t)dbl;
if (dbl != (double)*num) {
return -1;
if (split_num.significand > UINT32_MAX) {
return -ERANGE;
}
*num = (uint32_t)split_num.significand;
return 0;
}

View File

@ -383,7 +383,14 @@ test_decode_uint32(void)
v.len = 6;
i = 0;
CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0);
CU_ASSERT(i == 4)
CU_ASSERT(i == 4);
/* valid negative exponent */
v.start = "10e-1";
v.len = 5;
i = 0;
CU_ASSERT(spdk_json_decode_uint32(&v, &i) == 0);
CU_ASSERT(i == 1)
}
static void