Code

Include config.h in source files.
[sysdb.git] / src / utils / llist.c
1 /*
2  * SysDB - src/utils/llist.c
3  * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "utils/llist.h"
34 #include <assert.h>
35 #include <stdlib.h>
36 #include <strings.h>
38 #include <pthread.h>
40 /*
41  * private data types
42  */
44 struct sdb_llist_elem;
45 typedef struct sdb_llist_elem sdb_llist_elem_t;
47 struct sdb_llist_elem {
48         sdb_object_t *obj;
50         sdb_llist_elem_t *next;
51         sdb_llist_elem_t *prev;
52 };
54 struct sdb_llist {
55         pthread_rwlock_t lock;
57         sdb_llist_elem_t *head;
58         sdb_llist_elem_t *tail;
60         size_t length;
61 };
63 struct sdb_llist_iter {
64         sdb_llist_t *list;
65         sdb_llist_elem_t *elem;
66 };
68 /*
69  * private helper functions
70  */
72 /* Insert a new element after 'elem'. If 'elem' is NULL, insert at the head of
73  * the list. */
74 static int
75 sdb_llist_insert_after(sdb_llist_t *list, sdb_llist_elem_t *elem,
76                 sdb_object_t *obj)
77 {
78         sdb_llist_elem_t *new;
80         assert(list);
82         new = malloc(sizeof(*new));
83         if (! new)
84                 return -1;
86         new->obj  = obj;
87         if (elem)
88                 new->next = elem->next;
89         else if (list->head)
90                 new->next = list->head;
91         else
92                 new->next = NULL;
93         new->prev = elem;
95         if (elem) {
96                 if (elem->next)
97                         elem->next->prev = new;
98                 else
99                         list->tail = new;
100                 elem->next = new;
101         }
102         else {
103                 /* new entry will be new head */
104                 if (list->head)
105                         list->head->prev = new;
107                 list->head = new;
108                 if (! list->tail)
109                         list->tail = new;
110         }
112         assert(list->head && list->tail);
113         if (! list->length) {
114                 assert(list->head == list->tail);
115         }
117         sdb_object_ref(obj);
118         ++list->length;
119         return 0;
120 } /* sdb_llist_insert_after */
122 static sdb_llist_elem_t *
123 llist_search(sdb_llist_t *list,
124                 sdb_llist_lookup_cb lookup, const void *user_data)
126         sdb_llist_elem_t *elem;
128         assert(list && lookup);
130         for (elem = list->head; elem; elem = elem->next)
131                 if (! lookup(elem->obj, user_data))
132                         break;
133         return elem;
134 } /* llist_search */
136 static sdb_object_t *
137 sdb_llist_remove_elem(sdb_llist_t *list, sdb_llist_elem_t *elem)
139         sdb_object_t *obj;
141         assert(list && elem);
143         obj = elem->obj;
145         if (elem->prev)
146                 elem->prev->next = elem->next;
147         else {
148                 assert(elem == list->head);
149                 list->head = elem->next;
150         }
152         if (elem->next)
153                 elem->next->prev = elem->prev;
154         else {
155                 assert(elem == list->tail);
156                 list->tail = elem->prev;
157         }
159         elem->prev = elem->next = NULL;
160         free(elem);
162         --list->length;
163         return obj;
164 } /* sdb_llist_remove_elem */
166 /*
167  * public API
168  */
170 sdb_llist_t *
171 sdb_llist_create(void)
173         sdb_llist_t *list;
175         list = malloc(sizeof(*list));
176         if (! list)
177                 return NULL;
179         pthread_rwlock_init(&list->lock, /* attr = */ NULL);
181         list->head = list->tail = NULL;
182         list->length = 0;
183         return list;
184 } /* sdb_llist_create */
186 sdb_llist_t *
187 sdb_llist_clone(sdb_llist_t *list)
189         sdb_llist_t *new;
190         sdb_llist_elem_t *elem;
192         if (! list)
193                 return NULL;
195         new = sdb_llist_create();
196         if (! new)
197                 return NULL;
199         if (! list->length) {
200                 assert((! list->head) && (! list->tail));
201                 return new;
202         }
204         for (elem = list->head; elem; elem = elem->next) {
205                 if (sdb_llist_append(new, elem->obj)) {
206                         sdb_llist_destroy(new);
207                         return NULL;
208                 }
209         }
210         return new;
211 } /* sdb_llist_clone */
213 void
214 sdb_llist_destroy(sdb_llist_t *list)
216         sdb_llist_elem_t *elem;
218         if (! list)
219                 return;
221         pthread_rwlock_wrlock(&list->lock);
223         elem = list->head;
224         while (elem) {
225                 sdb_llist_elem_t *tmp = elem->next;
227                 sdb_object_deref(elem->obj);
228                 free(elem);
230                 elem = tmp;
231         }
233         list->head = list->tail = NULL;
234         list->length = 0;
236         pthread_rwlock_unlock(&list->lock);
237         pthread_rwlock_destroy(&list->lock);
238         free(list);
239 } /* sdb_llist_destroy */
241 int
242 sdb_llist_append(sdb_llist_t *list, sdb_object_t *obj)
244         int status;
246         if ((! list) || (! obj))
247                 return -1;
249         pthread_rwlock_wrlock(&list->lock);
250         status = sdb_llist_insert_after(list, list->tail, obj);
251         pthread_rwlock_unlock(&list->lock);
252         return status;
253 } /* sdb_llist_append */
255 int
256 sdb_llist_insert(sdb_llist_t *list, sdb_object_t *obj, size_t idx)
258         sdb_llist_elem_t *prev;
259         sdb_llist_elem_t *next;
261         int status;
263         size_t i;
265         if ((! list) || (! obj) || (idx > list->length))
266                 return -1;
268         pthread_rwlock_wrlock(&list->lock);
270         prev = NULL;
271         next = list->head;
273         for (i = 0; i < idx; ++i) {
274                 prev = next;
275                 next = next->next;
276         }
277         status = sdb_llist_insert_after(list, prev, obj);
278         pthread_rwlock_unlock(&list->lock);
279         return status;
280 } /* sdb_llist_insert */
282 int
283 sdb_llist_insert_sorted(sdb_llist_t *list,
284                 sdb_object_t *obj, sdb_llist_cmp_cb compare)
286         sdb_llist_elem_t *prev;
287         sdb_llist_elem_t *next;
289         int status;
291         if ((! list) || (! obj) || (! compare))
292                 return -1;
294         pthread_rwlock_wrlock(&list->lock);
296         prev = NULL;
297         next = list->head;
299         while (next) {
300                 if (compare(obj, next->obj) < 0)
301                         break;
303                 prev = next;
304                 next = next->next;
305         }
306         status = sdb_llist_insert_after(list, prev, obj);
307         pthread_rwlock_unlock(&list->lock);
308         return status;
309 } /* sdb_llist_insert_sorted */
311 sdb_object_t *
312 sdb_llist_get(sdb_llist_t *list, size_t i)
314         sdb_llist_elem_t *elem;
315         size_t j;
317         if ((! list) || (i >= list->length))
318                 return NULL;
320         for (elem = list->head, j = 0; j < i; elem = elem->next, ++j)
321                 /* iterate */;
323         assert(elem);
324         return elem->obj;
325 } /* sdb_llist_get */
327 sdb_object_t *
328 sdb_llist_search(sdb_llist_t *list,
329                 sdb_llist_lookup_cb lookup, const void *user_data)
331         sdb_llist_elem_t *elem;
333         if ((! list) || (! lookup))
334                 return NULL;
336         pthread_rwlock_rdlock(&list->lock);
337         elem = llist_search(list, lookup, user_data);
338         pthread_rwlock_unlock(&list->lock);
340         if (elem)
341                 return elem->obj;
342         return NULL;
343 } /* sdb_llist_search */
345 sdb_object_t *
346 sdb_llist_search_by_name(sdb_llist_t *list, const char *key)
348         sdb_llist_elem_t *elem;
350         if (! list)
351                 return NULL;
353         pthread_rwlock_rdlock(&list->lock);
355         for (elem = list->head; elem; elem = elem->next)
356                 if (! strcasecmp(elem->obj->name, key))
357                         break;
359         pthread_rwlock_unlock(&list->lock);
361         if (elem)
362                 return elem->obj;
363         return NULL;
364 } /* sdb_llist_search_by_name */
366 sdb_object_t *
367 sdb_llist_remove(sdb_llist_t *list,
368                 sdb_llist_lookup_cb lookup, const void *user_data)
370         sdb_llist_elem_t *elem;
371         sdb_object_t *obj = NULL;
373         if ((! list) || (! lookup))
374                 return NULL;
376         pthread_rwlock_wrlock(&list->lock);
377         elem = llist_search(list, lookup, user_data);
378         if (elem)
379                 obj = sdb_llist_remove_elem(list, elem);
380         pthread_rwlock_unlock(&list->lock);
382         return obj;
383 } /* sdb_llist_remove */
385 sdb_object_t *
386 sdb_llist_shift(sdb_llist_t *list)
388         sdb_object_t *obj;
390         if ((! list) || (! list->head))
391                 return NULL;
393         pthread_rwlock_wrlock(&list->lock);
394         obj = sdb_llist_remove_elem(list, list->head);
395         pthread_rwlock_unlock(&list->lock);
396         return obj;
397 } /* sdb_llist_shift */
399 sdb_llist_iter_t *
400 sdb_llist_get_iter(sdb_llist_t *list)
402         sdb_llist_iter_t *iter;
404         if (! list)
405                 return NULL;
407         iter = malloc(sizeof(*iter));
408         if (! iter)
409                 return NULL;
411         pthread_rwlock_rdlock(&list->lock);
413         iter->list = list;
414         iter->elem = list->head;
416         /* XXX: keep lock until destroying the iterator? */
417         pthread_rwlock_unlock(&list->lock);
418         return iter;
419 } /* sdb_llist_get_iter */
421 void
422 sdb_llist_iter_destroy(sdb_llist_iter_t *iter)
424         if (! iter)
425                 return;
427         iter->list = NULL;
428         iter->elem = NULL;
429         free(iter);
430 } /* sdb_llist_iter_destroy */
432 _Bool
433 sdb_llist_iter_has_next(sdb_llist_iter_t *iter)
435         if (! iter)
436                 return 0;
437         return iter->elem != NULL;
438 } /* sdb_llist_iter_has_next */
440 sdb_object_t *
441 sdb_llist_iter_get_next(sdb_llist_iter_t *iter)
443         sdb_object_t *obj;
445         if ((! iter) || (! iter->elem))
446                 return NULL;
448         pthread_rwlock_rdlock(&iter->list->lock);
450         /* XXX: increment ref-cnt for this object?
451          *      also: when letting an element take ownership of next and prev
452          *      elements, this might be a fairly cheap way to implement a weak
453          *      type of snapshotting */
455         obj = iter->elem->obj;
456         iter->elem = iter->elem->next;
458         pthread_rwlock_unlock(&iter->list->lock);
459         return obj;
460 } /* sdb_llist_iter_get_next */
462 int
463 sdb_llist_iter_remove_current(sdb_llist_iter_t *iter)
465         sdb_llist_elem_t *elem;
467         if ((! iter) || (! iter->list))
468                 return -1;
470         pthread_rwlock_wrlock(&iter->list->lock);
472         if (! iter->elem) /* reached end of list */
473                 elem = iter->list->tail;
474         else
475                 elem = iter->elem->prev;
476         if (elem)
477                 sdb_llist_remove_elem(iter->list, elem);
479         pthread_rwlock_unlock(&iter->list->lock);
481         if (! elem)
482                 return -1;
483         return 0;
484 } /* sdb_llist_iter_remove */
486 size_t
487 sdb_llist_len(sdb_llist_t *list)
489         if (! list)
490                 return 0;
491         return list->length;
492 } /* sdb_llist_len */
494 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */