This repository was archived by the owner on Jul 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlist.c
347 lines (305 loc) · 10.1 KB
/
list.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/***************************************************************************
list.c - Linked list implementation
-------------------
copyright : (C) 2021 Intel Corporation
SPDX-License-Identifier: LGPL-2.1-only
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License *
* version 2.1 as published by the Free Software Foundation; *
* *
***************************************************************************/
#include <stdlib.h>
#include "usbi3c_i.h"
/**
* @brief Find the tail of a linked list
*
* @param[in] list_head a pointer to the first node (head) of a linked list
* @return a pointer to the last node (tail) of the linked list
*/
struct list *list_tail(struct list *list_head)
{
struct list *node = list_head;
while (node && node->next != NULL) {
node = node->next;
}
return node;
}
/**
* @brief Prepend a node to a linked list
*
* @param[in] list_head a pointer to the first node (head) of a linked list
* @param[in] data the data to be inserted in a new node at the beginning of a linked list
* @return a pointer to the new head node of the linked list
*
* @note: if the order of the list is not important, prefer to use list_prepend over
* list_append, it is a little faster.
*/
struct list *list_prepend(struct list *list_head, void *data)
{
struct list *new_node;
new_node = (struct list *)malloc_or_die(sizeof(struct list));
new_node->next = list_head;
new_node->data = data;
return new_node;
}
/**
* @brief Append a node to a linked list
*
* @param[in] list_head a pointer to the first node (head) of a linked list
* @param[in] data the data to be inserted in a new node at the end of a linked list
* @return a pointer to the head node of the linked list
*/
struct list *list_append(struct list *list_head, void *data)
{
struct list *new_node, *last_node;
new_node = (struct list *)malloc_or_die(sizeof(struct list));
new_node->next = NULL;
new_node->data = data;
// make sure we append to the tail of the list
if (list_head != NULL) {
last_node = list_tail(list_head);
last_node->next = new_node;
} else {
// the linked list is empty, so add the new node to the head
list_head = new_node;
}
return list_head;
}
/**
* @brief Removes a node from the linked list.
*
* If a list_free_data_fn is provided, the data in the node is also freed,
* if it is not, then only the node is freed but the data is kept.
*
* @param[in] list_head the head of the list containing the node to be freed
* @param[in] list_node the node to be freed
* @param[in] list_free_data_fn the function to free the data in the node
* @return the new head of the list, this could have changed if the node being
* removed was the head of the list.
*/
struct list *list_free_node(struct list *list_head, struct list *list_node, list_free_data_fn_t list_free_data_fn)
{
struct list *node = NULL;
struct list *previous_node = NULL;
/* removing the node from a single linked list is tricky since we need
* to change the "next" value in the previous node, and we cannot go back,
* we will need to iterate the list again */
for (node = list_head; node; node = node->next) {
if (node->next == list_node) {
/* the next node is the one we want to free,
* keep a pointer to it so we can update its "next" member */
previous_node = node;
continue;
}
if (node == list_node) {
/* if a function to free the data was provided, use it,
* if it was not, the user probably wanted to remove the
* node from the list without deleting the data */
if (list_free_data_fn) {
list_free_data_fn(list_node->data);
}
if (previous_node) {
previous_node->next = node->next;
}
if (node == list_head) {
/* the node being deleted was the head of the list,
* we need to store the new head so we don't loose it */
list_head = node->next;
}
FREE(node);
break;
}
}
return list_head;
}
/**
* @brief Removes a all matching nodes from the linked list.
*
* If a list_free_data_fn is provided, the data in the node is also freed,
* if it is not, then only the node is freed but the data is kept.
*
* To search for the nodes to remove, the function uses a search
* criteria (item) and a comparison function.
*
* The comparison function should match this type definition:
* int (*comparison_fn_t)(const void *a, const void *b)
*
* The comparison_fn function should behave like this:
* - return 0 if there is a match with the search criteria
* - return value > 0 if there is no match and we want to continue the search
* - return value < 0 if there is no match and we want to stop the search
*
* @param[in] list_head the head of the list
* @param[in] item the search criteria
* @param[in] comparison_fn the function to compare data from the node with the item to match
* @param[in] list_free_data_fn the function to free the data in the node
* @return the new head of the list, this could have changed if the node being
* removed was the head of the list.
*/
struct list *list_free_matching_nodes(struct list *list_head, const void *item, comparison_fn_t comparison_fn, list_free_data_fn_t list_free_data_fn)
{
struct list *node = NULL;
struct list *next = NULL;
struct list *previous_node = NULL;
int comparison_result = 0;
/* removing the node from a single linked list is tricky since we need
* to change the "next" value in the previous node, and we cannot go back,
* we will need to iterate the list again */
node = list_head;
while (node) {
next = node->next;
comparison_result = comparison_fn(node->data, item);
if (comparison_result == 0) {
/* we have a match, if a function to free the data was
* provided, use it. If it was not, then remove the
* node from the list without deleting the data */
if (list_free_data_fn) {
list_free_data_fn(node->data);
}
if (previous_node) {
previous_node->next = next;
}
if (node == list_head) {
/* the node being deleted was the head of the list,
* we need to store the new head so we don't loose it */
list_head = next;
}
FREE(node);
} else if (comparison_result > 0) {
/* no match */
previous_node = node;
} else {
/* comparison_result < 0, stop the search */
break;
}
node = next;
}
return list_head;
}
/**
* @brief Frees all memory allocated by the linked list.
*
* It calls the appropriate list_free_data_fn for each node to free the data.
* list_free_data_fn can be NULL if no data needs to be destroyed.
*
* @param[in] list_head a pointer to the first node (head) of a linked list
* @param[in] list_free_data_fn the function to be used to destroy the data of the node
*/
void list_free_list_and_data(struct list **list_head, list_free_data_fn_t list_free_data_fn)
{
struct list *node = *list_head;
while (node) {
struct list *next = NULL;
// use whatever function was passed to free the data (if any)
if (list_free_data_fn) {
list_free_data_fn(node->data);
}
next = node->next;
FREE(node);
node = next;
}
*list_head = NULL;
}
/**
* @brief Frees the memory allocated by the liked list without freeing the data.
*
* @param[in] list_head a pointer to the first node (head) of a linked list
*/
void list_free_list(struct list **list_head)
{
list_free_list_and_data(list_head, NULL);
}
/**
* @brief Concatenates two lists.
*
* @param[in] list1 a list to have the other list concatenated to
* @param[in] list2 a list to concatenate
* @return the concatenated list (list1 + list2)
*/
struct list *list_concat(struct list *list1, struct list *list2)
{
struct list *tail;
if (list1 == NULL) {
return list2;
}
if (list2 == NULL) {
return list1;
}
tail = list_tail(list1);
tail->next = list2;
return list1;
}
/**
* @brief Searches for a node within a list.
*
* To search for the node, the function uses a search criteria and a
* comparison function.
*
* The comparison function should match this type definition:
* int (*comparison_fn_t)(const void *a, const void *b)
*
* The comparison_fn function should behave like this:
* - return 0 if a is equal to b
* - return value < 0 if a is lower than b
* - return value > 0 if a is bigger than b
*
* @param[in] list_head a pointer to the first node (head) of a linked list
* @param[in] item the search criteria
* @param[in] comparison_fn a function of the type comparison_fn_t used to match the criteria
* with the correct node in the list
* @return the list node that matches the criteria if found, or NULL if it doesn't find one
*/
struct list *list_search_node(struct list *list_head, const void *item, comparison_fn_t comparison_fn)
{
struct list *node = list_head;
for (; node; node = node->next) {
if (comparison_fn(node->data, item) == 0) {
return node;
}
}
return NULL;
}
/**
* @brief Searches for some data within a list.
*
* Similar to list_search_node() with the one difference that this function returns
* the data within the node, not the node.
*
* @param[in] list_head a pointer to the first node (head) of a linked list
* @param[in] item the search criteria
* @param[in] comparison_fn a function of the type comparison_fn_t used to match the criteria
* with the correct node in the list
* @return the data from the list node that match the criteria, or NULL if it doesn't find it
*/
void *list_search(struct list *list_head, const void *item, comparison_fn_t comparison_fn)
{
struct list *node = NULL;
node = list_search_node(list_head, item, comparison_fn);
if (node) {
return node->data;
}
return NULL;
}
/**
* @brief Gets the number of nodes in a linked list.
*
* @param[in] list_head a pointer to the first node (head) of a linked list
* @return the number of nodes in the list.
*/
int list_len(struct list *list_head)
{
struct list *node = NULL;
int len;
if (list_head == NULL) {
return 0;
}
len = 1;
node = list_head;
while ((node = node->next) != NULL) {
len++;
}
return len;
}