2018-08-21 13:24:17 +02:00
|
|
|
/** A generic linked list
|
|
|
|
*
|
|
|
|
* Linked lists a used for several data structures in the code.
|
|
|
|
*
|
|
|
|
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
2019-01-13 00:42:26 +01:00
|
|
|
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
2018-08-21 13:24:17 +02:00
|
|
|
* @license GNU General Public License (version 3)
|
|
|
|
*
|
2018-08-21 16:54:58 +02:00
|
|
|
* VILLAScommon
|
2018-08-21 13:24:17 +02:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <villas/list.h>
|
|
|
|
#include <villas/utils.h>
|
|
|
|
|
|
|
|
/* Compare functions */
|
|
|
|
static int cmp_lookup(const void *a, const void *b) {
|
2019-04-07 15:12:32 +02:00
|
|
|
struct name_tag {
|
2018-08-21 13:24:17 +02:00
|
|
|
char *name;
|
2019-04-07 15:12:32 +02:00
|
|
|
};
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2019-04-07 15:12:32 +02:00
|
|
|
const name_tag *obj = (struct name_tag *) a;
|
|
|
|
|
|
|
|
return strcmp(obj->name, (char *) b);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_contains(const void *a, const void *b) {
|
|
|
|
return a == b ? 0 : 1;
|
|
|
|
}
|
|
|
|
|
2018-10-21 20:26:28 +01:00
|
|
|
#ifdef __APPLE__
|
|
|
|
static int cmp_sort(void *thunk, const void *a, const void *b) {
|
|
|
|
#else
|
2018-08-21 13:24:17 +02:00
|
|
|
static int cmp_sort(const void *a, const void *b, void *thunk) {
|
2018-10-21 20:26:28 +01:00
|
|
|
#endif
|
2018-08-21 13:24:17 +02:00
|
|
|
cmp_cb_t cmp = (cmp_cb_t) thunk;
|
|
|
|
|
2018-10-21 20:26:28 +01:00
|
|
|
return cmp(*(const void **) a, *(const void **) b);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
int vlist_init(struct vlist *l)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
assert(l->state == STATE_DESTROYED);
|
|
|
|
|
2019-04-08 10:42:45 +02:00
|
|
|
pthread_mutex_init(&l->lock, nullptr);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
l->length = 0;
|
|
|
|
l->capacity = 0;
|
2019-04-08 10:42:45 +02:00
|
|
|
l->array = nullptr;
|
2018-08-21 13:24:17 +02:00
|
|
|
l->state = STATE_INITIALIZED;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
int vlist_destroy(struct vlist *l, dtor_cb_t destructor, bool release)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state != STATE_DESTROYED);
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(l); i++) {
|
|
|
|
void *e = vlist_at(l, i);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
if (destructor)
|
2018-08-22 11:29:39 +02:00
|
|
|
destructor(e);
|
2018-08-21 13:24:17 +02:00
|
|
|
if (release)
|
2018-08-22 11:29:39 +02:00
|
|
|
free(e);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
free(l->array);
|
|
|
|
|
|
|
|
l->length = -1;
|
|
|
|
l->capacity = 0;
|
2019-04-08 10:42:45 +02:00
|
|
|
l->array = nullptr;
|
2018-08-22 11:29:39 +02:00
|
|
|
l->state = STATE_DESTROYED;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
pthread_mutex_destroy(&l->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
void vlist_push(struct vlist *l, void *p)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
|
|
|
/* Resize array if out of capacity */
|
|
|
|
if (l->length >= l->capacity) {
|
|
|
|
l->capacity += LIST_CHUNKSIZE;
|
2019-04-07 15:12:32 +02:00
|
|
|
l->array = (void **) realloc(l->array, l->capacity * sizeof(void *));
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
l->array[l->length] = p;
|
|
|
|
l->length++;
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
}
|
|
|
|
|
2019-02-24 09:22:20 +01:00
|
|
|
int vlist_remove(struct vlist *l, size_t idx)
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
|
|
|
if (idx >= l->length)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (size_t i = idx; i < l->length - 1; i++)
|
|
|
|
l->array[i] = l->array[i+1];
|
|
|
|
|
|
|
|
l->length--;
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int vlist_insert(struct vlist *l, size_t idx, void *p)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
void *t, *o;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
|
|
|
if (idx >= l->length)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Resize array if out of capacity */
|
|
|
|
if (l->length + 1 > l->capacity) {
|
|
|
|
l->capacity += LIST_CHUNKSIZE;
|
2019-04-07 15:12:32 +02:00
|
|
|
l->array = (void **) realloc(l->array, l->capacity * sizeof(void *));
|
2019-02-24 09:22:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
o = p;
|
|
|
|
for (i = idx; i < l->length; i++) {
|
|
|
|
t = l->array[i];
|
|
|
|
l->array[i] = o;
|
|
|
|
o = t;
|
|
|
|
}
|
|
|
|
|
|
|
|
l->array[l->length] = o;
|
|
|
|
l->length++;
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void vlist_remove_all(struct vlist *l, void *p)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
int removed = 0;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(l); i++) {
|
|
|
|
if (vlist_at(l, i) == p)
|
2018-08-21 13:24:17 +02:00
|
|
|
removed++;
|
|
|
|
else
|
2019-01-07 10:27:59 +01:00
|
|
|
l->array[i - removed] = vlist_at(l, i);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
l->length -= removed;
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
void * vlist_lookup(struct vlist *l, const char *name)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
2019-01-07 10:27:59 +01:00
|
|
|
return vlist_search(l, cmp_lookup, (void *) name);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
ssize_t vlist_lookup_index(struct vlist *l, const char *name)
|
2018-08-22 11:29:39 +02:00
|
|
|
{
|
2019-01-07 10:27:59 +01:00
|
|
|
void *ptr = vlist_lookup(l, name);
|
2018-08-22 11:29:39 +02:00
|
|
|
if (!ptr)
|
|
|
|
return -1;
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
return vlist_index(l, ptr);
|
2018-08-22 11:29:39 +02:00
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
int vlist_contains(struct vlist *l, void *p)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
2019-01-07 10:27:59 +01:00
|
|
|
return vlist_count(l, cmp_contains, p);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
int vlist_count(struct vlist *l, cmp_cb_t cmp, void *ctx)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
int c = 0;
|
2018-08-22 11:29:39 +02:00
|
|
|
void *e;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(l); i++) {
|
|
|
|
e = vlist_at(l, i);
|
2018-08-21 13:24:17 +02:00
|
|
|
if (cmp(e, ctx) == 0)
|
|
|
|
c++;
|
|
|
|
}
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
void * vlist_search(struct vlist *l, cmp_cb_t cmp, void *ctx)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
void *e;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(l); i++) {
|
|
|
|
e = vlist_at(l, i);
|
2018-08-21 13:24:17 +02:00
|
|
|
if (cmp(e, ctx) == 0)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2019-04-08 10:42:45 +02:00
|
|
|
e = nullptr; /* not found */
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
out: pthread_mutex_unlock(&l->lock);
|
|
|
|
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
void vlist_sort(struct vlist *l, cmp_cb_t cmp)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
2018-10-21 20:26:28 +01:00
|
|
|
#ifdef __APPLE__
|
|
|
|
qsort_r(l->array, l->length, sizeof(void *), (void *) cmp, cmp_sort);
|
|
|
|
#else
|
2018-08-21 13:24:17 +02:00
|
|
|
qsort_r(l->array, l->length, sizeof(void *), cmp_sort, (void *) cmp);
|
2018-10-21 20:26:28 +01:00
|
|
|
#endif
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
}
|
|
|
|
|
2019-04-07 15:12:32 +02:00
|
|
|
int vlist_set(struct vlist *l, unsigned index, void *value)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
if (index >= l->length)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
l->array[index] = value;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
ssize_t vlist_index(struct vlist *l, void *p)
|
2018-08-22 11:29:39 +02:00
|
|
|
{
|
|
|
|
void *e;
|
|
|
|
ssize_t f;
|
|
|
|
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
assert(l->state == STATE_INITIALIZED);
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
for (size_t i = 0; i < vlist_length(l); i++) {
|
|
|
|
e = vlist_at(l, i);
|
2018-08-22 11:29:39 +02:00
|
|
|
if (e == p) {
|
|
|
|
f = i;
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f = -1;
|
|
|
|
|
|
|
|
found: pthread_mutex_unlock(&l->lock);
|
|
|
|
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2019-01-07 10:27:59 +01:00
|
|
|
void vlist_extend(struct vlist *l, size_t len, void *val)
|
2018-08-22 11:29:39 +02:00
|
|
|
{
|
2019-01-07 10:27:59 +01:00
|
|
|
while (vlist_length(l) < len)
|
|
|
|
vlist_push(l, val);
|
2018-08-22 11:29:39 +02:00
|
|
|
}
|
2019-04-16 07:59:28 +02:00
|
|
|
|
|
|
|
void vlist_filter(struct vlist *l, dtor_cb_t cb)
|
|
|
|
{
|
|
|
|
size_t u, j;
|
|
|
|
pthread_mutex_lock(&l->lock);
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < vlist_length(l); i++) {
|
|
|
|
void *e = vlist_at(l, i);
|
|
|
|
|
|
|
|
if (!cb(e))
|
|
|
|
l->array[j++] = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
l->length = j;
|
|
|
|
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
}
|