[pulseaudio-discuss] [PATCH v2 7/9] proplist: Add pa_proplist_update_info
Tanu Kaskinen
tanuk at iki.fi
Wed Feb 20 10:24:02 PST 2013
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.
---
src/map-file | 4 +
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
--
1.7.10.4
More information about the pulseaudio-discuss
mailing list