json: allow decoding of non-standard comments
Comments are not allowed in the JSON RFC, but some JSON libraries accept JavaScript-style comments. Add a flag that enables non-spec-compliant comment parsing. Change-Id: I9dfb66bb46ecff1a22d8af5a9c50620686a4707c Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
parent
0b8198ce27
commit
69c7ff06dc
@ -107,6 +107,13 @@ struct spdk_json_val {
|
|||||||
*/
|
*/
|
||||||
#define SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE 0x000000001
|
#define SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE 0x000000001
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow parsing of comments.
|
||||||
|
*
|
||||||
|
* Comments are not allowed by the JSON RFC, so this is not enabled by default.
|
||||||
|
*/
|
||||||
|
#define SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS 0x000000002
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse JSON data.
|
* Parse JSON data.
|
||||||
*
|
*
|
||||||
|
@ -373,6 +373,50 @@ done:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
json_valid_comment(const uint8_t *start, const uint8_t *buf_end)
|
||||||
|
{
|
||||||
|
const uint8_t *p = start;
|
||||||
|
bool multiline;
|
||||||
|
|
||||||
|
assert(buf_end > p);
|
||||||
|
if (buf_end - p < 2) {
|
||||||
|
return SPDK_JSON_PARSE_INCOMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p[0] != '/') {
|
||||||
|
return SPDK_JSON_PARSE_INVALID;
|
||||||
|
}
|
||||||
|
if (p[1] == '*') {
|
||||||
|
multiline = true;
|
||||||
|
} else if (p[1] == '/') {
|
||||||
|
multiline = false;
|
||||||
|
} else {
|
||||||
|
return SPDK_JSON_PARSE_INVALID;
|
||||||
|
}
|
||||||
|
p += 2;
|
||||||
|
|
||||||
|
if (multiline) {
|
||||||
|
while (p != buf_end - 1) {
|
||||||
|
if (p[0] == '*' && p[1] == '/') {
|
||||||
|
/* Include the terminating star and slash in the comment */
|
||||||
|
return p - start + 2;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (p != buf_end) {
|
||||||
|
if (*p == '\r' || *p == '\n') {
|
||||||
|
/* Do not include the line terminator in the comment */
|
||||||
|
return p - start;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SPDK_JSON_PARSE_INCOMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
struct json_literal {
|
struct json_literal {
|
||||||
enum spdk_json_val_type type;
|
enum spdk_json_val_type type;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
@ -574,6 +618,16 @@ spdk_json_parse(void *json, size_t size, struct spdk_json_val *values, size_t nu
|
|||||||
state = STATE_VALUE;
|
state = STATE_VALUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '/':
|
||||||
|
if (!(flags & SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS)) {
|
||||||
|
return SPDK_JSON_PARSE_INVALID;
|
||||||
|
}
|
||||||
|
rc = json_valid_comment(data, json_end);
|
||||||
|
if (rc < 0) return rc;
|
||||||
|
/* Skip over comment */
|
||||||
|
data += rc;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return SPDK_JSON_PARSE_INVALID;
|
return SPDK_JSON_PARSE_INVALID;
|
||||||
}
|
}
|
||||||
|
@ -55,18 +55,24 @@ static int g_cur_val;
|
|||||||
* Do two checks - first pass NULL for values to ensure the count is correct,
|
* Do two checks - first pass NULL for values to ensure the count is correct,
|
||||||
* then pass g_vals to get the actual values.
|
* then pass g_vals to get the actual values.
|
||||||
*/
|
*/
|
||||||
#define PARSE_PASS(in, num_vals, trailing) \
|
#define PARSE_PASS_FLAGS(in, num_vals, trailing, flags) \
|
||||||
BUF_SETUP(in); \
|
BUF_SETUP(in); \
|
||||||
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, 0) == num_vals); \
|
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, flags) == num_vals); \
|
||||||
memset(g_vals, 0, sizeof(g_vals)); \
|
memset(g_vals, 0, sizeof(g_vals)); \
|
||||||
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, g_vals, sizeof(g_vals), &g_end, SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE) == num_vals); \
|
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, g_vals, sizeof(g_vals), &g_end, flags | SPDK_JSON_PARSE_FLAG_DECODE_IN_PLACE) == num_vals); \
|
||||||
CU_ASSERT(g_end == g_buf + sizeof(in) - sizeof(trailing)); \
|
CU_ASSERT(g_end == g_buf + sizeof(in) - sizeof(trailing)); \
|
||||||
CU_ASSERT(memcmp(g_end, trailing, sizeof(trailing) - 1) == 0); \
|
CU_ASSERT(memcmp(g_end, trailing, sizeof(trailing) - 1) == 0); \
|
||||||
g_cur_val = 0
|
g_cur_val = 0
|
||||||
|
|
||||||
#define PARSE_FAIL(in, retval) \
|
#define PARSE_PASS(in, num_vals, trailing) \
|
||||||
|
PARSE_PASS_FLAGS(in, num_vals, trailing, 0)
|
||||||
|
|
||||||
|
#define PARSE_FAIL_FLAGS(in, retval, flags) \
|
||||||
BUF_SETUP(in); \
|
BUF_SETUP(in); \
|
||||||
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, 0) == retval)
|
CU_ASSERT(spdk_json_parse(g_buf, sizeof(in) - 1, NULL, 0, &g_end, flags) == retval)
|
||||||
|
|
||||||
|
#define PARSE_FAIL(in, retval) \
|
||||||
|
PARSE_FAIL_FLAGS(in, retval, 0)
|
||||||
|
|
||||||
#define VAL_STRING_MATCH(str, var_type) \
|
#define VAL_STRING_MATCH(str, var_type) \
|
||||||
CU_ASSERT(g_vals[g_cur_val].type == var_type); \
|
CU_ASSERT(g_vals[g_cur_val].type == var_type); \
|
||||||
@ -797,6 +803,61 @@ test_parse_nesting(void)
|
|||||||
PARSE_FAIL("{\"a\": [0, 1, 2]", SPDK_JSON_PARSE_INCOMPLETE);
|
PARSE_FAIL("{\"a\": [0, 1, 2]", SPDK_JSON_PARSE_INCOMPLETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_parse_comment(void)
|
||||||
|
{
|
||||||
|
/* Comments are not allowed by the JSON RFC */
|
||||||
|
PARSE_PASS("[0]", 3, "");
|
||||||
|
PARSE_FAIL("/* test */[0]", SPDK_JSON_PARSE_INVALID);
|
||||||
|
PARSE_FAIL("[/* test */0]", SPDK_JSON_PARSE_INVALID);
|
||||||
|
PARSE_FAIL("[0/* test */]", SPDK_JSON_PARSE_INVALID);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is allowed since the parser stops once it reads a complete JSON object.
|
||||||
|
* The next parse call would fail (see tests above) when parsing the comment.
|
||||||
|
*/
|
||||||
|
PARSE_PASS("[0]/* test */", 3, "/* test */");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test with non-standard comments enabled.
|
||||||
|
*/
|
||||||
|
PARSE_PASS_FLAGS("/* test */[0]", 3, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
VAL_ARRAY_BEGIN(1);
|
||||||
|
VAL_NUMBER("0");
|
||||||
|
VAL_ARRAY_END();
|
||||||
|
|
||||||
|
PARSE_PASS_FLAGS("[/* test */0]", 3, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
VAL_ARRAY_BEGIN(1);
|
||||||
|
VAL_NUMBER("0");
|
||||||
|
VAL_ARRAY_END();
|
||||||
|
|
||||||
|
PARSE_PASS_FLAGS("[0/* test */]", 3, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
VAL_ARRAY_BEGIN(1);
|
||||||
|
VAL_NUMBER("0");
|
||||||
|
VAL_ARRAY_END();
|
||||||
|
|
||||||
|
PARSE_FAIL_FLAGS("/* test */", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
PARSE_FAIL_FLAGS("[/* test */", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
PARSE_FAIL_FLAGS("[0/* test */", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Single-line comments
|
||||||
|
*/
|
||||||
|
PARSE_PASS_FLAGS("// test\n0", 1, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
VAL_NUMBER("0");
|
||||||
|
|
||||||
|
PARSE_PASS_FLAGS("// test\r\n0", 1, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
VAL_NUMBER("0");
|
||||||
|
|
||||||
|
PARSE_PASS_FLAGS("// [0] test\n0", 1, "", SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
VAL_NUMBER("0");
|
||||||
|
|
||||||
|
PARSE_FAIL_FLAGS("//", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
PARSE_FAIL_FLAGS("// test", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
PARSE_FAIL_FLAGS("//\n", SPDK_JSON_PARSE_INCOMPLETE, SPDK_JSON_PARSE_FLAG_ALLOW_COMMENTS);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
CU_pSuite suite = NULL;
|
CU_pSuite suite = NULL;
|
||||||
@ -822,7 +883,8 @@ int main(int argc, char **argv)
|
|||||||
CU_add_test(suite, "parse_number", test_parse_number) == NULL ||
|
CU_add_test(suite, "parse_number", test_parse_number) == NULL ||
|
||||||
CU_add_test(suite, "parse_array", test_parse_array) == NULL ||
|
CU_add_test(suite, "parse_array", test_parse_array) == NULL ||
|
||||||
CU_add_test(suite, "parse_object", test_parse_object) == NULL ||
|
CU_add_test(suite, "parse_object", test_parse_object) == NULL ||
|
||||||
CU_add_test(suite, "parse_nesting", test_parse_nesting) == NULL) {
|
CU_add_test(suite, "parse_nesting", test_parse_nesting) == NULL ||
|
||||||
|
CU_add_test(suite, "parse_comment", test_parse_comment) == NULL) {
|
||||||
CU_cleanup_registry();
|
CU_cleanup_registry();
|
||||||
return CU_get_error();
|
return CU_get_error();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user