From e50f7e8b4937e8c92aa25aa967843495f40f640e Mon Sep 17 00:00:00 2001 From: Yu-Ping Wu Date: Wed, 28 Jan 2026 12:30:50 +0800 Subject: [PATCH] commonlib/list: Add list_length() and more to API In a follow-up patch (CB:90962), the list will be changed to a circular one, and list_node fields 'next' and 'prev' will become private to the implementation. To allow smooth transition to circular lists for all call sites, add the following functions to the list API: - list_is_empty() - list_next() - list_prev() - list_first() - list_last() - list_length() All list API call sites are expected to use the public API instead of the raw 'next' and 'prev' pointers. Change-Id: Ib1040f5caab8550ea52db9b55a074d7d79c591e5 Signed-off-by: Yu-Ping Wu Reviewed-on: https://review.coreboot.org/c/coreboot/+/90961 Tested-by: build bot (Jenkins) Reviewed-by: Jakub "Kuba" Czapiga Reviewed-by: Julius Werner --- src/commonlib/include/commonlib/list.h | 35 ++++++++- src/commonlib/list.c | 21 +++++ tests/commonlib/list-test.c | 101 ++++++++++++++++--------- 3 files changed, 120 insertions(+), 37 deletions(-) diff --git a/src/commonlib/include/commonlib/list.h b/src/commonlib/include/commonlib/list.h index 970d68ea49..9bad52f3a3 100644 --- a/src/commonlib/include/commonlib/list.h +++ b/src/commonlib/include/commonlib/list.h @@ -5,6 +5,7 @@ #define __COMMONLIB_LIST_H__ #include +#include #include struct list_node { @@ -18,9 +19,41 @@ void list_remove(struct list_node *node); void list_insert_after(struct list_node *node, struct list_node *after); // Insert list_node node before list_node before in a doubly linked list. void list_insert_before(struct list_node *node, struct list_node *before); -// Appends the node to the end of the list. +// Append the node to the end of the list. void list_append(struct list_node *node, struct list_node *head); +// Return if the list is empty. +static inline bool list_is_empty(const struct list_node *head) +{ + return !head->next; +} + +// Get next node. +static inline const struct list_node *list_next(const struct list_node *node, + const struct list_node *head) +{ + return node->next; +}; + +// Get prev node. +static inline const struct list_node *list_prev(const struct list_node *node, + const struct list_node *head) +{ + return node->prev == head ? NULL : node->prev; +}; + +// Get first node. +static inline const struct list_node *list_first(const struct list_node *head) +{ + return list_is_empty(head) ? NULL : head->next; +} + +// Get last node. +const struct list_node *list_last(const struct list_node *head); + +// Get the number of list elements. +size_t list_length(const struct list_node *head); + #define list_for_each(ptr, head, member) \ for ((ptr) = container_of((head).next, typeof(*(ptr)), member); \ (uintptr_t)ptr + (uintptr_t)offsetof(typeof(*(ptr)), member); \ diff --git a/src/commonlib/list.c b/src/commonlib/list.c index b1030c8263..9d11d9606c 100644 --- a/src/commonlib/list.c +++ b/src/commonlib/list.c @@ -36,3 +36,24 @@ void list_append(struct list_node *node, struct list_node *head) list_insert_after(node, head); } + +const struct list_node *list_last(const struct list_node *head) +{ + const struct list_node *ptr = head; + while (ptr->next) + ptr = ptr->next; + return ptr == head ? NULL : ptr; +} + +size_t list_length(const struct list_node *head) +{ + struct { + struct list_node node; + } const *ptr; + size_t len = 0; + + list_for_each(ptr, *head, node) + len++; + + return len; +} diff --git a/tests/commonlib/list-test.c b/tests/commonlib/list-test.c index 4ca48a468f..568fa1d3dd 100644 --- a/tests/commonlib/list-test.c +++ b/tests/commonlib/list-test.c @@ -1,9 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-only */ -#include +#include #include #include -#include +#include +#include struct test_container { int value; @@ -11,10 +12,40 @@ struct test_container { struct list_node list_node; }; -void test_list_insert_after(void **state) +static void test_list_empty(void **state) +{ + struct list_node head = {}; + struct test_container *node; + + list_for_each(node, head, list_node) { + assert_true(false); + } + + assert_true(list_is_empty(&head)); + assert_null(list_first(&head)); + assert_null(list_last(&head)); + assert_int_equal(0, list_length(&head)); +} + +static void test_list_one_node(void **state) +{ + struct list_node head = {}; + struct test_container c = { .value = 1 }; + + list_insert_after(&c.list_node, &head); + + assert_false(list_is_empty(&head)); + assert_null(list_next(&c.list_node, &head)); + assert_null(list_prev(&c.list_node, &head)); + assert_ptr_equal(&c.list_node, list_first(&head)); + assert_ptr_equal(&c.list_node, list_last(&head)); + assert_int_equal(1, list_length(&head)); +} + +static void test_list_insert_after(void **state) { int i = 0; - struct list_node root = { .prev = NULL, .next = NULL }; + struct list_node head = { .prev = NULL, .next = NULL }; struct test_container *c1 = (struct test_container *)malloc(sizeof(*c1)); struct test_container *c2 = (struct test_container *)malloc(sizeof(*c2)); struct test_container *c3 = (struct test_container *)malloc(sizeof(*c2)); @@ -29,26 +60,33 @@ void test_list_insert_after(void **state) c2->value = values[1]; c3->value = values[2]; - list_insert_after(&c1->list_node, &root); + list_insert_after(&c1->list_node, &head); list_insert_after(&c2->list_node, &c1->list_node); list_insert_after(&c3->list_node, &c2->list_node); - list_for_each(ptr, root, list_node) { + list_for_each(ptr, head, list_node) { assert_int_equal(values[i], ptr->value); i++; } assert_int_equal(3, i); + assert_int_equal(3, list_length(&head)); + assert_false(list_is_empty(&head)); + + assert_ptr_equal(&c3->list_node, list_next(&c2->list_node, &head)); + assert_ptr_equal(&c1->list_node, list_prev(&c2->list_node, &head)); + assert_ptr_equal(&c1->list_node, list_first(&head)); + assert_ptr_equal(&c3->list_node, list_last(&head)); free(c3); free(c2); free(c1); } -void test_list_insert_before(void **state) +static void test_list_insert_before(void **state) { int i = 0; - struct list_node root = { .prev = NULL, .next = NULL }; + struct list_node head = { .prev = NULL, .next = NULL }; struct test_container *c1 = (struct test_container *)malloc(sizeof(*c1)); struct test_container *c2 = (struct test_container *)malloc(sizeof(*c2)); struct test_container *c3 = (struct test_container *)malloc(sizeof(*c2)); @@ -63,87 +101,78 @@ void test_list_insert_before(void **state) c2->value = values[1]; c3->value = values[2]; - list_insert_after(&c3->list_node, &root); + list_insert_after(&c3->list_node, &head); list_insert_before(&c2->list_node, &c3->list_node); list_insert_before(&c1->list_node, &c2->list_node); - list_for_each(ptr, root, list_node) { + list_for_each(ptr, head, list_node) { assert_int_equal(values[i], ptr->value); i++; } assert_int_equal(3, i); + assert_int_equal(3, list_length(&head)); + assert_false(list_is_empty(&head)); free(c3); free(c2); free(c1); } -void test_list_remove(void **state) +static void test_list_remove(void **state) { - struct list_node root = { .prev = NULL, .next = NULL }; + struct list_node head = { .prev = NULL, .next = NULL }; struct test_container *c1 = (struct test_container *)malloc(sizeof(*c1)); struct test_container *c2 = (struct test_container *)malloc(sizeof(*c2)); - struct test_container *ptr; - int len; - list_insert_after(&c1->list_node, &root); + list_insert_after(&c1->list_node, &head); list_insert_after(&c2->list_node, &c1->list_node); - len = 0; - list_for_each(ptr, root, list_node) { - len++; - } - assert_int_equal(2, len); + assert_int_equal(2, list_length(&head)); list_remove(&c1->list_node); - - len = 0; - list_for_each(ptr, root, list_node) { - len++; - } - assert_int_equal(1, len); + assert_int_equal(1, list_length(&head)); list_remove(&c2->list_node); - len = 0; - list_for_each(ptr, root, list_node) { - len++; - } - assert_int_equal(0, len); + assert_int_equal(0, list_length(&head)); free(c2); free(c1); } -void test_list_append(void **state) +static void test_list_append(void **state) { size_t idx; struct test_container *node; - struct list_node root = {}; + struct list_node head = {}; struct test_container nodes[] = { {1}, {2}, {3} }; for (idx = 0; idx < ARRAY_SIZE(nodes); ++idx) - list_append(&nodes[idx].list_node, &root); + list_append(&nodes[idx].list_node, &head); idx = 0; - list_for_each(node, root, list_node) { + list_for_each(node, head, list_node) { assert_ptr_equal(node, &nodes[idx]); idx++; } + + assert_int_equal(3, idx); + assert_int_equal(3, list_length(&head)); } int main(void) { const struct CMUnitTest tests[] = { + cmocka_unit_test(test_list_empty), + cmocka_unit_test(test_list_one_node), cmocka_unit_test(test_list_insert_after), cmocka_unit_test(test_list_insert_before), cmocka_unit_test(test_list_remove), cmocka_unit_test(test_list_append), }; - return cb_run_group_tests(tests, NULL, NULL); }