1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * lt-list.c
4 * Copyright (C) 2011-2012 Akira TAGOH
5 *
6 * Authors:
7 * Akira TAGOH <akira@tagoh.org>
8 *
9 * You may distribute under the terms of either the GNU
10 * Lesser General Public License or the Mozilla Public
11 * License, as specified in the README file.
12 */
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16
17 #include <stdlib.h>
18 #include "lt-mem.h"
19 #include "lt-messages.h"
20 #include "lt-list.h"
21
22
23 /**
24 * SECTION: lt-list
25 * @Short_Description: linked lists
26 * @Title: Doubly-Linked Lists
27 *
28 * The #lt_list_t object and its associated functions provide a standard
29 * doubly-linked list data structure.
30 */
31 struct _lt_list_t {
32 lt_mem_t parent;
33 lt_list_t *prev;
34 lt_list_t *next;
35 lt_pointer_t value;
36 };
37
38 /*< private >*/
39 static void
_lt_list_update(lt_pointer_t data)40 _lt_list_update(lt_pointer_t data)
41 {
42 lt_list_t *list = data;
43
44 if (list->next)
45 list->next->prev = list->prev;
46 if (list->prev)
47 list->prev->next = list->next;
48 }
49
50 static lt_list_t *
_lt_list_sort_merge(lt_list_t * l1,lt_list_t * l2,lt_compare_func_t func)51 _lt_list_sort_merge(lt_list_t *l1,
52 lt_list_t *l2,
53 lt_compare_func_t func)
54 {
55 int result;
56 lt_list_t list, *l = &list, *lprev = NULL;
57
58 while (l1 && l2) {
59 result = func(lt_list_value(l1), lt_list_value(l2));
60 if (result <= 0) {
61 l->next = l1;
62 l1 = lt_list_next(l1);
63 } else {
64 l->next = l2;
65 l2 = lt_list_next(l2);
66 }
67 l = lt_list_next(l);
68 l->prev = lprev;
69 lprev = l;
70 }
71 l->next = l1 ? l1 : l2;
72 l->next->prev = l;
73
74 return list.next;
75 }
76
77 /*< protected >*/
78 /**
79 * lt_list_new:
80 *
81 * Creates #lt_list_t object. this function is protected and not supposed
82 * to use in applications directly. Use lt_list_append() or lt_list_prepend()
83 * with %NULL as the first argument to newly allocate the object.
84 *
85 * Returns: (transfer full): a newly allocated #lt_list_t. it has to be freed
86 * with lt_list_unref().
87 */
88 lt_list_t *
lt_list_new(void)89 lt_list_new(void)
90 {
91 return lt_mem_alloc_object(sizeof (lt_list_t));
92 }
93
94 /*< public >*/
95
96 /**
97 * lt_list_ref:
98 * @list: a #lt_list_t.
99 *
100 * Increases the reference count of @list.
101 *
102 * Returns: (transfer none): the same @list object.
103 */
104 lt_list_t *
lt_list_ref(lt_list_t * list)105 lt_list_ref(lt_list_t *list)
106 {
107 lt_return_val_if_fail (list != NULL, NULL);
108
109 return lt_mem_ref(&list->parent);
110 }
111
112 /**
113 * lt_list_unref:
114 * @list: a #lt_list_t.
115 *
116 * Decreases the reference count of @list. when its reference count
117 * drops to 0, the object is finalized (i.e. its memory is freed).
118 */
119 void
lt_list_unref(lt_list_t * list)120 lt_list_unref(lt_list_t *list)
121 {
122 if (list)
123 lt_mem_unref(&list->parent);
124 }
125
126 /**
127 * lt_list_free:
128 * @data: a #lt_list_t.
129 *
130 * Frees all of the memory used by a #lt_list_t.
131 */
132 void
lt_list_free(lt_pointer_t data)133 lt_list_free(lt_pointer_t data)
134 {
135 lt_list_t *l = data, *list;
136
137 while (l) {
138 list = l;
139 l = l->next;
140 lt_list_unref(list);
141 }
142 }
143
144 /**
145 * lt_list_first:
146 * @list: a #lt_list_t.
147 *
148 * Gets the first element in a #lt_list_t.
149 *
150 * Returns: (transfer none): the first element in the #lt_list_t
151 * or %NULL if the #lt_list_t has no elements.
152 */
153 lt_list_t *
lt_list_first(lt_list_t * list)154 lt_list_first(lt_list_t *list)
155 {
156 if (list) {
157 while (list->prev)
158 list = list->prev;
159 }
160
161 return list;
162 }
163
164 /**
165 * lt_list_last:
166 * @list: a #lt_list_t.
167 *
168 * Gets the last element in a #lt_list_t.
169 *
170 * Returns: (transfer none): the last element in the #lt_list_t
171 * or %NULL if the #lt_list_t has no elements.
172 */
173 lt_list_t *
lt_list_last(lt_list_t * list)174 lt_list_last(lt_list_t *list)
175 {
176 if (list) {
177 while (list->next)
178 list = list->next;
179 }
180
181 return list;
182 }
183
184 /**
185 * lt_list_previous:
186 * @list: a #lt_list_t.
187 *
188 * Gets the previous element in a #lt_list_t.
189 *
190 * Returns: (transfer none): the previous element, or %NULL if there are no previous elements.
191 */
192 lt_list_t *
lt_list_previous(const lt_list_t * list)193 lt_list_previous(const lt_list_t *list)
194 {
195 return list ? list->prev : NULL;
196 }
197
198 /**
199 * lt_list_next:
200 * @list: a #lt_list_t.
201 *
202 * Gets the next element in a #lt_list_t.
203 *
204 * Returns: (transfer none): the next element, or %NULL if there are no more elements.
205 */
206 lt_list_t *
lt_list_next(const lt_list_t * list)207 lt_list_next(const lt_list_t *list)
208 {
209 return list ? list->next : NULL;
210 }
211
212 /**
213 * lt_list_value:
214 * @list: a #lt_list_t.
215 *
216 * Gets a value in a #lt_list_t.
217 *
218 * Returns: (transfer none): a pointer to be set to the #lt_list_t.
219 */
220 lt_pointer_t
lt_list_value(const lt_list_t * list)221 lt_list_value(const lt_list_t *list)
222 {
223 lt_return_val_if_fail (list != NULL, NULL);
224
225 return list->value;
226 }
227
228 /**
229 * lt_list_length:
230 * @list: a #lt_list_t.
231 *
232 * Gets the number of elements in a #lt_list_t.
233 *
234 * Returns: the number of elements in the #lt_list_t.
235 */
236 size_t
lt_list_length(const lt_list_t * list)237 lt_list_length(const lt_list_t *list)
238 {
239 size_t retval = 0;
240 const lt_list_t *l = list;
241
242 while (l) {
243 l = lt_list_next(l);
244 retval++;
245 }
246
247 return retval;
248 }
249
250 /**
251 * lt_list_append:
252 * @list: a #lt_list_t.
253 * @data: the data for the new element
254 * @func: (scope async): the call back function to destroy @data or %NULL
255 *
256 * Adds a new element on to the end of the list.
257 *
258 * Returns: the new start of the #lt_list_t.
259 */
260 lt_list_t *
lt_list_append(lt_list_t * list,lt_pointer_t data,lt_destroy_func_t func)261 lt_list_append(lt_list_t *list,
262 lt_pointer_t data,
263 lt_destroy_func_t func)
264 {
265 lt_list_t *l = lt_list_new();
266 lt_list_t *last;
267
268 l->value = data;
269 l->next = NULL;
270 lt_mem_add_ref(&l->parent, l, _lt_list_update);
271 if (func)
272 lt_mem_add_ref(&l->parent, data, func);
273 if (list) {
274 last = lt_list_last(list);
275 last->next = l;
276 l->prev = last;
277 } else {
278 l->prev = NULL;
279 list = l;
280 }
281
282 return list;
283 }
284
285 /**
286 * lt_list_prepend:
287 * @list: a #lt_list_t
288 * @data: the data for the new element
289 * @func: (scope async): the call back function to destroy @data or %NULL
290 *
291 * Adds a new element on to the start of the list.
292 *
293 * Returns: the new start of the #lt_list_t.
294 */
295 lt_list_t *
lt_list_prepend(lt_list_t * list,lt_pointer_t data,lt_destroy_func_t func)296 lt_list_prepend(lt_list_t *list,
297 lt_pointer_t data,
298 lt_destroy_func_t func)
299 {
300 lt_list_t *l = lt_list_new();
301
302 l->value = data;
303 l->next = list;
304 lt_mem_add_ref(&l->parent, l, _lt_list_update);
305 if (func)
306 lt_mem_add_ref(&l->parent, data, func);
307 if (list) {
308 l->prev = list->prev;
309 if (list->prev)
310 list->prev->next = l;
311 list->prev = l;
312 } else {
313 l->prev = NULL;
314 }
315
316 return l;
317 }
318
319 #if 0
320 /**
321 * lt_list_remove:
322 * @list: a #lt_list_t.
323 * @data: the data of the element to remove.
324 *
325 * Removes an element from a #lt_list_t.
326 * If two elements contain the same data, only the first is removed.
327 * If none of the elements contain the data, the #lt_list_t is unchanged.
328 * This works similar to lt_list_delete() though, the difference is
329 * this won't calls the finalizer to destroy the data in the element.
330 *
331 * Returns: the new start of the #lt_list_t.
332 */
333 lt_list_t *
334 lt_list_remove(lt_list_t *list,
335 lt_pointer_t data)
336 {
337 lt_list_t *l = list;
338
339 while (l) {
340 if (l->value == data) {
341 lt_mem_remove_ref(&l->parent, value);
342 list = lt_list_delete_link(list, l);
343 break;
344 } else {
345 l = l->next;
346 }
347 }
348
349 return list;
350 }
351
352 /**
353 * lt_list_delete:
354 * @list: a #lt_list_t.
355 * @data: the data of the element to remove.
356 *
357 * Removes an element from a #lt_list_t.
358 * If two elements contain the same data, only the first is removed.
359 * If none of the elements contain the data, the #lt_list_t is unchanged.
360 *
361 * Returns: the new start of the #lt_list_t.
362 */
363 lt_list_t *
364 lt_list_delete(lt_list_t *list,
365 lt_pointer_t data)
366 {
367 lt_list_t *l = list;
368
369 while (l) {
370 if (l->value == data) {
371 list = lt_list_delete_link(list, l);
372 break;
373 } else {
374 l = l->next;
375 }
376 }
377
378 return list;
379 }
380 #endif
381
382 /**
383 * lt_list_delete_link:
384 * @list: a #lt_list_t
385 * @link_: node to delete from @list
386 *
387 * Removes the node @link_ from the @list and frees it.
388 *
389 * Returns: the new head of @list
390 */
391 lt_list_t *
lt_list_delete_link(lt_list_t * list,lt_list_t * link_)392 lt_list_delete_link(lt_list_t *list,
393 lt_list_t *link_)
394 {
395 if (link_) {
396 if (link_ == list)
397 list = list->next;
398 lt_list_unref(link_);
399 }
400
401 return list;
402 }
403
404 /**
405 * lt_list_find:
406 * @list: a #lt_list_t
407 * @data: the element data to find
408 *
409 * Finds the element in a #lt_list_t which
410 * contains the given data.
411 *
412 * Returns: the found #lt_list_t element, or %NULL if it's not found
413 */
414 lt_list_t *
lt_list_find(lt_list_t * list,const lt_pointer_t data)415 lt_list_find(lt_list_t *list,
416 const lt_pointer_t data)
417 {
418 while (list) {
419 if (list->value == data)
420 break;
421 list = list->next;
422 }
423
424 return list;
425 }
426
427 /**
428 * lt_list_find_custom:
429 * @list: a #lt_list_t
430 * @data: the data passed to the function
431 * @func: (scope call): the function to call for each element.
432 * It should return 0 when the desired element is found
433 *
434 * Finds an element in a #lt_list_t, using a supplied function to
435 * find the desired element. It iterates over the list, calling
436 * the given function which should return 0 when the desired
437 * element is found. The function takes two const #lt_pointer_t
438 * arguments, the #lt_list_t element's data as the first argument
439 * and the given data.
440 *
441 * Returns: the found #lt_list_t element, or %NULL if it's not found
442 */
443 lt_list_t *
lt_list_find_custom(lt_list_t * list,const lt_pointer_t data,lt_compare_func_t func)444 lt_list_find_custom(lt_list_t *list,
445 const lt_pointer_t data,
446 lt_compare_func_t func)
447 {
448 lt_return_val_if_fail (func != NULL, NULL);
449
450 while (list) {
451 if (!func(list->value, data))
452 break;
453 list = list->next;
454 }
455
456 return list;
457 }
458
459 /**
460 * lt_list_sort:
461 * @list: a #lt_list_t
462 * @func: (scope call): the comparison function used to sort the #lt_list_t.
463 * This function is passed the data from 2 elements of the #lt_list_t
464 * and should return 0 if they are equal, a negative value if the
465 * first element comes before the second, or a positive value if
466 * the first element comes after the second.
467 *
468 * Sorts a #lt_list_t using the given comparison function.
469 *
470 * Returns: the start of the sorted #lt_list_t
471 */
472 lt_list_t *
lt_list_sort(lt_list_t * list,lt_compare_func_t func)473 lt_list_sort(lt_list_t *list,
474 lt_compare_func_t func)
475 {
476 lt_list_t *a, *b;
477 size_t i = 0, j = 0;
478
479 lt_return_val_if_fail (list != NULL, NULL);
480
481 if (!list->next)
482 return list;
483
484 a = b = list;
485 while (b->next) {
486 b = lt_list_next(b);
487 j++;
488 if ((j / 2) > i) {
489 a = lt_list_next(a);
490 i++;
491 }
492 }
493 b = a->next;
494 a->next = NULL;
495 b->prev = NULL;
496
497 return _lt_list_sort_merge(lt_list_sort(list, func),
498 lt_list_sort(b, func),
499 func);
500 }
501
502 /**
503 * lt_list_pop:
504 * @list: a #lt_list_t
505 * @data: a pointer to set the data in the first element
506 *
507 * Sets the data in the first element to @data and drop the element.
508 *
509 * Returns: the new head of @list.
510 */
511 lt_list_t *
lt_list_pop(lt_list_t * list,lt_pointer_t * data)512 lt_list_pop(lt_list_t *list,
513 lt_pointer_t *data)
514 {
515 lt_return_val_if_fail (list != NULL, NULL);
516
517 if (list->value)
518 lt_mem_remove_ref(&list->parent, list->value);
519 if (data)
520 *data = list->value;
521 list = lt_list_delete_link(list, list);
522
523 return list;
524 }
525