Skip to content

Commit

Permalink
json: support parsing and serializing 'int64_t'
Browse files Browse the repository at this point in the history
Up to now there was only support for parsing/encoding 32-bit integer
numbers, with no support for larger ones.

Introduce support for 'int64_t' type, so that large numbers can be
serialized into JSON payloads.

Signed-off-by: Marcin Niestroj <[email protected]>
(cherry picked from commit 878640f)
  • Loading branch information
mniestroj authored and mlohvynenko committed Sep 23, 2024
1 parent 1fd105b commit 1e3ab46
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 8 deletions.
2 changes: 2 additions & 0 deletions include/zephyr/data/json.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ enum json_tokens {
JSON_TOK_FLOAT = '1',
JSON_TOK_OPAQUE = '2',
JSON_TOK_OBJ_ARRAY = '3',
JSON_TOK_ENCODED_OBJ = '4',
JSON_TOK_INT64 = '5',
JSON_TOK_TRUE = 't',
JSON_TOK_FALSE = 'f',
JSON_TOK_NULL = 'n',
Expand Down
55 changes: 55 additions & 0 deletions lib/utils/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ static int element_token(enum json_tokens token)
case JSON_TOK_ARRAY_START:
case JSON_TOK_STRING:
case JSON_TOK_NUMBER:
case JSON_TOK_INT64:
case JSON_TOK_FLOAT:
case JSON_TOK_OPAQUE:
case JSON_TOK_OBJ_ARRAY:
Expand Down Expand Up @@ -444,6 +445,30 @@ static int decode_num(const struct json_token *token, int32_t *num)
return 0;
}

static int decode_int64(const struct json_token *token, int64_t *num)
{
char *endptr;
char prev_end;

prev_end = *token->end;
*token->end = '\0';

errno = 0;
*num = strtoll(token->start, &endptr, 10);

*token->end = prev_end;

if (errno != 0) {
return -errno;
}

if (endptr != token->end) {
return -EINVAL;
}

return 0;
}

