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:
parent
d7134439f6
commit
7089d58244
@ -92,6 +92,113 @@ spdk_json_strdup(const struct spdk_json_val *val)
|
|||||||
return s;
|
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
|
int
|
||||||
spdk_json_number_to_double(const struct spdk_json_val *val, double *num)
|
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
|
int
|
||||||
spdk_json_number_to_int32(const struct spdk_json_val *val, int32_t *num)
|
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)) {
|
rc = spdk_json_number_split(val, &split_num);
|
||||||
return -1;
|
if (rc) {
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
*num = (int32_t)dbl;
|
if (split_num.exponent) {
|
||||||
if (dbl != (double)*num) {
|
return -ERANGE;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
spdk_json_number_to_uint32(const struct spdk_json_val *val, uint32_t *num)
|
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)) {
|
rc = spdk_json_number_split(val, &split_num);
|
||||||
return -1;
|
if (rc) {
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dbl < 0) {
|
if (split_num.exponent || split_num.negative) {
|
||||||
return -1;
|
return -ERANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
*num = (uint32_t)dbl;
|
if (split_num.significand > UINT32_MAX) {
|
||||||
if (dbl != (double)*num) {
|
return -ERANGE;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
*num = (uint32_t)split_num.significand;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,7 +383,14 @@ test_decode_uint32(void)
|
|||||||
v.len = 6;
|
v.len = 6;
|
||||||
i = 0;
|
i = 0;
|
||||||
CU_ASSERT(spdk_json_decode_uint32(&v, &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
|
static void
|
||||||
|
Loading…
Reference in New Issue
Block a user