diff --git a/fuzzing/harness/fuzzer_lists.c b/fuzzing/harness/fuzzer_lists.c index c64f68fc5..8d3b75ad2 100644 --- a/fuzzing/harness/fuzzer_lists.c +++ b/fuzzing/harness/fuzzer_lists.c @@ -282,7 +282,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } fuzz_list_node_t *node = create_tracked_list_node(&data[offset], 16); offset += 16; - if (node && list_push_front(&dlist, &node->node)) { + if (node) { + list_push_front(&dlist, &node->node); mark_in_list(node); } break; @@ -294,7 +295,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } fuzz_list_node_t *node = create_tracked_list_node(&data[offset], 16); offset += 16; - if (node && list_push_back(&dlist, &node->node)) { + if (node) { + list_push_back(&dlist, &node->node); mark_in_list(node); } break; @@ -318,7 +320,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) fuzz_list_node_t *ref = get_tracked_node(ref_idx, true, true); if (ref) { fuzz_list_node_t *node = create_tracked_list_node(&data[offset], 16); - if (node && list_insert_after(&dlist, &ref->node, &node->node)) { + if (node) { + list_insert_after(&dlist, &ref->node, &node->node); mark_in_list(node); } } @@ -334,7 +337,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) fuzz_list_node_t *ref = get_tracked_node(ref_idx, true, true); if (ref) { fuzz_list_node_t *node = create_tracked_list_node(&data[offset], 16); - if (node && list_insert_before(&dlist, &ref->node, &node->node)) { + if (node) { + list_insert_before(&dlist, &ref->node, &node->node); mark_in_list(node); } } @@ -398,7 +402,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } fuzz_flist_node_t *node = create_tracked_flist_node(&data[offset], 16); offset += 16; - if (node && flist_push_front(&flist, &node->node)) { + if (node) { + flist_push_front(&flist, &node->node); mark_in_list(node); } break; @@ -410,7 +415,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) } fuzz_flist_node_t *node = create_tracked_flist_node(&data[offset], 16); offset += 16; - if (node && flist_push_back(&flist, &node->node)) { + if (node) { + flist_push_back(&flist, &node->node); mark_in_list(node); } break; @@ -434,7 +440,8 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) fuzz_flist_node_t *ref = get_tracked_node(ref_idx, true, false); if (ref) { fuzz_flist_node_t *node = create_tracked_flist_node(&data[offset], 16); - if (node && flist_insert_after(&flist, &ref->node, &node->node)) { + if (node) { + flist_insert_after(&flist, &ref->node, &node->node); mark_in_list(node); } } diff --git a/lib_lists/doc/mainpage.dox b/lib_lists/doc/mainpage.dox index 5a9e75201..11208ffb5 100644 --- a/lib_lists/doc/mainpage.dox +++ b/lib_lists/doc/mainpage.dox @@ -24,11 +24,11 @@ typedef struct flist_node_t { } flist_node_t; @endcode -**Usage:** Embed `flist_node_t` as the first member of your structure: +**Usage:** Embed `flist_node_t` as the first member of your structure, its name is irrelevant: @code{.c} typedef struct my_data_s { - flist_node_t node; // Must be first member + flist_node_t _node; // Must be first member int value; char name[32]; } my_data_t; @@ -49,16 +49,16 @@ typedef struct list_node_t { } list_node_t; @endcode -**Usage:** Embed `list_node_t` as the first member of your structure: +**Usage:** Embed `list_node_t` as the first member of your structure, its name is irrelevant: @code{.c} typedef struct my_data_s { - list_node_t node; // Must be first member + list_node_t _node; // Must be first member int value; char name[32]; } my_data_t; -list_node_t *my_list = NULL; // Empty doubly-linked list +my_data_t *my_list = NULL; // Empty doubly-linked list @endcode **Memory overhead:** 8-16 bytes per node (two pointers) @@ -66,14 +66,14 @@ list_node_t *my_list = NULL; // Empty doubly-linked list @subsection list_choosing Choosing Between Forward Lists and Doubly-Linked Lists | Feature | Forward List (`flist`) | Doubly-Linked (`list`) | -|--------------------------|------------------------|--------------------------| -| Memory per node | 4-8 bytes | 8-16 bytes | -| Insert/remove at front | O(1) | O(1) | -| Insert/remove at back | O(n) | O(1) ⚡ | -| Insert after node | O(1) | O(1) | -| Insert before node | Not available | O(1) ⚡ | -| Backward traversal | No | Yes ⚡ | -| Reverse operation | O(n) | O(n) | +|--------------------------|------------------------|------------------------| +| Memory per node | 4-8 bytes | 8-16 bytes | +| Insert/remove at front | O(1) ⚡ | O(1) ⚡ | +| Insert/remove at back | O(n) | O(n) | +| Insert after node | O(1) ⚡ | O(1) ⚡ | +| Insert before node | Not available | O(1) ⚡ | +| Backward traversal | No | Yes | +| Reverse operation | O(n) | O(n) | **Best practice:** Use forward lists when memory is critical and you don't need backward traversal or frequent tail operations. Use doubly-linked lists when you need bidirectional @@ -110,57 +110,47 @@ All operations are available for both forward lists and doubly-linked lists with - Forward list functions: `flist_*` - Doubly-linked list functions: `list_*` -All mutating functions return `bool` to indicate success or failure. - @subsection list_insertion Insertion Operations **Push Front** - Add a node at the beginning (O(1) for both types): @code{.c} // Forward list example -my_data_t *data = malloc(sizeof(my_data_t)); -data->node.next = NULL; // Must be NULL before insertion -data->value = 42; - -if (flist_push_front(&my_flist, &data->node)) { +my_data_t *data = malloc(sizeof(*data)); +if (data != NULL) { + memset(data, 0, sizeof(*data)); + data->value = 42; + flist_push_front((flist_node_t**)&my_flist, (flist_node_t*)data); // Successfully inserted } // Doubly-linked list example -my_data_t *data2 = malloc(sizeof(my_data_t)); -data2->node._list.next = NULL; -data2->node.prev = NULL; // Both pointers must be NULL -data2->value = 43; - -if (list_push_front(&my_list, &data2->node)) { +my_data_t *data2 = malloc(sizeof(*data2)); +if (data2 != NULL) { + memset(data2, 0, sizeof(*data2)); + data2->value = 43; + list_push_front((list_node_t**)&my_list, (list_node_t*)data2); // Successfully inserted } @endcode -**Push Back** - Add a node at the end: -- Forward list: O(n) - must traverse entire list -- Doubly-linked: O(1) - direct access via prev pointer ⚡ +**Push Back** - Add a node at the end, O(n) - must traverse entire list: @code{.c} -// Forward list: O(n) -if (flist_push_back(&my_flist, &data->node)) { - // Successfully inserted -} +// Forward list +flist_push_back((flist_node_t **)&my_flist, (flist_node_t*)data); -// Doubly-linked: O(1) - faster! -if (list_push_back(&my_list, &data->node)) { - // Successfully inserted -} +// Doubly-linked +list_push_back((list_node_t**)&my_list, (list_node_t*)data); @endcode -**Insert After** - Insert a node after a reference node (O(1) for both): +**Insert After** - Insert a node after a reference node, O(1): @code{.c} -my_data_t *new_data = malloc(sizeof(my_data_t)); -new_data->node._list.next = NULL; -new_data->node.prev = NULL; - -if (list_insert_after(&my_list, &ref_node, &new_data->node)) { +my_data_t *new_data = malloc(sizeof(*new_data)); +if (new_data != NULL) { + memset(new_data, 0, sizeof(*new_data)); + list_insert_after((list_node_t**)&my_list, (list_node_t*)ref_node, (list_node_t*)new_data); // Successfully inserted after ref_node } @endcode @@ -171,156 +161,125 @@ if (list_insert_after(&my_list, &ref_node, &new_data->node)) { @code{.c} // Only available for doubly-linked lists -my_data_t *new_data = malloc(sizeof(my_data_t)); -new_data->node._list.next = NULL; -new_data->node.prev = NULL; - -if (list_insert_before(&my_list, &ref_node, &new_data->node)) { +my_data_t *new_data = malloc(sizeof(*new_data)); +if (new_data != NULL) { + memset(new_data, 0, sizeof(*new_data)); + list_insert_before((list_node_t**)&my_list, (list_node_t*)ref_node, (list_node_t*)new_data); // Successfully inserted before ref_node } @endcode @subsection list_removal Removal Operations -All removal functions return `bool` to indicate success or failure (except `remove_if`). They accept an optional deletion callback to clean up node data. **Pop Front** - Remove the first node (O(1) for both): @code{.c} -void my_delete_func(flist_node_t *node) { - my_data_t *data = (my_data_t *)node; +void my_delete_func(my_data_t *node) { // Clean up data if needed - free(data); + free(node); } // Forward list -if (flist_pop_front(&my_flist, my_delete_func)) { - // First node was removed and freed -} +flist_pop_front((flist_node_t**)&my_flist, (f_list_node_del)&my_delete_func); // Doubly-linked list -if (list_pop_front(&my_list, (f_list_node_del)my_delete_func)) { - // First node was removed and freed -} +list_pop_front((list_node_t**)&my_list, (f_list_node_del)&my_delete_func); @endcode -**Pop Back** - Remove the last node: -- Forward list: O(n) - must find second-to-last node -- Doubly-linked: O(1) - direct access via prev pointer ⚡ +**Pop Back** - Remove the last node, O(n) - must find second-to-last node: @code{.c} -// Forward list: O(n) -if (flist_pop_back(&my_flist, my_delete_func)) { - // Last node was removed -} +// Forward list +flist_pop_back((flist_node_t**)&my_flist, (f_list_node_del)&my_delete_func); -// Doubly-linked: O(1) - faster! -if (list_pop_back(&my_list, (f_list_node_del)my_delete_func)) { - // Last node was removed -} +// Doubly-linked +list_pop_back((list_node_t**)&my_list, (f_list_node_del)&my_delete_func); @endcode -**Remove** - Remove a specific node (O(n) for forward, O(1) for doubly-linked): +**Remove** - Remove a specific node, O(n) - must find previous node @code{.c} -// Forward list: O(n) - must find previous node -if (flist_remove(&my_flist, &node_to_remove, my_delete_func)) { - // Node was found and removed -} +// Forward list +flist_remove((flist_node_t**)&my_flist, (flist_node_t*)node_to_remove, (f_list_node_del)&my_delete_func); -// Doubly-linked: O(1) - direct access via prev pointer ⚡ -if (list_remove(&my_list, &node_to_remove, (f_list_node_del)my_delete_func)) { - // Node was removed instantly -} +// Doubly-linked +list_remove((list_node_t**)&my_list, (list_node_t*)node_to_remove, (f_list_node_del)&my_delete_func); @endcode -**Remove If** - Remove all nodes matching a predicate (O(n) for both): +**Remove If** - Remove all nodes matching a predicate, O(n): @code{.c} -bool is_negative(const flist_node_t *node) { - const my_data_t *data = (const my_data_t *)node; - return data->value < 0; +bool is_negative(const my_data_t *node) { + return node->value < 0; } // Returns the number of removed nodes -size_t removed = flist_remove_if(&my_flist, is_negative, my_delete_func); +size_t removed = flist_remove_if((flist_node_t**)&my_flist, (f_list_node_pred)&is_negative, (f_list_node_del)&my_delete_func); printf("Removed %zu negative values\n", removed); @endcode -**Clear** - Remove all nodes (O(n) for both): +**Clear** - Remove all nodes, O(n): @code{.c} -if (flist_clear(&my_flist, my_delete_func)) { - // All nodes were removed and list is now empty -} +flist_clear((flist_node_t**)&my_flist, (f_list_node_del)&my_delete_func); +// All nodes were removed and list is now empty @endcode @subsection list_utilities Utility Operations -**Get Size** - Count the number of nodes (O(n) for both): +**Get Size** - Count the number of nodes, O(n): @code{.c} -size_t count = flist_size(&my_flist); -size_t count2 = list_size(&my_list); +size_t count = flist_size((flist_node_t**)&my_flist); +size_t count2 = list_size((list_node_t**)&my_list); @endcode -**Check Empty** - Test if list is empty (O(1) for both): +**Check Empty** - Test if list is empty, O(1): @code{.c} -if (flist_empty(&my_flist)) { +if (flist_empty((flist_node_t**)&my_flist)) { printf("Forward list is empty\n"); } -if (list_empty(&my_list)) { +if (list_empty((list_node_t**)&my_list)) { printf("Doubly-linked list is empty\n"); } @endcode -**Sort** - Sort the list using a comparison function (O(n²) for both): +**Sort** - Sort the list using a comparison function, O(n²): @code{.c} -bool my_compare_func(const flist_node_t *a, const flist_node_t *b) { - my_data_t *data_a = (my_data_t *)a; - my_data_t *data_b = (my_data_t *)b; - return data_a->value <= data_b->value; // true if in correct order +bool my_compare_func(const my_data_t *a, const my_data_t *b) { + return a->value <= b->value; // true if in correct order } // Forward list -if (flist_sort(&my_flist, my_compare_func)) { - // List is now sorted -} +flist_sort((flist_node_t**)&my_flist, (f_list_node_cmp)&my_compare_func); // Doubly-linked list -if (list_sort(&my_list, my_compare_func)) { - // List is now sorted -} +list_sort((list_node_t**)&my_list, (f_list_node_cmp)&my_compare_func); @endcode -**Unique** - Remove consecutive duplicate nodes (O(n) for both): +**Unique** - Remove consecutive duplicate nodes, O(n): @code{.c} -bool are_equal(const flist_node_t *a, const flist_node_t *b) { - const my_data_t *data_a = (const my_data_t *)a; - const my_data_t *data_b = (const my_data_t *)b; - return data_a->value == data_b->value; +bool are_equal(const my_data_t *a, const my_data_t *b) { + return a->value == b->value; } // Returns the number of removed duplicates -size_t removed = flist_unique(&my_flist, are_equal, my_delete_func); +size_t removed = flist_unique((flist_node_t**)&my_flist, (f_list_node_bin_pred)&are_equal, (f_list_node_del)&my_delete_func); printf("Removed %zu duplicates\n", removed); @endcode -**Reverse** - Reverse the order of nodes (O(n) for both): +**Reverse** - Reverse the order of nodes, O(n): @code{.c} -if (flist_reverse(&my_flist)) { - // List is now reversed -} +flist_reverse((flist_node_t**)&my_flist); -if (list_reverse(&my_list)) { - // List is now reversed -} +list_reverse((list_node_t**)&my_list); @endcode @section list_traversal List Traversal @@ -339,7 +298,7 @@ for (flist_node_t *node = my_flist; node != NULL; node = node->next) { @code{.c} // Forward iteration -for (list_node_t *node = my_list; node != NULL; node = (list_node_t *)node->_list.next) { +for (list_node_t *node = my_list; node != NULL; node = (list_node_t *)((flist_node_t*)node)->next) { my_data_t *data = (my_data_t *)node; printf("Value: %d\n", data->value); } @@ -348,8 +307,8 @@ for (list_node_t *node = my_list; node != NULL; node = (list_node_t *)node->_lis list_node_t *tail = my_list; if (tail != NULL) { // Find the tail - while (tail->_list.next != NULL) { - tail = (list_node_t *)tail->_list.next; + while (((flist_node_t*)tail)->next != NULL) { + tail = (list_node_t *)((flist_node_t*)tail)->next; } // Traverse backward for (list_node_t *node = tail; node != NULL; node = node->prev) { @@ -365,76 +324,49 @@ The library implements several safety checks: 1. **NULL pointer checks**: All functions validate their parameters 2. **Node state validation**: - - Forward list: Insertion functions verify that `node->next == NULL` - - Doubly-linked: Insertion functions verify that both `node->_list.next` and `node->prev` are NULL - - This prevents: + - Forward list: Insertion functions correctly initialize and update `node->next` to link the node into the list (for example, to the current head for `push_front`, or to `NULL` when inserting at the end) + - Doubly-linked: Insertion functions correctly initialize and update both `node->next` and `node->prev` to link the node between its neighbors in the list + - This consistent pointer management prevents: - Creating cycles in the list - Accidentally linking nodes from different lists - Losing track of existing node chains -3. **Return value checking**: All mutating operations return `bool`: - - `true`: Operation succeeded - - `false`: Invalid parameters or operation failed (e.g., node not found) - -4. **Counter operations**: `remove_if`, `unique`, and `size` return `size_t` with count information - -**Common error scenarios:** - -@code{.c} -// Forward list error -my_data_t *data = malloc(sizeof(my_data_t)); -data->node.next = some_other_node; // Already linked! - -if (!flist_push_front(&my_flist, &data->node)) { - // ERROR: node->next was not NULL - // This prevents accidentally breaking another list -} - -// Doubly-linked list error -my_data_t *data2 = malloc(sizeof(my_data_t)); -data2->node.prev = some_node; // Already linked! - -if (!list_push_front(&my_list, &data2->node)) { - // ERROR: node->prev was not NULL -} -@endcode +3. **Counter operations**: `remove_if`, `unique`, and `size` return `size_t` with count information @section list_best_practices Best Practices -1. **Always initialize node pointers to NULL** before insertion: +1. **Always initialize nodes** before insertion: @code{.c} // Forward list - my_data_t *data = malloc(sizeof(my_data_t)); - data->node.next = NULL; // Critical! + my_data_t *data = malloc(sizeof(*data)); + if (data != NULL) { + memset(data, 0, sizeof(*data)); + // insertion + } // Doubly-linked list - my_data_t *data2 = malloc(sizeof(my_data_t)); - data2->node._list.next = NULL; // Critical! - data2->node.prev = NULL; // Critical! - @endcode - -2. **Check return values** to detect errors: - @code{.c} - if (!flist_push_front(&my_flist, &data->node)) { - // Handle error + my_data_t *data2 = malloc(sizeof(*data2)); + if (data2 != NULL) { + memset(data2, 0, sizeof(*data2)); + // insertion } @endcode -3. **Use the deletion callback** to prevent memory leaks: +2. **Use the deletion callback** to prevent memory leaks: @code{.c} - flist_clear(&my_flist, my_delete_func); + flist_clear((flist_node_t**)&my_flist, (f_list_node_del)&my_delete_func); @endcode -4. **Choose the right list type:** +3. **Choose the right list type:** - Use `flist` (forward list) for minimal memory usage - - Use `list` (doubly-linked) for bidirectional access or frequent tail operations + - Use `list` (doubly-linked) for bidirectional access -5. **Prefer front operations** when order doesn't matter: +4. **Prefer front operations** when order doesn't matter: - `flist_push_front` is O(1) - `list_push_front` is O(1) - Both are faster than push_back for forward lists -6. **Don't reuse nodes** across multiple lists without proper unlinking +5. **Don't reuse nodes** across multiple lists without proper unlinking @section list_examples Complete Examples @@ -444,43 +376,47 @@ if (!list_push_front(&my_list, &data2->node)) { #include "lists.h" #include #include +#include typedef struct person_s { - flist_node_t node; + flist_node_t _node; char name[32]; int age; } person_t; -void delete_person(flist_node_t *node) { - person_t *person = (person_t *)node; +void delete_person(person_t *person) { printf("Deleting: %s\n", person->name); free(person); } int main(void) { - flist_node_t *people = NULL; + person_t *people = NULL; // Add some people - person_t *alice = malloc(sizeof(person_t)); - alice->node.next = NULL; - strcpy(alice->name, "Alice"); - alice->age = 30; - flist_push_front(&people, &alice->node); - - person_t *bob = malloc(sizeof(person_t)); - bob->node.next = NULL; - strcpy(bob->name, "Bob"); - bob->age = 25; - flist_push_back(&people, &bob->node); + person_t *alice = malloc(sizeof(*alice)); + if (alice != NULL) { + memset(alice, 0, sizeof(*alice)); + strcpy(alice->name, "Alice"); + alice->age = 30; + flist_push_front((flist_node_t**)&people, (flist_node_t*)alice); + } + + person_t *bob = malloc(sizeof(*bob)); + if (bob != NULL) { + memset(bob, 0, sizeof(*bob)); + strcpy(bob->name, "Bob"); + bob->age = 25; + flist_push_back((flist_node_t**)&people, (flist_node_t*)bob); + } // Print all people - for (flist_node_t *node = people; node != NULL; node = node->next) { + for (flist_node_t *node = (flist_node_t*)people; node != NULL; node = node->next) { person_t *p = (person_t *)node; printf("%s is %d years old\n", p->name, p->age); } // Clean up - flist_clear(&people, delete_person); + flist_clear((flist_node_t**)&people, (f_list_node_del)&delete_person); return 0; } @@ -494,55 +430,56 @@ int main(void) { #include typedef struct person_s { - list_node_t node; + list_node_t _node; char name[32]; int age; } person_t; -void delete_person_dl(flist_node_t *node) { - person_t *person = (person_t *)node; +void delete_person_dl(person_t *person) { printf("Deleting: %s\n", person->name); free(person); } int main(void) { - list_node_t *people = NULL; - - // Add people (O(1) for both front and back!) - person_t *alice = malloc(sizeof(person_t)); - alice->node._list.next = NULL; - alice->node.prev = NULL; - strcpy(alice->name, "Alice"); - alice->age = 30; - list_push_back(&people, &alice->node); // O(1) - fast! - - person_t *bob = malloc(sizeof(person_t)); - bob->node._list.next = NULL; - bob->node.prev = NULL; - strcpy(bob->name, "Bob"); - bob->age = 25; - list_push_back(&people, &bob->node); // O(1) - fast! + person_t *people = NULL; + + // Add people + person_t *alice = malloc(sizeof(*alice)); + if (alice != NULL) { + memset(alice, 0, sizeof(*alice)); + strcpy(alice->name, "Alice"); + alice->age = 30; + list_push_back((list_node_t**)&people, (list_node_t*)alice); + } + + person_t *bob = malloc(sizeof(*bob)); + if (bob != NULL) { + memset(bob, 0, sizeof(*bob)); + strcpy(bob->name, "Bob"); + bob->age = 25; + list_push_back((list_node_t**)&people, (list_node_t*)bob); + } // Forward traversal printf("Forward:\n"); - for (list_node_t *node = people; node != NULL; node = (list_node_t *)node->_list.next) { + for (flist_node_t *node = (flist_node_t*)people; node != NULL; node = node->next) { person_t *p = (person_t *)node; printf(" %s is %d years old\n", p->name, p->age); } // Backward traversal - only possible with doubly-linked! printf("Backward:\n"); - list_node_t *tail = people; - while (tail && tail->_list.next) { - tail = (list_node_t *)tail->_list.next; + flist_node_t *tail = (flist_node_t*)people; + while (tail && tail->next) { + tail = tail->next; } - for (list_node_t *node = tail; node != NULL; node = node->prev) { + for (list_node_t *node = (list_node_t*)tail; node != NULL; node = node->prev) { person_t *p = (person_t *)node; printf(" %s is %d years old\n", p->name, p->age); } // Clean up - list_clear(&people, delete_person_dl); + list_clear((list_node_t**)&people, (f_list_node_del)&delete_person_dl); return 0; } @@ -551,49 +488,39 @@ int main(void) { @subsection list_example_sort Sorting Example @code{.c} -bool compare_age(const flist_node_t *a, const flist_node_t *b) { - const person_t *pa = (const person_t *)a; - const person_t *pb = (const person_t *)b; - return pa->age <= pb->age; // Sort by age ascending +bool compare_age(const person_t *a, const person_t *b) { + return a->age <= b->age; // Sort by age ascending } // Sort forward list -if (flist_sort(&people, compare_age)) { - printf("Forward list sorted by age\n"); -} +flist_sort((flist_node_t**)&people, (f_list_node_cmp)&compare_age); // Sort doubly-linked list -if (list_sort(&people_dl, compare_age)) { - printf("Doubly-linked list sorted by age\n"); -} +list_sort((list_node_t**)&people_dl, (f_list_node_cmp)&compare_age); @endcode @subsection list_example_advanced Advanced: Remove Duplicates and Reverse @code{.c} // Remove consecutive duplicates after sorting -bool are_equal(const flist_node_t *a, const flist_node_t *b) { - const person_t *pa = (const person_t *)a; - const person_t *pb = (const person_t *)b; - return pa->age == pb->age; +bool are_equal(const person_t *a, const person_t *b) { + return a->age == b->age; } -flist_sort(&people, compare_age); -size_t removed = flist_unique(&people, are_equal, delete_person); +flist_sort((flist_node_t**)&people, (f_list_node_cmp)&compare_age); +size_t removed = flist_unique((flist_node_t**)&people, (f_list_node_bin_pred)&are_equal, (f_list_node_del)&delete_person); printf("Removed %zu duplicates\n", removed); // Reverse the list -if (flist_reverse(&people)) { - printf("List reversed (now descending order)\n"); -} +flist_reverse((flist_node_t**)&people); +printf("List reversed (now descending order)\n"); // Remove all minors -bool is_minor(const flist_node_t *node) { - const person_t *p = (const person_t *)node; +bool is_minor(const person_t *p) { return p->age < 18; } -size_t removed_minors = flist_remove_if(&people, is_minor, delete_person); +size_t removed_minors = flist_remove_if((flist_node_t**)&people, (f_list_node_pred)&is_minor, (f_list_node_del)&delete_person); printf("Removed %zu minors\n", removed_minors); @endcode @@ -601,18 +528,18 @@ printf("Removed %zu minors\n", removed_minors); @subsection flist_performance Forward List Performance -| Operation | Time Complexity | Notes | -|---------------------------|-----------------|----------------------------------| -| `flist_push_front` | O(1) | Constant time | -| `flist_pop_front` | O(1) | Constant time | +| Operation | Time Complexity | Notes | +|-------------------------|-----------------|----------------------------------| +| `flist_push_front` | O(1) ⚡ | Constant time | +| `flist_pop_front` | O(1) ⚡ | Constant time | | `flist_push_back` | O(n) | Must traverse entire list | | `flist_pop_back` | O(n) | Must find second-to-last node | -| `flist_insert_after` | O(1) | Constant time if reference known | +| `flist_insert_after` | O(1) ⚡ | Constant time if reference known | | `flist_remove` | O(n) | Must find previous node | | `flist_remove_if` | O(n) | Traverses list once | | `flist_clear` | O(n) | Must visit all nodes | | `flist_size` | O(n) | Must count all nodes | -| `flist_empty` | O(1) | Just checks if head is NULL | +| `flist_empty` | O(1) ⚡ | Just checks if head is NULL | | `flist_sort` | O(n²) | Bubble sort implementation | | `flist_unique` | O(n) | Single pass after sorting | | `flist_reverse` | O(n) | Single pass, reverses pointers | @@ -621,19 +548,19 @@ printf("Removed %zu minors\n", removed_minors); @subsection list_performance Doubly-Linked List Performance -| Operation | Time Complexity | Notes | -|---------------------------|-----------------|-----------------------------------| -| `list_push_front` | O(1) ⚡ | Constant time | -| `list_pop_front` | O(1) ⚡ | Constant time | -| `list_push_back` | O(1) ⚡ | **Direct access via prev!** | -| `list_pop_back` | O(1) ⚡ | **Direct access via prev!** | -| `list_insert_before` | O(1) ⚡ | **Direct access via prev!** | -| `list_insert_after` | O(1) ⚡ | Constant time | -| `list_remove` | O(1) ⚡ | **Direct access via prev!** | +| Operation | Time Complexity | Notes | +|-------------------------|-----------------|----------------------------------| +| `list_push_front` | O(1) ⚡ | Constant time | +| `list_pop_front` | O(1) ⚡ | Constant time | +| `list_push_back` | O(n) | Must traverse entire list | +| `list_pop_back` | O(n) | Must find second-to-last node | +| `list_insert_before` | O(1) ⚡ | **Direct access via prev!** | +| `list_insert_after` | O(1) ⚡ | Constant time | +| `list_remove` | O(n) | Must find previous node | | `list_remove_if` | O(n) | Traverses list once | | `list_clear` | O(n) | Must visit all nodes | | `list_size` | O(n) | Must count all nodes | -| `list_empty` | O(1) ⚡ | Just checks if head is NULL | +| `list_empty` | O(1) ⚡ | Just checks if head is NULL | | `list_sort` | O(n²) | Bubble sort implementation | | `list_unique` | O(n) | Single pass after sorting | | `list_reverse` | O(n) | Single pass, swaps pointers | @@ -649,8 +576,6 @@ printf("Removed %zu minors\n", removed_minors); - Example: Stack-like data structures, one-directional iteration **When to use doubly-linked lists (`list`):** -- Need frequent tail operations (O(1) vs O(n) ⚡) -- Need to insert/remove at known positions efficiently - Need bidirectional traversal - Memory overhead is acceptable - Example: LRU caches, undo/redo systems, navigation histories @@ -661,26 +586,20 @@ printf("Removed %zu minors\n", removed_minors); 1. **No backward traversal**: Cannot iterate backward through the list. -2. **No tail pointer**: `push_back` and `pop_back` are O(n). For frequent tail operations, - use doubly-linked lists instead. - -3. **No insert_before**: Cannot insert before a node without traversing from head. +2. **No insert_before**: Cannot insert before a node without traversing from head. Use doubly-linked lists for O(1) insert_before. -4. **Bubble sort**: The sort implementation is O(n²). For large lists, consider using +3. **Bubble sort**: The sort implementation is O(n²). For large lists, consider using an external sorting algorithm. @subsection list_limitations Doubly-Linked List Limitations -1. **Memory overhead**: Uses twice the memory per node compared to forward lists +1. **Memory overhead**: Uses twice the memory overhead per node compared to forward lists (two pointers instead of one). 2. **Bubble sort**: The sort implementation is O(n²). For large lists, consider using an external sorting algorithm. -3. **No tail caching**: While tail operations are O(1), finding the tail initially - requires traversal. For repeated tail access, cache the tail pointer in your application. - @subsection list_common_limitations Common Limitations (Both Types) 1. **No built-in search**: Searching requires manual traversal. Implement application-specific @@ -692,12 +611,13 @@ printf("Removed %zu minors\n", removed_minors); 3. **No automatic memory management**: Applications must manage node allocation and provide deletion callbacks. +4. **No tail pointer**: `push_back` and `pop_back` are O(n). + @section list_alternatives When to Use Alternatives Consider alternatives if you need: **Instead of forward lists (`flist`):** -- **Frequent tail operations**: Use doubly-linked list (`list`) for O(1) tail access ⚡ - **Bidirectional traversal**: Use doubly-linked list (`list`) - **Insert before operation**: Use doubly-linked list (`list`) for O(1) insert_before ⚡ diff --git a/lib_lists/lists.c b/lib_lists/lists.c index 7b3aaf4e1..df5c51806 100644 --- a/lib_lists/lists.c +++ b/lib_lists/lists.c @@ -11,89 +11,83 @@ #include "os_helpers.h" // ============================================================================ -// Internal shared functions with strict validation +// Internal functions used by both singly and doubly linked lists // ============================================================================ -static bool push_front_internal(flist_node_t **list, flist_node_t *node, bool doubly_linked) +static void push_front_internal(flist_node_t **list, flist_node_t *node, bool doubly_linked) { - if ((list == NULL) || (node == NULL)) { - return false; - } - node->next = *list; - *list = node; - if (doubly_linked) { - if (node->next != NULL) { - ((list_node_t *) node->next)->prev = (list_node_t *) node; + if ((list != NULL) && (node != NULL)) { + node->next = *list; + *list = node; + if (doubly_linked) { + if (node->next != NULL) { + ((list_node_t *) node->next)->prev = (list_node_t *) node; + } + ((list_node_t *) node)->prev = NULL; } - ((list_node_t *) node)->prev = NULL; } - return true; } -static bool pop_front_internal(flist_node_t **list, f_list_node_del del_func, bool doubly_linked) +static void pop_front_internal(flist_node_t **list, f_list_node_del del_func, bool doubly_linked) { flist_node_t *tmp; - if ((list == NULL) || (*list == NULL)) { - return false; - } - tmp = *list; - *list = tmp->next; - if (del_func != NULL) { - del_func(tmp); - } - if (doubly_linked) { - if (*list != NULL) { - (*(list_node_t **) list)->prev = NULL; + if (list != NULL) { + tmp = *list; + if (tmp != NULL) { + *list = tmp->next; + if (del_func != NULL) { + del_func(tmp); + } + if (doubly_linked) { + if (*list != NULL) { + (*(list_node_t **) list)->prev = NULL; + } + } } } - return true; } -static bool push_back_internal(flist_node_t **list, flist_node_t *node, bool doubly_linked) +static void push_back_internal(flist_node_t **list, flist_node_t *node, bool doubly_linked) { flist_node_t *tmp; - if ((list == NULL) || (node == NULL)) { - return false; - } - node->next = NULL; - if (*list == NULL) { - if (doubly_linked) { - ((list_node_t *) node)->prev = NULL; - } - *list = node; - } - else { - tmp = *list; - while (tmp->next != NULL) { - tmp = tmp->next; + if ((list != NULL) && (node != NULL)) { + node->next = NULL; + if (*list == NULL) { + if (doubly_linked) { + ((list_node_t *) node)->prev = NULL; + } + *list = node; } - if (doubly_linked) { - ((list_node_t *) node)->prev = (list_node_t *) tmp; + else { + tmp = *list; + while (tmp->next != NULL) { + tmp = tmp->next; + } + if (doubly_linked) { + ((list_node_t *) node)->prev = (list_node_t *) tmp; + } + tmp->next = node; } - tmp->next = node; } - return true; } -static bool insert_after_internal(flist_node_t *ref, flist_node_t *node, bool doubly_linked) +static void insert_after_internal(flist_node_t *ref, flist_node_t *node, bool doubly_linked) { - if ((ref == NULL) || (node == NULL)) { - return false; - } - if (doubly_linked) { - if (ref->next != NULL) { - ((list_node_t *) (ref->next))->prev = (list_node_t *) node; + if ((ref != NULL) && (node != NULL)) { + if (doubly_linked) { + if (ref->next != NULL) { + ((list_node_t *) (ref->next))->prev = (list_node_t *) node; + } + ((list_node_t *) node)->prev = (list_node_t *) ref; } - ((list_node_t *) node)->prev = (list_node_t *) ref; + node->next = ref->next; + ref->next = node; } - node->next = ref->next; - ref->next = node; - return true; } -static bool remove_internal(flist_node_t **list, +static void remove_internal(flist_node_t **list, flist_node_t *node, f_list_node_del del_func, bool doubly_linked) @@ -101,32 +95,34 @@ static bool remove_internal(flist_node_t **list, flist_node_t *it; flist_node_t *tmp; - if ((list == NULL) || (node == NULL) || (*list == NULL)) { - return false; - } - if (node == *list) { - // first element - return pop_front_internal(list, del_func, doubly_linked); - } - it = *list; - while ((it->next != node) && (it->next != NULL)) { - it = it->next; - } - if (it->next == NULL) { - // node not found - return false; - } - tmp = it->next->next; - if (doubly_linked) { - if (tmp != NULL) { - ((list_node_t *) tmp)->prev = ((list_node_t *) node)->prev; + if ((list != NULL) && (node != NULL)) { + if (node == *list) { + // first element + pop_front_internal(list, del_func, doubly_linked); + } + else { + it = *list; + if (it != NULL) { + while ((it->next != node) && (it->next != NULL)) { + it = it->next; + } + if (it->next == NULL) { + // node not found + return; + } + tmp = it->next->next; + if (doubly_linked) { + if (tmp != NULL) { + ((list_node_t *) tmp)->prev = ((list_node_t *) node)->prev; + } + } + if (del_func != NULL) { + del_func(it->next); + } + it->next = tmp; + } } } - if (del_func != NULL) { - del_func(it->next); - } - it->next = tmp; - return true; } static size_t remove_if_internal(flist_node_t **list, @@ -138,52 +134,48 @@ static size_t remove_if_internal(flist_node_t **list, flist_node_t *tmp; size_t count = 0; - if ((list == NULL) || (pred_func == NULL)) { - return 0; - } - node = *list; - while (node != NULL) { - tmp = node->next; - if (pred_func(node)) { - if (remove_internal(list, node, del_func, doubly_linked)) { + if ((list != NULL) && (pred_func != NULL)) { + node = *list; + while (node != NULL) { + tmp = node->next; + if (pred_func(node)) { + remove_internal(list, node, del_func, doubly_linked); count += 1; } + node = tmp; } - node = tmp; } return count; } -static bool sort_internal(flist_node_t **list, f_list_node_cmp cmp_func, bool doubly_linked) +static void sort_internal(flist_node_t **list, f_list_node_cmp cmp_func, bool doubly_linked) { flist_node_t **tmp; flist_node_t *a, *b; bool sorted; - if ((list == NULL) || (cmp_func == NULL)) { - return false; - } - do { - sorted = true; - for (tmp = list; (*tmp != NULL) && ((*tmp)->next != NULL); tmp = &(*tmp)->next) { - a = *tmp; - b = a->next; - if (cmp_func(a, b) == false) { - *tmp = b; - a->next = b->next; - b->next = a; - if (doubly_linked) { - ((list_node_t *) b)->prev = ((list_node_t *) a)->prev; - ((list_node_t *) a)->prev = (list_node_t *) b; - if (a->next != NULL) { - ((list_node_t *) a->next)->prev = (list_node_t *) a; + if ((list != NULL) && (cmp_func != NULL)) { + do { + sorted = true; + for (tmp = list; (*tmp != NULL) && ((*tmp)->next != NULL); tmp = &(*tmp)->next) { + a = *tmp; + b = a->next; + if (cmp_func(a, b) == false) { + *tmp = b; + a->next = b->next; + b->next = a; + if (doubly_linked) { + ((list_node_t *) b)->prev = ((list_node_t *) a)->prev; + ((list_node_t *) a)->prev = (list_node_t *) b; + if (a->next != NULL) { + ((list_node_t *) a->next)->prev = (list_node_t *) a; + } } + sorted = false; } - sorted = false; } - } - } while (!sorted); - return true; + } while (!sorted); + } } static size_t unique_internal(flist_node_t **list, @@ -193,95 +185,91 @@ static size_t unique_internal(flist_node_t **list, { size_t count = 0; - if ((list == NULL) || (pred_func == NULL)) { - return 0; - } - for (flist_node_t *ref = *list; ref != NULL; ref = ref->next) { - flist_node_t *node = ref->next; - while ((node != NULL) && (pred_func(ref, node))) { - flist_node_t *tmp = node->next; - if (remove_internal(list, node, del_func, doubly_linked)) { + if ((list != NULL) && (pred_func != NULL)) { + for (flist_node_t *ref = *list; ref != NULL; ref = ref->next) { + flist_node_t *node = ref->next; + while ((node != NULL) && (pred_func(ref, node))) { + flist_node_t *tmp = node->next; + remove_internal(list, node, del_func, doubly_linked); count += 1; + node = tmp; } - node = tmp; } } return count; } -static bool reverse_internal(flist_node_t **list, bool doubly_linked) +static void reverse_internal(flist_node_t **list, bool doubly_linked) { flist_node_t *node; flist_node_t *prev = NULL; flist_node_t *next; - if (list == NULL) { - return false; - } - node = *list; - while (node != NULL) { - next = node->next; - node->next = prev; - if (doubly_linked) { - ((list_node_t *) node)->prev = (list_node_t *) next; + if (list != NULL) { + node = *list; + while (node != NULL) { + next = node->next; + node->next = prev; + if (doubly_linked) { + ((list_node_t *) node)->prev = (list_node_t *) next; + } + *list = node; + prev = node; + node = next; } - *list = node; - prev = node; - node = next; } - return true; } // ============================================================================ // Forward list (singly-linked) functions // ============================================================================ -bool flist_push_front(flist_node_t **list, flist_node_t *node) +void flist_push_front(flist_node_t **list, flist_node_t *node) { - return push_front_internal(list, node, false); + push_front_internal(list, node, false); } -bool flist_pop_front(flist_node_t **list, f_list_node_del del_func) +void flist_pop_front(flist_node_t **list, f_list_node_del del_func) { - return pop_front_internal(list, del_func, false); + pop_front_internal(list, del_func, false); } -bool flist_push_back(flist_node_t **list, flist_node_t *node) +void flist_push_back(flist_node_t **list, flist_node_t *node) { - return push_back_internal(list, node, false); + push_back_internal(list, node, false); } -bool flist_pop_back(flist_node_t **list, f_list_node_del del_func) +void flist_pop_back(flist_node_t **list, f_list_node_del del_func) { flist_node_t *tmp; - if ((list == NULL) || (*list == NULL)) { - return false; - } - tmp = *list; - // only one element - if (tmp->next == NULL) { - return flist_pop_front(list, del_func); - } - while (tmp->next->next != NULL) { - tmp = tmp->next; - } - if (del_func != NULL) { - del_func(tmp->next); + if ((list != NULL) && (*list != NULL)) { + tmp = *list; + // only one element + if (tmp->next == NULL) { + flist_pop_front(list, del_func); + } + else { + while (tmp->next->next != NULL) { + tmp = tmp->next; + } + if (del_func != NULL) { + del_func(tmp->next); + } + tmp->next = NULL; + } } - tmp->next = NULL; - return true; } -bool flist_insert_after(flist_node_t **list, flist_node_t *ref, flist_node_t *node) +void flist_insert_after(flist_node_t **list, flist_node_t *ref, flist_node_t *node) { UNUSED(list); - return insert_after_internal(ref, node, false); + insert_after_internal(ref, node, false); } -bool flist_remove(flist_node_t **list, flist_node_t *node, f_list_node_del del_func) +void flist_remove(flist_node_t **list, flist_node_t *node, f_list_node_del del_func) { - return remove_internal(list, node, del_func, false); + remove_internal(list, node, del_func, false); } size_t flist_remove_if(flist_node_t **list, f_list_node_pred pred_func, f_list_node_del del_func) @@ -289,24 +277,22 @@ size_t flist_remove_if(flist_node_t **list, f_list_node_pred pred_func, f_list_n return remove_if_internal(list, pred_func, del_func, false); } -bool flist_clear(flist_node_t **list, f_list_node_del del_func) +void flist_clear(flist_node_t **list, f_list_node_del del_func) { flist_node_t *tmp; flist_node_t *next; - if (list == NULL) { - return false; - } - tmp = *list; - while (tmp != NULL) { - next = tmp->next; - if (del_func != NULL) { - del_func(tmp); + if (list != NULL) { + tmp = *list; + while (tmp != NULL) { + next = tmp->next; + if (del_func != NULL) { + del_func(tmp); + } + tmp = next; } - tmp = next; + *list = NULL; } - *list = NULL; - return true; } size_t flist_size(flist_node_t *const *list) @@ -323,12 +309,12 @@ size_t flist_size(flist_node_t *const *list) bool flist_empty(flist_node_t *const *list) { - return flist_size(list) == 0; + return (list == NULL) || (*list == NULL); } -bool flist_sort(flist_node_t **list, f_list_node_cmp cmp_func) +void flist_sort(flist_node_t **list, f_list_node_cmp cmp_func) { - return sort_internal(list, cmp_func, false); + sort_internal(list, cmp_func, false); } size_t flist_unique(flist_node_t **list, f_list_node_bin_pred pred_func, f_list_node_del del_func) @@ -336,58 +322,58 @@ size_t flist_unique(flist_node_t **list, f_list_node_bin_pred pred_func, f_list_ return unique_internal(list, pred_func, del_func, false); } -bool flist_reverse(flist_node_t **list) +void flist_reverse(flist_node_t **list) { - return reverse_internal(list, false); + reverse_internal(list, false); } // ============================================================================ // Doubly-linked list functions // ============================================================================ -bool list_push_front(list_node_t **list, list_node_t *node) +void list_push_front(list_node_t **list, list_node_t *node) { - return push_front_internal((flist_node_t **) list, (flist_node_t *) node, true); + push_front_internal((flist_node_t **) list, (flist_node_t *) node, true); } -bool list_pop_front(list_node_t **list, f_list_node_del del_func) +void list_pop_front(list_node_t **list, f_list_node_del del_func) { - return pop_front_internal((flist_node_t **) list, del_func, true); + pop_front_internal((flist_node_t **) list, del_func, true); } -bool list_push_back(list_node_t **list, list_node_t *node) +void list_push_back(list_node_t **list, list_node_t *node) { - return push_back_internal((flist_node_t **) list, (flist_node_t *) node, true); + push_back_internal((flist_node_t **) list, (flist_node_t *) node, true); } -bool list_pop_back(list_node_t **list, f_list_node_del del_func) +void list_pop_back(list_node_t **list, f_list_node_del del_func) { - return flist_pop_back((flist_node_t **) list, del_func); + flist_pop_back((flist_node_t **) list, del_func); } -bool list_insert_before(list_node_t **list, list_node_t *ref, list_node_t *node) +void list_insert_before(list_node_t **list, list_node_t *ref, list_node_t *node) { - if ((ref == NULL) || (node == NULL)) { - return false; - } - if (ref->prev == NULL) { - if ((list != NULL) && (*list == ref)) { - return list_push_front(list, node); + if ((ref != NULL) && (node != NULL)) { + if (ref->prev == NULL) { + if ((list != NULL) && (*list == ref)) { + list_push_front(list, node); + } + } + else { + list_insert_after(list, ref->prev, node); } - return false; } - return list_insert_after(list, ref->prev, node); } -bool list_insert_after(list_node_t **list, list_node_t *ref, list_node_t *node) +void list_insert_after(list_node_t **list, list_node_t *ref, list_node_t *node) { UNUSED(list); - return insert_after_internal((flist_node_t *) ref, (flist_node_t *) node, true); + insert_after_internal((flist_node_t *) ref, (flist_node_t *) node, true); } -bool list_remove(list_node_t **list, list_node_t *node, f_list_node_del del_func) +void list_remove(list_node_t **list, list_node_t *node, f_list_node_del del_func) { - return remove_internal((flist_node_t **) list, (flist_node_t *) node, del_func, true); + remove_internal((flist_node_t **) list, (flist_node_t *) node, del_func, true); } size_t list_remove_if(list_node_t **list, f_list_node_pred pred_func, f_list_node_del del_func) @@ -395,9 +381,9 @@ size_t list_remove_if(list_node_t **list, f_list_node_pred pred_func, f_list_nod return remove_if_internal((flist_node_t **) list, pred_func, del_func, true); } -bool list_clear(list_node_t **list, f_list_node_del del_func) +void list_clear(list_node_t **list, f_list_node_del del_func) { - return flist_clear((flist_node_t **) list, del_func); + flist_clear((flist_node_t **) list, del_func); } size_t list_size(list_node_t *const *list) @@ -407,12 +393,12 @@ size_t list_size(list_node_t *const *list) bool list_empty(list_node_t *const *list) { - return list_size(list) == 0; + return flist_empty((flist_node_t *const *) list); } -bool list_sort(list_node_t **list, f_list_node_cmp cmp_func) +void list_sort(list_node_t **list, f_list_node_cmp cmp_func) { - return sort_internal((flist_node_t **) list, cmp_func, true); + sort_internal((flist_node_t **) list, cmp_func, true); } size_t list_unique(list_node_t **list, f_list_node_bin_pred pred_func, f_list_node_del del_func) @@ -420,9 +406,9 @@ size_t list_unique(list_node_t **list, f_list_node_bin_pred pred_func, f_list_no return unique_internal((flist_node_t **) list, pred_func, del_func, true); } -bool list_reverse(list_node_t **list) +void list_reverse(list_node_t **list) { - return reverse_internal((flist_node_t **) list, true); + reverse_internal((flist_node_t **) list, true); } #endif // !defined(HAVE_ETH2) || defined(HAVE_SDK_LL_LIB) diff --git a/lib_lists/lists.h b/lib_lists/lists.h index b4c81ec62..bc1808f72 100644 --- a/lib_lists/lists.h +++ b/lib_lists/lists.h @@ -108,58 +108,58 @@ typedef bool (*f_list_node_bin_pred)(const flist_node_t *a, const flist_node_t * /** * @brief Add a node at the beginning of the forward list * @param[in,out] list Pointer to the list head - * @param[in] node Node to add (must have node->next == NULL) - * @return true on success, false on error + * @param[in] node Node to add (must not already be part of a list) + * @note If @p list or @p node is NULL, the function performs no action. * @note Time complexity: O(1) */ -bool flist_push_front(flist_node_t **list, flist_node_t *node); +void flist_push_front(flist_node_t **list, flist_node_t *node); /** * @brief Remove and delete the first node from the forward list * @param[in,out] list Pointer to the list head * @param[in] del_func Function to delete the node (can be NULL) - * @return true on success, false if list is empty or NULL + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(1) */ -bool flist_pop_front(flist_node_t **list, f_list_node_del del_func); +void flist_pop_front(flist_node_t **list, f_list_node_del del_func); /** * @brief Add a node at the end of the forward list * @param[in,out] list Pointer to the list head - * @param[in] node Node to add (must have node->next == NULL) - * @return true on success, false on error + * @param[in] node Node to add (must not already be part of a list) + * @note If @p list or @p node is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool flist_push_back(flist_node_t **list, flist_node_t *node); +void flist_push_back(flist_node_t **list, flist_node_t *node); /** * @brief Remove and delete the last node from the forward list * @param[in,out] list Pointer to the list head * @param[in] del_func Function to delete the node (can be NULL) - * @return true on success, false if list is empty or NULL + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool flist_pop_back(flist_node_t **list, f_list_node_del del_func); +void flist_pop_back(flist_node_t **list, f_list_node_del del_func); /** * @brief Insert a node after a reference node in the forward list * @param[in,out] list Pointer to the list head * @param[in] ref Reference node (must be in list) - * @param[in] node Node to insert (must have node->next == NULL) - * @return true on success, false on error + * @param[in] node Node to insert (must not already be part of a list) + * @note If @p ref or @p node is NULL, the function performs no action. * @note Time complexity: O(1) */ -bool flist_insert_after(flist_node_t **list, flist_node_t *ref, flist_node_t *node); +void flist_insert_after(flist_node_t **list, flist_node_t *ref, flist_node_t *node); /** * @brief Remove and delete a specific node from the forward list * @param[in,out] list Pointer to the list head * @param[in] node Node to remove (must be in list) * @param[in] del_func Function to delete the node (can be NULL) - * @return true on success, false if node not found or error + * @note If @p list or @p node is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool flist_remove(flist_node_t **list, flist_node_t *node, f_list_node_del del_func); +void flist_remove(flist_node_t **list, flist_node_t *node, f_list_node_del del_func); /** * @brief Remove all nodes matching a predicate from the forward list @@ -175,10 +175,10 @@ size_t flist_remove_if(flist_node_t **list, f_list_node_pred pred_func, f_list_n * @brief Remove and delete all nodes from the forward list * @param[in,out] list Pointer to the list head * @param[in] del_func Function to delete each node (can be NULL) - * @return true on success, false on error + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool flist_clear(flist_node_t **list, f_list_node_del del_func); +void flist_clear(flist_node_t **list, f_list_node_del del_func); /** * @brief Get the number of nodes in the forward list @@ -200,10 +200,10 @@ bool flist_empty(flist_node_t *const *list); * @brief Sort the forward list using a comparison function * @param[in,out] list Pointer to the list head * @param[in] cmp_func Comparison function (returns true if a < b) - * @return true on success, false on error - * @note Time complexity: O(n log n) - merge sort algorithm + * @note If @p list or @p cmp_func is NULL, the function performs no action. + * @note Time complexity: O(n²) - bubble sort algorithm */ -bool flist_sort(flist_node_t **list, f_list_node_cmp cmp_func); +void flist_sort(flist_node_t **list, f_list_node_cmp cmp_func); /** * @brief Remove consecutive duplicate nodes from the forward list @@ -219,10 +219,10 @@ size_t flist_unique(flist_node_t **list, f_list_node_bin_pred pred_func, f_list_ /** * @brief Reverse the order of nodes in the forward list * @param[in,out] list Pointer to the list head - * @return true on success, false on error + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool flist_reverse(flist_node_t **list); +void flist_reverse(flist_node_t **list); // ============================================================================ // Doubly-linked list functions @@ -231,69 +231,68 @@ bool flist_reverse(flist_node_t **list); /** * @brief Add a node at the beginning of the doubly-linked list * @param[in,out] list Pointer to the list head - * @param[in] node Node to add (must have node->_list.next == NULL and node->prev == NULL) - * @return true on success, false on error + * @param[in] node Node to add (must not already be part of a list) + * @note If @p list or @p node is NULL, the function performs no action. * @note Time complexity: O(1) */ -bool list_push_front(list_node_t **list, list_node_t *node); +void list_push_front(list_node_t **list, list_node_t *node); /** * @brief Remove and delete the first node from the doubly-linked list * @param[in,out] list Pointer to the list head * @param[in] del_func Function to delete the node (can be NULL) - * @return true on success, false if list is empty or NULL + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(1) */ -bool list_pop_front(list_node_t **list, f_list_node_del del_func); +void list_pop_front(list_node_t **list, f_list_node_del del_func); /** * @brief Add a node at the end of the doubly-linked list * @param[in,out] list Pointer to the list head - * @param[in] node Node to add (must have node->_list.next == NULL and node->prev == NULL) - * @return true on success, false on error + * @param[in] node Node to add (must not already be part of a list) + * @note If @p list or @p node is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool list_push_back(list_node_t **list, list_node_t *node); +void list_push_back(list_node_t **list, list_node_t *node); /** * @brief Remove and delete the last node from the doubly-linked list * @param[in,out] list Pointer to the list head * @param[in] del_func Function to delete the node (can be NULL) - * @return true on success, false if list is empty or NULL + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool list_pop_back(list_node_t **list, f_list_node_del del_func); +void list_pop_back(list_node_t **list, f_list_node_del del_func); /** * @brief Insert a node before a reference node in the doubly-linked list * @param[in,out] list Pointer to the list head * @param[in] ref Reference node (must be in list) - * @param[in] node Node to insert (must have node->_list.next == NULL and node->prev == NULL) - * @return true on success, false on error + * @param[in] node Node to insert (must not already be part of a list) + * @note If @p list or @p ref or @p node is NULL, the function performs no action. * @note Time complexity: O(1) - * @note This function is only available for doubly-linked lists */ -bool list_insert_before(list_node_t **list, list_node_t *ref, list_node_t *node); +void list_insert_before(list_node_t **list, list_node_t *ref, list_node_t *node); /** * @brief Insert a node after a reference node in the doubly-linked list * @param[in,out] list Pointer to the list head * @param[in] ref Reference node (must be in list) - * @param[in] node Node to insert (must have node->_list.next == NULL and node->prev == NULL) - * @return true on success, false on error + * @param[in] node Node to insert (must not already be part of a list) + * @note If @p ref or @p node is NULL, the function performs no action. * @note Time complexity: O(1) */ -bool list_insert_after(list_node_t **list, list_node_t *ref, list_node_t *node); +void list_insert_after(list_node_t **list, list_node_t *ref, list_node_t *node); /** * @brief Remove and delete a specific node from the doubly-linked list * @param[in,out] list Pointer to the list head * @param[in] node Node to remove (must be in list) * @param[in] del_func Function to delete the node (can be NULL) - * @return true on success, false if node not found or error + * @note If @p list or @p node is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool list_remove(list_node_t **list, list_node_t *node, f_list_node_del del_func); +void list_remove(list_node_t **list, list_node_t *node, f_list_node_del del_func); /** * @brief Remove all nodes matching a predicate from the doubly-linked list @@ -309,10 +308,10 @@ size_t list_remove_if(list_node_t **list, f_list_node_pred pred_func, f_list_nod * @brief Remove and delete all nodes from the doubly-linked list * @param[in,out] list Pointer to the list head * @param[in] del_func Function to delete each node (can be NULL) - * @return true on success, false on error + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool list_clear(list_node_t **list, f_list_node_del del_func); +void list_clear(list_node_t **list, f_list_node_del del_func); /** * @brief Get the number of nodes in the doubly-linked list @@ -334,10 +333,10 @@ bool list_empty(list_node_t *const *list); * @brief Sort the doubly-linked list using a comparison function * @param[in,out] list Pointer to the list head * @param[in] cmp_func Comparison function (returns true if a < b) - * @return true on success, false on error - * @note Time complexity: O(n log n) - merge sort algorithm + * @note If @p list or @p cmp_func is NULL, the function performs no action. + * @note Time complexity: O(n²) - bubble sort algorithm */ -bool list_sort(list_node_t **list, f_list_node_cmp cmp_func); +void list_sort(list_node_t **list, f_list_node_cmp cmp_func); /** * @brief Remove consecutive duplicate nodes from the doubly-linked list @@ -353,7 +352,7 @@ size_t list_unique(list_node_t **list, f_list_node_bin_pred pred_func, f_list_no /** * @brief Reverse the order of nodes in the doubly-linked list * @param[in,out] list Pointer to the list head - * @return true on success, false on error + * @note If @p list is NULL, the function performs no action. * @note Time complexity: O(n) */ -bool list_reverse(list_node_t **list); +void list_reverse(list_node_t **list); diff --git a/unit-tests/lib_lists/test_lists.c b/unit-tests/lib_lists/test_lists.c index 7ee466ff8..3b0bca1a0 100644 --- a/unit-tests/lib_lists/test_lists.c +++ b/unit-tests/lib_lists/test_lists.c @@ -116,17 +116,17 @@ static void test_flist_push_front(void **state) test_flist_node_t *node1, *node2, *node3; node1 = create_flist_node(1); - assert_true(flist_push_front(&list, &node1->node)); + flist_push_front(&list, &node1->node); assert_ptr_equal(list, &node1->node); assert_int_equal(flist_size(&list), 1); node2 = create_flist_node(2); - assert_true(flist_push_front(&list, &node2->node)); + flist_push_front(&list, &node2->node); assert_ptr_equal(list, &node2->node); assert_int_equal(flist_size(&list), 2); node3 = create_flist_node(3); - assert_true(flist_push_front(&list, &node3->node)); + flist_push_front(&list, &node3->node); assert_ptr_equal(list, &node3->node); assert_int_equal(flist_size(&list), 3); @@ -149,15 +149,15 @@ static void test_flist_push_back(void **state) test_flist_node_t *node1, *node2, *node3; node1 = create_flist_node(1); - assert_true(flist_push_back(&list, &node1->node)); + flist_push_back(&list, &node1->node); assert_int_equal(flist_size(&list), 1); node2 = create_flist_node(2); - assert_true(flist_push_back(&list, &node2->node)); + flist_push_back(&list, &node2->node); assert_int_equal(flist_size(&list), 2); node3 = create_flist_node(3); - assert_true(flist_push_back(&list, &node3->node)); + flist_push_back(&list, &node3->node); assert_int_equal(flist_size(&list), 3); // Verify order: 1 -> 2 -> 3 @@ -184,19 +184,21 @@ static void test_flist_pop_front(void **state) flist_push_back(&list, &node2->node); flist_push_back(&list, &node3->node); - assert_true(flist_pop_front(&list, delete_flist_node)); + flist_pop_front(&list, delete_flist_node); assert_int_equal(flist_size(&list), 2); assert_int_equal(((test_flist_node_t *) list)->value, 2); - assert_true(flist_pop_front(&list, delete_flist_node)); + flist_pop_front(&list, delete_flist_node); assert_int_equal(flist_size(&list), 1); assert_int_equal(((test_flist_node_t *) list)->value, 3); - assert_true(flist_pop_front(&list, delete_flist_node)); + flist_pop_front(&list, delete_flist_node); assert_int_equal(flist_size(&list), 0); assert_null(list); - assert_false(flist_pop_front(&list, delete_flist_node)); + flist_pop_front(&list, delete_flist_node); + assert_int_equal(flist_size(&list), 0); + assert_null(list); } // Test: flist pop_back @@ -212,18 +214,20 @@ static void test_flist_pop_back(void **state) flist_push_back(&list, &node2->node); flist_push_back(&list, &node3->node); - assert_true(flist_pop_back(&list, delete_flist_node)); + flist_pop_back(&list, delete_flist_node); assert_int_equal(flist_size(&list), 2); - assert_true(flist_pop_back(&list, delete_flist_node)); + flist_pop_back(&list, delete_flist_node); assert_int_equal(flist_size(&list), 1); assert_int_equal(((test_flist_node_t *) list)->value, 1); - assert_true(flist_pop_back(&list, delete_flist_node)); + flist_pop_back(&list, delete_flist_node); assert_int_equal(flist_size(&list), 0); assert_null(list); - assert_false(flist_pop_back(&list, delete_flist_node)); + flist_pop_back(&list, delete_flist_node); + assert_int_equal(flist_size(&list), 0); + assert_null(list); } // Test: flist insert_after @@ -238,7 +242,7 @@ static void test_flist_insert_after(void **state) flist_push_back(&list, &node3->node); test_flist_node_t *node2 = create_flist_node(2); - assert_true(flist_insert_after(&list, &node1->node, &node2->node)); + flist_insert_after(&list, &node1->node, &node2->node); assert_int_equal(flist_size(&list), 3); // Verify order: 1 -> 2 -> 3 @@ -265,7 +269,7 @@ static void test_flist_remove(void **state) flist_push_back(&list, &node2->node); flist_push_back(&list, &node3->node); - assert_true(flist_remove(&list, &node2->node, delete_flist_node)); + flist_remove(&list, &node2->node, delete_flist_node); assert_int_equal(flist_size(&list), 2); // Verify order: 1 -> 3 @@ -346,7 +350,7 @@ static void test_flist_reverse(void **state) flist_push_back(&list, &node->node); } - assert_true(flist_reverse(&list)); + flist_reverse(&list); // Verify reversed: 5, 4, 3, 2, 1 test_flist_node_t *current = (test_flist_node_t *) list; @@ -388,7 +392,7 @@ static void test_flist_sort(void **state) flist_push_back(&list, &node->node); } - assert_true(flist_sort(&list, compare_ascending_flist)); + flist_sort(&list, compare_ascending_flist); // Verify sorted: 1, 2, 3, 4 test_flist_node_t *current = (test_flist_node_t *) list; @@ -413,17 +417,17 @@ static void test_list_push_front(void **state) test_list_node_t *node1, *node2, *node3; node1 = create_list_node(1); - assert_true(list_push_front(&list, &node1->node)); + list_push_front(&list, &node1->node); assert_ptr_equal(list, &node1->node); assert_int_equal(list_size(&list), 1); node2 = create_list_node(2); - assert_true(list_push_front(&list, &node2->node)); + list_push_front(&list, &node2->node); assert_ptr_equal(list, &node2->node); assert_int_equal(list_size(&list), 2); node3 = create_list_node(3); - assert_true(list_push_front(&list, &node3->node)); + list_push_front(&list, &node3->node); assert_ptr_equal(list, &node3->node); assert_int_equal(list_size(&list), 3); @@ -438,7 +442,7 @@ static void test_list_push_front(void **state) list_clear(&list, delete_list_node); } -// Test: list push_back (O(1) - fast!) +// Test: list push_back static void test_list_push_back(void **state) { (void) state; @@ -446,15 +450,15 @@ static void test_list_push_back(void **state) test_list_node_t *node1, *node2, *node3; node1 = create_list_node(1); - assert_true(list_push_back(&list, &node1->node)); + list_push_back(&list, &node1->node); assert_int_equal(list_size(&list), 1); node2 = create_list_node(2); - assert_true(list_push_back(&list, &node2->node)); + list_push_back(&list, &node2->node); assert_int_equal(list_size(&list), 2); node3 = create_list_node(3); - assert_true(list_push_back(&list, &node3->node)); + list_push_back(&list, &node3->node); assert_int_equal(list_size(&list), 3); // Verify order: 1 -> 2 -> 3 @@ -481,22 +485,24 @@ static void test_list_pop_front(void **state) list_push_back(&list, &node2->node); list_push_back(&list, &node3->node); - assert_true(list_pop_front(&list, delete_list_node)); + list_pop_front(&list, delete_list_node); assert_int_equal(list_size(&list), 2); assert_int_equal(((test_list_node_t *) list)->value, 2); - assert_true(list_pop_front(&list, delete_list_node)); + list_pop_front(&list, delete_list_node); assert_int_equal(list_size(&list), 1); assert_int_equal(((test_list_node_t *) list)->value, 3); - assert_true(list_pop_front(&list, delete_list_node)); + list_pop_front(&list, delete_list_node); assert_int_equal(list_size(&list), 0); assert_null(list); - assert_false(list_pop_front(&list, delete_list_node)); + list_pop_front(&list, delete_list_node); + assert_int_equal(list_size(&list), 0); + assert_null(list); } -// Test: list pop_back (O(1) - fast!) +// Test: list pop_back static void test_list_pop_back(void **state) { (void) state; @@ -509,18 +515,20 @@ static void test_list_pop_back(void **state) list_push_back(&list, &node2->node); list_push_back(&list, &node3->node); - assert_true(list_pop_back(&list, delete_list_node)); + list_pop_back(&list, delete_list_node); assert_int_equal(list_size(&list), 2); - assert_true(list_pop_back(&list, delete_list_node)); + list_pop_back(&list, delete_list_node); assert_int_equal(list_size(&list), 1); assert_int_equal(((test_list_node_t *) list)->value, 1); - assert_true(list_pop_back(&list, delete_list_node)); + list_pop_back(&list, delete_list_node); assert_int_equal(list_size(&list), 0); assert_null(list); - assert_false(list_pop_back(&list, delete_list_node)); + list_pop_back(&list, delete_list_node); + assert_int_equal(list_size(&list), 0); + assert_null(list); } // Test: list insert_before (O(1) - unique to doubly-linked!) @@ -535,7 +543,7 @@ static void test_list_insert_before(void **state) list_push_back(&list, &node3->node); test_list_node_t *node2 = create_list_node(2); - assert_true(list_insert_before(&list, &node3->node, &node2->node)); + list_insert_before(&list, &node3->node, &node2->node); assert_int_equal(list_size(&list), 3); // Verify order: 1 -> 2 -> 3 @@ -548,7 +556,7 @@ static void test_list_insert_before(void **state) // Insert before head test_list_node_t *node0 = create_list_node(0); - assert_true(list_insert_before(&list, &node1->node, &node0->node)); + list_insert_before(&list, &node1->node, &node0->node); assert_ptr_equal(list, &node0->node); list_clear(&list, delete_list_node); @@ -566,7 +574,7 @@ static void test_list_insert_after(void **state) list_push_back(&list, &node3->node); test_list_node_t *node2 = create_list_node(2); - assert_true(list_insert_after(&list, &node1->node, &node2->node)); + list_insert_after(&list, &node1->node, &node2->node); assert_int_equal(list_size(&list), 3); // Verify order: 1 -> 2 -> 3 @@ -580,7 +588,7 @@ static void test_list_insert_after(void **state) list_clear(&list, delete_list_node); } -// Test: list remove (O(1) - fast!) +// Test: list remove static void test_list_remove(void **state) { (void) state; @@ -594,7 +602,7 @@ static void test_list_remove(void **state) list_push_back(&list, &node3->node); // Remove middle node (O(1)) - assert_true(list_remove(&list, &node2->node, delete_list_node)); + list_remove(&list, &node2->node, delete_list_node); assert_int_equal(list_size(&list), 2); // Verify order: 1 -> 3 @@ -675,7 +683,7 @@ static void test_list_reverse(void **state) list_push_back(&list, &node->node); } - assert_true(list_reverse(&list)); + list_reverse(&list); // Verify reversed: 5, 4, 3, 2, 1 test_list_node_t *current = (test_list_node_t *) list; @@ -717,7 +725,7 @@ static void test_list_sort(void **state) list_push_back(&list, &node->node); } - assert_true(list_sort(&list, compare_ascending_list)); + list_sort(&list, compare_ascending_list); // Verify sorted: 1, 2, 3, 4, 5 test_list_node_t *current = (test_list_node_t *) list;