From 8140932afdccbe29e7b03456f965b28a0abf24bb Mon Sep 17 00:00:00 2001 From: Georg Chini Date: Tue, 14 Jan 2020 16:41:59 +0100 Subject: message-params: Add read/write functions for various simple data types The following functions have been added: pa_message_params_write_double() - writes a double to a pa_message_params structure pa_message_params_write_int64() - writes an integer to a pa_message_params structure pa_message_params_write_uint64() - writes an unsigned to a pa_message_params structure pa_message_params_write_bool() - writes a boolean to a pa_message_params structure pa_message_params_read_double() - read a double from a parameter list pa_message_params_read_int64() - read an integer from a parameter list pa_message_params_read_uint64() - read an unsigned from a parameter list pa_message_params_read_bool() - read a boolean from a parameter list The patch also improves the doxygen documentation im message-params.h Part-of: --- src/map-file | 8 ++ src/pulse/message-params.c | 192 +++++++++++++++++++++++++++++++++++++++++++-- src/pulse/message-params.h | 62 ++++++++++++++- 3 files changed, 250 insertions(+), 12 deletions(-) diff --git a/src/map-file b/src/map-file index 4d196c11d..172fd441d 100644 --- a/src/map-file +++ b/src/map-file @@ -233,11 +233,19 @@ 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_int64; pa_message_params_read_raw; pa_message_params_read_string; +pa_message_params_read_uint64; 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_thread_make_realtime; pa_operation_cancel; diff --git a/src/pulse/message-params.c b/src/pulse/message-params.c index 236800bca..8885f95e8 100644 --- a/src/pulse/message-params.c +++ b/src/pulse/message-params.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -62,7 +63,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) { /* Empty or no string */ if (!current || *current == 0) - return 0; + return PA_MESSAGE_PARAMS_LIST_END; /* Find opening brace */ while (*current != 0) { @@ -79,7 +80,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) { /* unexpected closing brace, parse error */ if (*current == '}' && !found_backslash) - return -1; + return PA_MESSAGE_PARAMS_PARSE_ERROR; found_backslash = false; current++; @@ -87,7 +88,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) { /* No opening brace found, end of string */ if (*current == 0) - return 0; + return PA_MESSAGE_PARAMS_LIST_END; if (is_unpacked) *is_unpacked = true; @@ -118,7 +119,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) { /* Parse error, closing brace missing */ if (open_braces != 0) { *result = NULL; - return -1; + return PA_MESSAGE_PARAMS_PARSE_ERROR; } /* Replace } with 0 */ @@ -126,7 +127,7 @@ static int split_list(char *c, char **result, bool *is_unpacked, void **state) { *state = current + 1; - return 1; + return PA_MESSAGE_PARAMS_OK; } /* Read functions */ @@ -143,13 +144,13 @@ int pa_message_params_read_string(char *c, const char **result, void **state) { pa_assert(result); - if ((r = split_list(c, &start_pos, &is_unpacked, state)) == 1) + 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 = -1; + r = PA_MESSAGE_PARAMS_PARSE_ERROR; value = NULL; } @@ -165,6 +166,129 @@ 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; + + /* Convert to double */ + 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; +} + /* Write functions. The functions are wrapper functions around pa_strbuf, * so that the client does not need to use pa_strbuf directly. */ @@ -239,7 +363,8 @@ void pa_message_params_write_string(pa_message_params *params, const char *value 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 */ + /* Null value is written as empty element if add_braces is true. + * Otherwise nothing is written. */ if (!value) value = ""; @@ -248,3 +373,54 @@ void pa_message_params_write_raw(pa_message_params *params, const char *value, b 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}"); +} diff --git a/src/pulse/message-params.h b/src/pulse/message-params.h index f30164ff9..a2e0f9e8d 100644 --- a/src/pulse/message-params.h +++ b/src/pulse/message-params.h @@ -26,28 +26,68 @@ #include /** \file - * Utility functions for reading and writing message parameters */ + * 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.\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 raw data from a parameter list. Used to split a message parameter +/** 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); + +/** Read an integer from parameter list in c. \since 15.0 */ +int pa_message_params_read_int64(char *c, int64_t *result, 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. Escaped curly braces and backslashes +/** 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); +/** 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); + /** @} */ /** @{ \name Write functions */ -/** Create a new pa_message_params structure \since 15.0 */ +/** 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 */ @@ -62,6 +102,17 @@ 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); @@ -70,6 +121,9 @@ void pa_message_params_write_string(pa_message_params *params, const char *value * 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 -- cgit v1.2.1