From 03029e7bd7077834bb9c6b61bad19497b3f5558f Mon Sep 17 00:00:00 2001 From: Mykhailo Lohvynenko Date: Wed, 2 Oct 2024 14:48:31 +0300 Subject: [PATCH] json: support parsing and serializing 'uint64_t' Introduce support for 'uint64_t' type, so that unsigned numbers can be serialized into JSON payloads. Signed-off-by: Mykhailo Lohvynenko --- include/zephyr/data/json.h | 1 + lib/utils/json.c | 55 ++++++++++++++++++++++++++++++++++++++ tests/lib/json/src/main.c | 41 ++++++++++++++++++++++++---- 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/include/zephyr/data/json.h b/include/zephyr/data/json.h index a93eb427374..2e8182fd088 100644 --- a/include/zephyr/data/json.h +++ b/include/zephyr/data/json.h @@ -43,6 +43,7 @@ enum json_tokens { JSON_TOK_OBJ_ARRAY = '3', JSON_TOK_ENCODED_OBJ = '4', JSON_TOK_INT64 = '5', + JSON_TOK_UINT64 = '6', 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 c1170fcfec9..6aa5f27646e 100644 --- a/lib/utils/json.c +++ b/lib/utils/json.c @@ -309,6 +309,7 @@ static int element_token(enum json_tokens token) case JSON_TOK_STRING: case JSON_TOK_NUMBER: case JSON_TOK_INT64: + case JSON_TOK_UINT64: case JSON_TOK_FLOAT: case JSON_TOK_OPAQUE: case JSON_TOK_OBJ_ARRAY: @@ -469,6 +470,30 @@ static int decode_int64(const struct json_token *token, int64_t *num) return 0; } +static int decode_uint64(const struct json_token *token, uint64_t *num) +{ + char *endptr; + char prev_end; + + prev_end = *token->end; + *token->end = '\0'; + + errno = 0; + *num = strtoull(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) { @@ -483,6 +508,10 @@ static bool equivalent_types(enum json_tokens type1, enum json_tokens type2) return true; } + if (type1 == JSON_TOK_NUMBER && type2 == JSON_TOK_UINT64) { + return true; + } + if (type1 == JSON_TOK_STRING && type2 == JSON_TOK_OPAQUE) { return true; } @@ -545,6 +574,11 @@ static int64_t decode_value(struct json_obj *obj, return decode_int64(value, num); } + case JSON_TOK_UINT64: { + uint64_t *num = field; + + return decode_uint64(value, num); + } case JSON_TOK_OPAQUE: case JSON_TOK_FLOAT: { struct json_obj_token *obj_token = field; @@ -573,6 +607,8 @@ static ptrdiff_t get_elem_size(const struct json_obj_descr *descr) return sizeof(int32_t); case JSON_TOK_INT64: return sizeof(int64_t); + case JSON_TOK_UINT64: + return sizeof(uint64_t); case JSON_TOK_OPAQUE: case JSON_TOK_FLOAT: case JSON_TOK_OBJ_ARRAY: @@ -1035,6 +1071,23 @@ static int int64_encode(const int64_t *num, json_append_bytes_t append_bytes, return append_bytes(buf, (size_t)ret, data); } +static int uint64_encode(const uint64_t *num, json_append_bytes_t append_bytes, + void *data) +{ + char buf[sizeof("18446744073709551610")]; + int ret; + + ret = snprintk(buf, sizeof(buf), "%" PRIu64, *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) { @@ -1099,6 +1152,8 @@ static int encode(const struct json_obj_descr *descr, const void *val, return num_encode(ptr, append_bytes, data); case JSON_TOK_INT64: return int64_encode(ptr, append_bytes, data); + case JSON_TOK_UINT64: + return uint64_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/src/main.c b/tests/lib/json/src/main.c index ae142f85d19..baf2e0edd84 100644 --- a/tests/lib/json/src/main.c +++ b/tests/lib/json/src/main.c @@ -14,6 +14,7 @@ struct test_nested { bool nested_bool; const char *nested_string; int64_t nested_int64; + uint64_t nested_uint64; }; struct test_struct { @@ -22,6 +23,8 @@ struct test_struct { bool some_bool; int64_t some_int64; int64_t another_int64; + int64_t some_uint64; + int64_t another_uint64; struct test_nested some_nested_struct; int some_array[16]; size_t some_array_len; @@ -51,6 +54,9 @@ struct test_int_limits { int64_t int64_max; int64_t int64_cero; int64_t int64_min; + uint64_t uint64_max; + uint64_t uint64_cero; + uint64_t uint64_min; }; static const struct json_obj_descr nested_descr[] = { @@ -60,6 +66,8 @@ static const struct json_obj_descr nested_descr[] = { JSON_TOK_STRING), JSON_OBJ_DESCR_PRIM(struct test_nested, nested_int64, JSON_TOK_INT64), + JSON_OBJ_DESCR_PRIM(struct test_nested, nested_uint64, + JSON_TOK_UINT64), }; static const struct json_obj_descr test_descr[] = { @@ -70,6 +78,10 @@ static const struct json_obj_descr test_descr[] = { JSON_TOK_INT64), JSON_OBJ_DESCR_PRIM(struct test_struct, another_int64, JSON_TOK_INT64), + JSON_OBJ_DESCR_PRIM(struct test_struct, some_uint64, + JSON_TOK_UINT64), + JSON_OBJ_DESCR_PRIM(struct test_struct, another_uint64, + JSON_TOK_UINT64), JSON_OBJ_DESCR_OBJECT(struct test_struct, some_nested_struct, nested_descr), JSON_OBJ_DESCR_ARRAY(struct test_struct, some_array, @@ -104,6 +116,9 @@ static const struct json_obj_descr obj_limits_descr[] = { 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), + JSON_OBJ_DESCR_PRIM(struct test_int_limits, uint64_max, JSON_TOK_UINT64), + JSON_OBJ_DESCR_PRIM(struct test_int_limits, uint64_cero, JSON_TOK_UINT64), + JSON_OBJ_DESCR_PRIM(struct test_int_limits, uint64_min, JSON_TOK_UINT64), }; struct array { @@ -197,12 +212,15 @@ ZTEST(lib_json_test, test_json_encoding) .some_int = 42, .some_int64 = 1152921504606846977, .another_int64 = -2305843009213693937, + .some_uint64 = 18446744073709551615U, + .another_uint64 = 0, .some_bool = true, .some_nested_struct = { .nested_int = -1234, .nested_bool = false, .nested_string = "this should be escaped: \t", .nested_int64 = 4503599627370496, + .nested_uint64 = 18446744073709551610U, }, .some_array[0] = 1, .some_array[1] = 4, @@ -222,6 +240,7 @@ ZTEST(lib_json_test, test_json_encoding) .nested_bool = true, .nested_string = "no escape necessary", .nested_int64 = 4503599627370496, + .nested_uint64 = 18446744073709551610U, }, .nested_obj_array = { {1, true, "true"}, @@ -233,10 +252,13 @@ ZTEST(lib_json_test, test_json_encoding) "\"some_int\":42,\"some_bool\":true," "\"some_int64\":1152921504606846977," "\"another_int64\":-2305843009213693937," + "\"some_uint64\":18446744073709551615," + "\"another_uint64\":0," "\"some_nested_struct\":{\"nested_int\":-1234," "\"nested_bool\":false,\"nested_string\":" "\"this should be escaped: \\t\"," - "\"nested_int64\":4503599627370496}," + "\"nested_int64\":4503599627370496," + "\"nested_uint64\":18446744073709551610}," "\"some_array\":[1,4,8,16,32]," "\"another_b!@l\":true," "\"if\":false," @@ -244,10 +266,11 @@ ZTEST(lib_json_test, test_json_encoding) "\"4nother_ne$+\":{\"nested_int\":1234," "\"nested_bool\":true," "\"nested_string\":\"no escape necessary\"," - "\"nested_int64\":4503599627370496}," + "\"nested_int64\":4503599627370496," + "\"nested_uint64\":18446744073709551610}," "\"nested_obj_array\":[" - "{\"nested_int\":1,\"nested_bool\":true,\"nested_string\":\"true\",\"nested_int64\":0}," - "{\"nested_int\":0,\"nested_bool\":false,\"nested_string\":\"false\",\"nested_int64\":0}]" + "{\"nested_int\":1,\"nested_bool\":true,\"nested_string\":\"true\",\"nested_int64\":0,\"nested_uint64\":0}," + "{\"nested_int\":0,\"nested_bool\":false,\"nested_string\":\"false\",\"nested_int64\":0,\"nested_uint64\":0}]" "}"; char buffer[sizeof(encoded)]; int ret; @@ -274,6 +297,8 @@ ZTEST(lib_json_test, test_json_decoding) "\r ," "\"some_int64\":-4611686018427387904," "\"another_int64\":-2147483648," + "\"some_uint64\":18446744073709551615," + "\"another_uint64\":0," "\"some_nested_struct\":{ " "\"nested_int\":-1234,\n\n" "\"nested_bool\":false,\t" @@ -368,7 +393,10 @@ ZTEST(lib_json_test, test_json_limits) "\"int_min\":-2147483648," "\"int64_max\":9223372036854775807," "\"int64_cero\":0," - "\"int64_min\":-9223372036854775808" + "\"int64_min\":-9223372036854775808," + "\"uint64_max\":18446744073709551615," + "\"uint64_cero\":0," + "\"uint64_min\":0" "}"; struct test_int_limits limits = { @@ -378,6 +406,9 @@ ZTEST(lib_json_test, test_json_limits) .int64_max = INT64_MAX, .int64_cero = 0, .int64_min = INT64_MIN, + .uint64_max = UINT64_MAX, + .uint64_cero = 0, + .uint64_min = 0, }; char buffer[sizeof(encoded)];