From: Lennart Poettering Date: Wed, 18 Feb 2026 12:45:22 +0000 (+0100) Subject: json: add json_variant_compare() helper for comparint two json variants by order X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a98fc72e2970a36ec1f0d401b03f65fd8ef86398;p=thirdparty%2Fsystemd.git json: add json_variant_compare() helper for comparint two json variants by order --- diff --git a/src/libsystemd/sd-json/json-util.c b/src/libsystemd/sd-json/json-util.c index 41ac8c40976..7f90b7fc793 100644 --- a/src/libsystemd/sd-json/json-util.c +++ b/src/libsystemd/sd-json/json-util.c @@ -743,3 +743,105 @@ int json_dispatch_access_mode(const char *name, sd_json_variant *variant, sd_jso return 0; } + +int json_variant_compare(sd_json_variant *a, sd_json_variant *b) { + int r; + + r = CMP(!!a, !!b); + if (r != 0) + return r; + + if (sd_json_variant_equal(a, b)) + return 0; + + if (sd_json_variant_is_null(a)) + return -1; + if (sd_json_variant_is_null(b)) + return 1; + + if (sd_json_variant_is_string(a) && + sd_json_variant_is_string(b)) + return strcmp(sd_json_variant_string(a), sd_json_variant_string(b)); + + if (sd_json_variant_is_integer(a) && + sd_json_variant_is_integer(b)) + return CMP(sd_json_variant_integer(a), sd_json_variant_integer(b)); + + if (sd_json_variant_is_unsigned(a) && + sd_json_variant_is_unsigned(b)) + return CMP(sd_json_variant_unsigned(a), sd_json_variant_unsigned(b)); + + /* We cannot necessarily compare 64bit signed with unsigned, hence we go via sign checking instead */ + if (sd_json_variant_is_number(a) && sd_json_variant_is_number(b)) { + if (sd_json_variant_is_negative(a) && + !sd_json_variant_is_negative(b)) + return -1; + + if (!sd_json_variant_is_negative(a) && + sd_json_variant_is_negative(b)) + return 1; + } + + if (sd_json_variant_is_real(a) && + sd_json_variant_is_real(b)) + return CMP(sd_json_variant_real(a), sd_json_variant_real(b)); + + if (sd_json_variant_is_boolean(a) && + sd_json_variant_is_boolean(b)) + return CMP(sd_json_variant_boolean(a), sd_json_variant_boolean(b)); + + if (sd_json_variant_is_array(a) && + sd_json_variant_is_array(b)) { + + size_t n = sd_json_variant_elements(a), + m = sd_json_variant_elements(b); + for (size_t i = 0; i < n || i < m; i++) { + + if (i >= n) + return -1; + if (i >= m) + return 1; + + r = json_variant_compare( + sd_json_variant_by_index(a, i), + sd_json_variant_by_index(b, i)); + if (r != 0) + return r; + } + + return 0; + } + + if (sd_json_variant_is_object(a) && + sd_json_variant_is_object(b)) { + const char *k, *lowest = NULL; + sd_json_variant *v; + int result = 0; + + JSON_VARIANT_OBJECT_FOREACH(k, v, a) { + if (lowest && strcmp(k, lowest) >= 0) + continue; + + r = json_variant_compare(v, sd_json_variant_by_key(b, k)); + if (r != 0) { + lowest = k; + result = r; + } + } + + JSON_VARIANT_OBJECT_FOREACH(k, v, b) { + if (lowest && strcmp(k, lowest) >= 0) + continue; + + r = json_variant_compare(v, sd_json_variant_by_key(a, k)); + if (r != 0) { + lowest = k; + result = -r; + } + } + + return result; + } + + return CMP(sd_json_variant_type(a), sd_json_variant_type(b)); +} diff --git a/src/libsystemd/sd-json/json-util.h b/src/libsystemd/sd-json/json-util.h index d06a72aef9a..847725a41e2 100644 --- a/src/libsystemd/sd-json/json-util.h +++ b/src/libsystemd/sd-json/json-util.h @@ -273,3 +273,5 @@ int json_variant_new_fd_info(sd_json_variant **ret, int fd); char *json_underscorify(char *p); char *json_dashify(char *p); + +int json_variant_compare(sd_json_variant *a, sd_json_variant *b); diff --git a/src/test/test-json.c b/src/test/test-json.c index 1f0e07d7d4d..679152fd955 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -1523,4 +1523,69 @@ TEST(access_mode) { &mm), ERANGE); } +static void test_json_variant_compare_one(const char *a, const char *b, int expected) { + int r; + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *aa = NULL; + if (!isempty(a)) + ASSERT_OK(sd_json_parse(a, /* flags= */ 0, &aa, /* reterr_line= */ NULL, /* reterr_column= */ NULL)); + + _cleanup_(sd_json_variant_unrefp) sd_json_variant *bb = NULL; + if (!isempty(b)) + ASSERT_OK(sd_json_parse(b, /* flags= */ 0, &bb, /* reterr_line= */ NULL, /* reterr_column= */ NULL)); + + r = json_variant_compare(aa, bb); + + log_debug("%s vs %s → %i (expected %i)", a, b, r, expected); + + if (expected < 0) + ASSERT_LT(r, 0); + else if (expected > 0) + ASSERT_GT(r, 0); + else + ASSERT_EQ(r, 0); + + r = json_variant_compare(bb, aa); + + if (expected < 0) + ASSERT_GT(r, 0); + else if (expected > 0) + ASSERT_LT(r, 0); + else + ASSERT_EQ(r, 0); +} + +TEST(json_variant_compare) { + test_json_variant_compare_one("null", "\"a\"", -1); + test_json_variant_compare_one(NULL, "\"a\"", -1); + test_json_variant_compare_one("0", "1", -1); + test_json_variant_compare_one("1", "0", 1); + test_json_variant_compare_one("0", "0", 0); + test_json_variant_compare_one("1", "1", 0); + test_json_variant_compare_one("1", "null", 1); + test_json_variant_compare_one("null", "1", -1); + test_json_variant_compare_one("null", "null", 0); + test_json_variant_compare_one("false", "true", -1); + test_json_variant_compare_one("true", "false", 1); + test_json_variant_compare_one("true", "true", 0); + test_json_variant_compare_one("false", "false", 0); + test_json_variant_compare_one("\"a\"", "\"b\"", -1); + test_json_variant_compare_one("\"b\"", "\"a\"", 1); + test_json_variant_compare_one("18446744073709551615", "0", 1); + test_json_variant_compare_one("0", "18446744073709551615", -1); + test_json_variant_compare_one("18446744073709551615", "18446744073709551615", 0); + test_json_variant_compare_one("-9223372036854775808", "18446744073709551615", -1); + test_json_variant_compare_one("18446744073709551615", "-9223372036854775808", 1); + test_json_variant_compare_one("1.1", "3.4", -1); + test_json_variant_compare_one("1", "3.4", -1); + test_json_variant_compare_one("[1,2]", "[1,2]", 0); + test_json_variant_compare_one("[1,2]", "[2,1]", -1); + test_json_variant_compare_one("[1,2]", "[1,2,3]", -1); + test_json_variant_compare_one("{}", "{\"a\":\"b\"}", -1); + test_json_variant_compare_one("{\"a\":\"b\"}", "{\"a\":\"b\"}", 0); + test_json_variant_compare_one("{\"a\":\"b\"}", "{\"b\":\"c\"}", 1); + test_json_variant_compare_one("{\"a\":\"b\",\"b\":\"c\"}", "{\"b\":\"c\",\"a\":\"b\"}", 0); + test_json_variant_compare_one("{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":\"b\"}", 1); +} + DEFINE_TEST_MAIN(LOG_DEBUG);