From 1e3ab46e41a612c65bc238d10d1db9a7ddba0b38 Mon Sep 17 00:00:00 2001 From: Marcin Niestroj Date: Tue, 4 Jun 2024 18:31:13 +0200 Subject: [PATCH] json: support parsing and serializing 'int64_t' 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 (cherry picked from commit 878640fa0817a5c6684758d68dcb824e24571612) --- include/zephyr/data/json.h | 2 ++ lib/utils/json.c | 55 ++++++++++++++++++++++++++++++++++++++ tests/lib/json/prj.conf | 2 +- tests/lib/json/src/main.c | 55 +++++++++++++++++++++++++++++++++----- 4 files changed, 106 insertions(+), 8 deletions(-) diff --git a/include/zephyr/data/json.h b/include/zephyr/data/json.h index b8b386d968ac013..8690fb33b117659 100644 --- a/include/zephyr/data/json.h +++ b/include/zephyr/data/json.h @@ -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', diff --git a/lib/utils/json.c b/lib/utils/json.c index 2041db757e97f01..22d9e84103af71b 100644 --- a/lib/utils/json.c +++ b/lib/utils/json.c @@ -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: @@ -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) { @@ -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; } @@ -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; @@ -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: @@ -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) { @@ -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: diff --git a/tests/lib/json/prj.conf b/tests/lib/json/prj.conf index 700ea224bba2f70..4a6c8522abd68dd 100644 --- a/tests/lib/json/prj.conf +++ b/tests/lib/json/prj.conf @@ -1,3 +1,3 @@ CONFIG_JSON_LIBRARY=y CONFIG_ZTEST=y -CONFIG_ZTEST_STACK_SIZE=2048 +CONFIG_ZTEST_STACK_SIZE=3072 diff --git a/tests/lib/json/src/main.c b/tests/lib/json/src/main.c index b055651ddf239f4..a3ee0bee5f93fac 100644 --- a/tests/lib/json/src/main.c +++ b/tests/lib/json/src/main.c @@ -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; @@ -45,6 +48,9 @@ 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[] = { @@ -52,12 +58,18 @@ static const struct json_obj_descr nested_descr[] = { 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, @@ -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 { @@ -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, @@ -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"}, @@ -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; @@ -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," @@ -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\"}]" @@ -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, @@ -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, @@ -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)];