1 /*******************************************************************************
2 * Copyright (c) 2009, 2020 IBM Corp.
3 *
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v2.0
6 * and Eclipse Distribution License v1.0 which accompany this distribution.
7 *
8 * The Eclipse Public License is available at
9 * https://www.eclipse.org/legal/epl-2.0/
10 * and the Eclipse Distribution License is available at
11 * http://www.eclipse.org/org/documents/edl-v10.php.
12 *
13 * Contributors:
14 * Ian Craggs - initial API and implementation and/or initial documentation
15 * Ian Craggs - updates for the async client
16 *******************************************************************************/
17
18 /**
19 * @file
20 * \brief functions which apply to linked list structures.
21 *
22 * These linked lists can hold data of any sort, pointed to by the content pointer of the
23 * ListElement structure. ListElements hold the points to the next and previous items in the
24 * list.
25 * */
26
27 #include "LinkedList.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "Heap.h"
33
34
35 static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent);
36
37
38 /**
39 * Sets a list structure to empty - all null values. Does not remove any items from the list.
40 * @param newl a pointer to the list structure to be initialized
41 */
ListZero(List * newl)42 void ListZero(List* newl)
43 {
44 memset(newl, '\0', sizeof(List));
45 }
46
47
48 /**
49 * Allocates and initializes a new list structure.
50 * @return a pointer to the new list structure
51 */
ListInitialize(void)52 List* ListInitialize(void)
53 {
54 List* newl = malloc(sizeof(List));
55 if (newl)
56 ListZero(newl);
57 return newl;
58 }
59
60
61 /**
62 * Append an already allocated ListElement and content to a list. Can be used to move
63 * an item from one list to another.
64 * @param aList the list to which the item is to be added
65 * @param content the list item content itself
66 * @param newel the ListElement to be used in adding the new item
67 * @param size the size of the element
68 */
ListAppendNoMalloc(List * aList,void * content,ListElement * newel,size_t size)69 void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size)
70 { /* for heap use */
71 newel->content = content;
72 newel->next = NULL;
73 newel->prev = aList->last;
74 if (aList->first == NULL)
75 aList->first = newel;
76 else
77 aList->last->next = newel;
78 aList->last = newel;
79 ++(aList->count);
80 aList->size += size;
81 }
82
83
84 /**
85 * Append an item to a list.
86 * @param aList the list to which the item is to be added
87 * @param content the list item content itself
88 * @param size the size of the element
89 */
ListAppend(List * aList,void * content,size_t size)90 ListElement* ListAppend(List* aList, void* content, size_t size)
91 {
92 ListElement* newel = malloc(sizeof(ListElement));
93 if (newel)
94 ListAppendNoMalloc(aList, content, newel, size);
95 return newel;
96 }
97
98
99 /**
100 * Insert an item to a list at a specific position.
101 * @param aList the list to which the item is to be added
102 * @param content the list item content itself
103 * @param size the size of the element
104 * @param index the position in the list. If NULL, this function is equivalent
105 * to ListAppend.
106 */
ListInsert(List * aList,void * content,size_t size,ListElement * index)107 ListElement* ListInsert(List* aList, void* content, size_t size, ListElement* index)
108 {
109 ListElement* newel = malloc(sizeof(ListElement));
110
111 if (newel == NULL)
112 return newel;
113 if ( index == NULL )
114 ListAppendNoMalloc(aList, content, newel, size);
115 else
116 {
117 newel->content = content;
118 newel->next = index;
119 newel->prev = index->prev;
120
121 index->prev = newel;
122 if ( newel->prev != NULL )
123 newel->prev->next = newel;
124 else
125 aList->first = newel;
126
127 ++(aList->count);
128 aList->size += size;
129 }
130 return newel;
131 }
132
133
134 /**
135 * Finds an element in a list by comparing the content pointers, rather than the contents
136 * @param aList the list in which the search is to be conducted
137 * @param content pointer to the list item content itself
138 * @return the list item found, or NULL
139 */
140 #if defined(IOT_CONNECT) || defined(IOT_LITEOS_ADAPT)
_ListFind(List * aList,void * content)141 ListElement* _ListFind(List* aList, void* content) // conflict with libbt_host.a
142 #else
143 ListElement* ListFind(List* aList, void* content)
144 #endif
145 {
146 return ListFindItem(aList, content, NULL);
147 }
148
149
150 /**
151 * Finds an element in a list by comparing the content or pointer to the content. A callback
152 * function is used to define the method of comparison for each element.
153 * @param aList the list in which the search is to be conducted
154 * @param content pointer to the content to look for
155 * @param callback pointer to a function which compares each element (NULL means compare by content pointer)
156 * @return the list element found, or NULL
157 */
ListFindItem(List * aList,void * content,int (* callback)(void *,void *))158 ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*))
159 {
160 ListElement* rc = NULL;
161
162 if (aList->current != NULL && ((callback == NULL && aList->current->content == content) ||
163 (callback != NULL && callback(aList->current->content, content))))
164 rc = aList->current;
165 else
166 {
167 ListElement* current = NULL;
168
169 /* find the content */
170 while (ListNextElement(aList, ¤t) != NULL)
171 {
172 if (callback == NULL)
173 {
174 if (current->content == content)
175 {
176 rc = current;
177 break;
178 }
179 }
180 else
181 {
182 if (callback(current->content, content))
183 {
184 rc = current;
185 break;
186 }
187 }
188 }
189 if (rc != NULL)
190 aList->current = rc;
191 }
192 return rc;
193 }
194
195
196 /**
197 * Removes and optionally frees an element in a list by comparing the content.
198 * A callback function is used to define the method of comparison for each element.
199 * @param aList the list in which the search is to be conducted
200 * @param content pointer to the content to look for
201 * @param callback pointer to a function which compares each element
202 * @param freeContent boolean value to indicate whether the item found is to be freed
203 * @return 1=item removed, 0=item not removed
204 */
ListUnlink(List * aList,void * content,int (* callback)(void *,void *),int freeContent)205 static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent)
206 {
207 ListElement* next = NULL;
208 ListElement* saved = aList->current;
209 int saveddeleted = 0;
210
211 if (!ListFindItem(aList, content, callback))
212 return 0; /* false, did not remove item */
213
214 if (aList->current->prev == NULL)
215 /* so this is the first element, and we have to update the "first" pointer */
216 aList->first = aList->current->next;
217 else
218 aList->current->prev->next = aList->current->next;
219
220 if (aList->current->next == NULL)
221 aList->last = aList->current->prev;
222 else
223 aList->current->next->prev = aList->current->prev;
224
225 next = aList->current->next;
226 if (freeContent)
227 {
228 free(aList->current->content);
229 aList->current->content = NULL;
230 }
231 if (saved == aList->current)
232 saveddeleted = 1;
233 free(aList->current);
234 if (saveddeleted)
235 aList->current = next;
236 else
237 aList->current = saved;
238 --(aList->count);
239 return 1; /* successfully removed item */
240 }
241
242
243 /**
244 * Removes but does not free an item in a list by comparing the pointer to the content.
245 * @param aList the list in which the search is to be conducted
246 * @param content pointer to the content to look for
247 * @return 1=item removed, 0=item not removed
248 */
ListDetach(List * aList,void * content)249 int ListDetach(List* aList, void* content)
250 {
251 return ListUnlink(aList, content, NULL, 0);
252 }
253
254
255 /**
256 * Removes and frees an item in a list by comparing the pointer to the content.
257 * @param aList the list from which the item is to be removed
258 * @param content pointer to the content to look for
259 * @return 1=item removed, 0=item not removed
260 */
ListRemove(List * aList,void * content)261 int ListRemove(List* aList, void* content)
262 {
263 return ListUnlink(aList, content, NULL, 1);
264 }
265
266
267 /**
268 * Removes and frees an the first item in a list.
269 * @param aList the list from which the item is to be removed
270 * @return 1=item removed, 0=item not removed
271 */
ListDetachHead(List * aList)272 void* ListDetachHead(List* aList)
273 {
274 void *content = NULL;
275 if (aList->count > 0)
276 {
277 ListElement* first = aList->first;
278 if (aList->current == first)
279 aList->current = first->next;
280 if (aList->last == first) /* i.e. no of items in list == 1 */
281 aList->last = NULL;
282 content = first->content;
283 aList->first = aList->first->next;
284 if (aList->first)
285 aList->first->prev = NULL;
286 free(first);
287 --(aList->count);
288 }
289 return content;
290 }
291
292
293 /**
294 * Removes and frees an the first item in a list.
295 * @param aList the list from which the item is to be removed
296 * @return 1=item removed, 0=item not removed
297 */
298 #if defined(IOT_CONNECT) || defined(IOT_LITEOS_ADAPT)
_ListRemoveHead(List * aList)299 int _ListRemoveHead(List* aList) // conflict with libbt_host.a
300 #else
301 int ListRemoveHead(List* aList)
302 #endif
303 {
304 free(ListDetachHead(aList));
305 return 0;
306 }
307
308
309 /**
310 * Removes but does not free the last item in a list.
311 * @param aList the list from which the item is to be removed
312 * @return the last item removed (or NULL if none was)
313 */
ListPopTail(List * aList)314 void* ListPopTail(List* aList)
315 {
316 void* content = NULL;
317 if (aList->count > 0)
318 {
319 ListElement* last = aList->last;
320 if (aList->current == last)
321 aList->current = last->prev;
322 if (aList->first == last) /* i.e. no of items in list == 1 */
323 aList->first = NULL;
324 content = last->content;
325 aList->last = aList->last->prev;
326 if (aList->last)
327 aList->last->next = NULL;
328 free(last);
329 --(aList->count);
330 }
331 return content;
332 }
333
334
335 /**
336 * Removes but does not free an element in a list by comparing the content.
337 * A callback function is used to define the method of comparison for each element.
338 * @param aList the list in which the search is to be conducted
339 * @param content pointer to the content to look for
340 * @param callback pointer to a function which compares each element
341 * @return 1=item removed, 0=item not removed
342 */
ListDetachItem(List * aList,void * content,int (* callback)(void *,void *))343 int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*))
344 { /* do not free the content */
345 return ListUnlink(aList, content, callback, 0);
346 }
347
348
349 /**
350 * Removes and frees an element in a list by comparing the content.
351 * A callback function is used to define the method of comparison for each element
352 * @param aList the list in which the search is to be conducted
353 * @param content pointer to the content to look for
354 * @param callback pointer to a function which compares each element
355 * @return 1=item removed, 0=item not removed
356 */
ListRemoveItem(List * aList,void * content,int (* callback)(void *,void *))357 int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*))
358 { /* remove from list and free the content */
359 return ListUnlink(aList, content, callback, 1);
360 }
361
362
363 /**
364 * Removes and frees all items in a list, leaving the list ready for new items.
365 * @param aList the list to which the operation is to be applied
366 */
ListEmpty(List * aList)367 void ListEmpty(List* aList)
368 {
369 while (aList->first != NULL)
370 {
371 ListElement* first = aList->first;
372 if (first->content != NULL)
373 {
374 free(first->content);
375 first->content = NULL;
376 }
377 aList->first = first->next;
378 free(first);
379 }
380 aList->count = 0;
381 aList->size = 0;
382 aList->current = aList->first = aList->last = NULL;
383 }
384
385 /**
386 * Removes and frees all items in a list, and frees the list itself
387 * @param aList the list to which the operation is to be applied
388 */
ListFree(List * aList)389 void ListFree(List* aList)
390 {
391 ListEmpty(aList);
392 free(aList);
393 }
394
395
396 /**
397 * Removes and but does not free all items in a list, and frees the list itself
398 * @param aList the list to which the operation is to be applied
399 */
ListFreeNoContent(List * aList)400 void ListFreeNoContent(List* aList)
401 {
402 while (aList->first != NULL)
403 {
404 ListElement* first = aList->first;
405 aList->first = first->next;
406 free(first);
407 }
408 free(aList);
409 }
410
411
412 /**
413 * Forward iteration through a list
414 * @param aList the list to which the operation is to be applied
415 * @param pos pointer to the current position in the list. NULL means start from the beginning of the list
416 * This is updated on return to the same value as that returned from this function
417 * @return pointer to the current list element
418 */
ListNextElement(List * aList,ListElement ** pos)419 ListElement* ListNextElement(List* aList, ListElement** pos)
420 {
421 return *pos = (*pos == NULL) ? aList->first : (*pos)->next;
422 }
423
424
425 /**
426 * Backward iteration through a list
427 * @param aList the list to which the operation is to be applied
428 * @param pos pointer to the current position in the list. NULL means start from the end of the list
429 * This is updated on return to the same value as that returned from this function
430 * @return pointer to the current list element
431 */
ListPrevElement(List * aList,ListElement ** pos)432 ListElement* ListPrevElement(List* aList, ListElement** pos)
433 {
434 return *pos = (*pos == NULL) ? aList->last : (*pos)->prev;
435 }
436
437
438 /**
439 * List callback function for comparing integers
440 * @param a first integer value
441 * @param b second integer value
442 * @return boolean indicating whether a and b are equal
443 */
intcompare(void * a,void * b)444 int intcompare(void* a, void* b)
445 {
446 return *((int*)a) == *((int*)b);
447 }
448
449
450 /**
451 * List callback function for comparing C strings
452 * @param a first integer value
453 * @param b second integer value
454 * @return boolean indicating whether a and b are equal
455 */
stringcompare(void * a,void * b)456 int stringcompare(void* a, void* b)
457 {
458 return strcmp((char*)a, (char*)b) == 0;
459 }
460
461
462 #if defined(UNIT_TESTS)
463
464
main(int argc,char * argv[])465 int main(int argc, char *argv[])
466 {
467 int i, *ip, *todelete;
468 ListElement* current = NULL;
469 List* l = ListInitialize();
470 printf("List initialized\n");
471
472 for (i = 0; i < 10; i++)
473 {
474 ip = malloc(sizeof(int));
475 *ip = i;
476 ListAppend(l, (void*)ip, sizeof(int));
477 if (i==5)
478 todelete = ip;
479 printf("List element appended %d\n", *((int*)(l->last->content)));
480 }
481
482 printf("List contents:\n");
483 current = NULL;
484 while (ListNextElement(l, ¤t) != NULL)
485 printf("List element: %d\n", *((int*)(current->content)));
486
487 printf("List contents in reverse order:\n");
488 current = NULL;
489 while (ListPrevElement(l, ¤t) != NULL)
490 printf("List element: %d\n", *((int*)(current->content)));
491
492 /* if ListFindItem(l, *ip, intcompare)->content */
493
494 printf("List contents having deleted element %d:\n", *todelete);
495 ListRemove(l, todelete);
496 current = NULL;
497 while (ListNextElement(l, ¤t) != NULL)
498 printf("List element: %d\n", *((int*)(current->content)));
499
500 i = 9;
501 ListRemoveItem(l, &i, intcompare);
502 printf("List contents having deleted another element, %d, size now %d:\n", i, l->size);
503 current = NULL;
504 while (ListNextElement(l, ¤t) != NULL)
505 printf("List element: %d\n", *((int*)(current->content)));
506
507 ListFree(l);
508 printf("List freed\n");
509 }
510
511 #endif
512
513
514
515
516
517