Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
[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 #include "utils/llist.h"
30 #include <assert.h>
31 #include <stdlib.h>
32 #include <strings.h>
34 #include <pthread.h>
36 /*
37  * private data types
38  */
40 struct sdb_llist_elem;
41 typedef struct sdb_llist_elem sdb_llist_elem_t;
43 struct sdb_llist_elem {
44         sdb_object_t *obj;
46         sdb_llist_elem_t *next;
47         sdb_llist_elem_t *prev;
48 };
50 struct sdb_llist {
51         pthread_rwlock_t lock;
53         sdb_llist_elem_t *head;
54         sdb_llist_elem_t *tail;
56         size_t length;
57 };
59 struct sdb_llist_iter {
60         sdb_llist_t *list;
61         sdb_llist_elem_t *elem;
62 };
64 /*
65  * private helper functions
66  */
68 /* Insert a new element after 'elem'. If 'elem' is NULL, insert at the head of
69  * the list. */
70 static int
71 sdb_llist_insert_after(sdb_llist_t *list, sdb_llist_elem_t *elem,
72                 sdb_object_t *obj)
73 {
74         sdb_llist_elem_t *new;
76         assert(list);
78         new = malloc(sizeof(*new));
79         if (! new)
80                 return -1;
82         new->obj  = obj;
83         if (elem)
84                 new->next = elem->next;
85         else if (list->head)
86                 new->next = list->head;
87         else
88                 new->next = NULL;
89         new->prev = elem;
91         if (elem) {
92                 if (elem->next)
93                         elem->next->prev = new;
94                 else
95                         list->tail = new;
96                 elem->next = new;
97         }
98         else {
99                 /* new entry will be new head */
100                 if (list->head)
101                         list->head->prev = new;
103                 list->head = new;
104                 if (! list->tail)
105                         list->tail = new;
106         }
108         assert(list->head && list->tail);
109         if (! list->length) {
110                 assert(list->head == list->tail);
111         }
113         sdb_object_ref(obj);
114         ++list->length;
115         return 0;
116 } /* sdb_llist_insert_after */
118 static sdb_llist_elem_t *
119 llist_search(sdb_llist_t *list,
120                 sdb_llist_lookup_cb lookup, const void *user_data)
122         sdb_llist_elem_t *elem;
124         assert(list && lookup);
126         for (elem = list->head; elem; elem = elem->next)
127                 if (! lookup(elem->obj, user_data))
128                         break;
129         return elem;
130 } /* llist_search */
132 static sdb_object_t *
133 sdb_llist_remove_elem(sdb_llist_t *list, sdb_llist_elem_t *elem)
135         sdb_object_t *obj;
137         assert(list && elem);
139         obj = elem->obj;
141         if (elem->prev)
142                 elem->prev->next = elem->next;
143         else {
144                 assert(elem == list->head);
145                 list->head = elem->next;
146         }
148         if (elem->next)
149                 elem->next->prev = elem->prev;
150         else {
151                 assert(elem == list->tail);
152                 list->tail = elem->prev;
153         }
155         elem->prev = elem->next = NULL;
156         free(elem);
158         --list->length;
159         return obj;
160 } /* sdb_llist_remove_elem */
162 /*
163  * public API
164  */
166 sdb_llist_t *
167 sdb_llist_create(void)
169         sdb_llist_t *list;
171         list = malloc(sizeof(*list));
172         if (! list)
173                 return NULL;
175         pthread_rwlock_init(&list->lock, /* attr = */ NULL);
177         list->head = list->tail = NULL;
178         list->length = 0;
179         return list;
180 } /* sdb_llist_create */
182 sdb_llist_t *
183 sdb_llist_clone(sdb_llist_t *list)
185         sdb_llist_t *new;
186         sdb_llist_elem_t *elem;
188         if (! list)
189                 return NULL;
191         new = sdb_llist_create();
192         if (! new)
193                 return NULL;
195         if (! list->length) {
196                 assert((! list->head) && (! list->tail));
197                 return new;
198         }
200         for (elem = list->head; elem; elem = elem->next) {
201                 if (sdb_llist_append(new, elem->obj)) {
202                         sdb_llist_destroy(new);
203                         return NULL;
204                 }
205         }
206         return new;
207 } /* sdb_llist_clone */
209 void
210 sdb_llist_destroy(sdb_llist_t *list)
212         sdb_llist_elem_t *elem;
214         if (! list)
215                 return;
217         pthread_rwlock_wrlock(&list->lock);
219         elem = list->head;
220         while (elem) {
221                 sdb_llist_elem_t *tmp = elem->next;
223                 sdb_object_deref(elem->obj);
224                 free(elem);
226                 elem = tmp;
227         }
229         list->head = list->tail = NULL;
230         list->length = 0;
232         pthread_rwlock_unlock(&list->lock);
233         pthread_rwlock_destroy(&list->lock);
234         free(list);
235 } /* sdb_llist_destroy */
237 int
238 sdb_llist_append(sdb_llist_t *list, sdb_object_t *obj)
240         int status;
242         if ((! list) || (! obj))
243                 return -1;
245         pthread_rwlock_wrlock(&list->lock);
246         status = sdb_llist_insert_after(list, list->tail, obj);
247         pthread_rwlock_unlock(&list->lock);
248         return status;
249 } /* sdb_llist_append */
251 int
252 sdb_llist_insert(sdb_llist_t *list, sdb_object_t *obj, size_t idx)
254         sdb_llist_elem_t *prev;
255         sdb_llist_elem_t *next;
257         int status;
259         size_t i;
261         if ((! list) || (! obj) || (idx > list->length))
262                 return -1;
264         pthread_rwlock_wrlock(&list->lock);
266         prev = NULL;
267         next = list->head;
269         for (i = 0; i < idx; ++i) {
270                 prev = next;
271                 next = next->next;
272         }
273         status = sdb_llist_insert_after(list, prev, obj);
274         pthread_rwlock_unlock(&list->lock);
275         return status;
276 } /* sdb_llist_insert */
278 int
279 sdb_llist_insert_sorted(sdb_llist_t *list,
280                 sdb_object_t *obj, sdb_llist_cmp_cb compare)
282         sdb_llist_elem_t *prev;
283         sdb_llist_elem_t *next;
285         int status;
287         if ((! list) || (! obj) || (! compare))
288                 return -1;
290         pthread_rwlock_wrlock(&list->lock);
292         prev = NULL;
293         next = list->head;
295         while (next) {
296                 if (compare(obj, next->obj) < 0)
297                         break;
299                 prev = next;
300                 next = next->next;
301         }
302         status = sdb_llist_insert_after(list, prev, obj);
303         pthread_rwlock_unlock(&list->lock);
304         return status;
305 } /* sdb_llist_insert_sorted */
307 sdb_object_t *
308 sdb_llist_search(sdb_llist_t *list,
309                 sdb_llist_lookup_cb lookup, const void *user_data)
311         sdb_llist_elem_t *elem;
313         if ((! list) || (! lookup))
314                 return NULL;
316         pthread_rwlock_rdlock(&list->lock);
317         elem = llist_search(list, lookup, user_data);
318         pthread_rwlock_unlock(&list->lock);
320         if (elem)
321                 return elem->obj;
322         return NULL;
323 } /* sdb_llist_search */
325 sdb_object_t *
326 sdb_llist_search_by_name(sdb_llist_t *list, const char *key)
328         sdb_llist_elem_t *elem;
330         if (! list)
331                 return NULL;
333         pthread_rwlock_rdlock(&list->lock);
335         for (elem = list->head; elem; elem = elem->next)
336                 if (! strcasecmp(elem->obj->name, key))
337                         break;
339         pthread_rwlock_unlock(&list->lock);
341         if (elem)
342                 return elem->obj;
343         return NULL;
344 } /* sdb_llist_search_by_name */
346 sdb_object_t *
347 sdb_llist_remove(sdb_llist_t *list,
348                 sdb_llist_lookup_cb lookup, const void *user_data)
350         sdb_llist_elem_t *elem;
351         sdb_object_t *obj = NULL;
353         if ((! list) || (! lookup))
354                 return NULL;
356         pthread_rwlock_wrlock(&list->lock);
357         elem = llist_search(list, lookup, user_data);
358         if (elem)
359                 obj = sdb_llist_remove_elem(list, elem);
360         pthread_rwlock_unlock(&list->lock);
362         return obj;
363 } /* sdb_llist_remove */
365 sdb_object_t *
366 sdb_llist_shift(sdb_llist_t *list)
368         sdb_object_t *obj;
370         if ((! list) || (! list->head))
371                 return NULL;
373         pthread_rwlock_wrlock(&list->lock);
374         obj = sdb_llist_remove_elem(list, list->head);
375         pthread_rwlock_unlock(&list->lock);
376         return obj;
377 } /* sdb_llist_shift */
379 sdb_llist_iter_t *
380 sdb_llist_get_iter(sdb_llist_t *list)
382         sdb_llist_iter_t *iter;
384         if (! list)
385                 return NULL;
387         iter = malloc(sizeof(*iter));
388         if (! iter)
389                 return NULL;
391         pthread_rwlock_rdlock(&list->lock);
393         iter->list = list;
394         iter->elem = list->head;
396         /* XXX: keep lock until destroying the iterator? */
397         pthread_rwlock_unlock(&list->lock);
398         return iter;
399 } /* sdb_llist_get_iter */
401 void
402 sdb_llist_iter_destroy(sdb_llist_iter_t *iter)
404         if (! iter)
405                 return;
407         iter->list = NULL;
408         iter->elem = NULL;
409         free(iter);
410 } /* sdb_llist_iter_destroy */
412 _Bool
413 sdb_llist_iter_has_next(sdb_llist_iter_t *iter)
415         if (! iter)
416                 return 0;
417         return iter->elem != NULL;
418 } /* sdb_llist_iter_has_next */
420 sdb_object_t *
421 sdb_llist_iter_get_next(sdb_llist_iter_t *iter)
423         sdb_object_t *obj;
425         if ((! iter) || (! iter->elem))
426                 return NULL;
428         pthread_rwlock_rdlock(&iter->list->lock);
430         obj = iter->elem->obj;
431         iter->elem = iter->elem->next;
433         pthread_rwlock_unlock(&iter->list->lock);
434         return obj;
435 } /* sdb_llist_iter_get_next */
437 int
438 sdb_llist_iter_remove_current(sdb_llist_iter_t *iter)
440         sdb_llist_elem_t *elem;
442         if ((! iter) || (! iter->list))
443                 return -1;
445         pthread_rwlock_wrlock(&iter->list->lock);
447         if (! iter->elem) /* reached end of list */
448                 elem = iter->list->tail;
449         else
450                 elem = iter->elem->prev;
451         if (elem)
452                 sdb_llist_remove_elem(iter->list, elem);
454         pthread_rwlock_unlock(&iter->list->lock);
456         if (! elem)
457                 return -1;
458         return 0;
459 } /* sdb_llist_iter_remove */
461 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */