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