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)
121 {
122 sdb_llist_elem_t *elem;
124 if ((! list) || (! lookup))
125 return NULL;
127 for (elem = list->head; elem; elem = elem->next)
128 if (! lookup(elem->obj, user_data))
129 break;
130 return elem;
131 } /* llist_search */
133 static sdb_object_t *
134 sdb_llist_remove_elem(sdb_llist_t *list, sdb_llist_elem_t *elem)
135 {
136 sdb_object_t *obj;
138 assert(list && elem);
140 obj = elem->obj;
142 if (elem->prev)
143 elem->prev->next = elem->next;
144 else {
145 assert(elem == list->head);
146 list->head = elem->next;
147 }
149 if (elem->next)
150 elem->next->prev = elem->prev;
151 else {
152 assert(elem == list->tail);
153 list->tail = elem->prev;
154 }
156 elem->prev = elem->next = NULL;
157 free(elem);
159 --list->length;
160 return obj;
161 } /* sdb_llist_remove_elem */
163 /*
164 * public API
165 */
167 sdb_llist_t *
168 sdb_llist_create(void)
169 {
170 sdb_llist_t *list;
172 list = malloc(sizeof(*list));
173 if (! list)
174 return NULL;
176 pthread_rwlock_init(&list->lock, /* attr = */ NULL);
178 list->head = list->tail = NULL;
179 list->length = 0;
180 return list;
181 } /* sdb_llist_create */
183 sdb_llist_t *
184 sdb_llist_clone(sdb_llist_t *list)
185 {
186 sdb_llist_t *new;
187 sdb_llist_elem_t *elem;
189 if (! list)
190 return NULL;
192 new = sdb_llist_create();
193 if (! new)
194 return NULL;
196 if (! list->length) {
197 assert((! list->head) && (! list->tail));
198 return new;
199 }
201 for (elem = list->head; elem; elem = elem->next) {
202 if (sdb_llist_append(new, elem->obj)) {
203 sdb_llist_destroy(new);
204 return NULL;
205 }
206 }
207 return new;
208 } /* sdb_llist_clone */
210 void
211 sdb_llist_destroy(sdb_llist_t *list)
212 {
213 sdb_llist_elem_t *elem;
215 if (! list)
216 return;
218 pthread_rwlock_wrlock(&list->lock);
220 elem = list->head;
221 while (elem) {
222 sdb_llist_elem_t *tmp = elem->next;
224 sdb_object_deref(elem->obj);
225 free(elem);
227 elem = tmp;
228 }
230 list->head = list->tail = NULL;
231 list->length = 0;
233 pthread_rwlock_unlock(&list->lock);
234 pthread_rwlock_destroy(&list->lock);
235 free(list);
236 } /* sdb_llist_destroy */
238 int
239 sdb_llist_append(sdb_llist_t *list, sdb_object_t *obj)
240 {
241 int status;
243 if ((! list) || (! obj))
244 return -1;
246 pthread_rwlock_wrlock(&list->lock);
247 status = sdb_llist_insert_after(list, list->tail, obj);
248 pthread_rwlock_unlock(&list->lock);
249 return status;
250 } /* sdb_llist_append */
252 int
253 sdb_llist_insert(sdb_llist_t *list, sdb_object_t *obj, size_t idx)
254 {
255 sdb_llist_elem_t *prev;
256 sdb_llist_elem_t *next;
258 int status;
260 size_t i;
262 if ((! list) || (! obj) || (idx > list->length))
263 return -1;
265 pthread_rwlock_wrlock(&list->lock);
267 prev = NULL;
268 next = list->head;
270 for (i = 0; i < idx; ++i) {
271 prev = next;
272 next = next->next;
273 }
274 status = sdb_llist_insert_after(list, prev, obj);
275 pthread_rwlock_unlock(&list->lock);
276 return status;
277 } /* sdb_llist_insert */
279 int
280 sdb_llist_insert_sorted(sdb_llist_t *list,
281 sdb_object_t *obj, sdb_llist_cmp_cb compare)
282 {
283 sdb_llist_elem_t *prev;
284 sdb_llist_elem_t *next;
286 int status;
288 if ((! list) || (! obj) || (! compare))
289 return -1;
291 pthread_rwlock_wrlock(&list->lock);
293 prev = NULL;
294 next = list->head;
296 while (next) {
297 if (compare(obj, next->obj) < 0)
298 break;
300 prev = next;
301 next = next->next;
302 }
303 status = sdb_llist_insert_after(list, prev, obj);
304 pthread_rwlock_unlock(&list->lock);
305 return status;
306 } /* sdb_llist_insert_sorted */
308 sdb_object_t *
309 sdb_llist_search(sdb_llist_t *list,
310 sdb_llist_lookup_cb lookup, const void *user_data)
311 {
312 sdb_llist_elem_t *elem;
314 pthread_rwlock_rdlock(&list->lock);
315 elem = llist_search(list, lookup, user_data);
316 pthread_rwlock_unlock(&list->lock);
318 if (elem)
319 return elem->obj;
320 return NULL;
321 } /* sdb_llist_search */
323 sdb_object_t *
324 sdb_llist_search_by_name(sdb_llist_t *list, const char *key)
325 {
326 sdb_llist_elem_t *elem;
328 if (! list)
329 return NULL;
331 pthread_rwlock_rdlock(&list->lock);
333 for (elem = list->head; elem; elem = elem->next)
334 if (! strcasecmp(elem->obj->name, key))
335 break;
337 pthread_rwlock_unlock(&list->lock);
339 if (elem)
340 return elem->obj;
341 return NULL;
342 } /* sdb_llist_search_by_name */
344 sdb_object_t *
345 sdb_llist_remove(sdb_llist_t *list,
346 sdb_llist_lookup_cb lookup, const void *user_data)
347 {
348 sdb_llist_elem_t *elem;
349 sdb_object_t *obj = NULL;
351 pthread_rwlock_wrlock(&list->lock);
352 elem = llist_search(list, lookup, user_data);
353 if (elem)
354 obj = sdb_llist_remove_elem(list, elem);
355 pthread_rwlock_unlock(&list->lock);
357 return obj;
358 } /* sdb_llist_remove */
360 sdb_object_t *
361 sdb_llist_shift(sdb_llist_t *list)
362 {
363 sdb_object_t *obj;
365 if ((! list) || (! list->head))
366 return NULL;
368 pthread_rwlock_wrlock(&list->lock);
369 obj = sdb_llist_remove_elem(list, list->head);
370 pthread_rwlock_unlock(&list->lock);
371 return obj;
372 } /* sdb_llist_shift */
374 sdb_llist_iter_t *
375 sdb_llist_get_iter(sdb_llist_t *list)
376 {
377 sdb_llist_iter_t *iter;
379 if (! list)
380 return NULL;
382 iter = malloc(sizeof(*iter));
383 if (! iter)
384 return NULL;
386 pthread_rwlock_rdlock(&list->lock);
388 iter->list = list;
389 iter->elem = list->head;
391 /* XXX: keep lock until destroying the iterator? */
392 pthread_rwlock_unlock(&list->lock);
393 return iter;
394 } /* sdb_llist_get_iter */
396 void
397 sdb_llist_iter_destroy(sdb_llist_iter_t *iter)
398 {
399 if (! iter)
400 return;
402 iter->list = NULL;
403 iter->elem = NULL;
404 free(iter);
405 } /* sdb_llist_iter_destroy */
407 _Bool
408 sdb_llist_iter_has_next(sdb_llist_iter_t *iter)
409 {
410 if (! iter)
411 return 0;
412 return iter->elem != NULL;
413 } /* sdb_llist_iter_has_next */
415 sdb_object_t *
416 sdb_llist_iter_get_next(sdb_llist_iter_t *iter)
417 {
418 sdb_object_t *obj;
420 if ((! iter) || (! iter->elem))
421 return NULL;
423 pthread_rwlock_rdlock(&iter->list->lock);
425 obj = iter->elem->obj;
426 iter->elem = iter->elem->next;
428 pthread_rwlock_unlock(&iter->list->lock);
429 return obj;
430 } /* sdb_llist_iter_get_next */
432 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */