[pulseaudio-commits] [Git][pulseaudio/pulseaudio][master] 5 commits: json: improve supported numbers
PulseAudio Marge Bot
gitlab at gitlab.freedesktop.org
Sat Mar 27 11:21:31 UTC 2021
PulseAudio Marge Bot pushed to branch master at PulseAudio / pulseaudio
Commits:
1df4a311 by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
json: improve supported numbers
Use 64bit signed integers and fix double value conversion.
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>
- - - - -
0ba768b2 by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
json: add JSON encoder
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>
- - - - -
1dd05f4a by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
message-params: use JSON instead of custom format
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>
- - - - -
a2db3fcf by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
message-params: clean up
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>
- - - - -
7ca50bab by Igor V. Kovalenko at 2021-03-27T11:18:22+00:00
bluetooth: update messaging api reference
Part-of: <https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/merge_requests/525>
- - - - -
15 changed files:
- doc/messaging_api.txt
- doxygen/doxygen.conf.in
- src/meson.build
- src/modules/bluetooth/module-bluez5-device.c
- src/pulse/json.c
- src/pulse/json.h
- src/pulse/map-file
- src/pulse/meson.build
- − src/pulse/message-params.c
- − src/pulse/message-params.h
- src/pulsecore/core.c
- src/pulsecore/message-handler.c
- src/pulsecore/message-handler.h
- src/tests/json-test.c
- src/utils/pactl.c
Changes:
=====================================
doc/messaging_api.txt
=====================================
@@ -4,26 +4,34 @@ The message API allows any object within pulseaudio to register a message
handler. A message handler is a function that can be called by clients using
PA_COMMAND_SEND_OBJECT_MESSAGE. A message consists at least of an object path
and a message command, both specified as strings. Additional parameters can
-be specified using a single string, but are not mandatory. The message handler
-returns an error number as defined in def.h and also returns a string in
-the "response" variable. If the string is not empty it consists of elements.
-Curly braces are used to separate elements. Each element can itself contain
-further elements. For example consider a message that returns multiple elements
-which each contain an integer and an array of float. A response string would
-look like that:
-{{Integer} {{1st float} {2nd float} ...}}{...}
-Any characters that are not enclosed in curly braces are ignored (all characters
-between { and {, between } and } and between } and {). The same syntax is used
-to specify message parameters. The reference further down lists available messages,
-their parameters and return values. If a return value is enclosed in {}, this
-means that multiple elements of the same type may be returned.
-
-For string parameters that contain curly braces or backslashes, those characters
-must be escaped by adding a "\" before them.
+be specified using a single string in JSON format, but are not mandatory..
+
+The message handler returns an error number as defined in def.h and also returns
+a string in the "response" variable. Non-empty response will be in JSON format.
+
+The reference further down lists available messages, their parameters
+and return values.
Reference:
Object path: /core
Message: list-handlers
Parameters: None
-Return value: {{{Handler name} {Description}} ...}
+Return value: JSON array of handler description objects
+ [{"name":"Handler name","description":"Description"} ...]
+
+Object path: /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez
+Message: list-codecs
+Parameters: None
+Return value: JSON array of codec description objects
+ [{"name":"codec1","description":"Codec 1"} ...]
+
+Object path: /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez
+Message: get-codec
+Parameters: None
+Return value: "codec name"
+
+Object path: /card/bluez_card.XX_XX_XX_XX_XX_XX/bluez
+Message: switch-codec
+Parameters: "codec name"
+Return value: none
=====================================
doxygen/doxygen.conf.in
=====================================
@@ -683,7 +683,6 @@ INPUT = @top_srcdir@/src/pulse/channelmap.h \
@top_srcdir@/src/pulse/mainloop-api.h \
@top_srcdir@/src/pulse/mainloop-signal.h \
@top_srcdir@/src/pulse/mainloop.h \
- @top_srcdir@/src/pulse/message-params.h \
@top_srcdir@/src/pulse/operation.h \
@top_srcdir@/src/pulse/proplist.h \
@top_srcdir@/src/pulse/pulseaudio.h \
=====================================
src/meson.build
=====================================
@@ -5,7 +5,6 @@ libpulsecommon_sources = [
'pulse/format.c',
'pulse/json.c',
'pulse/mainloop-api.c',
- 'pulse/message-params.c',
'pulse/xmalloc.c',
'pulse/proplist.c',
'pulse/utf8.c',
@@ -76,7 +75,6 @@ libpulsecommon_headers = [
'pulse/format.h',
'pulse/json.h',
'pulse/mainloop-api.h',
- 'pulse/message-params.h',
'pulse/xmalloc.h',
'pulse/proplist.h',
'pulse/utf8.h',
=====================================
src/modules/bluetooth/module-bluez5-device.c
=====================================
@@ -27,11 +27,11 @@
#include <arpa/inet.h>
+#include <pulse/json.h>
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/utf8.h>
#include <pulse/util.h>
-#include <pulse/message-params.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-rtclock.h>
@@ -2458,7 +2458,7 @@ static char *list_codecs(struct userdata *u) {
const pa_a2dp_codec_capabilities *a2dp_capabilities;
const pa_a2dp_codec_id *key;
pa_hashmap *a2dp_endpoints;
- pa_message_params *param;
+ pa_json_encoder *encoder;
unsigned int i;
bool is_a2dp_sink;
void *state;
@@ -2467,9 +2467,9 @@ static char *list_codecs(struct userdata *u) {
a2dp_endpoints = is_a2dp_sink ? u->device->a2dp_sink_endpoints : u->device->a2dp_source_endpoints;
- param = pa_message_params_new();
+ encoder = pa_json_encoder_new();
- pa_message_params_begin_list(param);
+ pa_json_encoder_begin_element_array(encoder);
PA_HASHMAP_FOREACH_KV(key, a2dp_capabilities, a2dp_endpoints, state) {
for (i = 0; i < pa_bluetooth_a2dp_codec_count(); i++) {
@@ -2479,23 +2479,23 @@ static char *list_codecs(struct userdata *u) {
if (memcmp(key, &a2dp_codec->id, sizeof(pa_a2dp_codec_id)) == 0) {
if (a2dp_codec->can_be_supported(is_a2dp_sink)) {
- pa_message_params_begin_list(param);
+ pa_json_encoder_begin_element_object(encoder);
- pa_message_params_write_string(param, a2dp_codec->name);
- pa_message_params_write_string(param, a2dp_codec->description);
+ pa_json_encoder_add_member_string(encoder, "name", a2dp_codec->name);
+ pa_json_encoder_add_member_string(encoder, "description", a2dp_codec->description);
- pa_message_params_end_list(param);
+ pa_json_encoder_end_object(encoder);
}
}
}
}
- pa_message_params_end_list(param);
+ pa_json_encoder_end_array(encoder);
- return pa_message_params_to_string_free(param);
+ return pa_json_encoder_to_string_free(encoder);
}
-static int bluez5_device_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
+static int bluez5_device_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
char *message_handler_path;
pa_hashmap *capabilities_hashmap;
pa_bluetooth_profile_t profile;
@@ -2503,8 +2503,6 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
const char *codec_name;
struct userdata *u;
bool is_a2dp_sink;
- void *state = NULL;
- int err;
pa_assert(u = (struct userdata *)userdata);
pa_assert(message);
@@ -2539,9 +2537,17 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
}
if (pa_streq(message, "switch-codec")) {
- err = pa_message_params_read_string(message_parameters, &codec_name, &state);
- if (err < 0)
- return err;
+ if (!parameters) {
+ pa_log_info("Codec switching operation requires codec name string parameter");
+ return -PA_ERR_INVALID;
+ }
+
+ if (pa_json_object_get_type(parameters) != PA_JSON_TYPE_STRING) {
+ pa_log_info("Codec name object parameter must be a string");
+ return -PA_ERR_INVALID;
+ }
+
+ codec_name = pa_json_object_get_string(parameters);
if (u->a2dp_codec && pa_streq(codec_name, u->a2dp_codec->name)) {
pa_log_info("Requested codec is currently selected codec");
@@ -2599,15 +2605,13 @@ static int bluez5_device_message_handler(const char *object_path, const char *me
*response = list_codecs(u);
return PA_OK;
} else if (pa_streq(message, "get-codec")) {
- pa_message_params *param;
- param = pa_message_params_new();
+ pa_json_encoder *encoder;
+ encoder = pa_json_encoder_new();
if (u->a2dp_codec)
- pa_message_params_write_string(param, u->a2dp_codec->name);
- else
- pa_message_params_write_string(param, "none");
+ pa_json_encoder_add_element_string(encoder, u->a2dp_codec->name);
- *response = pa_message_params_to_string_free(param);
+ *response = pa_json_encoder_to_string_free(encoder);
return PA_OK;
}
=====================================
src/pulse/json.c
=====================================
@@ -35,7 +35,7 @@ struct pa_json_object {
pa_json_type type;
union {
- int int_value;
+ int64_t int_value;
double double_value;
bool bool_value;
char *string_value;
@@ -44,6 +44,30 @@ struct pa_json_object {
};
};
+/* JSON encoder context type */
+typedef enum pa_json_context_type {
+ /* Top-level context of empty encoder. JSON element can be added. */
+ PA_JSON_CONTEXT_EMPTY = 0,
+ /* Top-level context of encoder with an element. JSON element cannot be added. */
+ PA_JSON_CONTEXT_TOP = 1,
+ /* JSON array context. JSON elements can be added. */
+ PA_JSON_CONTEXT_ARRAY = 2,
+ /* JSON object context. JSON object members can be added. */
+ PA_JSON_CONTEXT_OBJECT = 3,
+} pa_json_context_type_t;
+
+typedef struct encoder_context {
+ pa_json_context_type_t type;
+ int counter;
+ struct encoder_context *next;
+} encoder_context;
+
+/* JSON encoder structure, a wrapper for pa_strbuf and encoder context */
+struct pa_json_encoder {
+ pa_strbuf *buffer;
+ encoder_context *context;
+};
+
static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
static pa_json_object* json_object_new(void) {
@@ -194,33 +218,22 @@ error:
}
static const char* parse_number(const char *str, pa_json_object *obj) {
- bool negative = false, has_fraction = false, has_exponent = false, valid = false;
- unsigned int integer = 0;
- unsigned int fraction = 0;
- unsigned int fraction_digits = 0;
- int exponent = 0;
-
- if (*str == '-') {
- negative = true;
- str++;
- }
+ bool has_fraction = false, has_exponent = false, valid = false;
+ char *candidate = NULL;
+ const char *s = str;
- if (*str == '0') {
+ if (*s == '-')
+ s++;
+
+ if (*s == '0') {
valid = true;
- str++;
+ s++;
goto fraction;
}
- while (is_digit(*str)) {
+ while (is_digit(*s)) {
valid = true;
-
- if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) {
- pa_log("Integer overflow while parsing number");
- goto error;
- }
-
- integer = (integer * 10) + (*str - '0');
- str++;
+ s++;
}
fraction:
@@ -230,22 +243,14 @@ fraction:
goto error;
}
- if (*str == '.') {
+ if (*s == '.') {
has_fraction = true;
- str++;
+ s++;
valid = false;
- while (is_digit(*str)) {
+ while (is_digit(*s)) {
valid = true;
-
- if (fraction > (UINT_MAX / 10)) {
- pa_log("Integer overflow while parsing fractional part of number");
- goto error;
- }
-
- fraction = (fraction * 10) + (*str - '0');
- fraction_digits++;
- str++;
+ s++;
}
if (!valid) {
@@ -254,52 +259,50 @@ fraction:
}
}
- if (*str == 'e' || *str == 'E') {
- bool exponent_negative = false;
-
+ if (*s == 'e' || *s == 'E') {
has_exponent = true;
- str++;
+ s++;
valid = false;
- if (*str == '-') {
- exponent_negative = true;
- str++;
- } else if (*str == '+')
- str++;
+ if (*s == '-' || *s == '+')
+ s++;
- while (is_digit(*str)) {
+ while (is_digit(*s)) {
valid = true;
-
- if (exponent > (INT_MAX / 10)) {
- pa_log("Integer overflow while parsing exponent part of number");
- goto error;
- }
-
- exponent = (exponent * 10) + (*str - '0');
- str++;
+ s++;
}
if (!valid) {
pa_log("No digit in exponent while parsing fraction");
goto error;
}
-
- if (exponent_negative)
- exponent *= -1;
}
+ /* Number format looks good, now try to extract the value.
+ * Here 's' points just after the string which will be consumed. */
+
+ candidate = pa_xstrndup(str, s - str);
+
if (has_fraction || has_exponent) {
+ if (pa_atod(candidate, &obj->double_value) < 0) {
+ pa_log("Cannot convert string '%s' to double value", str);
+ goto error;
+ }
obj->type = PA_JSON_TYPE_DOUBLE;
- obj->double_value =
- (negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent);
} else {
+ if (pa_atoi64(candidate, &obj->int_value) < 0) {
+ pa_log("Cannot convert string '%s' to int64_t value", str);
+ goto error;
+ }
obj->type = PA_JSON_TYPE_INT;
- obj->int_value = (negative ? -1 : 1) * integer;
}
- return str;
+ pa_xfree(candidate);
+
+ return s;
error:
+ pa_xfree(candidate);
return NULL;
}
@@ -522,7 +525,7 @@ void pa_json_object_free(pa_json_object *obj) {
pa_xfree(obj);
}
-int pa_json_object_get_int(const pa_json_object *o) {
+int64_t pa_json_object_get_int(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
return o->int_value;
}
@@ -547,6 +550,11 @@ const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o,
return pa_hashmap_get(o->object_values, name);
}
+const pa_hashmap *pa_json_object_get_object_member_hashmap(const pa_json_object *o) {
+ pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+ return o->object_values;
+}
+
int pa_json_object_get_array_length(const pa_json_object *o) {
pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
return pa_idxset_size(o->array_values);
@@ -612,3 +620,443 @@ bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
pa_assert_not_reached();
}
}
+
+/* Write functions. The functions are wrapper functions around pa_strbuf,
+ * so that the client does not need to use pa_strbuf directly. */
+
+static void json_encoder_context_push(pa_json_encoder *encoder, pa_json_context_type_t type) {
+ pa_assert(encoder);
+
+ encoder_context *head = pa_xnew0(encoder_context, 1);
+ head->type = type;
+ head->next = encoder->context;
+ encoder->context = head;
+}
+
+/* Returns type of context popped off encoder context stack. */
+static pa_json_context_type_t json_encoder_context_pop(pa_json_encoder *encoder) {
+ encoder_context *head;
+ pa_json_context_type_t type;
+
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+
+ type = encoder->context->type;
+
+ head = encoder->context->next;
+ pa_xfree(encoder->context);
+ encoder->context = head;
+
+ return type;
+}
+
+pa_json_encoder *pa_json_encoder_new(void) {
+ pa_json_encoder *encoder;
+
+ encoder = pa_xnew(pa_json_encoder, 1);
+ encoder->buffer = pa_strbuf_new();
+
+ encoder->context = NULL;
+ json_encoder_context_push(encoder, PA_JSON_CONTEXT_EMPTY);
+
+ return encoder;
+}
+
+void pa_json_encoder_free(pa_json_encoder *encoder) {
+ pa_json_context_type_t type;
+ pa_assert(encoder);
+
+ /* should have exactly one encoder context left at this point */
+ pa_assert(encoder->context);
+ type = json_encoder_context_pop(encoder);
+ pa_assert(encoder->context == NULL);
+
+ pa_assert(type == PA_JSON_CONTEXT_TOP || type == PA_JSON_CONTEXT_EMPTY);
+ if (type == PA_JSON_CONTEXT_EMPTY)
+ pa_log_warn("JSON encoder is empty.");
+
+ if (encoder->buffer)
+ pa_strbuf_free(encoder->buffer);
+
+ pa_xfree(encoder);
+}
+
+char *pa_json_encoder_to_string_free(pa_json_encoder *encoder) {
+ char *result;
+
+ pa_assert(encoder);
+
+ result = pa_strbuf_to_string_free(encoder->buffer);
+
+ encoder->buffer = NULL;
+ pa_json_encoder_free(encoder);
+
+ return result;
+}
+
+static void json_encoder_insert_delimiter(pa_json_encoder *encoder) {
+ pa_assert(encoder);
+
+ if (encoder->context->counter++)
+ pa_strbuf_putc(encoder->buffer, ',');
+}
+
+/* Escapes p to create valid JSON string.
+ * The caller has to free the returned string. */
+static char *pa_json_escape(const char *p) {
+ const char *s;
+ char *out_string, *output;
+ int char_count = strlen(p);
+
+ /* Maximum number of characters in output string
+ * including trailing 0. */
+ char_count = 2 * char_count + 1;
+
+ /* allocate output string */
+ out_string = pa_xmalloc(char_count);
+ output = out_string;
+
+ /* write output string */
+ for (s = p; *s; ++s) {
+ switch (*s) {
+ case '"':
+ *output++ = '\\';
+ *output++ = '"';
+ break;
+ case '\\':
+ *output++ = '\\';
+ *output++ = '\\';
+ break;
+ case '\b':
+ *output++ = '\\';
+ *output++ = 'b';
+ break;
+
+ case '\f':
+ *output++ = '\\';
+ *output++ = 'f';
+ break;
+
+ case '\n':
+ *output++ = '\\';
+ *output++ = 'n';
+ break;
+
+ case '\r':
+ *output++ = '\\';
+ *output++ = 'r';
+ break;
+
+ case '\t':
+ *output++ = '\\';
+ *output++ = 't';
+ break;
+ default:
+ if (*s < 0x20 || *s > 0x7E) {
+ pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *s);
+ pa_xfree(out_string);
+ return NULL;
+ }
+ *output++ = *s;
+ break;
+ }
+ }
+
+ *output = 0;
+
+ return out_string;
+}
+
+static void json_write_string_escaped(pa_json_encoder *encoder, const char *value) {
+ char *escaped_value;
+
+ pa_assert(encoder);
+
+ escaped_value = pa_json_escape(value);
+ pa_strbuf_printf(encoder->buffer, "\"%s\"", escaped_value);
+ pa_xfree(escaped_value);
+}
+
+/* Writes an opening curly brace */
+void pa_json_encoder_begin_element_object(pa_json_encoder *encoder) {
+ pa_assert(encoder);
+ pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+ pa_strbuf_putc(encoder->buffer, '{');
+
+ json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
+}
+
+/* Writes an opening curly brace */
+void pa_json_encoder_begin_member_object(pa_json_encoder *encoder, const char *name) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ pa_strbuf_putc(encoder->buffer, '{');
+
+ json_encoder_context_push(encoder, PA_JSON_CONTEXT_OBJECT);
+}
+
+/* Writes a closing curly brace */
+void pa_json_encoder_end_object(pa_json_encoder *encoder) {
+ pa_json_context_type_t type;
+ pa_assert(encoder);
+
+ type = json_encoder_context_pop(encoder);
+ pa_assert(type == PA_JSON_CONTEXT_OBJECT);
+
+ pa_strbuf_putc(encoder->buffer, '}');
+}
+
+/* Writes an opening bracket */
+void pa_json_encoder_begin_element_array(pa_json_encoder *encoder) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type != PA_JSON_CONTEXT_TOP);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+ pa_strbuf_putc(encoder->buffer, '[');
+
+ json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
+}
+
+/* Writes member name and an opening bracket */
+void pa_json_encoder_begin_member_array(pa_json_encoder *encoder, const char *name) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ pa_strbuf_putc(encoder->buffer, '[');
+
+ json_encoder_context_push(encoder, PA_JSON_CONTEXT_ARRAY);
+}
+
+/* Writes a closing bracket */
+void pa_json_encoder_end_array(pa_json_encoder *encoder) {
+ pa_json_context_type_t type;
+ pa_assert(encoder);
+
+ type = json_encoder_context_pop(encoder);
+ pa_assert(type == PA_JSON_CONTEXT_ARRAY);
+
+ pa_strbuf_putc(encoder->buffer, ']');
+}
+
+void pa_json_encoder_add_element_string(pa_json_encoder *encoder, const char *value) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, value);
+}
+
+void pa_json_encoder_add_member_string(pa_json_encoder *encoder, const char *name, const char *value) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ /* Null value is written as empty element */
+ if (!value)
+ value = "";
+
+ json_write_string_escaped(encoder, value);
+}
+
+static void json_write_null(pa_json_encoder *encoder) {
+ pa_assert(encoder);
+
+ pa_strbuf_puts(encoder->buffer, "null");
+}
+
+void pa_json_encoder_add_element_null(pa_json_encoder *encoder) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_null(encoder);
+}
+
+void pa_json_encoder_add_member_null(pa_json_encoder *encoder, const char *name) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ json_write_null(encoder);
+}
+
+static void json_write_bool(pa_json_encoder *encoder, bool value) {
+ pa_assert(encoder);
+
+ pa_strbuf_puts(encoder->buffer, value ? "true" : "false");
+}
+
+void pa_json_encoder_add_element_bool(pa_json_encoder *encoder, bool value) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_bool(encoder, value);
+}
+
+void pa_json_encoder_add_member_bool(pa_json_encoder *encoder, const char *name, bool value) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ json_write_bool(encoder, value);
+}
+
+static void json_write_int(pa_json_encoder *encoder, int64_t value) {
+ pa_assert(encoder);
+
+ pa_strbuf_printf(encoder->buffer, "%"PRId64, value);
+}
+
+void pa_json_encoder_add_element_int(pa_json_encoder *encoder, int64_t value) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_int(encoder, value);
+}
+
+void pa_json_encoder_add_member_int(pa_json_encoder *encoder, const char *name, int64_t value) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ json_write_int(encoder, value);
+}
+
+static void json_write_double(pa_json_encoder *encoder, double value, int precision) {
+ pa_assert(encoder);
+ pa_strbuf_printf(encoder->buffer, "%.*f", precision, value);
+}
+
+void pa_json_encoder_add_element_double(pa_json_encoder *encoder, double value, int precision) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_double(encoder, value, precision);
+}
+
+void pa_json_encoder_add_member_double(pa_json_encoder *encoder, const char *name, double value, int precision) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ json_write_double(encoder, value, precision);
+}
+
+static void json_write_raw(pa_json_encoder *encoder, const char *raw_string) {
+ pa_assert(encoder);
+ pa_strbuf_puts(encoder->buffer, raw_string);
+}
+
+void pa_json_encoder_add_element_raw_json(pa_json_encoder *encoder, const char *raw_json_string) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_EMPTY || encoder->context->type == PA_JSON_CONTEXT_ARRAY);
+
+ if (encoder->context->type == PA_JSON_CONTEXT_EMPTY)
+ encoder->context->type = PA_JSON_CONTEXT_TOP;
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_raw(encoder, raw_json_string);
+}
+
+void pa_json_encoder_add_member_raw_json(pa_json_encoder *encoder, const char *name, const char *raw_json_string) {
+ pa_assert(encoder);
+ pa_assert(encoder->context);
+ pa_assert(encoder->context->type == PA_JSON_CONTEXT_OBJECT);
+ pa_assert(name && name[0]);
+
+ json_encoder_insert_delimiter(encoder);
+
+ json_write_string_escaped(encoder, name);
+
+ pa_strbuf_putc(encoder->buffer, ':');
+
+ json_write_raw(encoder, raw_json_string);
+}
=====================================
src/pulse/json.h
=====================================
@@ -17,7 +17,11 @@
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
+#pragma once
+
#include <stdbool.h>
+#include <stdint.h>
+#include <pulsecore/hashmap.h>
#define PA_DOUBLE_IS_EQUAL(x, y) (((x) - (y)) < 0.000001 && ((x) - (y)) > -0.000001)
@@ -40,14 +44,67 @@ void pa_json_object_free(pa_json_object *obj);
/* All pointer members that are returned are valid while the corresponding object is valid */
-int pa_json_object_get_int(const pa_json_object *o);
+int64_t pa_json_object_get_int(const pa_json_object *o);
double pa_json_object_get_double(const pa_json_object *o);
bool pa_json_object_get_bool(const pa_json_object *o);
const char* pa_json_object_get_string(const pa_json_object *o);
const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name);
+/** Returns pa_hashmap (char* -> const pa_json_object*) to iterate over object members. \since 15.0 */
+const pa_hashmap *pa_json_object_get_object_member_hashmap(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);
+
+/** @{ \name Write functions */
+
+/** Structure which holds a JSON encoder. Wrapper for pa_strbuf and encoder context. \since 15.0 */
+typedef struct pa_json_encoder pa_json_encoder;
+
+/** Create a new pa_json_encoder structure. \since 15.0 */
+pa_json_encoder *pa_json_encoder_new(void);
+/** Free a pa_json_encoder structure. \since 15.0 */
+void pa_json_encoder_free(pa_json_encoder *encoder);
+/** Convert pa_json_encoder to string, free pa_json_encoder structure.
+ * The returned string needs to be freed with pa_xree(). \since 15.0 */
+char *pa_json_encoder_to_string_free(pa_json_encoder *encoder);
+
+/** Start appending JSON object element by writing an opening brace. \since 15.0 */
+void pa_json_encoder_begin_element_object(pa_json_encoder *encoder);
+/** Start appending JSON object member to JSON object. \since 15.0 */
+void pa_json_encoder_begin_member_object(pa_json_encoder *encoder, const char *name);
+/** End appending JSON object element or member to JSON object. \since 15.0 */
+void pa_json_encoder_end_object(pa_json_encoder *encoder);
+/** Start appending JSON array element by writing an opening bracket. \since 15.0 */
+void pa_json_encoder_begin_element_array(pa_json_encoder *encoder);
+/** Start appending JSON array member to JSON object. \since 15.0 */
+void pa_json_encoder_begin_member_array(pa_json_encoder *encoder, const char *name);
+/** End appending JSON array element or member to JSON object. \since 15.0 */
+void pa_json_encoder_end_array(pa_json_encoder *encoder);
+/** Append null element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_null(pa_json_encoder *encoder);
+/** Append null member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_null(pa_json_encoder *encoder, const char *name);
+/** Append boolean element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_bool(pa_json_encoder *encoder, bool value);
+/** Append boolean member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_bool(pa_json_encoder *encoder, const char *name, bool value);
+/** Append string element to JSON. Value will be escaped. \since 15.0 */
+void pa_json_encoder_add_element_string(pa_json_encoder *encoder, const char *value);
+/** Append string member to JSON object. Value will be escaped. \since 15.0 */
+void pa_json_encoder_add_member_string(pa_json_encoder *encoder, const char *name, const char *value);
+/** Append integer element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_int(pa_json_encoder *encoder, int64_t value);
+/** Append integer member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_int(pa_json_encoder *encoder, const char *name, int64_t value);
+/** Append double element to JSON. \since 15.0 */
+void pa_json_encoder_add_element_double(pa_json_encoder *encoder, double value, int precision);
+/** Append double member to JSON object. \since 15.0 */
+void pa_json_encoder_add_member_double(pa_json_encoder *encoder, const char *name, double value, int precision);
+/** Append raw json string element to JSON. String will be written as is. \since 15.0 */
+void pa_json_encoder_add_element_raw_json(pa_json_encoder *encoder, const char *raw_json_string);
+/** Append raw json string member to JSON object. String will be written as is. \since 15.0 */
+void pa_json_encoder_add_member_raw_json(pa_json_encoder *encoder, const char *name, const char *raw_json_string);
=====================================
src/pulse/map-file
=====================================
@@ -215,6 +215,39 @@ pa_gettimeofday;
pa_glib_mainloop_free;
pa_glib_mainloop_get_api;
pa_glib_mainloop_new;
+pa_json_encoder_add_element_bool;
+pa_json_encoder_add_element_double;
+pa_json_encoder_add_element_int;
+pa_json_encoder_add_element_null;
+pa_json_encoder_add_element_raw_json;
+pa_json_encoder_add_element_string;
+pa_json_encoder_add_member_bool;
+pa_json_encoder_add_member_double;
+pa_json_encoder_add_member_int;
+pa_json_encoder_add_member_null;
+pa_json_encoder_add_member_raw_json;
+pa_json_encoder_add_member_string;
+pa_json_encoder_begin_element_array;
+pa_json_encoder_begin_element_object;
+pa_json_encoder_begin_member_array;
+pa_json_encoder_begin_member_object;
+pa_json_encoder_end_array;
+pa_json_encoder_end_object;
+pa_json_encoder_free;
+pa_json_encoder_new;
+pa_json_encoder_to_string_free;
+pa_json_object_equal;
+pa_json_object_free;
+pa_json_object_get_array_length;
+pa_json_object_get_array_member;
+pa_json_object_get_bool;
+pa_json_object_get_double;
+pa_json_object_get_int;
+pa_json_object_get_object_member;
+pa_json_object_get_object_member_hashmap;
+pa_json_object_get_string;
+pa_json_object_get_type;
+pa_json_parse;
pa_locale_to_utf8;
pa_mainloop_api_once;
pa_mainloop_dispatch;
@@ -229,27 +262,6 @@ pa_mainloop_quit;
pa_mainloop_run;
pa_mainloop_set_poll_func;
pa_mainloop_wakeup;
-pa_message_params_begin_list;
-pa_message_params_end_list;
-pa_message_params_free;
-pa_message_params_new;
-pa_message_params_read_bool;
-pa_message_params_read_double;
-pa_message_params_read_double_array;
-pa_message_params_read_int64;
-pa_message_params_read_int64_array;
-pa_message_params_read_raw;
-pa_message_params_read_string;
-pa_message_params_read_string_array;
-pa_message_params_read_uint64;
-pa_message_params_read_uint64_array;
-pa_message_params_to_string_free;
-pa_message_params_write_bool;
-pa_message_params_write_double;
-pa_message_params_write_int64;
-pa_message_params_write_raw;
-pa_message_params_write_string;
-pa_message_params_write_uint64;
pa_msleep;
pa_operation_cancel;
pa_operation_get_state;
=====================================
src/pulse/meson.build
=====================================
@@ -19,7 +19,6 @@ libpulse_sources = [
'mainloop-api.c',
'mainloop-signal.c',
'mainloop.c',
- 'message-params.c',
'operation.c',
'proplist.c',
'rtclock.c',
@@ -48,10 +47,10 @@ libpulse_headers = [
'format.h',
'gccmacro.h',
'introspect.h',
+ 'json.h',
'mainloop-api.h',
'mainloop-signal.h',
'mainloop.h',
- 'message-params.h',
'operation.h',
'proplist.h',
'pulseaudio.h',
=====================================
src/pulse/message-params.c deleted
=====================================
@@ -1,641 +0,0 @@
-/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <locale.h>
-#include <sys/types.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/macro.h>
-#include <pulsecore/strbuf.h>
-#include <pulsecore/core-util.h>
-
-#include "message-params.h"
-
-/* Message parameter structure, a wrapper for pa_strbuf */
-struct pa_message_params {
- pa_strbuf *buffer;
-};
-
-/* Helper functions */
-
-/* Count number of top level elements in parameter list */
-static int count_elements(const char *c) {
- const char *s;
- uint32_t element_count;
- bool found_element, found_backslash;
- int open_braces;
-
- if (!c || *c == 0)
- return PA_MESSAGE_PARAMS_LIST_END;
-
- element_count = 0;
- open_braces = 0;
- found_element = false;
- found_backslash = false;
- s = c;
-
- /* Count elements in list */
- while (*s != 0) {
-
- /* Skip escaped curly braces. */
- if (*s == '\\' && !found_backslash) {
- found_backslash = true;
- s++;
- continue;
- }
-
- if (*s == '{' && !found_backslash) {
- found_element = true;
- open_braces++;
- }
- if (*s == '}' && !found_backslash)
- open_braces--;
-
- /* unexpected closing brace, parse error */
- if (open_braces < 0)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- if (open_braces == 0 && found_element) {
- element_count++;
- found_element = false;
- }
-
- found_backslash = false;
- s++;
- }
-
- /* missing closing brace, parse error */
- if (open_braces > 0)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- return element_count;
-}
-
-/* Split the specified string into elements. An element is defined as
- * a sub-string between curly braces. The function is needed to parse
- * the parameters of messages. Each time it is called it returns the
- * position of the current element in result and the state pointer is
- * advanced to the next list element. On return, the parameter
- * *is_unpacked indicates if the string is plain text or contains a
- * sub-list. is_unpacked may be NULL.
- *
- * The variable state points to, should be initialized to NULL before
- * the first call. The function returns 1 on success, 0 if end of string
- * is encountered and -1 on parse error.
- *
- * result is set to NULL on end of string or parse error. */
-static int split_list(char *c, char **result, bool *is_unpacked, void **state) {
- char *current = *state ? *state : c;
- uint32_t open_braces;
- bool found_backslash = false;
-
- pa_assert(result);
-
- *result = NULL;
-
- /* Empty or no string */
- if (!current || *current == 0)
- return PA_MESSAGE_PARAMS_LIST_END;
-
- /* Find opening brace */
- while (*current != 0) {
-
- /* Skip escaped curly braces. */
- if (*current == '\\' && !found_backslash) {
- found_backslash = true;
- current++;
- continue;
- }
-
- if (*current == '{' && !found_backslash)
- break;
-
- /* unexpected closing brace, parse error */
- if (*current == '}' && !found_backslash)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- found_backslash = false;
- current++;
- }
-
- /* No opening brace found, end of string */
- if (*current == 0)
- return PA_MESSAGE_PARAMS_LIST_END;
-
- if (is_unpacked)
- *is_unpacked = true;
- *result = current + 1;
- found_backslash = false;
- open_braces = 1;
-
- while (open_braces != 0 && *current != 0) {
- current++;
-
- /* Skip escaped curly braces. */
- if (*current == '\\' && !found_backslash) {
- found_backslash = true;
- continue;
- }
-
- if (*current == '{' && !found_backslash) {
- open_braces++;
- if (is_unpacked)
- *is_unpacked = false;
- }
- if (*current == '}' && !found_backslash)
- open_braces--;
-
- found_backslash = false;
- }
-
- /* Parse error, closing brace missing */
- if (open_braces != 0) {
- *result = NULL;
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- }
-
- /* Replace } with 0 */
- *current = 0;
-
- *state = current + 1;
-
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read functions */
-
-/* Read a string from the parameter list. The state pointer is
- * advanced to the next element of the list. Returns a pointer
- * to a sub-string within c. Escape characters will be removed
- * from the string. The result must not be freed. */
-int pa_message_params_read_string(char *c, const char **result, void **state) {
- char *start_pos;
- char *value = NULL;
- int r;
- bool is_unpacked = true;
-
- pa_assert(result);
-
- if ((r = split_list(c, &start_pos, &is_unpacked, state)) == PA_MESSAGE_PARAMS_OK)
- value = start_pos;
-
- /* Check if we got a plain string not containing further lists */
- if (!is_unpacked) {
- /* Parse error */
- r = PA_MESSAGE_PARAMS_PARSE_ERROR;
- value = NULL;
- }
-
- if (value)
- *result = pa_unescape(value);
-
- return r;
-}
-
-/* A wrapper for split_list() to distinguish between reading pure
- * string data and raw data which may contain further lists. */
-int pa_message_params_read_raw(char *c, char **result, void **state) {
- return split_list(c, result, NULL, state);
-}
-
-/* Read a double from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_double(char *c, double *result, void **state) {
- char *start_pos, *end_pos, *s;
- int err;
- struct lconv *locale;
- double value;
- bool is_unpacked = true;
-
- pa_assert(result);
-
- if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- /* Empty element */
- if (!*start_pos)
- return PA_MESSAGE_PARAMS_IS_NULL;
-
- /* Check if we got a plain string not containing further lists */
- if (!is_unpacked)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- /* Get decimal separator for current locale */
- locale = localeconv();
-
- /* Replace decimal point with the correct character for the
- * current locale. This assumes that no thousand separator
- * is used. */
- for (s = start_pos; *s; s++) {
- if (*s == '.' || *s == ',')
- *s = *locale->decimal_point;
- }
-
- /* Convert to double */
- errno = 0;
- value = strtod(start_pos, &end_pos);
-
- /* Conversion error or string contains invalid characters. If the
- * whole string was used for conversion, end_pos should point to
- * the end of the string. */
- if (errno != 0 || *end_pos != 0 || end_pos == start_pos)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- *result = value;
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read an integer from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_int64(char *c, int64_t *result, void **state) {
- char *start_pos;
- int err;
- int64_t value;
- bool is_unpacked = true;
-
- pa_assert(result);
-
- if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- /* Empty element */
- if (!*start_pos)
- return PA_MESSAGE_PARAMS_IS_NULL;
-
- /* Check if we got a plain string not containing further lists */
- if (!is_unpacked)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- /* Convert to int64 */
- if (pa_atoi64(start_pos, &value) < 0)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- *result = value;
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read an unsigned integer from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_uint64(char *c, uint64_t *result, void **state) {
- char *start_pos;
- int err;
- uint64_t value;
- bool is_unpacked = true;
-
- pa_assert(result);
-
- if ((err = split_list(c, &start_pos, &is_unpacked, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- /* Empty element */
- if (!*start_pos)
- return PA_MESSAGE_PARAMS_IS_NULL;
-
- /* Check if we got a plain string not containing further lists */
- if (!is_unpacked)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- /* Convert to int64 */
- if (pa_atou64(start_pos, &value) < 0)
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
-
- *result = value;
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Read a boolean from the parameter list. The state pointer is
- * advanced to the next element of the list. */
-int pa_message_params_read_bool(char *c, bool *result, void **state) {
- int err;
- uint64_t value;
-
- pa_assert(result);
-
- if ((err = pa_message_params_read_uint64(c, &value, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- *result = false;
- if (value)
- *result = true;
-
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to a string array. */
-int pa_message_params_read_string_array(char *c, const char ***results, int *length, void **state) {
- void *state1 = NULL;
- int element_count, i;
- int err;
- const char **values;
- char *start_pos;
-
- pa_assert(results);
- pa_assert(length);
-
- if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- /* Count elements, return if no element was found or parse error. */
- element_count = count_elements(start_pos);
- if (element_count < 0) {
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- } else if (element_count == 0) {
- *length = 0;
- return PA_MESSAGE_PARAMS_OK;
- }
-
- /* Allocate array */
- values = pa_xmalloc0(element_count * sizeof(char *));
-
- for (i = 0; (err = pa_message_params_read_string(start_pos, &(values[i]), &state1)) > 0; i++)
- ;
-
- if (err < 0) {
- pa_xfree(values);
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- }
-
- *results = values;
- *length = element_count;
-
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to a double array. */
-int pa_message_params_read_double_array(char *c, double **results, int *length, void **state) {
- double *values;
- void *state1 = NULL;
- int element_count, i;
- int err;
- char *start_pos;
-
- pa_assert(results);
- pa_assert(length);
-
- if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- /* Count elements, return if no element was found or parse error. */
- element_count = count_elements(start_pos);
- if (element_count < 0) {
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- } else if (element_count == 0) {
- *length = 0;
- return PA_MESSAGE_PARAMS_OK;
- }
-
- /* Allocate array */
- values = pa_xmalloc0(element_count * sizeof(double));
-
- for (i = 0; (err = pa_message_params_read_double(start_pos, &(values[i]), &state1)) > 0; i++)
- ;
-
- if (err < 0) {
- pa_xfree(values);
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- }
-
- *results = values;
- *length = element_count;
-
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to an int64 array. */
-int pa_message_params_read_int64_array(char *c, int64_t **results, int *length, void **state) {
- int64_t *values;
- void *state1 = NULL;
- int element_count, i;
- int err;
- char *start_pos;
-
- pa_assert(results);
- pa_assert(length);
-
- if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- /* Count elements, return if no element was found or parse error. */
- element_count = count_elements(start_pos);
- if (element_count < 0) {
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- } else if (element_count == 0) {
- *length = 0;
- return PA_MESSAGE_PARAMS_OK;
- }
-
- /* Allocate array */
- values = pa_xmalloc0(element_count * sizeof(int64_t));
-
- for (i = 0; (err = pa_message_params_read_int64(start_pos, &(values[i]), &state1)) > 0; i++)
- ;
-
- if (err < 0) {
- pa_xfree(values);
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- }
-
- *results = values;
- *length = element_count;
-
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Converts a parameter list to an uint64 array. */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length, void **state) {
- uint64_t *values;
- void *state1 = NULL;
- int element_count, i;
- int err;
- char *start_pos;
-
- pa_assert(results);
- pa_assert(length);
-
- if ((err = split_list(c, &start_pos, NULL, state)) != PA_MESSAGE_PARAMS_OK)
- return err;
-
- /* Count elements, return if no element was found or parse error. */
- element_count = count_elements(start_pos);
- if (element_count < 0) {
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- } else if (element_count == 0) {
- *length = 0;
- return PA_MESSAGE_PARAMS_OK;
- }
-
- /* Allocate array */
- values = pa_xmalloc0(element_count * sizeof(uint64_t));
-
- for (i = 0; (err = pa_message_params_read_uint64(start_pos, &(values[i]), &state1)) > 0; i++)
- ;
-
- if (err < 0) {
- pa_xfree(values);
- return PA_MESSAGE_PARAMS_PARSE_ERROR;
- }
-
- *results = values;
- *length = element_count;
-
- return PA_MESSAGE_PARAMS_OK;
-}
-
-/* Write functions. The functions are wrapper functions around pa_strbuf,
- * so that the client does not need to use pa_strbuf directly. */
-
-/* Creates a new pa_message_param structure */
-pa_message_params *pa_message_params_new(void) {
- pa_message_params *params;
-
- params = pa_xnew(pa_message_params, 1);
- params->buffer = pa_strbuf_new();
-
- return params;
-}
-
-/* Frees a pa_message_params structure */
-void pa_message_params_free(pa_message_params *params) {
- pa_assert(params);
-
- pa_strbuf_free(params->buffer);
- pa_xfree(params);
-}
-
-/* Converts a pa_message_param structure to string and frees the structure.
- * The returned string needs to be freed with pa_xree(). */
-char *pa_message_params_to_string_free(pa_message_params *params) {
- char *result;
-
- pa_assert(params);
-
- result = pa_strbuf_to_string_free(params->buffer);
-
- pa_xfree(params);
- return result;
-}
-
-/* Writes an opening curly brace */
-void pa_message_params_begin_list(pa_message_params *params) {
-
- pa_assert(params);
-
- pa_strbuf_putc(params->buffer, '{');
-}
-
-/* Writes a closing curly brace */
-void pa_message_params_end_list(pa_message_params *params) {
-
- pa_assert(params);
-
- pa_strbuf_putc(params->buffer, '}');
-}
-
-/* Writes a string to a message_params structure, adding curly braces
- * around the string and escaping curly braces within the string. */
-void pa_message_params_write_string(pa_message_params *params, const char *value) {
- char *output;
-
- pa_assert(params);
-
- /* Null value is written as empty element */
- if (!value)
- value = "";
-
- output = pa_escape(value, "{}");
- pa_strbuf_printf(params->buffer, "{%s}", output);
-
- pa_xfree(output);
-}
-
-/* Writes a raw string to a message_params structure, adding curly braces
- * around the string if add_braces is true. This function can be used to
- * write parts of a string or whole parameter lists that have been prepared
- * elsewhere (for example an array). */
-void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces) {
- pa_assert(params);
-
- /* Null value is written as empty element if add_braces is true.
- * Otherwise nothing is written. */
- if (!value)
- value = "";
-
- if (add_braces)
- pa_strbuf_printf(params->buffer, "{%s}", value);
- else
- pa_strbuf_puts(params->buffer, value);
-}
-
-/* Writes a double to a message_params structure, adding curly braces.
- * precision gives the number of significant digits, not digits after
- * the decimal point. */
-void pa_message_params_write_double(pa_message_params *params, double value, int precision) {
- char *buf, *s;
-
- pa_assert(params);
-
- /* We do not care about locale because we do not know which locale is
- * used on the server side. If the decimal separator is a comma, we
- * replace it with a dot to achieve consistent output on all locales. */
- buf = pa_sprintf_malloc("{%.*g}", precision, value);
- for (s = buf; *s; s++) {
- if (*s == ',') {
- *s = '.';
- break;
- }
- }
-
- pa_strbuf_puts(params->buffer, buf);
-
- pa_xfree(buf);
-}
-
-/* Writes an integer to a message_param structure, adding curly braces. */
-void pa_message_params_write_int64(pa_message_params *params, int64_t value) {
-
- pa_assert(params);
-
- pa_strbuf_printf(params->buffer, "{%lli}", (long long)value);
-}
-
-/* Writes an unsigned integer to a message_params structure, adding curly braces. */
-void pa_message_params_write_uint64(pa_message_params *params, uint64_t value) {
-
- pa_assert(params);
-
- pa_strbuf_printf(params->buffer, "{%llu}", (unsigned long long)value);
-}
-
-/* Writes a boolean to a message_params structure, adding curly braces. */
-void pa_message_params_write_bool(pa_message_params *params, bool value) {
-
- pa_assert(params);
-
- if (value)
- pa_strbuf_puts(params->buffer, "{1}");
- else
- pa_strbuf_puts(params->buffer, "{0}");
-}
=====================================
src/pulse/message-params.h deleted
=====================================
@@ -1,157 +0,0 @@
-#ifndef foomessagehelperhfoo
-#define foomessagehelperhfoo
-
-/***
- This file is part of PulseAudio.
-
- PulseAudio is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- PulseAudio is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <stddef.h>
-#include <stdbool.h>
-#include <inttypes.h>
-
-#include <pulse/cdecl.h>
-#include <pulse/version.h>
-
-/** \file
- * Utility functions for reading and writing message parameters.
- * All read functions return a value from pa_message_params_error_code
- * and the read value in result (or *result for string functions).
- * The string read functions read_string() and read_raw() return a pointer
- * to a sub-string within the parameter list in *result, therefore the
- * string in *result must not be freed and is only valid within the
- * message handler callback function. If the string is needed outside
- * the callback, it must be copied using pa_xstrdup().
- * When a read function is called, the state pointer is advanced to the
- * next list element. The variable state points to should be initialized
- * to NULL before the first call.
- * All read functions except read_raw() preserve a default value passed
- * in result if the call fails. For the array functions, results must be
- * initialized prior to the call either to NULL or to an array with default
- * values. If the function succeeds, the default array will be freed and
- * the number of elements in the result array is returned.\n\n
- * Write functions operate on a pa_message_params structure which is a
- * wrapper for pa_strbuf. A parameter list or sub-list is started by a
- * call to begin_list() and ended by a call to end_list().
- * A pa_message_params structure must be converted to a string using
- * pa_message_params_to_string_free() before it can be passed to a
- * message handler. */
-
-PA_C_DECL_BEGIN
-
-/** Structure which holds a parameter list. Wrapper for pa_strbuf \since 15.0 */
-typedef struct pa_message_params pa_message_params;
-
-/** Read function return values \since 15.0 */
-enum pa_message_params_error_code {
- /** No value (empty element) found for numeric or boolean value */
- PA_MESSAGE_PARAMS_IS_NULL = -2,
- /** Error encountered while parsing a value */
- PA_MESSAGE_PARAMS_PARSE_ERROR = -1,
- /** End of parameter list reached */
- PA_MESSAGE_PARAMS_LIST_END = 0,
- /** Parsing successful */
- PA_MESSAGE_PARAMS_OK = 1,
-};
-
-/** @{ \name Read functions */
-
-/** Read a boolean from parameter list in c. \since 15.0 */
-int pa_message_params_read_bool(char *c, bool *result, void **state);
-
-/** Read a double from parameter list in c. \since 15.0 */
-int pa_message_params_read_double(char *c, double *result, void **state);
-
-/** Converts a parameter list to a double array. Empty elements in the parameter
- * list are treated as error. Returns allocated array in *results and array size in *length.
- * The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_double_array(char *c, double **results, int *length, void **state);
-
-/** Read an integer from parameter list in c. \since 15.0 */
-int pa_message_params_read_int64(char *c, int64_t *result, void **state);
-
-/** Converts a parameter list to an int64 array. Empty elements in the parameter
- * list are treated as error. Returns allocated array in *results and array size in *length.
- * The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_int64_array(char *c, int64_t **results, int *length, void **state);
-
-/** Read raw data from parameter list in c. Used to split a message parameter
- * string into list elements. The string returned in *result must not be freed. \since 15.0 */
-int pa_message_params_read_raw(char *c, char **result, void **state);
-
-/** Read a string from a parameter list in c. Escaped curly braces and backslashes
- * will be unescaped. \since 15.0 */
-int pa_message_params_read_string(char *c, const char **result, void **state);
-
-/** Convert a parameter list to a string array. Escaping is removed from
- * the strings. Returns allocated array of pointers to sub-strings within c in
- * *results and stores array size in *length. The returned array must be
- * freed with pa_xfree(), but not the strings within the array. \since 15.0 */
-int pa_message_params_read_string_array(char *c, const char ***results, int *length, void **state);
-
-/** Read an unsigned integer from parameter list in c. \since 15.0 */
-int pa_message_params_read_uint64(char *c, uint64_t *result, void **state);
-
-/** Converts a parameter list to an uint64 array. Empty elements in the parameter
- * list are treated as error. Returns allocated array in *results and array size in *length.
- * The returned array must be freed with pa_xfree(). \since 15.0 */
-int pa_message_params_read_uint64_array(char *c, uint64_t **results, int *length, void **state);
-
-/** @} */
-
-/** @{ \name Write functions */
-
-/** Create a new pa_message_params structure. \since 15.0 */
-pa_message_params *pa_message_params_new(void);
-
-/** Free a pa_message_params structure. \since 15.0 */
-void pa_message_params_free(pa_message_params *params);
-
-/** Convert pa_message_params to string, free pa_message_params structure. \since 15.0 */
-char *pa_message_params_to_string_free(pa_message_params *params);
-
-/** Start a list by writing an opening brace. \since 15.0 */
-void pa_message_params_begin_list(pa_message_params *params);
-
-/** End a list by writing a closing brace. \since 15.0 */
-void pa_message_params_end_list(pa_message_params *params);
-
-/** Append a boolean to parameter list. \since 15.0 */
-void pa_message_params_write_bool(pa_message_params *params, bool value);
-
-/** Append a double to parameter list. Precision gives the number of
- * significant digits. The decimal separator will always be written as
- * dot, regardless which locale is used. \since 15.0 */
-void pa_message_params_write_double(pa_message_params *params, double value, int precision);
-
-/** Append an integer to parameter list. \since 15.0 */
-void pa_message_params_write_int64(pa_message_params *params, int64_t value);
-
-/** Append string to parameter list. Curly braces and backslashes will be escaped. \since 15.0 */
-void pa_message_params_write_string(pa_message_params *params, const char *value);
-
-/** Append raw string to parameter list. Used to write incomplete strings
- * or complete parameter lists (for example arrays). Adds curly braces around
- * the string if add_braces is true. \since 15.0 */
-void pa_message_params_write_raw(pa_message_params *params, const char *value, bool add_braces);
-
-/** Append an unsigned integer to parameter list. \since 15.0 */
-void pa_message_params_write_uint64(pa_message_params *params, uint64_t value);
-
-/** @} */
-
-PA_C_DECL_END
-
-#endif
=====================================
src/pulsecore/core.c
=====================================
@@ -29,7 +29,6 @@
#include <pulse/rtclock.h>
#include <pulse/timeval.h>
#include <pulse/xmalloc.h>
-#include <pulse/message-params.h>
#include <pulsecore/module.h>
#include <pulsecore/core-rtclock.h>
@@ -66,29 +65,27 @@ static void core_free(pa_object *o);
/* Returns a list of handlers. */
static char *message_handler_list(pa_core *c) {
- pa_message_params *param;
+ pa_json_encoder *encoder;
void *state = NULL;
struct pa_message_handler *handler;
- param = pa_message_params_new();
+ encoder = pa_json_encoder_new();
- pa_message_params_begin_list(param);
+ pa_json_encoder_begin_element_array(encoder);
PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
- pa_message_params_begin_list(param);
+ pa_json_encoder_begin_element_object(encoder);
- /* object_path cannot contain characters that need escaping, therefore
- * pa_message_params_write_raw() can safely be used here. */
- pa_message_params_write_raw(param, handler->object_path, true);
- pa_message_params_write_string(param, handler->description);
+ pa_json_encoder_add_member_string(encoder, "name", handler->object_path);
+ pa_json_encoder_add_member_string(encoder, "description", handler->description);
- pa_message_params_end_list(param);
+ pa_json_encoder_end_object(encoder);
}
- pa_message_params_end_list(param);
+ pa_json_encoder_end_array(encoder);
- return pa_message_params_to_string_free(param);
+ return pa_json_encoder_to_string_free(encoder);
}
-static int core_message_handler(const char *object_path, const char *message, char *message_parameters, char **response, void *userdata) {
+static int core_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
pa_core *c;
pa_assert(c = (pa_core *) userdata);
=====================================
src/pulsecore/message-handler.c
=====================================
@@ -105,7 +105,8 @@ void pa_message_handler_unregister(pa_core *c, const char *object_path) {
int pa_message_handler_send_message(pa_core *c, const char *object_path, const char *message, const char *message_parameters, char **response) {
struct pa_message_handler *handler;
int ret;
- char *parameter_copy, *path_copy;
+ char *path_copy;
+ pa_json_object *parameters = NULL;
pa_assert(c);
pa_assert(object_path);
@@ -125,14 +126,21 @@ int pa_message_handler_send_message(pa_core *c, const char *object_path, const c
return -PA_ERR_NOENTITY;
}
- parameter_copy = pa_xstrdup(message_parameters);
+ pa_xfree(path_copy);
+
+ if (message_parameters) {
+ parameters = pa_json_parse(message_parameters);
+
+ if (!parameters)
+ return -PA_ERR_INVALID;
+ }
/* The handler is expected to return an error code and may also
return an error string in response */
- ret = handler->callback(handler->object_path, message, parameter_copy, response, handler->userdata);
+ ret = handler->callback(handler->object_path, message, parameters, response, handler->userdata);
- pa_xfree(parameter_copy);
- pa_xfree(path_copy);
+ if (parameters)
+ pa_json_object_free(parameters);
return ret;
}
=====================================
src/pulsecore/message-handler.h
=====================================
@@ -19,6 +19,7 @@
***/
#include <pulsecore/core.h>
+#include <pulse/json.h>
/* Message handler types and functions */
@@ -26,7 +27,7 @@
typedef int (*pa_message_handler_cb_t)(
const char *object_path,
const char *message,
- char *message_parameters,
+ const pa_json_object *parameters,
char **response,
void *userdata);
=====================================
src/tests/json-test.c
=====================================
@@ -24,6 +24,7 @@
#include <check.h>
#include <pulse/json.h>
+#include <pulse/xmalloc.h>
#include <pulsecore/core-util.h>
START_TEST (string_test) {
@@ -50,11 +51,54 @@ START_TEST (string_test) {
}
END_TEST
+START_TEST (encoder_string_test) {
+ const char *test_strings[] = {
+ "", "test", "test123", "123", "newline\n", " spaces ",
+ "lots of spaces", "esc\nape", "escape a \" quote",
+ };
+
+ pa_json_object *o;
+ unsigned int i;
+ pa_json_encoder *encoder;
+ const pa_json_object *v;
+ char *received;
+
+ encoder = pa_json_encoder_new();
+
+ pa_json_encoder_begin_element_array(encoder);
+
+ for (i = 0; i < PA_ELEMENTSOF(test_strings); i++) {
+ pa_json_encoder_add_element_string(encoder, test_strings[i]);
+ }
+
+ pa_json_encoder_end_array(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_strings));
+
+ for (i = 0; i < PA_ELEMENTSOF(test_strings); i++) {
+ v = pa_json_object_get_array_member(o, i);
+
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+ fail_unless(pa_streq(pa_json_object_get_string(v), test_strings[i]));
+ }
+
+ pa_json_object_free(o);
+}
+END_TEST
+
START_TEST(int_test) {
pa_json_object *o;
unsigned int i;
const char *ints_parse[] = { "1", "-1", "1234", "0" };
- const int ints_compare[] = { 1, -1, 1234, 0 };
+ const int64_t ints_compare[] = { 1, -1, 1234, 0 };
+ char *ulong_max_str;
for (i = 0; i < PA_ELEMENTSOF(ints_parse); i++) {
o = pa_json_parse(ints_parse[i]);
@@ -65,12 +109,58 @@ START_TEST(int_test) {
pa_json_object_free(o);
}
+
+ /* test that parser would fail on integer overflow */
+ ulong_max_str = pa_sprintf_malloc("%"PRIu64, ULONG_MAX);
+ o = pa_json_parse(ulong_max_str);
+ fail_unless(o == NULL);
+ pa_xfree(ulong_max_str);
+}
+END_TEST
+
+START_TEST(encoder_int_test) {
+ const int64_t test_ints[] = { 1, -1, 1234, 0, LONG_MIN, LONG_MAX };
+
+ pa_json_object *o;
+ unsigned int i;
+ pa_json_encoder *encoder;
+ const pa_json_object *v;
+ char *received;
+
+ encoder = pa_json_encoder_new();
+
+ pa_json_encoder_begin_element_array(encoder);
+
+ for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+ pa_json_encoder_add_element_int(encoder, test_ints[i]);
+ }
+
+ pa_json_encoder_end_array(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_ints));
+
+ for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+ v = pa_json_object_get_array_member(o, i);
+
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_INT);
+ fail_unless(pa_json_object_get_int(v) == test_ints[i]);
+ }
+
+ pa_json_object_free(o);
}
END_TEST
START_TEST(double_test) {
pa_json_object *o;
unsigned int i;
+ char *very_large_double_str;
const char *doubles_parse[] = {
"1.0", "-1.1", "1234e2", "1234e0", "0.1234", "-0.1234", "1234e-1", "1234.5e-1", "1234.5e+2",
};
@@ -87,6 +177,52 @@ START_TEST(double_test) {
pa_json_object_free(o);
}
+
+ /* test that parser would fail on double exponent overflow */
+ very_large_double_str = pa_sprintf_malloc("%"PRIu64"e%"PRIu64, ULONG_MAX, ULONG_MAX);
+ o = pa_json_parse(very_large_double_str);
+ fail_unless(o == NULL);
+ pa_xfree(very_large_double_str);
+}
+END_TEST
+
+START_TEST(encoder_double_test) {
+ const double test_doubles[] = {
+ 1.0, -1.1, 123400.0, 1234.0, 0.1234, -0.1234, 123.4, 123.45, 123450.0,
+ };
+ pa_json_object *o;
+ unsigned int i;
+ pa_json_encoder *encoder;
+ const pa_json_object *v;
+ char *received;
+
+ encoder = pa_json_encoder_new();
+
+ pa_json_encoder_begin_element_array(encoder);
+
+ for (i = 0; i < PA_ELEMENTSOF(test_doubles); i++) {
+ pa_json_encoder_add_element_double(encoder, test_doubles[i], 6);
+ }
+
+ pa_json_encoder_end_array(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_doubles));
+
+ for (i = 0; i < PA_ELEMENTSOF(test_doubles); i++) {
+ v = pa_json_object_get_array_member(o, i);
+
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_DOUBLE);
+ fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), test_doubles[i]));
+ }
+
+ pa_json_object_free(o);
}
END_TEST
@@ -102,6 +238,25 @@ START_TEST(null_test) {
}
END_TEST
+START_TEST(encoder_null_test) {
+ pa_json_object *o;
+ pa_json_encoder *encoder;
+ char *received;
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_add_element_null(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_NULL);
+
+ pa_json_object_free(o);
+}
+END_TEST
+
START_TEST(bool_test) {
pa_json_object *o;
@@ -123,6 +278,46 @@ START_TEST(bool_test) {
}
END_TEST
+START_TEST(encoder_bool_test) {
+ const bool test_bools[] = {
+ true, false
+ };
+ pa_json_object *o;
+ unsigned int i;
+ pa_json_encoder *encoder;
+ const pa_json_object *v;
+ char *received;
+
+ encoder = pa_json_encoder_new();
+
+ pa_json_encoder_begin_element_array(encoder);
+
+ for (i = 0; i < PA_ELEMENTSOF(test_bools); i++) {
+ pa_json_encoder_add_element_bool(encoder, test_bools[i]);
+ }
+
+ pa_json_encoder_end_array(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(o) == PA_ELEMENTSOF(test_bools));
+
+ for (i = 0; i < PA_ELEMENTSOF(test_bools); i++) {
+ v = pa_json_object_get_array_member(o, i);
+
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+ fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_bool(v), test_bools[i]));
+ }
+
+ pa_json_object_free(o);
+}
+END_TEST
+
START_TEST(object_test) {
pa_json_object *o;
const pa_json_object *v;
@@ -178,6 +373,165 @@ START_TEST(object_test) {
}
END_TEST
+START_TEST(object_member_iterator_test) {
+ pa_json_object *o;
+ const pa_json_object *v;
+ const char *k;
+ void *state;
+ size_t i;
+
+ struct {
+ bool visited;
+ const char *key;
+ pa_json_type type;
+ union {
+ const char *str;
+ int64_t n;
+ } value;
+ } expected_entries[] = {
+ { .key = "name", .type = PA_JSON_TYPE_STRING, .value.str = "sample 1" },
+ { .key = "number", .type = PA_JSON_TYPE_INT, .value.n = 42 },
+ };
+
+ o = pa_json_parse(" { \"name\" : \"sample 1\", \"number\": 42 } ");
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ PA_HASHMAP_FOREACH_KV(k, v, pa_json_object_get_object_member_hashmap(o), state) {
+ fail_unless(k != NULL);
+ fail_unless(v != NULL);
+ for (i = 0; i < PA_ELEMENTSOF(expected_entries); ++i) {
+ if (pa_streq(expected_entries[i].key, k)) {
+ fail_unless(!expected_entries[i].visited);
+ fail_unless(expected_entries[i].type == pa_json_object_get_type(v));
+ switch (expected_entries[i].type) {
+ case PA_JSON_TYPE_STRING:
+ fail_unless(pa_streq(expected_entries[i].value.str, pa_json_object_get_string(v)));
+ break;
+ case PA_JSON_TYPE_INT:
+ fail_unless(expected_entries[i].value.n == pa_json_object_get_int(v));
+ break;
+ default:
+ /* unreachable */
+ fail_unless(false);
+ break;
+ }
+ expected_entries[i].visited = true;
+ }
+ }
+ }
+
+ pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_object_test) {
+ pa_json_object *o;
+ const pa_json_object *v;
+ pa_json_encoder *encoder;
+ char *received;
+
+ /* { "name" : "A Person" } */
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(encoder);
+ pa_json_encoder_add_member_string(encoder, "name", "A Person");
+ pa_json_encoder_end_object(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ v = pa_json_object_get_object_member(o, "name");
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+ fail_unless(pa_streq(pa_json_object_get_string(v), "A Person"));
+
+ pa_json_object_free(o);
+
+ /* { "age" : -45.3e-0 } */
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(encoder);
+ pa_json_encoder_add_member_double(encoder, "age", -45.3e-0, 2);
+ pa_json_encoder_end_object(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ 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(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(v), -45.3));
+
+ pa_json_object_free(o);
+
+ /* {"person":true} */
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(encoder);
+ pa_json_encoder_add_member_bool(encoder, "person", true);
+ pa_json_encoder_end_object(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ v = pa_json_object_get_object_member(o, "person");
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+ fail_unless(pa_json_object_get_bool(v) == true);
+
+ pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_member_object_test) {
+ pa_json_object *o;
+ const pa_json_object *v;
+ pa_json_encoder *encoder;
+ char *received;
+
+ /* { "parent": { "child": false } } */
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(encoder);
+
+ pa_json_encoder_begin_member_object(encoder, "parent");
+ pa_json_encoder_add_member_bool(encoder, "child", false);
+ pa_json_encoder_end_object(encoder);
+
+ pa_json_encoder_end_object(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ v = pa_json_object_get_object_member(o, "parent");
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT);
+ v = pa_json_object_get_object_member(v, "child");
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+ fail_unless(pa_json_object_get_bool(v) == false);
+
+ pa_json_object_free(o);
+}
+END_TEST
+
START_TEST(array_test) {
pa_json_object *o;
const pa_json_object *v, *v2;
@@ -229,13 +583,241 @@ START_TEST(array_test) {
}
END_TEST
+START_TEST(encoder_element_array_test) {
+ pa_json_object *o;
+ const pa_json_object *v, *v2;
+
+ pa_json_encoder *encoder;
+ char *received;
+ pa_json_encoder *subobject;
+ char *subobject_string;
+
+ /* [ ] */
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_array(encoder);
+ pa_json_encoder_end_array(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(o) == 0);
+
+ pa_json_object_free(o);
+
+ /* ["a member"] */
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_array(encoder);
+ pa_json_encoder_add_element_string(encoder, "a member");
+ pa_json_encoder_end_array(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(o) == 1);
+
+ v = pa_json_object_get_array_member(o, 0);
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+ fail_unless(pa_streq(pa_json_object_get_string(v), "a member"));
+
+ pa_json_object_free(o);
+
+ /* [\"a member\", 1234.5, { \"another\": true } ] */
+
+ subobject = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(subobject);
+ pa_json_encoder_add_member_bool(subobject, "another", true);
+ pa_json_encoder_end_object(subobject);
+ subobject_string = pa_json_encoder_to_string_free(subobject);
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_array(encoder);
+ pa_json_encoder_add_element_string(encoder, "a member");
+ pa_json_encoder_add_element_double(encoder, 1234.5, 1);
+ pa_json_encoder_add_element_raw_json(encoder, subobject_string);
+ pa_xfree(subobject_string);
+ pa_json_encoder_end_array(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(o) == 3);
+
+ v = pa_json_object_get_array_member(o, 0);
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_STRING);
+ fail_unless(pa_streq(pa_json_object_get_string(v), "a member"));
+ 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(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);
+ v2 =pa_json_object_get_object_member(v, "another");
+ fail_unless(v2 != NULL);
+ fail_unless(pa_json_object_get_type(v2) == PA_JSON_TYPE_BOOL);
+ fail_unless(pa_json_object_get_bool(v2) == true);
+
+ pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_member_array_test) {
+ pa_json_object *o;
+ unsigned int i;
+ const pa_json_object *v;
+ const pa_json_object *e;
+ pa_json_encoder *encoder;
+ char *received;
+
+ const int64_t test_ints[] = { 1, -1, 1234, 0, LONG_MIN, LONG_MAX };
+
+ /* { "parameters": [ 1, -1, 1234, 0, -9223372036854775808, 9223372036854775807 ] } */
+
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(encoder);
+
+ pa_json_encoder_begin_member_array(encoder, "parameters");
+ for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+ pa_json_encoder_add_element_int(encoder, test_ints[i]);
+ }
+ pa_json_encoder_end_array(encoder);
+
+ pa_json_encoder_end_object(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ v = pa_json_object_get_object_member(o, "parameters");
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(v) == PA_ELEMENTSOF(test_ints));
+
+ for (i = 0; i < PA_ELEMENTSOF(test_ints); i++) {
+ e = pa_json_object_get_array_member(v, i);
+
+ fail_unless(e != NULL);
+ fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_INT);
+ fail_unless(pa_json_object_get_int(e) == test_ints[i]);
+ }
+
+ pa_json_object_free(o);
+}
+END_TEST
+
+START_TEST(encoder_member_raw_json_test) {
+ pa_json_object *o;
+ const pa_json_object *v;
+ const pa_json_object *e;
+ pa_json_encoder *encoder;
+ char *received;
+ pa_json_encoder *subobject;
+ char *subobject_string;
+
+ /* { "parameters": [1, "a", 2.0] } */
+
+ subobject = pa_json_encoder_new();
+ pa_json_encoder_begin_element_array(subobject);
+ pa_json_encoder_add_element_int(subobject, 1);
+ pa_json_encoder_add_element_string(subobject, "a");
+ pa_json_encoder_add_element_double(subobject, 2.0, 6);
+ pa_json_encoder_end_array(subobject);
+ subobject_string = pa_json_encoder_to_string_free(subobject);
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(encoder);
+
+ pa_json_encoder_add_member_raw_json(encoder, "parameters", subobject_string);
+ pa_xfree(subobject_string);
+
+ pa_json_encoder_end_object(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ v = pa_json_object_get_object_member(o, "parameters");
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_ARRAY);
+ fail_unless(pa_json_object_get_array_length(v) == 3);
+ e = pa_json_object_get_array_member(v, 0);
+ fail_unless(e != NULL);
+ fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_INT);
+ fail_unless(pa_json_object_get_int(e) == 1);
+ e = pa_json_object_get_array_member(v, 1);
+ fail_unless(e != NULL);
+ fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_STRING);
+ fail_unless(pa_streq(pa_json_object_get_string(e), "a"));
+ e = pa_json_object_get_array_member(v, 2);
+ fail_unless(e != NULL);
+ fail_unless(pa_json_object_get_type(e) == PA_JSON_TYPE_DOUBLE);
+ fail_unless(PA_DOUBLE_IS_EQUAL(pa_json_object_get_double(e), 2.0));
+
+ pa_json_object_free(o);
+
+ /* { "parent": { "child": false } } */
+
+ subobject = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(subobject);
+ pa_json_encoder_add_member_bool(subobject, "child", false);
+ pa_json_encoder_end_object(subobject);
+ subobject_string = pa_json_encoder_to_string_free(subobject);
+
+ encoder = pa_json_encoder_new();
+ pa_json_encoder_begin_element_object(encoder);
+
+ pa_json_encoder_add_member_raw_json(encoder, "parent", subobject_string);
+ pa_xfree(subobject_string);
+
+ pa_json_encoder_end_object(encoder);
+
+ received = pa_json_encoder_to_string_free(encoder);
+ o = pa_json_parse(received);
+ pa_xfree(received);
+
+ fail_unless(o != NULL);
+ fail_unless(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
+
+ v = pa_json_object_get_object_member(o, "parent");
+ fail_unless(v != NULL);
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_OBJECT);
+ v = pa_json_object_get_object_member(v, "child");
+ fail_unless(pa_json_object_get_type(v) == PA_JSON_TYPE_BOOL);
+ fail_unless(pa_json_object_get_bool(v) == false);
+
+ pa_json_object_free(o);
+}
+END_TEST
+
START_TEST(bad_test) {
unsigned int i;
const char *bad_parse[] = {
"\"" /* Quote not closed */,
"123456789012345678901234567890" /* Overflow */,
+#if 0 /* TODO: check rounding the value is OK */
"0.123456789012345678901234567890" /* Overflow */,
+#endif
"1e123456789012345678901234567890" /* Overflow */,
+ "1e-10000" /* Underflow */,
"1e" /* Bad number string */,
"1." /* Bad number string */,
"1.e3" /* Bad number string */,
@@ -266,12 +848,23 @@ int main(int argc, char *argv[]) {
s = suite_create("JSON");
tc = tcase_create("json");
tcase_add_test(tc, string_test);
+ tcase_add_test(tc, encoder_string_test);
tcase_add_test(tc, int_test);
+ tcase_add_test(tc, encoder_int_test);
tcase_add_test(tc, double_test);
+ tcase_add_test(tc, encoder_double_test);
tcase_add_test(tc, null_test);
+ tcase_add_test(tc, encoder_null_test);
tcase_add_test(tc, bool_test);
+ tcase_add_test(tc, encoder_bool_test);
tcase_add_test(tc, object_test);
+ tcase_add_test(tc, encoder_member_object_test);
+ tcase_add_test(tc, object_member_iterator_test);
+ tcase_add_test(tc, encoder_object_test);
tcase_add_test(tc, array_test);
+ tcase_add_test(tc, encoder_element_array_test);
+ tcase_add_test(tc, encoder_member_array_test);
+ tcase_add_test(tc, encoder_member_raw_json_test);
tcase_add_test(tc, bad_test);
suite_add_tcase(s, tc);
=====================================
src/utils/pactl.c
=====================================
@@ -35,8 +35,8 @@
#include <sndfile.h>
#include <pulse/pulseaudio.h>
-#include <pulse/message-params.h>
#include <pulse/ext-device-restore.h>
+#include <pulse/json.h>
#include <pulsecore/i18n.h>
#include <pulsecore/macro.h>
@@ -897,10 +897,10 @@ static void send_message_callback(pa_context *c, int success, char *response, vo
}
static void list_handlers_callback(pa_context *c, int success, char *response, void *userdata) {
- void *state = NULL;
- char *handler_list;
- char *handler_struct;
int err;
+ pa_json_object *o;
+ int i;
+ const pa_json_object *v, *path, *description;
if (!success) {
pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c)));
@@ -908,29 +908,45 @@ static void list_handlers_callback(pa_context *c, int success, char *response, v
return;
}
- if (pa_message_params_read_raw(response, &handler_list, &state) <= 0) {
+ o = pa_json_parse(response);
+
+ if (!o) {
pa_log(_("list-handlers message response could not be parsed correctly"));
+ pa_json_object_free(o);
+ quit(1);
+ return;
+ }
+
+ if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) {
+ pa_log(_("list-handlers message response is not a JSON array"));
+ pa_json_object_free(o);
quit(1);
return;
}
- state = NULL;
- while ((err = pa_message_params_read_raw(handler_list, &handler_struct, &state)) > 0) {
- void *state2 = NULL;
- const char *path;
- const char *description;
+ err = 0;
- if (pa_message_params_read_string(handler_struct, &path, &state2) <= 0) {
+ for (i = 0; i < pa_json_object_get_array_length(o); ++i) {
+ v = pa_json_object_get_array_member(o, i);
+ if (pa_json_object_get_type(v) != PA_JSON_TYPE_OBJECT) {
+ pa_log(_("list-handlers message response array element %d is not a JSON object"), i);
err = -1;
break;
}
- if (pa_message_params_read_string(handler_struct, &description, &state2) <= 0) {
+
+ path = pa_json_object_get_object_member(v, "name");
+ if (!path || pa_json_object_get_type(path) != PA_JSON_TYPE_STRING) {
+ err = -1;
+ break;
+ }
+ description = pa_json_object_get_object_member(v, "description");
+ if (!description || pa_json_object_get_type(description) != PA_JSON_TYPE_STRING) {
err = -1;
break;
}
if (short_list_format)
- printf("%s\n", path);
+ printf("%s\n", pa_json_object_get_string(path));
else {
if (nl)
printf("\n");
@@ -938,17 +954,20 @@ static void list_handlers_callback(pa_context *c, int success, char *response, v
printf("Message Handler %s\n"
"\tDescription: %s\n",
- path,
- description);
+ pa_json_object_get_string(path),
+ pa_json_object_get_string(description));
}
}
if (err < 0) {
pa_log(_("list-handlers message response could not be parsed correctly"));
+ pa_json_object_free(o);
quit(1);
return;
}
+ pa_json_object_free(o);
+
complete_action();
}
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/02027518afa3717724d1a545c74662aaf753ff8e...7ca50bab2f946237bbe6418bf9e7dbd01fada1bd
--
View it on GitLab: https://gitlab.freedesktop.org/pulseaudio/pulseaudio/-/compare/02027518afa3717724d1a545c74662aaf753ff8e...7ca50bab2f946237bbe6418bf9e7dbd01fada1bd
You're receiving this email because of your account on gitlab.freedesktop.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/pulseaudio-commits/attachments/20210327/cb90dfa4/attachment-0001.htm>
More information about the pulseaudio-commits
mailing list