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.
This commit is contained in:
Barnabás Pőcze 2021-06-12 16:24:59 +02:00
parent 5459c759ee
commit be18d052ad
3 changed files with 189 additions and 0 deletions

View file

@ -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); 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) static struct properties *properties_new(int prealloc)
{ {
struct properties *impl; struct properties *impl;
@ -613,3 +643,78 @@ int pw_properties_serialize_dict(FILE *f, const struct spa_dict *dict, uint32_t
} }
return count; 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);
}
}

View file

@ -103,6 +103,11 @@ pw_properties_iterate(const struct pw_properties *properties, void **state);
#define PW_PROPERTIES_FLAG_NL (1<<0) #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_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) { static inline bool pw_properties_parse_bool(const char *value) {
return spa_atob(value); return spa_atob(value);
} }

View file

@ -605,6 +605,84 @@ PWTEST(properties_update)
return PWTEST_PASS; 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_SUITE(properties)
{ {
pwtest_add(properties_abi, PWTEST_NOARG); pwtest_add(properties_abi, PWTEST_NOARG);
@ -624,6 +702,7 @@ PWTEST_SUITE(properties)
pwtest_add(properties_new_dict, PWTEST_NOARG); pwtest_add(properties_new_dict, PWTEST_NOARG);
pwtest_add(properties_new_json, PWTEST_NOARG); pwtest_add(properties_new_json, PWTEST_NOARG);
pwtest_add(properties_update, PWTEST_NOARG); pwtest_add(properties_update, PWTEST_NOARG);
pwtest_add(properties_move, PWTEST_NOARG);
return PWTEST_PASS; return PWTEST_PASS;
} }