[pulseaudio-discuss] [PATCH v2 7/9] proplist: Add pa_proplist_update_info
David Henningsson
david.henningsson at canonical.com
Thu Mar 14 06:58:52 PDT 2013
On 02/20/2013 07:24 PM, Tanu Kaskinen wrote:
> I was writing function pa_device_port_update_proplist(), and I wanted
> it to send change notifications only if the proplist actually changes.
> pa_proplist_update() doesn't provide any indication about whether the
> proplist changed or not, so some kind of a solution was needed.
>
> The simple solution would be to create a copy of the port proplist
> before calling pa_proplist_update() and check if the copy equals the
> port proplist after calling pa_proplist_update(). That felt overly
> wasteful, however: it would mean copying the whole property list and
> comparing every property in it whenever someone changes even just one
> property.
>
> So, I invented a more complex solution: a pa_proplist_update_info
> object that holds a description of per-property operations to be
> applied to a property list. pa_proplist_apply_update_info() iterates
> through the operations and applies them one by one, keeping track of
> whether the operations cause actual changes.
I guess that's one way to solve it. I would probably have gone for the
slightly simpler solution of just keeping a flag inside the proplist.
The proplist object itself will set the flag whenever a "real" change
occurs, and it can be manually reset by just calling, say
pa_proplist_reset_change_flag() or so.
> ---
> src/map-file | 4 +
I don't think we need to add this to the external API unless somebody
complains about missing that feature.
> src/pulse/proplist.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++----
> src/pulse/proplist.h | 70 ++++++++++++++++
> 3 files changed, 279 insertions(+), 15 deletions(-)
>
> diff --git a/src/map-file b/src/map-file
> index 91d61c2..3bd5ddb 100644
> --- a/src/map-file
> +++ b/src/map-file
> @@ -224,6 +224,7 @@ pa_operation_set_state_callback;
> pa_operation_unref;
> pa_parse_sample_format;
> pa_path_get_filename;
> +pa_proplist_apply_update_info;
> pa_proplist_clear;
> pa_proplist_contains;
> pa_proplist_copy;
> @@ -246,6 +247,9 @@ pa_proplist_to_string_sep;
> pa_proplist_unset;
> pa_proplist_unset_many;
> pa_proplist_update;
> +pa_proplist_update_info_add;
> +pa_proplist_update_info_free;
> +pa_proplist_update_info_new;
> pa_rtclock_now;
> pa_sample_format_is_be;
> pa_sample_format_is_le;
> diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
> index abd551b..5236179 100644
> --- a/src/pulse/proplist.c
> +++ b/src/pulse/proplist.c
> @@ -35,12 +35,22 @@
>
> #include "proplist.h"
>
> +struct pa_proplist_update_info {
> + pa_hashmap *items;
> +};
> +
> struct property {
> char *key;
> void *value;
> size_t nbytes;
> };
>
> +struct update_info_item {
> + pa_proplist_operation_t operation;
> + char *key;
> + char *sets_value;
> +};
> +
> #define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
> #define MAKE_PROPLIST(p) ((pa_proplist*) (p))
>
> @@ -73,11 +83,33 @@ void pa_proplist_free(pa_proplist* p) {
> pa_hashmap_free(MAKE_HASHMAP(p), (pa_free_cb_t) property_free);
> }
>
> +/* Returns true if the proplist changed. */
> +static bool pa_proplist_sets_internal(pa_proplist *proplist, const char *key, const char *value) {
> + size_t nbytes;
> + struct property *property;
> +
> + nbytes = strlen(value) + 1;
> + property = pa_hashmap_get(MAKE_HASHMAP(proplist), key);
> +
> + if (!property) {
> + property = pa_xnew(struct property, 1);
> + property->key = pa_xstrdup(key);
> + pa_hashmap_put(MAKE_HASHMAP(proplist), property->key, property);
> + } else {
> + if (nbytes == property->nbytes && !memcmp(value, property->value, nbytes))
> + return false;
> +
> + pa_xfree(property->value);
> + }
> +
> + property->value = pa_xstrdup(value);
> + property->nbytes = nbytes;
> +
> + return true;
> +}
> +
> /** Will accept only valid UTF-8 */
> int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
> - struct property *prop;
> - pa_bool_t add = FALSE;
> -
> pa_assert(p);
> pa_assert(key);
> pa_assert(value);
> @@ -85,18 +117,7 @@ int pa_proplist_sets(pa_proplist *p, const char *key, const char *value) {
> if (!pa_proplist_key_valid(key) || !pa_utf8_valid(value))
> return -1;
>
> - if (!(prop = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
> - prop = pa_xnew(struct property, 1);
> - prop->key = pa_xstrdup(key);
> - add = TRUE;
> - } else
> - pa_xfree(prop->value);
> -
> - prop->value = pa_xstrdup(value);
> - prop->nbytes = strlen(value)+1;
> -
> - if (add)
> - pa_hashmap_put(MAKE_HASHMAP(p), prop->key, prop);
> + pa_proplist_sets_internal(p, key, value);
>
> return 0;
> }
> @@ -708,3 +729,172 @@ int pa_proplist_equal(pa_proplist *a, pa_proplist *b) {
>
> return 1;
> }
> +
> +static struct update_info_item *update_info_item_new(pa_proplist_operation_t operation, const char *key) {
> + struct update_info_item *item;
> +
> + pa_assert(key);
> +
> + item = pa_xnew0(struct update_info_item, 1);
> + item->operation = operation;
> + item->key = pa_xstrdup(key);
> +
> + return item;
> +}
> +
> +static void update_info_item_free(struct update_info_item *item) {
> + pa_assert(item);
> +
> + pa_xfree(item->sets_value);
> + pa_xfree(item->key);
> + pa_xfree(item);
> +}
> +
> +pa_proplist_update_info *pa_proplist_update_info_new(void) {
> + pa_proplist_update_info *info;
> +
> + info = pa_xnew0(pa_proplist_update_info, 1);
> + info->items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
> +
> + return info;
> +}
> +
> +void pa_proplist_update_info_free(pa_proplist_update_info *info) {
> + pa_assert(info);
> +
> + pa_hashmap_free(info->items, (pa_free_cb_t) update_info_item_free);
> + pa_xfree(info);
> +}
> +
> +int pa_proplist_update_info_add(pa_proplist_update_info *info, int operation, ...) {
> + /* This function will first validate and copy the function arguments into
> + * the new_items hashmap. After all inputs have been validated, the
> + * new_items contents are moved into info->items. If validation errors
> + * occur, info won't be altered. */
> +
> + pa_proplist_operation_t op;
> + va_list ap;
> + pa_hashmap *new_items;
> + const char *key;
> + struct update_info_item *new_item;
> + void *state;
> +
> + pa_assert(info);
> +
> + op = operation;
> +
> + if (op == PA_PROPLIST_OPERATION_INVALID)
> + return 0;
> +
> + va_start(ap, operation);
> + new_items = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
> +
> + /* The "copy and validate arguments" loop. */
> + while (op != PA_PROPLIST_OPERATION_INVALID) {
> + switch (op) {
> + case PA_PROPLIST_OPERATION_UNSET: {
> + struct update_info_item *item;
> +
> + key = va_arg(ap, const char *);
> +
> + if (!key || !pa_proplist_key_valid(key))
> + goto fail;
> +
> + item = pa_hashmap_get(new_items, key);
> +
> + if (item)
> + item->operation = PA_PROPLIST_OPERATION_UNSET;
> + else {
> + item = update_info_item_new(PA_PROPLIST_OPERATION_UNSET, key);
> + pa_hashmap_put(new_items, key, item);
> + }
> +
> + break;
> + }
> +
> + case PA_PROPLIST_OPERATION_SETS: {
> + const char *value;
> + struct update_info_item *item;
> +
> + key = va_arg(ap, const char *);
> +
> + if (!key || !pa_proplist_key_valid(key))
> + goto fail;
> +
> + value = va_arg(ap, const char *);
> +
> + if (!value || !pa_utf8_valid(value))
> + goto fail;
> +
> + item = pa_hashmap_get(new_items, key);
> +
> + if (item)
> + item->operation = PA_PROPLIST_OPERATION_SETS;
> + else {
> + item = update_info_item_new(PA_PROPLIST_OPERATION_SETS, key);
> + pa_hashmap_put(new_items, key, item);
> + }
> +
> + pa_xfree(item->sets_value);
> + item->sets_value = pa_xstrdup(value);
> +
> + break;
> + }
> +
> + default:
> + goto fail;
> + }
> +
> + op = va_arg(ap, int);
> + }
> +
> + va_end(ap);
> +
> + /* The "move items from new_items to info->items" loop. */
> + PA_HASHMAP_FOREACH(new_item, new_items, state) {
> + struct update_info_item *old_item;
> +
> + old_item = pa_hashmap_remove(info->items, new_item->key);
> +
> + if (old_item)
> + update_info_item_free(old_item);
> +
> + pa_hashmap_put(info->items, new_item->key, new_item);
> + }
> +
> + pa_hashmap_free(new_items, NULL);
> +
> + return 0;
> +
> +fail:
> + va_end(ap);
> + pa_hashmap_free(new_items, (pa_free_cb_t) update_info_item_free);
> +
> + return -PA_ERR_INVALID;
> +}
> +
> +int pa_proplist_apply_update_info(pa_proplist *proplist, pa_proplist_update_info *info) {
> + struct update_info_item *item;
> + void *state;
> + bool changed = false;
> +
> + pa_assert(proplist);
> + pa_assert(info);
> +
> + PA_HASHMAP_FOREACH(item, info->items, state) {
> + switch (item->operation) {
> + case PA_PROPLIST_OPERATION_INVALID:
> + pa_assert_not_reached();
> +
> + case PA_PROPLIST_OPERATION_UNSET:
> + changed |= pa_proplist_unset(proplist, item->key) >= 0;
> + break;
> +
> + case PA_PROPLIST_OPERATION_SETS:
> + changed |= pa_proplist_sets_internal(proplist, item->key, item->sets_value);
> + break;
> + }
> + }
> +
> + return changed;
> +}
> diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
> index cb53cf4..a2bbeff 100644
> --- a/src/pulse/proplist.h
> +++ b/src/pulse/proplist.h
> @@ -273,6 +273,28 @@ PA_C_DECL_BEGIN
> * as keys and arbitrary data as values. \since 0.9.11 */
> typedef struct pa_proplist pa_proplist;
>
> +/** A description of changes to be applied to a property list. \since 4.0 */
> +typedef struct pa_proplist_update_info pa_proplist_update_info;
> +
> +/** Operation type enumeration for pa_proplist_update_info_add(). \since 4.0 */
> +typedef enum {
> + PA_PROPLIST_OPERATION_INVALID = -1,
> + /**< Invalid operation type. */
> +
> + PA_PROPLIST_OPERATION_UNSET = 0,
> + /**< Unset a property. Takes the property name as a parameter. */
> +
> + PA_PROPLIST_OPERATION_SETS
> + /**< Set a string property. Takes the property name and the new value as
> + * parameters. Accepts only UTF-8 strings. */
> +} pa_proplist_operation_t;
> +
> +/** \cond fulldocs */
> +#define PA_PROPLIST_OPERATION_INVALID PA_PROPLIST_OPERATION_INVALID
> +#define PA_PROPLIST_OPERATION_UNSET PA_PROPLIST_OPERATION_UNSET
> +#define PA_PROPLIST_OPERATION_SETS PA_PROPLIST_OPERATION_SETS
> +/** \endcond */
> +
> /** Allocate a property list. \since 0.9.11 */
> pa_proplist* pa_proplist_new(void);
>
> @@ -406,6 +428,54 @@ int pa_proplist_isempty(pa_proplist *p);
> * \since 0.9.16 */
> int pa_proplist_equal(pa_proplist *a, pa_proplist *b);
>
> +/** Allocate an empty update info object. \since 4.0 */
> +pa_proplist_update_info *pa_proplist_update_info_new(void);
> +
> +/** Frees "info". \since 4.0 */
> +void pa_proplist_update_info_free(pa_proplist_update_info *info);
> +
> +/** Add update operations to "info". The arguments are a list of operations,
> + * each item in the list consisting of the operation type and parameters
> + * specific for that operation. The operation type must be one of the constants
> + * defined in the #pa_proplist_operation_t enumeration. The operation
> + * parameters are documented in the documentation of #pa_proplist_operation_t.
> + * The list is terminated with #PA_PROPLIST_OPERATION_INVALID. Any strings
> + * given as operation parameters are copied. Returns 0 on success, or
> + * -PA_ERR_INVALID in case of invalid arguments. In case of failure, "info" is
> + * not modified at all.
> + *
> + * Example:
> + *
> + * char *foo = ...;
> + * char *bar = ...;
> + * pa_proplist *proplist = ...;
> + * pa_proplist_update_info *info = pa_proplist_info_new();
> + * int changed;
> + *
> + * if (foo && bar)
> + * pa_proplist_update_info_add(info,
> + * PA_PROPLIST_OPERATION_SETS, "some.property", foo,
> + * PA_PROPLIST_OPERATION_SETS, "some.other.property", bar,
> + * PA_PROPLIST_OPERATION_INVALID);
> + * else
> + * pa_proplist_update_info_add(info,
> + * PA_PROPLIST_OPERATION_UNSET, "some.property",
> + * PA_PROPLIST_OPEARTION_UNSET, "some.other.property",
> + * PA_PROPLIST_OPERATION_INVALID);
> + *
> + * changed = pa_proplist_apply_update_info(proplist, info);
> + * pa_proplist_update_info_free(info);
> + *
> + * if (changed)
> + * printf("Property list changed!\n");
> + *
> + * \since 4.0 */
> +int pa_proplist_update_info_add(pa_proplist_update_info *info, int operation, ...);
> +
> +/** Apply a list of updates to "proplist". Returns a non-zero value if the
> + * property list changed. \since 4.0 */
> +int pa_proplist_apply_update_info(pa_proplist *proplist, pa_proplist_update_info *info);
> +
> PA_C_DECL_END
>
> #endif
>
--
David Henningsson, Canonical Ltd.
https://launchpad.net/~diwic
More information about the pulseaudio-discuss
mailing list