static bool equivalent_types(enum json_tokens type1, enum json_tokens type2)
{
if (type1 == JSON_TOK_TRUE || type1 == JSON_TOK_FALSE) {
Expand All @@ -454,6 +479,10 @@ static bool equivalent_types(enum json_tokens type1, enum json_tokens type2)
return true;
}

if (type1 == JSON_TOK_NUMBER && type2 == JSON_TOK_INT64) {
return true;
}

if (type1 == JSON_TOK_STRING && type2 == JSON_TOK_OPAQUE) {
return true;
}
Expand Down Expand Up @@ -511,6 +540,11 @@ static int64_t decode_value(struct json_obj *obj,

return decode_num(value, num);
}
case JSON_TOK_INT64: {
int64_t *num = field;

return decode_int64(value, num);
}
case JSON_TOK_OPAQUE:
case JSON_TOK_FLOAT: {
struct json_obj_token *obj_token = field;
Expand All @@ -537,6 +571,8 @@ static ptrdiff_t get_elem_size(const struct json_obj_descr *descr)
switch (descr->type) {
case JSON_TOK_NUMBER:
return sizeof(int32_t);
case JSON_TOK_INT64:
return sizeof(int64_t);
case JSON_TOK_OPAQUE:
case JSON_TOK_FLOAT:
case JSON_TOK_OBJ_ARRAY:
Expand Down Expand Up @@ -977,6 +1013,23 @@ static int num_encode(const int32_t *num, json_append_bytes_t append_bytes,
return append_bytes(buf, (size_t)ret, data);
}

static int int64_encode(const int64_t *num, json_append_bytes_t append_bytes,
void *data)
{
char buf[sizeof("-9223372036854775808")];
int ret;

ret = snprintk(buf, sizeof(buf), "%" PRId64, *num);
if (ret < 0) {
return ret;
}
if (ret >= (int)sizeof(buf)) {
return -ENOMEM;
}

return append_bytes(buf, (size_t)ret, data);
}

static int float_ascii_encode(struct json_obj_token *num, json_append_bytes_t append_bytes,
void *data)
{
Expand Down Expand Up @@ -1032,6 +1085,8 @@ static int encode(const struct json_obj_descr *descr, const void *val,
ptr, append_bytes, data);
case JSON_TOK_NUMBER:
return num_encode(ptr, append_bytes, data);
case JSON_TOK_INT64:
return int64_encode(ptr, append_bytes, data);
case JSON_TOK_FLOAT:
return float_ascii_encode(ptr, append_bytes, data);
case JSON_TOK_OPAQUE:
Expand Down
2 changes: 1 addition & 1 deletion tests/lib/json/prj.conf
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
CONFIG_JSON_LIBRARY=y
CONFIG_ZTEST=y
CONFIG_ZTEST_STACK_SIZE=2048
CONFIG_ZTEST_STACK_SIZE=3072
55 changes: 48 additions & 7 deletions tests/lib/json/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ struct test_nested {
int nested_int;
bool nested_bool;
const char *nested_string;
int64_t nested_int64;
};

struct test_struct {
const char *some_string;
int some_int;
bool some_bool;
int64_t some_int64;
int64_t another_int64;
struct test_nested some_nested_struct;
int some_array[16];
size_t some_array_len;
Expand All @@ -45,19 +48,28 @@ struct test_int_limits {
int int_max;
int int_cero;
int int_min;
int64_t int64_max;
int64_t int64_cero;
int64_t int64_min;
};

static const struct json_obj_descr nested_descr[] = {
JSON_OBJ_DESCR_PRIM(struct test_nested, nested_int, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_nested, nested_bool, JSON_TOK_TRUE),
JSON_OBJ_DESCR_PRIM(struct test_nested, nested_string,
JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct test_nested, nested_int64,
JSON_TOK_INT64),
};

static const struct json_obj_descr test_descr[] = {
JSON_OBJ_DESCR_PRIM(struct test_struct, some_string, JSON_TOK_STRING),
JSON_OBJ_DESCR_PRIM(struct test_struct, some_int, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_struct, some_bool, JSON_TOK_TRUE),
JSON_OBJ_DESCR_PRIM(struct test_struct, some_int64,
JSON_TOK_INT64),
JSON_OBJ_DESCR_PRIM(struct test_struct, another_int64,
JSON_TOK_INT64),
JSON_OBJ_DESCR_OBJECT(struct test_struct, some_nested_struct,
nested_descr),
JSON_OBJ_DESCR_ARRAY(struct test_struct, some_array,
Expand Down Expand Up @@ -89,6 +101,9 @@ static const struct json_obj_descr obj_limits_descr[] = {
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int_max, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int_cero, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int_min, JSON_TOK_NUMBER),
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int64_max, JSON_TOK_INT64),
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int64_cero, JSON_TOK_INT64),
JSON_OBJ_DESCR_PRIM(struct test_int_limits, int64_min, JSON_TOK_INT64),
};

struct array {
Expand Down Expand Up @@ -148,11 +163,14 @@ ZTEST(lib_json_test, test_json_encoding)
struct test_struct ts = {
.some_string = "zephyr 123\uABCD",
.some_int = 42,
.some_int64 = 1152921504606846977,
.another_int64 = -2305843009213693937,
.some_bool = true,
.some_nested_struct = {
.nested_int = -1234,
.nested_bool = false,
.nested_string = "this should be escaped: \t"
.nested_string = "this should be escaped: \t",
.nested_int64 = 4503599627370496,
},
.some_array[0] = 1,
.some_array[1] = 4,
Expand All @@ -171,6 +189,7 @@ ZTEST(lib_json_test, test_json_encoding)
.nested_int = 1234,
.nested_bool = true,
.nested_string = "no escape necessary",
.nested_int64 = 4503599627370496,
},
.nested_obj_array = {
{1, true, "true"},
Expand All @@ -180,19 +199,23 @@ ZTEST(lib_json_test, test_json_encoding)
};
char encoded[] = "{\"some_string\":\"zephyr 123\uABCD\","
"\"some_int\":42,\"some_bool\":true,"
"\"some_int64\":1152921504606846977,"
"\"another_int64\":-2305843009213693937,"
"\"some_nested_struct\":{\"nested_int\":-1234,"
"\"nested_bool\":false,\"nested_string\":"
"\"this should be escaped: \\t\"},"
"\"this should be escaped: \\t\","
"\"nested_int64\":4503599627370496},"
"\"some_array\":[1,4,8,16,32],"
"\"another_b!@l\":true,"
"\"if\":false,"
"\"another-array\":[2,3,5,7],"
"\"4nother_ne$+\":{\"nested_int\":1234,"
"\"nested_bool\":true,"
"\"nested_string\":\"no escape necessary\"},"
"\"nested_string\":\"no escape necessary\","
"\"nested_int64\":4503599627370496},"
"\"nested_obj_array\":["
"{\"nested_int\":1,\"nested_bool\":true,\"nested_string\":\"true\"},"
"{\"nested_int\":0,\"nested_bool\":false,\"nested_string\":\"false\"}]"
"{\"nested_int\":1,\"nested_bool\":true,\"nested_string\":\"true\",\"nested_int64\":0},"
"{\"nested_int\":0,\"nested_bool\":false,\"nested_string\":\"false\",\"nested_int64\":0}]"
"}";
char buffer[sizeof(encoded)];
int ret;
Expand All @@ -217,10 +240,13 @@ ZTEST(lib_json_test, test_json_decoding)
"\"some_bool\":true \t "
"\n"
"\r ,"
"\"some_int64\":-4611686018427387904,"
"\"another_int64\":-2147483648,"
"\"some_nested_struct\":{ "
"\"nested_int\":-1234,\n\n"
"\"nested_bool\":false,\t"
"\"nested_string\":\"this should be escaped: \\t\","
"\"nested_int64\":9223372036854775807,"
"\"extra_nested_array\":[0,-1]},"
"\"extra_struct\":{\"nested_bool\":false},"
"\"extra_bool\":true,"
Expand All @@ -230,7 +256,8 @@ ZTEST(lib_json_test, test_json_decoding)
"\"another-array\":[2,3,5,7],"
"\"4nother_ne$+\":{\"nested_int\":1234,"
"\"nested_bool\":true,"
"\"nested_string\":\"no escape necessary\"},"
"\"nested_string\":\"no escape necessary\","
"\"nested_int64\":-9223372036854775806},"
"\"nested_obj_array\":["
"{\"nested_int\":1,\"nested_bool\":true,\"nested_string\":\"true\"},"
"{\"nested_int\":0,\"nested_bool\":false,\"nested_string\":\"false\"}]"
Expand All @@ -249,8 +276,14 @@ ZTEST(lib_json_test, test_json_decoding)
"String not decoded correctly");
zassert_equal(ts.some_int, 42, "Positive integer not decoded correctly");
zassert_equal(ts.some_bool, true, "Boolean not decoded correctly");
zassert_equal(ts.some_int64, -4611686018427387904,
"int64 not decoded correctly");
zassert_equal(ts.another_int64, -2147483648,
"int64 not decoded correctly");
zassert_equal(ts.some_nested_struct.nested_int, -1234,
"Nested negative integer not decoded correctly");
zassert_equal(ts.some_nested_struct.nested_int64, 9223372036854775807,
"Nested int64 not decoded correctly");
zassert_equal(ts.some_nested_struct.nested_bool, false,
"Nested boolean value not decoded correctly");
zassert_true(!strcmp(ts.some_nested_struct.nested_string,
Expand All @@ -272,6 +305,8 @@ ZTEST(lib_json_test, test_json_decoding)
"Decoded named array not with expected values");
zassert_equal(ts.xnother_nexx.nested_int, 1234,
"Named nested integer not decoded correctly");
zassert_equal(ts.xnother_nexx.nested_int64, -9223372036854775806,
"Named nested int64 not decoded correctly");
zassert_equal(ts.xnother_nexx.nested_bool, true,
"Named nested boolean not decoded correctly");
zassert_true(!strcmp(ts.xnother_nexx.nested_string,
Expand All @@ -298,13 +333,19 @@ ZTEST(lib_json_test, test_json_limits)
int ret = 0;
char encoded[] = "{\"int_max\":2147483647,"
"\"int_cero\":0,"
"\"int_min\":-2147483648"
"\"int_min\":-2147483648,"
"\"int64_max\":9223372036854775807,"
"\"int64_cero\":0,"
"\"int64_min\":-9223372036854775808"
"}";

struct test_int_limits limits = {
.int_max = INT_MAX,
.int_cero = 0,
.int_min = INT_MIN,
.int64_max = INT64_MAX,
.int64_cero = 0,
.int64_min = INT64_MIN,
};

char buffer[sizeof(encoded)];
Expand Down

0 comments on commit 1e3ab46

Please sign in to comment.