mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-25 07:00:06 +01:00
util: add a number of list helpers
The list_debug function is ifdef'd out, it'll be useful to debug whenever we have some list corruption. Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1209>
This commit is contained in:
parent
57a0a219b0
commit
ec9c39da18
3 changed files with 231 additions and 0 deletions
|
|
@ -30,6 +30,34 @@
|
|||
#include <stdbool.h>
|
||||
|
||||
#include "util-list.h"
|
||||
#include "libinput-util.h"
|
||||
|
||||
#if 0
|
||||
static void
|
||||
list_debug(struct list *list)
|
||||
{
|
||||
struct list *head = list;
|
||||
|
||||
assert((head->next != NULL && head->prev != NULL) ||
|
||||
!"list->next|prev is NULL, possibly missing list_init()");
|
||||
|
||||
trace("list %p:", head);
|
||||
trace(" head->next %p", head->next);
|
||||
trace(" head->prev %p", head->prev);
|
||||
struct list *elm = head->next;
|
||||
int index = 0;
|
||||
while (elm != head) {
|
||||
trace("..%-3d: %p->next %p",
|
||||
index, elm, elm->next);
|
||||
trace("..%-3d: %p->prev %p",
|
||||
index, elm, elm->prev);
|
||||
elm = elm->next;
|
||||
index++;
|
||||
if (index > 20)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
list_init(struct list *list)
|
||||
|
|
@ -65,6 +93,45 @@ list_append(struct list *list, struct list *elm)
|
|||
elm->prev->next = elm;
|
||||
}
|
||||
|
||||
void
|
||||
list_chain(struct list *list, struct list *other)
|
||||
{
|
||||
assert((list->next != NULL && list->prev != NULL) ||
|
||||
!"list->next|prev is NULL, possibly missing list_init()");
|
||||
assert((other->next != NULL && other->prev != NULL) ||
|
||||
!"other->next|prev is NULL, possibly missing list_init()");
|
||||
|
||||
if (list_empty(other))
|
||||
return;
|
||||
|
||||
struct list *last = list->prev;
|
||||
struct list *first = other->next;
|
||||
|
||||
first->prev = last;
|
||||
last->next = first;
|
||||
|
||||
list->prev->next = other->next;
|
||||
other->next->prev = list->prev;
|
||||
other->prev->next = list;
|
||||
list->prev = other->prev;
|
||||
list_init(other);
|
||||
}
|
||||
|
||||
size_t
|
||||
list_length(const struct list *list)
|
||||
{
|
||||
assert((list->next != NULL && list->prev != NULL) ||
|
||||
!"list->next|prev is NULL, possibly missing list_init()");
|
||||
|
||||
size_t count = 0;
|
||||
const struct list *elm;
|
||||
|
||||
for (elm = list->next; elm != list; elm = elm->next)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void
|
||||
list_remove(struct list *elm)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@ void list_insert(struct list *list, struct list *elm);
|
|||
*/
|
||||
void list_append(struct list *list, struct list *elm);
|
||||
|
||||
/**
|
||||
* Chain other onto list, resetting other to be the empty list.
|
||||
*/
|
||||
void list_chain(struct list *list, struct list *other);
|
||||
|
||||
size_t list_length(const struct list *list);
|
||||
|
||||
/**
|
||||
* Takes the given pointer ands inserts it to the list with the pointer's field.
|
||||
* The pointer is reset to NULL. Use this to prevent automatic cleanup
|
||||
|
|
@ -187,6 +194,51 @@ bool list_empty(const struct list *list);
|
|||
#define list_first_entry_by_type(head, container_type, member) \
|
||||
container_of((head)->next, container_type, member)
|
||||
|
||||
/**
|
||||
* Given a list 'head', return the last entry of type 'pos' that has a
|
||||
* member 'link'.
|
||||
*
|
||||
* The 'pos' argument is solely used to determine the type be returned and
|
||||
* not modified otherwise. It is common to use the same pointer that the
|
||||
* return value of list_last_entry() is assigned to, for example:
|
||||
*
|
||||
* @code
|
||||
* struct foo {
|
||||
* struct list list_of_bars;
|
||||
* };
|
||||
*
|
||||
* struct bar {
|
||||
* struct list link;
|
||||
* }
|
||||
*
|
||||
* struct foo *f = get_a_foo();
|
||||
* struct bar *b = 0; // initialize to avoid static analysis errors
|
||||
* b = list_last_entry(&f->list_of_bars, b, link);
|
||||
* @endcode
|
||||
*/
|
||||
#define list_last_entry(head, pointer_of_type, member) \
|
||||
container_of((head)->prev, __typeof__(*pointer_of_type), member)
|
||||
|
||||
/**
|
||||
* Given a list 'head', return the last entry of type 'container_type' that
|
||||
* has a member 'link'.
|
||||
*
|
||||
* @code
|
||||
* struct foo {
|
||||
* struct list list_of_bars;
|
||||
* };
|
||||
*
|
||||
* struct bar {
|
||||
* struct list link;
|
||||
* }
|
||||
*
|
||||
* struct foo *f = get_a_foo();
|
||||
* struct bar *b = list_last_entry(&f->list_of_bars, struct bar, link);
|
||||
* @endcode
|
||||
*/
|
||||
#define list_last_entry_by_type(head, container_type, member) \
|
||||
container_of((head)->prev, container_type, member)
|
||||
|
||||
/**
|
||||
* Iterate through the list.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1862,6 +1862,57 @@ START_TEST(list_test_append)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(list_test_chain)
|
||||
{
|
||||
struct list_test {
|
||||
int val;
|
||||
struct list node;
|
||||
} tests[] = {
|
||||
{ .val = 1 },
|
||||
{ .val = 2 },
|
||||
{ .val = 3 },
|
||||
{ .val = 4 },
|
||||
};
|
||||
struct list l1, l2;
|
||||
struct list_test *t;
|
||||
int val;
|
||||
|
||||
list_init(&l1);
|
||||
list_init(&l2);
|
||||
|
||||
list_chain(&l1, &l2);
|
||||
litest_assert(list_empty(&l2));
|
||||
|
||||
list_append(&l2, &tests[0].node);
|
||||
list_append(&l2, &tests[1].node);
|
||||
list_chain(&l1, &l2);
|
||||
litest_assert(list_empty(&l2));
|
||||
|
||||
val = 1;
|
||||
list_for_each_safe(t, &l1, node) {
|
||||
litest_assert_int_eq(t->val, val);
|
||||
val++;
|
||||
list_remove(&t->node);
|
||||
}
|
||||
litest_assert_int_eq(val, 3);
|
||||
|
||||
list_append(&l1, &tests[0].node);
|
||||
list_append(&l1, &tests[1].node);
|
||||
list_append(&l2, &tests[2].node);
|
||||
list_append(&l2, &tests[3].node);
|
||||
|
||||
list_chain(&l1, &l2);
|
||||
litest_assert(list_empty(&l2));
|
||||
|
||||
val = 1;
|
||||
list_for_each(t, &l1, node) {
|
||||
litest_assert_int_eq(t->val, val);
|
||||
val++;
|
||||
}
|
||||
litest_assert_int_eq(val, 5);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(list_test_foreach)
|
||||
{
|
||||
struct list_test {
|
||||
|
|
@ -1895,6 +1946,65 @@ START_TEST(list_test_foreach)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(list_test_first_last)
|
||||
{
|
||||
struct list_test {
|
||||
int val;
|
||||
struct list node;
|
||||
} tests[] = {
|
||||
{ .val = 1 },
|
||||
{ .val = 2 },
|
||||
{ .val = 3 },
|
||||
{ .val = 4 },
|
||||
};
|
||||
struct list head;
|
||||
|
||||
list_init(&head);
|
||||
|
||||
ARRAY_FOR_EACH(tests, t) {
|
||||
list_append(&head, &t->node);
|
||||
}
|
||||
|
||||
struct list_test *first;
|
||||
struct list_test *last;
|
||||
|
||||
first = list_first_entry(&head, first, node);
|
||||
last = list_last_entry(&head, last, node);
|
||||
litest_assert_ptr_eq(first, &tests[0]);
|
||||
litest_assert_ptr_eq(last, &tests[3]);
|
||||
|
||||
struct list_test *second;
|
||||
struct list_test *penultimate;
|
||||
|
||||
second = list_first_entry(&first->node, first, node);
|
||||
penultimate = list_last_entry(&last->node, last, node);
|
||||
litest_assert_ptr_eq(second, &tests[1]);
|
||||
litest_assert_ptr_eq(penultimate, &tests[2]);
|
||||
|
||||
/* Now remove nodes */
|
||||
|
||||
/* No change expected */
|
||||
list_remove(&tests[2].node);
|
||||
first = list_first_entry(&head, first, node);
|
||||
last = list_last_entry(&head, last, node);
|
||||
litest_assert_ptr_eq(first, &tests[0]);
|
||||
litest_assert_ptr_eq(last, &tests[3]);
|
||||
|
||||
list_remove(&tests[3].node);
|
||||
first = list_first_entry(&head, first, node);
|
||||
last = list_last_entry(&head, last, node);
|
||||
litest_assert_ptr_eq(first, &tests[0]);
|
||||
litest_assert_ptr_eq(last, &tests[1]);
|
||||
|
||||
list_remove(&tests[0].node);
|
||||
first = list_first_entry(&head, first, node);
|
||||
last = list_last_entry(&head, last, node);
|
||||
litest_assert_ptr_eq(first, &tests[1]);
|
||||
litest_assert_ptr_eq(last, &tests[1]);
|
||||
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(strverscmp_test)
|
||||
{
|
||||
litest_assert_int_eq(libinput_strverscmp("", ""), 0);
|
||||
|
|
@ -2507,6 +2617,8 @@ int main(void)
|
|||
ADD_TEST(list_test_insert);
|
||||
ADD_TEST(list_test_append);
|
||||
ADD_TEST(list_test_foreach);
|
||||
ADD_TEST(list_test_first_last);
|
||||
ADD_TEST(list_test_chain);
|
||||
ADD_TEST(strverscmp_test);
|
||||
ADD_TEST(streq_test);
|
||||
ADD_TEST(strneq_test);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue