diff --git a/spa/include/spa/utils/json.h b/spa/include/spa/utils/json.h index de849b0c4..30d14f5a2 100644 --- a/spa/include/spa/utils/json.h +++ b/spa/include/spa/utils/json.h @@ -219,11 +219,25 @@ static inline bool spa_json_is_null(const char *val, int len) /* float */ static inline int spa_json_parse_float(const char *val, int len, float *result) { + char buf[96]; char *end; - if (strspn(val, "+-0123456789.Ee") < (size_t)len) + int pos; + + if (len >= (int)sizeof(buf)) return 0; - *result = spa_strtof(val, &end); - return len > 0 && end == val + len; + + for (pos = 0; pos < len; ++pos) { + switch (val[pos]) { + case '+': case '-': case '0' ... '9': case '.': case 'e': case 'E': break; + default: return 0; + } + } + + memcpy(buf, val, len); + buf[len] = '\0'; + + *result = spa_strtof(buf, &end); + return len > 0 && end == buf + len; } static inline bool spa_json_is_float(const char *val, int len) @@ -256,9 +270,17 @@ static inline char *spa_json_format_float(char *str, int size, float val) /* int */ static inline int spa_json_parse_int(const char *val, int len, int *result) { + char buf[64]; char *end; - *result = strtol(val, &end, 0); - return len > 0 && end == val + len; + + if (len >= (int)sizeof(buf)) + return 0; + + memcpy(buf, val, len); + buf[len] = '\0'; + + *result = strtol(buf, &end, 0); + return len > 0 && end == buf + len; } static inline bool spa_json_is_int(const char *val, int len) { diff --git a/test/test-spa-json.c b/test/test-spa-json.c index 063a14ed9..a97ddb22d 100644 --- a/test/test-spa-json.c +++ b/test/test-spa-json.c @@ -29,6 +29,7 @@ PWTEST(json_abi) #define TYPE_TRUE 5 #define TYPE_FALSE 6 #define TYPE_FLOAT 7 +#define TYPE_INT 8 static void check_type(int type, const char *value, int len) { @@ -38,9 +39,25 @@ static void check_type(int type, const char *value, int len) pwtest_bool_eq(spa_json_is_bool(value, len), (type == TYPE_BOOL || type == TYPE_TRUE || type == TYPE_FALSE)); pwtest_bool_eq(spa_json_is_null(value, len), (type == TYPE_NULL)); - pwtest_bool_eq(spa_json_is_true(value, len), (type == TYPE_TRUE || type == TYPE_BOOL)); - pwtest_bool_eq(spa_json_is_false(value, len), (type == TYPE_FALSE || type == TYPE_BOOL)); - pwtest_bool_eq(spa_json_is_float(value, len), (type == TYPE_FLOAT)); + if (type == TYPE_BOOL) { + pwtest_bool_true(spa_json_is_true(value, len) || spa_json_is_false(value, len)); + } else { + pwtest_bool_eq(spa_json_is_true(value, len), type == TYPE_TRUE); + pwtest_bool_eq(spa_json_is_false(value, len), type == TYPE_FALSE); + } + + switch (type) { + case TYPE_FLOAT: + pwtest_bool_true(spa_json_is_float(value, len)); + break; + case TYPE_INT: + pwtest_bool_true(spa_json_is_int(value, len)); + break; + default: + pwtest_bool_false(spa_json_is_float(value, len)); + pwtest_bool_false(spa_json_is_int(value, len)); + break; + } } static void expect_type(struct spa_json *it, int type) @@ -51,6 +68,29 @@ static void expect_type(struct spa_json *it, int type) check_type(type, value, len); } +static void expect_end(struct spa_json *it) +{ + const char *value; + struct spa_json it2; + + pwtest_int_eq(spa_json_next(it, &value), 0); + + /* end is idempotent */ + memcpy(&it2, it, sizeof(*it)); + pwtest_int_eq(spa_json_next(it, &value), 0); + pwtest_int_eq(memcmp(&it2, it, sizeof(*it)), 0); +} + +static void expect_array(struct spa_json *it, struct spa_json *sub) +{ + pwtest_int_eq(spa_json_enter_array(it, sub), 1); +} + +static void expect_object(struct spa_json *it, struct spa_json *sub) +{ + pwtest_int_eq(spa_json_enter_object(it, sub), 1); +} + static void expect_string(struct spa_json *it, const char *str) { const char *value; @@ -62,6 +102,18 @@ static void expect_string(struct spa_json *it, const char *str) spa_json_parse_stringn(value, len, s, len+1); pwtest_str_eq(s, str); } + +static void expect_string_or_bare(struct spa_json *it, const char *str) +{ + const char *value; + int len; + char *s; + pwtest_int_gt((len = spa_json_next(it, &value)), 0); + s = alloca(len+1); + pwtest_int_eq(spa_json_parse_stringn(value, len, s, len+1), 1); + pwtest_str_eq(s, str); +} + static void expect_float(struct spa_json *it, float val) { const char *value; @@ -73,6 +125,37 @@ static void expect_float(struct spa_json *it, float val) pwtest_double_eq(f, val); } +static void expect_int(struct spa_json *it, int val) +{ + const char *value; + int len; + int f = 0; + pwtest_int_gt((len = spa_json_next(it, &value)), 0); + check_type(TYPE_INT, value, len); + pwtest_int_gt(spa_json_parse_int(value, len, &f), 0); + pwtest_int_eq(f, val); +} + +static void expect_bool(struct spa_json *it, bool val) +{ + const char *value; + int len; + bool f = false; + pwtest_int_gt((len = spa_json_next(it, &value)), 0); + check_type(TYPE_BOOL, value, len); + check_type(val ? TYPE_TRUE : TYPE_FALSE, value, len); + pwtest_int_gt(spa_json_parse_bool(value, len, &f), 0); + pwtest_int_eq(f, val); +} + +static void expect_null(struct spa_json *it) +{ + const char *value; + int len; + pwtest_int_gt((len = spa_json_next(it, &value)), 0); + check_type(TYPE_NULL, value, len); +} + PWTEST(json_parse) { struct spa_json it[5]; @@ -135,6 +218,52 @@ PWTEST(json_parse) expect_string(&it[3], "1.9"); expect_float(&it[3], 1.9f); + /* non-null terminated strings OK */ + json = "1.234"; + spa_json_init(&it[0], json, 4); + expect_float(&it[0], 1.23); + expect_end(&it[0]); + + json = "1234"; + spa_json_init(&it[0], json, 3); + expect_int(&it[0], 123); + expect_end(&it[0]); + + json = "truey"; + spa_json_init(&it[0], json, 4); + expect_bool(&it[0], true); + expect_end(&it[0]); + + json = "falsey"; + spa_json_init(&it[0], json, 5); + expect_bool(&it[0], false); + expect_end(&it[0]); + + json = "nully"; + spa_json_init(&it[0], json, 4); + expect_null(&it[0]); + expect_end(&it[0]); + + json = "{}y{]"; + spa_json_init(&it[0], json, 2); + expect_object(&it[0], &it[1]); + expect_end(&it[0]); + + json = "[]y{]"; + spa_json_init(&it[0], json, 2); + expect_array(&it[0], &it[1]); + expect_end(&it[0]); + + json = "helloy"; + spa_json_init(&it[0], json, 5); + expect_string_or_bare(&it[0], "hello"); + expect_end(&it[0]); + + json = "\"hello\"y"; + spa_json_init(&it[0], json, 7); + expect_string(&it[0], "hello"); + expect_end(&it[0]); + return PWTEST_PASS; }