mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-11-04 13:29:59 -05:00
json: improve supported numbers
Use 64bit signed integers and fix double value conversion. Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>
This commit is contained in:
parent
02027518af
commit
1df4a311d4
3 changed files with 58 additions and 61 deletions
|
|
@ -35,7 +35,7 @@ struct pa_json_object {
|
||||||
pa_json_type type;
|
pa_json_type type;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
int int_value;
|
int64_t int_value;
|
||||||
double double_value;
|
double double_value;
|
||||||
bool bool_value;
|
bool bool_value;
|
||||||
char *string_value;
|
char *string_value;
|
||||||
|
|
@ -194,33 +194,22 @@ error:
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* parse_number(const char *str, pa_json_object *obj) {
|
static const char* parse_number(const char *str, pa_json_object *obj) {
|
||||||
bool negative = false, has_fraction = false, has_exponent = false, valid = false;
|
bool has_fraction = false, has_exponent = false, valid = false;
|
||||||
unsigned int integer = 0;
|
char *candidate = NULL;
|
||||||
unsigned int fraction = 0;
|
const char *s = str;
|
||||||
unsigned int fraction_digits = 0;
|
|
||||||
int exponent = 0;
|
|
||||||
|
|
||||||
if (*str == '-') {
|
if (*s == '-')
|
||||||
negative = true;
|
s++;
|
||||||
str++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*str == '0') {
|
if (*s == '0') {
|
||||||
valid = true;
|
valid = true;
|
||||||
str++;
|
s++;
|
||||||
goto fraction;
|
goto fraction;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (is_digit(*str)) {
|
while (is_digit(*s)) {
|
||||||
valid = true;
|
valid = true;
|
||||||
|
s++;
|
||||||
if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) {
|
|
||||||
pa_log("Integer overflow while parsing number");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
integer = (integer * 10) + (*str - '0');
|
|
||||||
str++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fraction:
|
fraction:
|
||||||
|
|
@ -230,22 +219,14 @@ fraction:
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*str == '.') {
|
if (*s == '.') {
|
||||||
has_fraction = true;
|
has_fraction = true;
|
||||||
str++;
|
s++;
|
||||||
valid = false;
|
valid = false;
|
||||||
|
|
||||||
while (is_digit(*str)) {
|
while (is_digit(*s)) {
|
||||||
valid = true;
|
valid = true;
|
||||||
|
s++;
|
||||||
if (fraction > (UINT_MAX / 10)) {
|
|
||||||
pa_log("Integer overflow while parsing fractional part of number");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
fraction = (fraction * 10) + (*str - '0');
|
|
||||||
fraction_digits++;
|
|
||||||
str++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
|
|
@ -254,52 +235,50 @@ fraction:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*str == 'e' || *str == 'E') {
|
if (*s == 'e' || *s == 'E') {
|
||||||
bool exponent_negative = false;
|
|
||||||
|
|
||||||
has_exponent = true;
|
has_exponent = true;
|
||||||
str++;
|
s++;
|
||||||
valid = false;
|
valid = false;
|
||||||
|
|
||||||
if (*str == '-') {
|
if (*s == '-' || *s == '+')
|
||||||
exponent_negative = true;
|
s++;
|
||||||
str++;
|
|
||||||
} else if (*str == '+')
|
|
||||||
str++;
|
|
||||||
|
|
||||||
while (is_digit(*str)) {
|
while (is_digit(*s)) {
|
||||||
valid = true;
|
valid = true;
|
||||||
|
s++;
|
||||||
if (exponent > (INT_MAX / 10)) {
|
|
||||||
pa_log("Integer overflow while parsing exponent part of number");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
exponent = (exponent * 10) + (*str - '0');
|
|
||||||
str++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
pa_log("No digit in exponent while parsing fraction");
|
pa_log("No digit in exponent while parsing fraction");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exponent_negative)
|
|
||||||
exponent *= -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Number format looks good, now try to extract the value.
|
||||||
|
* Here 's' points just after the string which will be consumed. */
|
||||||
|
|
||||||
|
candidate = pa_xstrndup(str, s - str);
|
||||||
|
|
||||||
if (has_fraction || has_exponent) {
|
if (has_fraction || has_exponent) {
|
||||||
|
if (pa_atod(candidate, &obj->double_value) < 0) {
|
||||||
|
pa_log("Cannot convert string '%s' to double value", str);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
obj->type = PA_JSON_TYPE_DOUBLE;
|
obj->type = PA_JSON_TYPE_DOUBLE;
|
||||||
obj->double_value =
|
|
||||||
(negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent);
|
|
||||||
} else {
|
} else {
|
||||||
|
if (pa_atoi64(candidate, &obj->int_value) < 0) {
|
||||||
|
pa_log("Cannot convert string '%s' to int64_t value", str);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
obj->type = PA_JSON_TYPE_INT;
|
obj->type = PA_JSON_TYPE_INT;
|
||||||
obj->int_value = (negative ? -1 : 1) * integer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return str;
|
pa_xfree(candidate);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
pa_xfree(candidate);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -522,7 +501,7 @@ void pa_json_object_free(pa_json_object *obj) {
|
||||||
pa_xfree(obj);
|
pa_xfree(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
int pa_json_object_get_int(const pa_json_object *o) {
|
int64_t pa_json_object_get_int(const pa_json_object *o) {
|
||||||
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
|
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
|
||||||
return o->int_value;
|
return o->int_value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
***/
|
***/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
|
#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ void pa_json_object_free(pa_json_object *obj);
|
||||||
|
|
||||||
/* All pointer members that are returned are valid while the corresponding object is valid */
|
/* All pointer members that are returned are valid while the corresponding object is valid */
|
||||||
|
|
||||||
int pa_json_object_get_int(const pa_json_object *o);
|
int64_t pa_json_object_get_int(const pa_json_object *o);
|
||||||
double pa_json_object_get_double(const pa_json_object *o);
|
double pa_json_object_get_double(const pa_json_object *o);
|
||||||
bool pa_json_object_get_bool(const pa_json_object *o);
|
bool pa_json_object_get_bool(const pa_json_object *o);
|
||||||
const char* pa_json_object_get_string(const pa_json_object *o);
|
const char* pa_json_object_get_string(const pa_json_object *o);
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,8 @@ START_TEST(int_test) {
|
||||||
pa_json_object *o;
|
pa_json_object *o;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
const char *ints_parse[] = { "1", "-1", "1234", "0" };
|
const char *ints_parse[] = { "1", "-1", "1234", "0" };
|
||||||
const int ints_compare[] = { 1, -1, 1234, 0 };
|
const int64_t ints_compare[] = { 1, -1, 1234, 0 };
|
||||||
|
char *ulong_max_str;
|
||||||
|
|
||||||
for (i = 0; i < PA_ELEMENTSOF(ints_parse); i++) {
|
for (i = 0; i < PA_ELEMENTSOF(ints_parse); i++) {
|
||||||
o = pa_json_parse(ints_parse[i]);
|
o = pa_json_parse(ints_parse[i]);
|
||||||
|
|
@ -65,12 +66,19 @@ START_TEST(int_test) {
|
||||||
|
|
||||||
pa_json_object_free(o);
|
pa_json_object_free(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* test that parser would fail on integer overflow */
|
||||||
|
ulong_max_str = pa_sprintf_malloc("%"PRIu64, ULONG_MAX);
|
||||||
|
o = pa_json_parse(ulong_max_str);
|
||||||
|
fail_unless(o == NULL);
|
||||||
|
pa_xfree(ulong_max_str);
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
START_TEST(double_test) {
|
START_TEST(double_test) {
|
||||||
pa_json_object *o;
|
pa_json_object *o;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
char *very_large_double_str;
|
||||||
const char *doubles_parse[] = {
|
const char *doubles_parse[] = {
|
||||||
"1.0", "-1.1", "1234e2", "1234e0", "0.1234", "-0.1234", "1234e-1", "1234.5e-1", "1234.5e+2",
|
"1.0", "-1.1", "1234e2", "1234e0", "0.1234", "-0.1234", "1234e-1", "1234.5e-1", "1234.5e+2",
|
||||||
};
|
};
|
||||||
|
|
@ -87,6 +95,12 @@ START_TEST(double_test) {
|
||||||
|
|
||||||
pa_json_object_free(o);
|
pa_json_object_free(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* test that parser would fail on double exponent overflow */
|
||||||
|
very_large_double_str = pa_sprintf_malloc("%"PRIu64"e%"PRIu64, ULONG_MAX, ULONG_MAX);
|
||||||
|
o = pa_json_parse(very_large_double_str);
|
||||||
|
fail_unless(o == NULL);
|
||||||
|
pa_xfree(very_large_double_str);
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
@ -234,8 +248,11 @@ START_TEST(bad_test) {
|
||||||
const char *bad_parse[] = {
|
const char *bad_parse[] = {
|
||||||
"\"" /* Quote not closed */,
|
"\"" /* Quote not closed */,
|
||||||
"123456789012345678901234567890" /* Overflow */,
|
"123456789012345678901234567890" /* Overflow */,
|
||||||
|
#if 0 /* TODO: check rounding the value is OK */
|
||||||
"0.123456789012345678901234567890" /* Overflow */,
|
"0.123456789012345678901234567890" /* Overflow */,
|
||||||
|
#endif
|
||||||
"1e123456789012345678901234567890" /* Overflow */,
|
"1e123456789012345678901234567890" /* Overflow */,
|
||||||
|
"1e-10000" /* Underflow */,
|
||||||
"1e" /* Bad number string */,
|
"1e" /* Bad number string */,
|
||||||
"1." /* Bad number string */,
|
"1." /* Bad number string */,
|
||||||
"1.e3" /* Bad number string */,
|
"1.e3" /* Bad number string */,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue