From 181e9c6d5d11cb1e5d36a2777eeb233ad8ed00e5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 9 Oct 2008 18:15:23 +0200 Subject: big pile of updates to match more what happened with libcanberra --- src/proplist.c | 413 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 src/proplist.c (limited to 'src/proplist.c') diff --git a/src/proplist.c b/src/proplist.c new file mode 100644 index 0000000..ceab702 --- /dev/null +++ b/src/proplist.c @@ -0,0 +1,413 @@ +/*** + This file is part of libsydney. + + Copyright 2007-2008 Lennart Poettering + + libsydney is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation, either version 2.1 of the + License, or (at your option) any later version. + + libsydney is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with libsydney. If not, see + . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include "sydney.h" +#include "proplist.h" +#include "macro.h" +#include "malloc.h" + +static unsigned calc_hash(const char *c) { + unsigned hash = 0; + + for (; *c; c++) + hash = 31 * hash + (unsigned) *c; + + return hash; +} + +/** + * sa_proplist_create: + * @p: A pointer where to fill in a pointer for the new property list. + * + * Allocate a new empty property list. + * + * Returns: 0 on success, negative error code on error. + */ +int sa_proplist_create(sa_proplist **_p) { + sa_proplist *p; + sa_return_val_if_fail(_p, SA_ERROR_INVALID); + + if (!(p = sa_new0(sa_proplist, 1))) + return SA_ERROR_OOM; + + if (!(p->mutex = sa_mutex_new(FALSE, FALSE))) { + sa_free(p); + return SA_ERROR_OOM; + } + + *_p = p; + + return SA_SUCCESS; +} + +static int _unset(sa_proplist *p, const char *key) { + sa_prop *prop, *nprop; + unsigned i; + + sa_return_val_if_fail(p, SA_ERROR_INVALID); + sa_return_val_if_fail(key, SA_ERROR_INVALID); + + i = calc_hash(key) % N_HASHTABLE; + + nprop = NULL; + for (prop = p->prop_hashtable[i]; prop; nprop = prop, prop = prop->next_in_slot) + if (strcmp(prop->key, key) == 0) + break; + + if (prop) { + if (nprop) + nprop->next_in_slot = prop->next_in_slot; + else + p->prop_hashtable[i] = prop->next_in_slot; + + if (prop->prev_item) + prop->prev_item->next_item = prop->next_item; + else + p->first_item = prop->next_item; + + if (prop->next_item) + prop->next_item->prev_item = prop->prev_item; + + sa_free(prop->key); + sa_free(prop); + } + + return SA_SUCCESS; +} + +/** + * sa_proplist_sets: + * @p: The property list to add this key/value pair to + * @key: The key for this key/value pair + * @value: The value for this key/value pair + * + * Add a new string key/value pair to the property list. + * + * Returns: 0 on success, negative error code on error. + */ + +int sa_proplist_sets(sa_proplist *p, const char *key, const char *value) { + sa_return_val_if_fail(p, SA_ERROR_INVALID); + sa_return_val_if_fail(key, SA_ERROR_INVALID); + sa_return_val_if_fail(value, SA_ERROR_INVALID); + + return sa_proplist_set(p, key, value, strlen(value)+1); +} + +/** + * sa_proplist_setf: + * @p: The property list to add this key/value pair to + * @key: The key for this key/value pair + * @format: The format string for the value for this key/value pair + * @...: The parameters for the format string + * + * Much like sa_proplist_sets(): add a new string key/value pair to + * the property list. Takes a standard C format string plus arguments + * and formats a string of it. + * + * Returns: 0 on success, negative error code on error. + */ + +int sa_proplist_setf(sa_proplist *p, const char *key, const char *format, ...) { + int ret; + char *k; + sa_prop *prop; + size_t size = 100; + unsigned h; + + sa_return_val_if_fail(p, SA_ERROR_INVALID); + sa_return_val_if_fail(key, SA_ERROR_INVALID); + sa_return_val_if_fail(format, SA_ERROR_INVALID); + + if (!(k = sa_strdup(key))) + return SA_ERROR_OOM; + + for (;;) { + va_list ap; + int r; + + if (!(prop = sa_malloc(SA_ALIGN(sizeof(sa_prop)) + size))) { + sa_free(k); + return SA_ERROR_OOM; + } + + va_start(ap, format); + r = vsnprintf(SA_PROP_DATA(prop), size, format, ap); + va_end(ap); + + ((char*) SA_PROP_DATA(prop))[size-1] = 0; + + if (r > -1 && (size_t) r < size) { + prop->nbytes = (size_t) r+1; + break; + } + + if (r > -1) /* glibc 2.1 */ + size = (size_t) r+1; + else /* glibc 2.0 */ + size *= 2; + + sa_free(prop); + } + + prop->key = k; + + sa_mutex_lock(p->mutex); + + if ((ret = _unset(p, key)) < 0) { + sa_free(prop); + sa_free(k); + goto finish; + } + + h = calc_hash(key) % N_HASHTABLE; + + prop->next_in_slot = p->prop_hashtable[h]; + p->prop_hashtable[h] = prop; + + prop->prev_item = NULL; + prop->next_item = p->first_item; + p->first_item = prop; + +finish: + + sa_mutex_unlock(p->mutex); + + return ret; +} + +/** + * sa_proplist_set: + * @p: The property list to add this key/value pair to + * @key: The key for this key/value pair + * @data: The binary value for this key value pair + * @nbytes: The size of thebinary value for this key value pair. + * + * Add a new binary key/value pair to the property list. + * + * Returns: 0 on success, negative error code on error. + */ + +int sa_proplist_set(sa_proplist *p, const char *key, const void *data, size_t nbytes) { + int ret; + char *k; + sa_prop *prop; + unsigned h; + + sa_return_val_if_fail(p, SA_ERROR_INVALID); + sa_return_val_if_fail(key, SA_ERROR_INVALID); + sa_return_val_if_fail(!nbytes || data, SA_ERROR_INVALID); + + if (!(k = sa_strdup(key))) + return SA_ERROR_OOM; + + if (!(prop = sa_malloc(SA_ALIGN(sizeof(sa_prop)) + nbytes))) { + sa_free(k); + return SA_ERROR_OOM; + } + + prop->key = k; + prop->nbytes = nbytes; + memcpy(SA_PROP_DATA(prop), data, nbytes); + + sa_mutex_lock(p->mutex); + + if ((ret = _unset(p, key)) < 0) { + sa_free(prop); + sa_free(k); + goto finish; + } + + h = calc_hash(key) % N_HASHTABLE; + + prop->next_in_slot = p->prop_hashtable[h]; + p->prop_hashtable[h] = prop; + + prop->prev_item = NULL; + prop->next_item = p->first_item; + p->first_item = prop; + +finish: + + sa_mutex_unlock(p->mutex); + + return ret; +} + +/* Not exported, not self-locking */ +sa_prop* sa_proplist_get_unlocked(sa_proplist *p, const char *key) { + sa_prop *prop; + unsigned i; + + sa_return_val_if_fail(p, NULL); + sa_return_val_if_fail(key, NULL); + + i = calc_hash(key) % N_HASHTABLE; + + for (prop = p->prop_hashtable[i]; prop; prop = prop->next_in_slot) + if (strcmp(prop->key, key) == 0) + return prop; + + return NULL; +} + +/* Not exported, not self-locking */ +const char* sa_proplist_gets_unlocked(sa_proplist *p, const char *key) { + sa_prop *prop; + + sa_return_val_if_fail(p, NULL); + sa_return_val_if_fail(key, NULL); + + if (!(prop = sa_proplist_get_unlocked(p, key))) + return NULL; + + if (!memchr(SA_PROP_DATA(prop), 0, prop->nbytes)) + return NULL; + + return SA_PROP_DATA(prop); +} + +/** + * sa_proplist_destroy: + * @p: The property list to destroy + * + * Destroys a property list that was created with sa_proplist_create() earlier. + * + * Returns: 0 on success, negative error code on error. + */ + +int sa_proplist_destroy(sa_proplist *p) { + sa_prop *prop, *nprop; + + sa_return_val_if_fail(p, SA_ERROR_INVALID); + + for (prop = p->first_item; prop; prop = nprop) { + nprop = prop->next_item; + sa_free(prop->key); + sa_free(prop); + } + + sa_mutex_free(p->mutex); + + sa_free(p); + + return SA_SUCCESS; +} + +static int merge_into(sa_proplist *a, sa_proplist *b) { + int ret = SA_SUCCESS; + sa_prop *prop; + + sa_return_val_if_fail(a, SA_ERROR_INVALID); + sa_return_val_if_fail(b, SA_ERROR_INVALID); + + sa_mutex_lock(b->mutex); + + for (prop = b->first_item; prop; prop = prop->next_item) + if ((ret = sa_proplist_set(a, prop->key, SA_PROP_DATA(prop), prop->nbytes)) < 0) + break; + + sa_mutex_unlock(b->mutex); + + return ret; +} + +int sa_proplist_merge(sa_proplist **_a, sa_proplist *b, sa_proplist *c) { + sa_proplist *a; + int ret; + + sa_return_val_if_fail(_a, SA_ERROR_INVALID); + sa_return_val_if_fail(b, SA_ERROR_INVALID); + sa_return_val_if_fail(c, SA_ERROR_INVALID); + + if ((ret = sa_proplist_create(&a)) < 0) + return ret; + + if ((ret = merge_into(a, b)) < 0 || + (ret = merge_into(a, c)) < 0) { + sa_proplist_destroy(a); + return ret; + } + + *_a = a; + return SA_SUCCESS; +} + +sa_bool_t sa_proplist_contains(sa_proplist *p, const char *key) { + sa_bool_t b; + + sa_return_val_if_fail(p, FALSE); + sa_return_val_if_fail(key, FALSE); + + sa_mutex_lock(p->mutex); + b = !!sa_proplist_get_unlocked(p, key); + sa_mutex_unlock(p->mutex); + + return b; +} + +int sa_proplist_merge_ap(sa_proplist *p, va_list ap) { + int ret; + + sa_return_val_if_fail(p, SA_ERROR_INVALID); + + for (;;) { + const char *key, *value; + + if (!(key = va_arg(ap, const char*))) + break; + + if (!(value = va_arg(ap, const char*))) + return SA_ERROR_INVALID; + + if ((ret = sa_proplist_sets(p, key, value)) < 0) + return ret; + } + + return SA_SUCCESS; +} + +int sa_proplist_from_ap(sa_proplist **_p, va_list ap) { + int ret; + sa_proplist *p; + + sa_return_val_if_fail(_p, SA_ERROR_INVALID); + + if ((ret = sa_proplist_create(&p)) < 0) + return ret; + + if ((ret = sa_proplist_merge_ap(p, ap)) < 0) + goto fail; + + *_p = p; + + return SA_SUCCESS; + +fail: + sa_assert_se(sa_proplist_destroy(p) == SA_SUCCESS); + + return ret; +} -- cgit