From be18d052adbad3bc06cebfc09350b94325034ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Sat, 12 Jun 2021 16:24:59 +0200 Subject: [PATCH] pipewire: properties: introduce pw_properties_move Add pw_properties_move(dst, dst_key, src, src_key, fallback_value) which can move a key-value pair from one property list into another, optionally using the fallback value as value if `src_key` cannot be found in `src`. Add tests as well. This commit also adds `pw_properties_rename()`, which can be used to change the key in a dictionary, but that is not made part of the public API yet. --- src/pipewire/properties.c | 105 ++++++++++++++++++++++++++++++++++++++ src/pipewire/properties.h | 5 ++ test/test-properties.c | 79 ++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) diff --git a/src/pipewire/properties.c b/src/pipewire/properties.c index b09987924..74b5aa92a 100644 --- a/src/pipewire/properties.c +++ b/src/pipewire/properties.c @@ -96,6 +96,36 @@ static struct spa_dict_item *find_item(const struct pw_properties *this, const c return pw_array_get_unchecked(&impl->items, index, struct spa_dict_item); } +static int rename_item(struct pw_properties *properties, struct spa_dict_item *item, + const char *new_key) +{ + struct spa_dict_item *item2; + + if (SPA_UNLIKELY(spa_streq(item->key, new_key))) + return 0; + + item2 = find_item(properties, new_key); + if (item2 != NULL) { + free((char *) item2->value); + free((char *) item->key); + + item->key = item2->key; + + remove_item(properties, item2); + } else { + new_key = strdup(new_key); + if (new_key == NULL) + return -errno; + + free((char *) item->key); + item->key = new_key; + + SPA_FLAG_CLEAR(properties->dict.flags, SPA_DICT_FLAG_SORTED); + } + + return 1; +} + static struct properties *properties_new(int prealloc) { struct properties *impl; @@ -613,3 +643,78 @@ int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t } return count; } + +static int pw_properties_rename(struct pw_properties *properties, + const char *old_key, const char *new_key) +{ + spa_assert(properties); + spa_assert(old_key); + spa_assert(new_key); + + struct spa_dict_item *item; + + item = find_item(properties, old_key); + if (item == NULL) + return -ENOENT; + + return rename_item(properties, item, new_key); +} + +/** Move a key-value pair from one property list into another + * + * \param dst a \ref pw_properties into which to move + * \param dst_key key in \a new + * \param src a \ref pw_properties from which to move + * \param src_key key in \a src + * \param fallback_value the value to use if \a src_key cannot be found in \a src + * \return 1 if \a dst has been changed, 0 if no updates were done, or + * -ENOENT if \a src_key could not be found and \a fallback_value + * was not specified. + * + * This function finds \a src_key in \a src, and moves the corresponding value + * into \a dst under the key \a dst_key. If \a src_key is not found in \a src, + * then \a fallback_value will be used instead. If \a src_key is not found, + * and \a fallback_value is NULL, then -ENOENT is returned. \a src and \a dst + * may refer to the same object. \a src_key and \a dst_key may be the same. + * + * \since 0.3.31 + */ +SPA_EXPORT +int pw_properties_move(struct pw_properties *dst, const char *dst_key, + struct pw_properties *src, const char *src_key, + const char *fallback_value) +{ + spa_assert(dst); + spa_assert(dst_key); + spa_assert(src); + spa_assert(src_key); + + struct spa_dict_item *item; + int res; + + if (SPA_LIKELY(src != dst)) { + item = find_item(src, src_key); + if (item != NULL) { + res = do_replace(dst, dst_key, (char *) item->value, false); + + free((char *) item->key); + remove_item(src, item); + + return res; + } + + if (fallback_value != NULL) + return do_replace(dst, dst_key, (char *) fallback_value, true); + + return -ENOENT; + } else { + if (fallback_value == NULL) + return pw_properties_rename(dst, src_key, dst_key); + + item = find_item(dst, src_key); + if (item != NULL) + return rename_item(dst, item, dst_key); + + return do_replace(dst, dst_key, (char *) fallback_value, true); + } +} diff --git a/src/pipewire/properties.h b/src/pipewire/properties.h index 7c2632dc3..925bb57ab 100644 --- a/src/pipewire/properties.h +++ b/src/pipewire/properties.h @@ -103,6 +103,11 @@ pw_properties_iterate(const struct pw_properties *properties, void **state); #define PW_PROPERTIES_FLAG_NL (1<<0) int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t flags); +int +pw_properties_move(struct pw_properties *dst, const char *dst_key, + struct pw_properties *src, const char *src_key, + const char *fallback_value); + static inline bool pw_properties_parse_bool(const char *value) { return spa_atob(value); } diff --git a/test/test-properties.c b/test/test-properties.c index 0a4d38754..682693b23 100644 --- a/test/test-properties.c +++ b/test/test-properties.c @@ -605,6 +605,84 @@ PWTEST(properties_update) return PWTEST_PASS; } +PWTEST(properties_move) +{ + struct pw_properties *a = pw_properties_new(NULL, NULL), + *b = pw_properties_new(NULL, NULL); + + pwtest_ptr_notnull(a); + pwtest_ptr_notnull(b); + + pwtest_int_eq(pw_properties_set(a, "some key", "foo"), 1); + pwtest_int_eq(pw_properties_set(a, "some other key", "bar"), 1); + + /* + * a = {"some key" : "foo", "some other key" : "bar"} + * b = {} + */ + + pwtest_int_eq(pw_properties_move(b, "different key", a, "some key", NULL), 1); + pwtest_str_eq(pw_properties_get(b, "different key"), "foo"); + pwtest_str_eq(pw_properties_get(a, "some key"), NULL); + + /* + * a = {"some other key" : "bar"} + * b = {"different key" : "foo"} + */ + + pwtest_ptr_null(pw_properties_get(a, "server_address")); + pwtest_int_eq(pw_properties_move(b, "server.address", a, "server_address", NULL), -ENOENT); + pwtest_str_eq(pw_properties_get(b, "server.address"), NULL); + + /* + * a = {"some other key" : "bar"} + * b = {"different key" : "foo"} + */ + + pwtest_int_eq(pw_properties_move(b, "server.port", a, "server_port", "4444"), 1); + pwtest_str_eq(pw_properties_get(b, "server.port"), "4444"); + pwtest_str_eq(pw_properties_get(a, "server_port"), NULL); + + /* + * a = {"some other key" : "bar"} + * b = {"different key" : "foo", "server.port" : "4444"} + */ + + pwtest_int_eq(pw_properties_move(b, "server.port", b, "server.port", NULL), 0); + pwtest_str_eq(pw_properties_get(b, "server.port"), "4444"); + + /* + * a = {"some other key" : "bar"} + * b = {"different key" : "foo", "server.port" : "4444"} + */ + + pwtest_int_eq(pw_properties_move(b, "server_port", b, "server.port", NULL), 1); + pwtest_str_eq(pw_properties_get(b, "server_port"), "4444"); + pwtest_str_eq(pw_properties_get(b, "server.port"), NULL); + + /* + * a = {"some other key" : "bar"} + * b = {"different key" : "foo", "server_port" : "4444"} + */ + + pwtest_int_eq(pw_properties_move(b, "server_port", a, "server_port", "9999"), 1); + pwtest_str_eq(pw_properties_get(b, "server_port"), "9999"); + + /* + * a = {"some other key" : "bar"} + * b = {"different key" : "foo", "server_port" : "9999"} + */ + + pwtest_int_eq(pw_properties_move(b, "server_port", b, "server.port", "8888"), 1); + pwtest_str_eq(pw_properties_get(b, "server_port"), "8888"); + pwtest_str_eq(pw_properties_get(b, "server.port"), NULL); + + pw_properties_free(a); + pw_properties_free(b); + + return PWTEST_PASS; +} + PWTEST_SUITE(properties) { pwtest_add(properties_abi, PWTEST_NOARG); @@ -624,6 +702,7 @@ PWTEST_SUITE(properties) pwtest_add(properties_new_dict, PWTEST_NOARG); pwtest_add(properties_new_json, PWTEST_NOARG); pwtest_add(properties_update, PWTEST_NOARG); + pwtest_add(properties_move, PWTEST_NOARG); return PWTEST_PASS; }