From c692ec3afdba560398fd9a6871cddcca6d5c9cc5 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Wed, 1 Jun 2016 17:18:32 +0530 Subject: [PATCH] format: Drop dependency on json-c json-c has a symbol clash (json_object_get_type) with json-glib (which at least a number of our GNOME clients use). This patch moves to our own JSON parser so that we can avoid this kind of situation altogether. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=95135 Signed-off-by: Arun Raghavan --- configure.ac | 4 - src/Makefile.am | 8 +- src/pulse/format.c | 226 ++++++++++++++++++++++++-------------------------- src/pulse/json.c | 80 +++++++++++++++--- src/pulse/json.h | 4 + src/tests/json-test.c | 8 +- 6 files changed, 187 insertions(+), 143 deletions(-) diff --git a/configure.ac b/configure.ac index 4edc8e0..7b484a9 100644 --- a/configure.ac +++ b/configure.ac @@ -676,10 +676,6 @@ AS_IF([test "x$enable_tests" = "xyes" && test "x$HAVE_LIBCHECK" = "x0"], AM_CONDITIONAL([HAVE_TESTS], [test "x$HAVE_LIBCHECK" = x1]) -#### json parsing #### - -PKG_CHECK_MODULES(LIBJSON, [ json-c >= 0.11 ]) - #### Sound file #### PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ]) diff --git a/src/Makefile.am b/src/Makefile.am index c6b998c..7b19497 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -739,9 +739,9 @@ else libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += pulsecore/poll-posix.c pulsecore/poll.h endif -libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS) $(LIBSNDFILE_CFLAGS) +libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS) libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version -libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBJSON_LIBS) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSNDFILE_LIBS) +libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSNDFILE_LIBS) if HAVE_MEMFD libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \ @@ -893,8 +893,8 @@ libpulse_la_SOURCES = \ pulse/volume.c pulse/volume.h \ pulse/xmalloc.c pulse/xmalloc.h -libpulse_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS) -libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la +libpulse_la_CFLAGS = $(AM_CFLAGS) +libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINOR@.la libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO) if HAVE_DBUS diff --git a/src/pulse/format.c b/src/pulse/format.c index c2a1552..ee8b7ac 100644 --- a/src/pulse/format.c +++ b/src/pulse/format.c @@ -23,8 +23,7 @@ #include #endif -#include - +#include #include #include @@ -32,6 +31,7 @@ #include #include #include +#include #include "format.h" @@ -236,7 +236,8 @@ int pa_format_info_to_sample_spec(const pa_format_info *f, pa_sample_spec *ss, p pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char *key) { const char *str; - json_object *o, *o1; + pa_json_object *o; + const pa_json_object *o1; pa_prop_type_t type; pa_assert(f); @@ -246,47 +247,47 @@ pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char if (!str) return PA_PROP_TYPE_INVALID; - o = json_tokener_parse(str); + o = pa_json_parse(str); if (!o) return PA_PROP_TYPE_INVALID; - switch (json_object_get_type(o)) { - case json_type_int: + switch (pa_json_object_get_type(o)) { + case PA_JSON_TYPE_INT: type = PA_PROP_TYPE_INT; break; - case json_type_string: + case PA_JSON_TYPE_STRING: type = PA_PROP_TYPE_STRING; break; - case json_type_array: - if (json_object_array_length(o) == 0) { + case PA_JSON_TYPE_ARRAY: + if (pa_json_object_get_array_length(o) == 0) { /* Unlikely, but let's account for this anyway. We need at * least one element to figure out the array type. */ type = PA_PROP_TYPE_INVALID; break; } - o1 = json_object_array_get_idx(o, 1); + o1 = pa_json_object_get_array_member(o, 0); - if (json_object_get_type(o1) == json_type_int) + if (pa_json_object_get_type(o1) == PA_JSON_TYPE_INT) type = PA_PROP_TYPE_INT_ARRAY; - else if (json_object_get_type(o1) == json_type_string) + else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_STRING) type = PA_PROP_TYPE_STRING_ARRAY; else type = PA_PROP_TYPE_INVALID; break; - case json_type_object: + case PA_JSON_TYPE_OBJECT: /* We actually know at this point that it's a int range, but let's * confirm. */ - if (!json_object_object_get_ex(o, PA_JSON_MIN_KEY, NULL)) { + if (!pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) { type = PA_PROP_TYPE_INVALID; break; } - if (!json_object_object_get_ex(o, PA_JSON_MAX_KEY, NULL)) { + if (!pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) { type = PA_PROP_TYPE_INVALID; break; } @@ -299,13 +300,13 @@ pa_prop_type_t pa_format_info_get_prop_type(const pa_format_info *f, const char break; } - json_object_put(o); + pa_json_object_unref(o); return type; } int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v) { const char *str; - json_object *o; + pa_json_object *o; pa_assert(f); pa_assert(key); @@ -315,27 +316,28 @@ int pa_format_info_get_prop_int(const pa_format_info *f, const char *key, int *v if (!str) return -PA_ERR_NOENTITY; - o = json_tokener_parse(str); + o = pa_json_parse(str); if (!o) { pa_log_debug("Failed to parse format info property '%s'.", key); return -PA_ERR_INVALID; } - if (json_object_get_type(o) != json_type_int) { + if (pa_json_object_get_type(o) != PA_JSON_TYPE_INT) { pa_log_debug("Format info property '%s' type is not int.", key); - json_object_put(o); + pa_json_object_unref(o); return -PA_ERR_INVALID; } - *v = json_object_get_int(o); - json_object_put(o); + *v = pa_json_object_get_int(o); + pa_json_object_unref(o); return 0; } int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, int *min, int *max) { const char *str; - json_object *o, *o1; + pa_json_object *o; + const pa_json_object *o1; int ret = -PA_ERR_INVALID; pa_assert(f); @@ -347,24 +349,26 @@ int pa_format_info_get_prop_int_range(const pa_format_info *f, const char *key, if (!str) return -PA_ERR_NOENTITY; - o = json_tokener_parse(str); + o = pa_json_parse(str); if (!o) { pa_log_debug("Failed to parse format info property '%s'.", key); return -PA_ERR_INVALID; } - if (json_object_get_type(o) != json_type_object) + if (pa_json_object_get_type(o) != PA_JSON_TYPE_OBJECT) goto out; - if (!json_object_object_get_ex(o, PA_JSON_MIN_KEY, &o1)) + if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MIN_KEY)) || + (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT)) goto out; - *min = json_object_get_int(o1); + *min = pa_json_object_get_int(o1); - if (!json_object_object_get_ex(o, PA_JSON_MAX_KEY, &o1)) + if (!(o1 = pa_json_object_get_object_member(o, PA_JSON_MAX_KEY)) || + (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT)) goto out; - *max = json_object_get_int(o1); + *max = pa_json_object_get_int(o1); ret = 0; @@ -372,13 +376,14 @@ out: if (ret < 0) pa_log_debug("Format info property '%s' is not a valid int range.", key); - json_object_put(o); + pa_json_object_unref(o); return ret; } int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, int **values, int *n_values) { const char *str; - json_object *o, *o1; + pa_json_object *o; + const pa_json_object *o1; int i, ret = -PA_ERR_INVALID; pa_assert(f); @@ -390,26 +395,26 @@ int pa_format_info_get_prop_int_array(const pa_format_info *f, const char *key, if (!str) return -PA_ERR_NOENTITY; - o = json_tokener_parse(str); + o = pa_json_parse(str); if (!o) { pa_log_debug("Failed to parse format info property '%s'.", key); return -PA_ERR_INVALID; } - if (json_object_get_type(o) != json_type_array) + if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) goto out; - *n_values = json_object_array_length(o); + *n_values = pa_json_object_get_array_length(o); *values = pa_xnew(int, *n_values); for (i = 0; i < *n_values; i++) { - o1 = json_object_array_get_idx(o, i); + o1 = pa_json_object_get_array_member(o, i); - if (json_object_get_type(o1) != json_type_int) { + if (pa_json_object_get_type(o1) != PA_JSON_TYPE_INT) { goto out; } - (*values)[i] = json_object_get_int(o1); + (*values)[i] = pa_json_object_get_int(o1); } ret = 0; @@ -418,13 +423,13 @@ out: if (ret < 0) pa_log_debug("Format info property '%s' is not a valid int array.", key); - json_object_put(o); + pa_json_object_unref(o); return ret; } int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, char **v) { const char *str = NULL; - json_object *o; + pa_json_object *o; pa_assert(f); pa_assert(key); @@ -434,27 +439,28 @@ int pa_format_info_get_prop_string(const pa_format_info *f, const char *key, cha if (!str) return -PA_ERR_NOENTITY; - o = json_tokener_parse(str); + o = pa_json_parse(str); if (!o) { pa_log_debug("Failed to parse format info property '%s'.", key); return -PA_ERR_INVALID; } - if (json_object_get_type(o) != json_type_string) { + if (pa_json_object_get_type(o) != PA_JSON_TYPE_STRING) { pa_log_debug("Format info property '%s' type is not string.", key); - json_object_put(o); + pa_json_object_unref(o); return -PA_ERR_INVALID; } - *v = pa_xstrdup(json_object_get_string(o)); - json_object_put(o); + *v = pa_xstrdup(pa_json_object_get_string(o)); + pa_json_object_unref(o); return 0; } int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *key, char ***values, int *n_values) { const char *str; - json_object *o, *o1; + pa_json_object *o; + const pa_json_object *o1; int i, ret = -PA_ERR_INVALID; pa_assert(f); @@ -466,26 +472,26 @@ int pa_format_info_get_prop_string_array(const pa_format_info *f, const char *ke if (!str) return -PA_ERR_NOENTITY; - o = json_tokener_parse(str); + o = pa_json_parse(str); if (!o) { pa_log_debug("Failed to parse format info property '%s'.", key); return -PA_ERR_INVALID; } - if (json_object_get_type(o) != json_type_array) + if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) goto out; - *n_values = json_object_array_length(o); + *n_values = pa_json_object_get_array_length(o); *values = pa_xnew(char *, *n_values); for (i = 0; i < *n_values; i++) { - o1 = json_object_array_get_idx(o, i); + o1 = pa_json_object_get_array_member(o, i); - if (json_object_get_type(o1) != json_type_string) { + if (pa_json_object_get_type(o1) != PA_JSON_TYPE_STRING) { goto out; } - (*values)[i] = pa_xstrdup(json_object_get_string(o1)); + (*values)[i] = pa_xstrdup(pa_json_object_get_string(o1)); } ret = 0; @@ -494,7 +500,7 @@ out: if (ret < 0) pa_log_debug("Format info property '%s' is not a valid string array.", key); - json_object_put(o); + pa_json_object_unref(o); return ret; } @@ -528,85 +534,76 @@ void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map } void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) { - json_object *o; - pa_assert(f); pa_assert(key); - o = json_object_new_int(value); - - pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); - - json_object_put(o); + pa_proplist_setf(f->plist, key, "%d", value); } void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) { - json_object *o; + pa_strbuf *buf; + char *str; int i; pa_assert(f); pa_assert(key); + pa_assert(n_values > 0); - o = json_object_new_array(); + buf = pa_strbuf_new(); - for (i = 0; i < n_values; i++) - json_object_array_add(o, json_object_new_int(values[i])); + pa_strbuf_printf(buf, "[ %d", values[0]); - pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); + for (i = 1; i < n_values; i++) + pa_strbuf_printf(buf, ", %d", values[i]); - json_object_put(o); + pa_strbuf_printf(buf, " ]"); + str = pa_strbuf_to_string_free(buf); + + pa_proplist_sets(f->plist, key, str); + pa_xfree (str); } void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) { - json_object *o; - pa_assert(f); pa_assert(key); - o = json_object_new_object(); - - json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min)); - json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max)); - - pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); - - json_object_put(o); + pa_proplist_setf(f->plist, key, "{ \"" PA_JSON_MIN_KEY "\": %d, \"" PA_JSON_MAX_KEY "\": %d }", + min, max); } void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) { - json_object *o; - pa_assert(f); pa_assert(key); - o = json_object_new_string(value); - - pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); - - json_object_put(o); + pa_proplist_setf(f->plist, key, "\"%s\"", value); } void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) { - json_object *o; + pa_strbuf *buf; + char *str; int i; pa_assert(f); pa_assert(key); - o = json_object_new_array(); + buf = pa_strbuf_new(); - for (i = 0; i < n_values; i++) - json_object_array_add(o, json_object_new_string(values[i])); + pa_strbuf_printf(buf, "[ \"%s\"", values[0]); + + for (i = 1; i < n_values; i++) + pa_strbuf_printf(buf, ", \"%s\"", values[i]); - pa_proplist_sets(f->plist, key, json_object_to_json_string(o)); + pa_strbuf_printf(buf, " ]"); + str = pa_strbuf_to_string_free(buf); - json_object_put(o); + pa_proplist_sets(f->plist, key, str); + pa_xfree (str); } -static bool pa_json_is_fixed_type(json_object *o) { - switch(json_object_get_type(o)) { - case json_type_object: - case json_type_array: +static bool pa_json_is_fixed_type(pa_json_object *o) { + switch(pa_json_object_get_type(o)) { + case PA_JSON_TYPE_OBJECT: + case PA_JSON_TYPE_ARRAY: return false; default: @@ -614,20 +611,15 @@ static bool pa_json_is_fixed_type(json_object *o) { } } -static int pa_json_value_equal(json_object *o1, json_object *o2) { - return (json_object_get_type(o1) == json_object_get_type(o2)) && - pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2)); -} - static int pa_format_info_prop_compatible(const char *one, const char *two) { - json_object *o1 = NULL, *o2 = NULL; + pa_json_object *o1 = NULL, *o2 = NULL; int i, ret = 0; - o1 = json_tokener_parse(one); + o1 = pa_json_parse(one); if (!o1) goto out; - o2 = json_tokener_parse(two); + o2 = pa_json_parse(two); if (!o2) goto out; @@ -635,46 +627,46 @@ static int pa_format_info_prop_compatible(const char *one, const char *two) { pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), false); if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) { - ret = pa_json_value_equal(o1, o2); + ret = pa_json_object_equal(o1, o2); goto out; } if (pa_json_is_fixed_type(o1)) { - json_object *tmp = o2; + pa_json_object *tmp = o2; o2 = o1; o1 = tmp; } /* o2 is now a fixed type, and o1 is not */ - if (json_object_get_type(o1) == json_type_array) { - for (i = 0; i < json_object_array_length(o1); i++) { - if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) { + if (pa_json_object_get_type(o1) == PA_JSON_TYPE_ARRAY) { + for (i = 0; i < pa_json_object_get_array_length(o1); i++) { + if (pa_json_object_equal(pa_json_object_get_array_member(o1, i), o2)) { ret = 1; break; } } - } else if (json_object_get_type(o1) == json_type_object) { + } else if (pa_json_object_get_type(o1) == PA_JSON_TYPE_OBJECT) { /* o1 should be a range type */ int min, max, v; - json_object *o_min = NULL, *o_max = NULL; + const pa_json_object *o_min = NULL, *o_max = NULL; - if (json_object_get_type(o2) != json_type_int) { + if (pa_json_object_get_type(o2) != PA_JSON_TYPE_INT) { /* We don't support non-integer ranges */ goto out; } - if (!json_object_object_get_ex(o1, PA_JSON_MIN_KEY, &o_min) || - json_object_get_type(o_min) != json_type_int) + if (!(o_min = pa_json_object_get_object_member(o1, PA_JSON_MIN_KEY)) || + pa_json_object_get_type(o_min) != PA_JSON_TYPE_INT) goto out; - if (!json_object_object_get_ex(o1, PA_JSON_MAX_KEY, &o_max) || - json_object_get_type(o_max) != json_type_int) + if (!(o_max = pa_json_object_get_object_member(o1, PA_JSON_MAX_KEY)) || + pa_json_object_get_type(o_max) != PA_JSON_TYPE_INT) goto out; - v = json_object_get_int(o2); - min = json_object_get_int(o_min); - max = json_object_get_int(o_max); + v = pa_json_object_get_int(o2); + min = pa_json_object_get_int(o_min); + max = pa_json_object_get_int(o_max); ret = v >= min && v <= max; } else { @@ -683,9 +675,9 @@ static int pa_format_info_prop_compatible(const char *one, const char *two) { out: if (o1) - json_object_put(o1); + pa_json_object_unref(o1); if (o2) - json_object_put(o2); + pa_json_object_unref(o2); return ret; } diff --git a/src/pulse/json.c b/src/pulse/json.c index 7cb33ef..8484bba 100644 --- a/src/pulse/json.c +++ b/src/pulse/json.c @@ -44,8 +44,6 @@ struct pa_json_object { }; }; -#define JSON_OBJECT_TYPE(o) ((o)->type) - static const char* parse_value(const char *str, const char *end, pa_json_object **obj); static pa_json_object* json_object_new(void) { @@ -272,7 +270,7 @@ static const char *parse_object(const char *str, pa_json_object *obj) { str++; /* Consume leading '{' or ',' */ str = parse_value(str, ":", &name); - if (!str || JSON_OBJECT_TYPE(name) != PA_JSON_TYPE_STRING) { + if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) { pa_log("Could not parse key for object"); goto error; } @@ -408,7 +406,7 @@ static const char* parse_value(const char *str, const char *end, pa_json_object } } - if (JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_INIT) { + if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) { /* We didn't actually get any data */ pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end)); goto error; @@ -444,14 +442,14 @@ pa_json_object* pa_json_parse(const char *str) { } pa_json_type pa_json_object_get_type(const pa_json_object *obj) { - return JSON_OBJECT_TYPE(obj); + return obj->type; } void pa_json_object_unref(pa_json_object *obj) { if (PA_REFCNT_DEC(obj) > 0) return; - switch (JSON_OBJECT_TYPE(obj)) { + switch (pa_json_object_get_type(obj)) { case PA_JSON_TYPE_INIT: case PA_JSON_TYPE_INT: case PA_JSON_TYPE_DOUBLE: @@ -479,36 +477,92 @@ void pa_json_object_unref(pa_json_object *obj) { } int pa_json_object_get_int(const pa_json_object *o) { - pa_return_val_if_fail(JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_INT, 0); + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT); return o->int_value; } double pa_json_object_get_double(const pa_json_object *o) { - pa_return_val_if_fail(JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_DOUBLE, 0); + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE); return o->double_value; } bool pa_json_object_get_bool(const pa_json_object *o) { - pa_return_val_if_fail(JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_BOOL, false); + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL); return o->bool_value; } const char* pa_json_object_get_string(const pa_json_object *o) { - pa_return_val_if_fail(JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_STRING, NULL); + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING); return o->string_value; } const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) { - pa_return_val_if_fail(JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_OBJECT, NULL); + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT); return pa_hashmap_get(o->object_values, name); } int pa_json_object_get_array_length(const pa_json_object *o) { - pa_return_val_if_fail(JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_ARRAY, 0); + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY); return pa_idxset_size(o->array_values); } const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) { - pa_return_val_if_fail(JSON_OBJECT_TYPE(o) == PA_JSON_TYPE_ARRAY, NULL); + pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY); return pa_idxset_get_by_index(o->array_values, index); } + +bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) { + int i; + + if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2)) + return false; + + switch (pa_json_object_get_type(o1)) { + case PA_JSON_TYPE_NULL: + return true; + + case PA_JSON_TYPE_BOOL: + return o1->bool_value == o2->bool_value; + + case PA_JSON_TYPE_INT: + return o1->int_value == o2->int_value; + + case PA_JSON_TYPE_DOUBLE: + return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value); + + case PA_JSON_TYPE_STRING: + return pa_streq(o1->string_value, o2->string_value); + + case PA_JSON_TYPE_ARRAY: + if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2)) + return false; + + for (i = 0; i < pa_json_object_get_array_length(o1); i++) { + if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i), + pa_json_object_get_array_member(o2, i))) + return false; + } + + return true; + + case PA_JSON_TYPE_OBJECT: { + void *state; + const char *key; + const pa_json_object *v1, *v2; + + if (pa_hashmap_size(o1->object_values) != pa_hashmap_size(o2->object_values)) + return false; + + PA_HASHMAP_FOREACH_KV(key, v1, o1->object_values, state) { + v2 = pa_json_object_get_object_member(o2, key); + if (!v2 || !pa_json_object_equal(v1, v2)) + return false; + } + + return true; + } + + default: + pa_assert_not_reached(); + } +} diff --git a/src/pulse/json.h b/src/pulse/json.h index 99c22ec..d8a946f 100644 --- a/src/pulse/json.h +++ b/src/pulse/json.h @@ -19,6 +19,8 @@ #include +#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001) + typedef enum { PA_JSON_TYPE_INIT = 0, PA_JSON_TYPE_NULL, @@ -47,3 +49,5 @@ const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, int pa_json_object_get_array_length(const pa_json_object *o); const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index); + +bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2); diff --git a/src/tests/json-test.c b/src/tests/json-test.c index e028e68..2e1ca6b 100644 --- a/src/tests/json-test.c +++ b/src/tests/json-test.c @@ -26,8 +26,6 @@ #include #include -#define IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001) - START_TEST (string_test) { pa_json_object *o; unsigned int i; @@ -85,7 +83,7 @@ START_TEST(double_test) { fail_unless(o != NULL); fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE); - fail_unless(IS_EQUAL(pa_json_object_get_double(o), doubles_compare[i])); + fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(o), doubles_compare[i])); pa_json_object_unref(o); } @@ -149,7 +147,7 @@ START_TEST(object_test) { v = pa_json_object_get_object_member(o, "age"); fail_unless(v != NULL); fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE); - fail_unless(IS_EQUAL(pa_json_object_get_double(v), -45.3)); + fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), -45.3)); pa_json_object_unref(o); @@ -205,7 +203,7 @@ START_TEST(array_test) { v = pa_json_object_get_array_member(o, 1); fail_unless(v != NULL); fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE); - fail_unless(IS_EQUAL(pa_json_object_get_double(v), 1234.5)); + fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), 1234.5)); v = pa_json_object_get_array_member(o, 2); fail_unless(v != NULL); fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT); -- 2.7.4