Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_list.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
28#include "nilorea/n_common.h"
29#include "nilorea/n_log.h"
30#include "nilorea/n_list.h"
31
37LIST* new_generic_list(size_t max_items) {
38 LIST* list = NULL;
39
40 Malloc(list, LIST, 1);
41 __n_assert(list, return NULL);
42
43 list->nb_max_items = max_items;
44 list->nb_items = 0;
45
46 list->start = list->end = NULL;
47
48 return list;
49} /* new_generic_list */
50
57LIST_NODE* new_list_node(void* ptr, void (*destructor)(void* ptr)) {
58 LIST_NODE* node = NULL;
59
60 Malloc(node, LIST_NODE, 1);
61 __n_assert(node, n_log(LOG_ERR, "Error allocating node for ptr %p", ptr); return NULL);
62
63 node->ptr = ptr;
64 node->destroy_func = destructor;
65 node->next = node->prev = NULL;
66
67 return node;
68} /* new_list_node(...) */
69
76void* remove_list_node_f(LIST* list, LIST_NODE* node) {
77 void* ptr = NULL;
78
79 __n_assert(list, n_log(LOG_ERR, "can't remove from NULL list"); return NULL);
80 __n_assert(list->start, n_log(LOG_ERR, "can't remove from NULL list->start"); return NULL);
81 __n_assert(list->end, n_log(LOG_ERR, "can't remove from NULL list->end"); return NULL);
82 __n_assert(node, n_log(LOG_ERR, "can't remove from NULL node"); return NULL);
83
84 ptr = node->ptr;
85 if (node->prev && node->next) {
86 node->prev->next = node->next;
87 node->next->prev = node->prev;
88 } else {
89 if (node->prev == NULL && node->next) {
90 node->next->prev = NULL;
91 list->start = node->next;
92 } else {
93 if (node->prev && node->next == NULL) {
94 node->prev->next = NULL;
95 list->end = node->prev;
96 } else {
97 if (node->prev == NULL && node->next == NULL) {
98 /* removing last item */
99 list->start = list->end = NULL;
100 }
101 }
102 }
103 }
104 Free(node);
105 if (list->nb_items > 0) {
106 list->nb_items--;
107 }
108 return ptr;
109} /* remove_list_node_f(...) */
110
117int list_node_push(LIST* list, LIST_NODE* node) {
118 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return FALSE);
119 __n_assert(node, n_log(LOG_ERR, "invalid node: NULL"); return FALSE);
120
121 if (list->nb_max_items > 0 && (list->nb_items >= list->nb_max_items)) {
122 n_log(LOG_ERR, "list is full");
123 return FALSE;
124 }
125 node->next = NULL;
126 if (list->end) {
127 list->end->next = node;
128 node->prev = list->end;
129 list->end = node;
130 } else {
131 node->prev = NULL;
132 list->start = list->end = node;
133 }
134 list->nb_items++;
135 return TRUE;
136} /* list_node_push() */
137
144 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return NULL);
145
146 if (list->nb_items == 0 || list->end == NULL)
147 return NULL;
148
149 LIST_NODE* nodeptr = NULL;
150
151 nodeptr = list->end;
152 if (list->end->prev) {
153 list->end = list->end->prev;
154 list->end->next = NULL;
155 }
156 list->nb_items--;
157 if (list->nb_items == 0)
158 list->start = list->end = NULL;
159
160 nodeptr->prev = NULL;
161 nodeptr->next = NULL;
162 return nodeptr;
163} /* list_node_pop() */
164
171 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return NULL);
172
173 if (list->nb_items == 0 || list->start == NULL)
174 return NULL;
175
176 LIST_NODE* nodeptr = NULL;
177 nodeptr = list->start;
178
179 if (list->start->next) {
180 list->start = list->start->next;
181 list->start->prev = NULL;
182 }
183
184 list->nb_items--;
185
186 if (list->nb_items == 0)
187 list->start = list->end = NULL;
188
189 nodeptr->prev = NULL;
190 nodeptr->next = NULL;
191 return nodeptr;
192} /* list_node_shift() */
193
201 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return FALSE);
202 __n_assert(node, n_log(LOG_ERR, "invalid node: NULL"); return FALSE);
203
204 if (list->nb_max_items > 0 && (list->nb_items >= list->nb_max_items)) {
205 n_log(LOG_ERR, "list is full");
206 return FALSE;
207 }
208 node->prev = NULL;
209 if (list->start) {
210 link_node(node, list->start);
211 list->start = node;
212 } else {
213 node->next = NULL;
214 list->start = list->end = node;
215 }
216 list->nb_items++;
217
218 return TRUE;
219} /* list_node_unshift() */
220
228int list_push(LIST* list, void* ptr, void (*destructor)(void* ptr)) {
229 LIST_NODE* node = NULL;
230
231 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return FALSE);
232 __n_assert(ptr, n_log(LOG_ERR, "invalid ptr: NULL"); return FALSE);
233
234 if (list->nb_max_items > 0 && (list->nb_items >= list->nb_max_items)) {
235 n_log(LOG_ERR, "list is full");
236 return FALSE;
237 }
238
239 node = new_list_node(ptr, destructor);
240 __n_assert(node, n_log(LOG_ERR, "Couldn't allocate new node"); return FALSE);
241
242 if (list->end) {
243 list->end->next = node;
244 node->prev = list->end;
245 list->end = node;
246 } else {
247 list->start = list->end = node;
248 }
249 list->nb_items++;
250 return TRUE;
251} /* list_push( ... ) */
252
261int list_push_sorted(LIST* list, void* ptr, int (*comparator)(const void* a, const void* b), void (*destructor)(void* ptr)) {
262 LIST_NODE* nodeptr = NULL;
263 int ret = TRUE;
264
265 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return FALSE);
266 __n_assert(ptr, n_log(LOG_ERR, "invalid ptr: NULL"); return FALSE);
267 __n_assert(comparator, n_log(LOG_ERR, "invalid comparator: NULL"); return FALSE);
268
269 if (list->nb_max_items > 0 && (list->nb_items >= list->nb_max_items)) {
270 n_log(LOG_ERR, "list is full");
271 return FALSE;
272 }
273
274 if (list->end) {
275 nodeptr = list->end;
276 while (nodeptr && (comparator(ptr, nodeptr->ptr) < 0))
277 nodeptr = nodeptr->prev;
278
279 if (!nodeptr) {
280 /* It's the lower ranked element in the sort */
281 ret = list_unshift(list, ptr, destructor);
282 if (ret == FALSE) {
283 n_log(LOG_ERR, "Couldn't list_unshift in list %p ptr %p with destructor %p", list, ptr, destructor);
284 return FALSE;
285 }
286 } else {
287 /* we have a match inside the list. let's insert the datas */
288 LIST_NODE* node_next = nodeptr->next;
289 LIST_NODE* newnode = new_list_node(ptr, destructor);
290 __n_assert(newnode, n_log(LOG_ERR, "Couldn't allocate new node"); return FALSE);
291
292 if (node_next) {
293 link_node(newnode, node_next);
294 } else
295 list->end = newnode;
296
297 link_node(nodeptr, newnode);
298 list->nb_items++;
299 }
300 } else {
301 ret = list_push(list, ptr, destructor);
302 if (ret == FALSE) {
303 n_log(LOG_ERR, "Couldn't list_push in list %p ptr %p with destructor %p", list, ptr, destructor);
304 return FALSE;
305 }
306 }
307 return ret;
308} /* list_push_sorted( ... ) */
309
317int list_unshift(LIST* list, void* ptr, void (*destructor)(void* ptr)) {
318 LIST_NODE* node = NULL;
319
320 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return FALSE);
321 __n_assert(ptr, n_log(LOG_ERR, "invalid ptr: NULL"); return FALSE);
322
323 if (list->nb_max_items > 0 && (list->nb_items >= list->nb_max_items)) {
324 n_log(LOG_ERR, "list is full");
325 return FALSE;
326 }
327 node = new_list_node(ptr, destructor);
328 __n_assert(node, n_log(LOG_ERR, "Couldn't allocate new node"); return FALSE);
329
330 if (list->start) {
331 link_node(node, list->start);
332 list->start = node;
333 } else {
334 list->start = list->end = node;
335 }
336 list->nb_items++;
337
338 return TRUE;
339} /* list_unshift_f(...) */
340
349int list_unshift_sorted(LIST* list, void* ptr, int (*comparator)(const void* a, const void* b), void (*destructor)(void* ptr)) {
350 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return FALSE);
351 __n_assert(ptr, n_log(LOG_ERR, "invalid ptr: NULL"); return FALSE);
352 __n_assert(comparator, n_log(LOG_ERR, "invalid comparator: NULL"); return FALSE);
353
354 LIST_NODE* nodeptr = NULL;
355 int ret = TRUE;
356
357 if (list->nb_max_items > 0 && (list->nb_items >= list->nb_max_items)) {
358 n_log(LOG_ERR, "list is full");
359 return FALSE;
360 }
361
362 if (list->start) {
363 nodeptr = list->start;
364 while (nodeptr && (comparator(ptr, nodeptr->ptr) > 0))
365 nodeptr = nodeptr->next;
366
367 if (!nodeptr) {
368 /* It's the higher ranked element in the sort */
369 ret = list_push(list, ptr, destructor);
370 if (ret == FALSE) {
371 n_log(LOG_ERR, "Couldn't list_push in list %p ptr %p with destructor %p", list, ptr, destructor);
372 return FALSE;
373 }
374 } else {
375 /* we have a match inside the list. let's insert the datas */
376 LIST_NODE* node_prev = nodeptr->prev;
377 LIST_NODE* newnode = new_list_node(ptr, destructor);
378 __n_assert(newnode, n_log(LOG_ERR, "Couldn't allocate new node"); return FALSE);
379
380 if (node_prev) {
381 link_node(node_prev, newnode);
382 } else
383 list->start = newnode;
384
385 link_node(newnode, nodeptr);
386 list->nb_items++;
387 }
388 } else {
389 ret = list_unshift(list, ptr, destructor);
390 if (ret == FALSE) {
391 n_log(LOG_ERR, "Couldn't list_unshift in list %p ptr %p with destructor %p", list, ptr, destructor);
392 return FALSE;
393 }
394 }
395 return ret;
396} /* list_unshift_sorted(...) */
397
403void* list_pop_f(LIST* list) {
404 LIST_NODE* nodeptr = NULL;
405 void* ptr = NULL;
406
407 __n_assert(list, n_log(LOG_ERR, "invalid list: NULL"); return NULL);
408
409 if (list->nb_items == 0 || list->end == NULL)
410 return NULL;
411
412 nodeptr = list->end;
413 ptr = nodeptr->ptr;
414
415 if (list->end->prev) {
416 list->end = list->end->prev;
417 list->end->next = NULL;
418 }
419
420 list->nb_items--;
421 if (list->nb_items == 0)
422 list->start = list->end = NULL;
423
424 Free(nodeptr);
425
426 return ptr;
427} /* list_pop_f( ... ) */
428
436void* list_shift_f(LIST* list, char* file, size_t line) {
437 LIST_NODE* nodeptr = NULL;
438 void* ptr = NULL;
439
440 __n_assert(list, n_log(LOG_ERR, "%s:%d: invalid list: NULL", file, line); return NULL);
441
442 if (list->nb_items == 0 || list->start == NULL)
443 return NULL;
444
445 nodeptr = list->start;
446 ptr = nodeptr->ptr;
447
448 if (list->start->next) {
449 list->start = list->start->next;
450 list->start->prev = NULL;
451 }
452
453 list->nb_items--;
454
455 if (list->nb_items == 0)
456 list->start = list->end = NULL;
457
458 Free(nodeptr);
459
460 return ptr;
461} /* list_shift_f(...)*/
462
469LIST_NODE* list_search(LIST* list, const void* ptr) {
470 __n_assert(list, return NULL);
471
472 list_foreach(node, list) {
473 if (node->ptr == ptr)
474 return node;
475 }
476 return NULL;
477} /* list_search */
478
485LIST_NODE* list_search_with_f(LIST* list, int (*checkfunk)(void* ptr)) {
486 __n_assert(list, return NULL);
487 __n_assert(checkfunk, return NULL);
488
489 list_foreach(node, list) {
490 if (checkfunk(node->ptr))
491 return node;
492 }
493 return NULL;
494} /* list_search */
495
501int list_empty(LIST* list) {
502 LIST_NODE* node = NULL;
503
504 __n_assert(list, n_log(LOG_ERR, "list is NULL"); return FALSE);
505
506 node = list->start;
507 while (node) {
508 LIST_NODE* node_ptr = node;
509 node = node->next;
510 if (node_ptr->destroy_func != NULL) {
511 node_ptr->destroy_func(node_ptr->ptr);
512 }
513 Free(node_ptr);
514 }
515 list->start = list->end = NULL;
516 list->nb_items = 0;
517 return TRUE;
518} /* list_empty( ... ) */
519
526int list_empty_with_f(LIST* list, void (*free_fnct)(void* ptr)) {
527 LIST_NODE* node = NULL;
528
529 __n_assert(list, n_log(LOG_ERR, "list is NULL"); return FALSE);
530
531 node = list->start;
532 while (node) {
533 LIST_NODE* node_ptr = node;
534 node = node->next;
535 if (free_fnct) free_fnct(node_ptr->ptr);
536 Free(node_ptr);
537 }
538 list->start = list->end = NULL;
539 list->nb_items = 0;
540 return TRUE;
541} /* list_empty_with_f */
542
548int list_destroy(LIST** list) {
549 __n_assert(list && (*list), n_log(LOG_ERR, "list already destroyed"); return FALSE);
550 list_empty((*list));
551 Free((*list));
552 return TRUE;
553} /* free_list( ... ) */
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
Definition n_common.h:204
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
LIST_NODE * end
pointer to the end of the list
Definition n_list.h:68
void * ptr
void pointer to store
Definition n_list.h:46
size_t nb_max_items
Maximum number of items in the list.
Definition n_list.h:63
struct LIST_NODE * prev
pointer to the previous node
Definition n_list.h:54
LIST_NODE * start
pointer to the start of the list
Definition n_list.h:66
size_t nb_items
number of item currently in the list
Definition n_list.h:61
void(* destroy_func)(void *ptr)
pointer to destructor function if any, else NULL
Definition n_list.h:49
struct LIST_NODE * next
pointer to the next node
Definition n_list.h:52
void * list_pop_f(LIST *list)
Get a pointer from the end of the list.
Definition n_list.c:403
void * list_shift_f(LIST *list, char *file, size_t line)
Get a pointer from the start of the list.
Definition n_list.c:436
int list_empty(LIST *list)
Empty a LIST list of pointers.
Definition n_list.c:501
LIST_NODE * list_search(LIST *list, const void *ptr)
search ptr in list
Definition n_list.c:469
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
Definition n_list.c:228
int list_node_unshift(LIST *list, LIST_NODE *node)
Add a pointer at the start of the list.
Definition n_list.c:200
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
Definition n_list.h:89
int list_unshift(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer at the start of the list.
Definition n_list.c:317
LIST_NODE * list_node_shift(LIST *list)
Get a LIST_NODE pointer from the start of the list.
Definition n_list.c:170
LIST_NODE * new_list_node(void *ptr, void(*destructor)(void *ptr))
Allocate a new node to link in a list.
Definition n_list.c:57
#define link_node(__NODE_1, __NODE_2)
Macro helper for linking two nodes, with NULL safety.
Definition n_list.h:78
int list_destroy(LIST **list)
Empty and Free a list container.
Definition n_list.c:548
int list_unshift_sorted(LIST *list, void *ptr, int(*comparator)(const void *a, const void *b), void(*destructor)(void *ptr))
Add a pointer sorted in the list , starting by the start of the list.
Definition n_list.c:349
LIST_NODE * list_node_pop(LIST *list)
Get a LIST_NODE pointer from the end of the list.
Definition n_list.c:143
void * remove_list_node_f(LIST *list, LIST_NODE *node)
Internal function called each time we need to get a node out of a list.
Definition n_list.c:76
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Definition n_list.c:37
int list_push_sorted(LIST *list, void *ptr, int(*comparator)(const void *a, const void *b), void(*destructor)(void *ptr))
Add a pointer sorted in the list , starting by the end of the list.
Definition n_list.c:261
int list_empty_with_f(LIST *list, void(*free_fnct)(void *ptr))
Empty a LIST list of pointers.
Definition n_list.c:526
LIST_NODE * list_search_with_f(LIST *list, int(*checkfunk)(void *ptr))
search ptr in list
Definition n_list.c:485
int list_node_push(LIST *list, LIST_NODE *node)
Add a filled node to the end of the list.
Definition n_list.c:117
Structure of a generic LIST container.
Definition n_list.h:59
Structure of a generic list node.
Definition n_list.h:44
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:89
#define LOG_ERR
error conditions
Definition n_log.h:76
Common headers and low-level functions & define.
List structures and definitions.
Generic log system.