2023-08-31 17:35:12 +02:00
|
|
|
/* A generic linked list.
|
2018-08-21 13:24:17 +02:00
|
|
|
*
|
|
|
|
* Linked lists a used for several data structures in the code.
|
|
|
|
*
|
2023-08-31 11:17:07 +02:00
|
|
|
* Author: Steffen Vogel <post@steffenvogel.de>
|
|
|
|
* SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
2023-08-28 12:31:18 +02:00
|
|
|
*/
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-11-11 21:23:53 +01:00
|
|
|
#include <algorithm>
|
2023-09-07 13:19:19 +02:00
|
|
|
#include <array>
|
2020-11-11 21:23:53 +01:00
|
|
|
|
2019-06-23 16:26:44 +02:00
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2021-08-11 12:40:19 -04:00
|
|
|
#include <villas/list.hpp>
|
2019-04-23 12:57:51 +02:00
|
|
|
#include <villas/utils.hpp>
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2021-08-11 12:40:19 -04:00
|
|
|
using namespace villas;
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
int villas::list_init(struct List *l) {
|
|
|
|
pthread_mutex_init(&l->lock, nullptr);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->length = 0;
|
|
|
|
l->capacity = 0;
|
|
|
|
l->array = nullptr;
|
|
|
|
l->state = State::INITIALIZED;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return 0;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
int villas::list_destroy(struct List *l, dtor_cb_t destructor, bool release) {
|
|
|
|
pthread_mutex_lock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state != State::DESTROYED);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
for (size_t i = 0; i < list_length(l); i++) {
|
|
|
|
void *e = list_at(l, i);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
if (destructor)
|
|
|
|
destructor(e);
|
|
|
|
if (release)
|
|
|
|
free(e);
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
free(l->array);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->length = -1;
|
|
|
|
l->capacity = 0;
|
|
|
|
l->array = nullptr;
|
|
|
|
l->state = State::DESTROYED;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
|
|
|
pthread_mutex_destroy(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return 0;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
void villas::list_push(struct List *l, void *p) {
|
|
|
|
pthread_mutex_lock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Resize array if out of capacity
|
|
|
|
if (l->length >= l->capacity) {
|
|
|
|
l->capacity += LIST_CHUNKSIZE;
|
|
|
|
l->array = (void **)realloc(l->array, l->capacity * sizeof(void *));
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->array[l->length] = p;
|
|
|
|
l->length++;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
void villas::list_clear(struct List *l) {
|
|
|
|
pthread_mutex_lock(&l->lock);
|
2020-10-21 20:59:23 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->length = 0;
|
2021-06-21 16:07:12 -04:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2020-10-21 20:59:23 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
int villas::list_remove(struct List *l, size_t idx) {
|
|
|
|
pthread_mutex_lock(&l->lock);
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
if (idx >= l->length)
|
|
|
|
return -1;
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
for (size_t i = idx; i < l->length - 1; i++)
|
|
|
|
l->array[i] = l->array[i + 1];
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->length--;
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return 0;
|
2019-02-24 09:22:20 +01:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
int villas::list_insert(struct List *l, size_t idx, void *p) {
|
|
|
|
size_t i;
|
|
|
|
void *t, *o;
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_lock(&l->lock);
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
if (idx >= l->length)
|
|
|
|
return -1;
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
// Resize array if out of capacity
|
|
|
|
if (l->length + 1 > l->capacity) {
|
|
|
|
l->capacity += LIST_CHUNKSIZE;
|
|
|
|
l->array = (void **)realloc(l->array, l->capacity * sizeof(void *));
|
|
|
|
}
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
o = p;
|
|
|
|
for (i = idx; i < l->length; i++) {
|
|
|
|
t = l->array[i];
|
|
|
|
l->array[i] = o;
|
|
|
|
o = t;
|
|
|
|
}
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->array[l->length] = o;
|
|
|
|
l->length++;
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2019-02-24 09:22:20 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return 0;
|
2019-02-24 09:22:20 +01:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
void villas::list_remove_all(struct List *l, void *p) {
|
|
|
|
int removed = 0;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_lock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
for (size_t i = 0; i < list_length(l); i++) {
|
|
|
|
if (list_at(l, i) == p)
|
|
|
|
removed++;
|
|
|
|
else
|
|
|
|
l->array[i - removed] = list_at(l, i);
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->length -= removed;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
int villas::list_contains(struct List *l, void *p) {
|
|
|
|
return list_count(
|
|
|
|
l, [](const void *a, const void *b) { return a == b ? 0 : 1; }, p);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
int villas::list_count(struct List *l, cmp_cb_t cmp, void *ctx) {
|
|
|
|
int c = 0;
|
|
|
|
void *e;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_lock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
for (size_t i = 0; i < list_length(l); i++) {
|
|
|
|
e = list_at(l, i);
|
|
|
|
if (cmp(e, ctx) == 0)
|
|
|
|
c++;
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return c;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
void *villas::list_search(struct List *l, cmp_cb_t cmp, const void *ctx) {
|
|
|
|
void *e;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_lock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
for (size_t i = 0; i < list_length(l); i++) {
|
|
|
|
e = list_at(l, i);
|
|
|
|
if (cmp(e, ctx) == 0)
|
|
|
|
goto out;
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
e = nullptr; // Not found
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
out:
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return e;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
void villas::list_sort(struct List *l, cmp_cb_t cmp) {
|
|
|
|
pthread_mutex_lock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
auto array = std::vector<void *>(l->array, l->array + l->length);
|
2020-11-11 21:23:53 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
std::sort(array.begin(), array.end(),
|
|
|
|
[cmp](void *&a, void *&b) -> bool { return cmp(a, b) < 0; });
|
2020-11-11 21:23:53 +01:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
std::copy(array.begin(), array.end(), l->array);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
int villas::list_set(struct List *l, unsigned index, void *value) {
|
|
|
|
if (index >= l->length)
|
|
|
|
return -1;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->array[index] = value;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return 0;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
ssize_t villas::list_index(struct List *l, void *p) {
|
|
|
|
void *e;
|
|
|
|
ssize_t f;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_lock(&l->lock);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
assert(l->state == State::INITIALIZED);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
for (size_t i = 0; i < list_length(l); i++) {
|
|
|
|
e = list_at(l, i);
|
|
|
|
if (e == p) {
|
|
|
|
f = i;
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
}
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
f = -1;
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
found:
|
|
|
|
pthread_mutex_unlock(&l->lock);
|
2018-08-22 11:29:39 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
return f;
|
2018-08-22 11:29:39 +02:00
|
|
|
}
|
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
void villas::list_extend(struct List *l, size_t len, void *val) {
|
|
|
|
while (list_length(l) < len)
|
|
|
|
list_push(l, val);
|
2018-08-22 11:29:39 +02:00
|
|
|
}
|
2019-04-16 07:59:28 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
void villas::list_filter(struct List *l, dtor_cb_t cb) {
|
|
|
|
size_t i, j;
|
|
|
|
pthread_mutex_lock(&l->lock);
|
2019-04-16 07:59:28 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
for (i = 0, j = 0; i < list_length(l); i++) {
|
|
|
|
void *e = list_at(l, i);
|
2019-04-16 07:59:28 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
if (!cb(e))
|
|
|
|
l->array[j++] = e;
|
|
|
|
}
|
2019-04-16 07:59:28 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
l->length = j;
|
2019-04-16 07:59:28 +02:00
|
|
|
|
2023-09-07 13:19:19 +02:00
|
|
|
pthread_mutex_unlock(&l->lock);
|
2019-04-16 07:59:28 +02:00
|
|
|
}
|