1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-list.c Generic linked list utility (internal to D-Bus implementation)
3 *
4 * Copyright (C) 2002 Red Hat, Inc.
5 *
6 * Licensed under the Academic Free License version 2.1
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 */
23
24 #include <config.h>
25 #include "dbus-internals.h"
26 #include "dbus-list.h"
27 #include "dbus-mempool.h"
28 #include "dbus-threads-internal.h"
29
30 /**
31 * @defgroup DBusList Linked list
32 * @ingroup DBusInternals
33 * @brief DBusList data structure
34 *
35 * Types and functions related to DBusList.
36 */
37
38 static DBusMemPool *list_pool;
39 _DBUS_DEFINE_GLOBAL_LOCK (list);
40
41 /**
42 * @defgroup DBusListInternals Linked list implementation details
43 * @ingroup DBusInternals
44 * @brief DBusList implementation details
45 *
46 * The guts of DBusList.
47 *
48 * @{
49 */
50
51 /* the mem pool is probably a speed hit, with the thread
52 * lock, though it does still save memory - unknown.
53 */
54 static DBusList*
alloc_link(void * data)55 alloc_link (void *data)
56 {
57 DBusList *link;
58
59 _DBUS_LOCK (list);
60
61 if (list_pool == NULL)
62 {
63 list_pool = _dbus_mem_pool_new (sizeof (DBusList), TRUE);
64
65 if (list_pool == NULL)
66 {
67 _DBUS_UNLOCK (list);
68 return NULL;
69 }
70
71 link = _dbus_mem_pool_alloc (list_pool);
72 if (link == NULL)
73 {
74 _dbus_mem_pool_free (list_pool);
75 list_pool = NULL;
76 _DBUS_UNLOCK (list);
77 return NULL;
78 }
79 }
80 else
81 {
82 link = _dbus_mem_pool_alloc (list_pool);
83 }
84
85 if (link)
86 link->data = data;
87
88 _DBUS_UNLOCK (list);
89
90 return link;
91 }
92
93 static void
free_link(DBusList * link)94 free_link (DBusList *link)
95 {
96 _DBUS_LOCK (list);
97 if (_dbus_mem_pool_dealloc (list_pool, link))
98 {
99 _dbus_mem_pool_free (list_pool);
100 list_pool = NULL;
101 }
102
103 _DBUS_UNLOCK (list);
104 }
105
106 static void
link_before(DBusList ** list,DBusList * before_this_link,DBusList * link)107 link_before (DBusList **list,
108 DBusList *before_this_link,
109 DBusList *link)
110 {
111 if (*list == NULL)
112 {
113 link->prev = link;
114 link->next = link;
115 *list = link;
116 }
117 else
118 {
119 link->next = before_this_link;
120 link->prev = before_this_link->prev;
121 before_this_link->prev = link;
122 link->prev->next = link;
123
124 if (before_this_link == *list)
125 *list = link;
126 }
127 }
128
129 static void
link_after(DBusList ** list,DBusList * after_this_link,DBusList * link)130 link_after (DBusList **list,
131 DBusList *after_this_link,
132 DBusList *link)
133 {
134 if (*list == NULL)
135 {
136 link->prev = link;
137 link->next = link;
138 *list = link;
139 }
140 else
141 {
142 link->prev = after_this_link;
143 link->next = after_this_link->next;
144 after_this_link->next = link;
145 link->next->prev = link;
146 }
147 }
148
149 #ifdef DBUS_ENABLE_STATS
150 void
_dbus_list_get_stats(dbus_uint32_t * in_use_p,dbus_uint32_t * in_free_list_p,dbus_uint32_t * allocated_p)151 _dbus_list_get_stats (dbus_uint32_t *in_use_p,
152 dbus_uint32_t *in_free_list_p,
153 dbus_uint32_t *allocated_p)
154 {
155 _DBUS_LOCK (list);
156 _dbus_mem_pool_get_stats (list_pool, in_use_p, in_free_list_p, allocated_p);
157 _DBUS_UNLOCK (list);
158 }
159 #endif
160
161 /** @} */
162
163 /**
164 * @addtogroup DBusList
165 * @{
166 */
167
168 /**
169 * @struct DBusList
170 *
171 * A node in a linked list.
172 *
173 * DBusList is a circular list; that is, the tail of the list
174 * points back to the head of the list. The empty list is
175 * represented by a #NULL pointer.
176 */
177
178 /**
179 * @def _dbus_list_get_next_link
180 *
181 * Gets the next link in the list, or #NULL if
182 * there are no more links. Used for iteration.
183 *
184 * @code
185 * DBusList *link;
186 * link = _dbus_list_get_first_link (&list);
187 * while (link != NULL)
188 * {
189 * printf ("value is %p\n", link->data);
190 * link = _dbus_list_get_next_link (&link);
191 * }
192 * @endcode
193 *
194 * @param list address of the list head.
195 * @param link current link.
196 * @returns the next link, or %NULL if none.
197 *
198 */
199
200 /**
201 * @def _dbus_list_get_prev_link
202 *
203 * Gets the previous link in the list, or #NULL if
204 * there are no more links. Used for iteration.
205 *
206 * @code
207 * DBusList *link;
208 * link = _dbus_list_get_last_link (&list);
209 * while (link != NULL)
210 * {
211 * printf ("value is %p\n", link->data);
212 * link = _dbus_list_get_prev_link (&link);
213 * }
214 * @endcode
215 *
216 * @param list address of the list head.
217 * @param link current link.
218 * @returns the previous link, or %NULL if none.
219 *
220 */
221
222 /**
223 * Allocates a linked list node. Useful for preallocating
224 * nodes and using _dbus_list_append_link() to avoid
225 * allocations.
226 *
227 * @param data the value to store in the link.
228 * @returns a newly allocated link.
229 */
230 DBusList*
_dbus_list_alloc_link(void * data)231 _dbus_list_alloc_link (void *data)
232 {
233 return alloc_link (data);
234 }
235
236 /**
237 * Frees a linked list node allocated with _dbus_list_alloc_link.
238 * Does not free the data in the node.
239 *
240 * @param link the list node
241 */
242 void
_dbus_list_free_link(DBusList * link)243 _dbus_list_free_link (DBusList *link)
244 {
245 free_link (link);
246 }
247
248
249 /**
250 * Appends a value to the list. May return #FALSE
251 * if insufficient memory exists to add a list link.
252 * This is a constant-time operation.
253 *
254 * @param list address of the list head.
255 * @param data the value to append.
256 * @returns #TRUE on success.
257 */
258 dbus_bool_t
_dbus_list_append(DBusList ** list,void * data)259 _dbus_list_append (DBusList **list,
260 void *data)
261 {
262 if (!_dbus_list_prepend (list, data))
263 return FALSE;
264
265 /* Now cycle the list forward one so the prepended node is the tail */
266 *list = (*list)->next;
267
268 return TRUE;
269 }
270
271 /**
272 * Prepends a value to the list. May return #FALSE
273 * if insufficient memory exists to add a list link.
274 * This is a constant-time operation.
275 *
276 * @param list address of the list head.
277 * @param data the value to prepend.
278 * @returns #TRUE on success.
279 */
280 dbus_bool_t
_dbus_list_prepend(DBusList ** list,void * data)281 _dbus_list_prepend (DBusList **list,
282 void *data)
283 {
284 DBusList *link;
285
286 link = alloc_link (data);
287 if (link == NULL)
288 return FALSE;
289
290 link_before (list, *list, link);
291
292 return TRUE;
293 }
294
295 /**
296 * Appends a link to the list.
297 * Cannot fail due to out of memory.
298 * This is a constant-time operation.
299 *
300 * @param list address of the list head.
301 * @param link the link to append.
302 */
303 void
_dbus_list_append_link(DBusList ** list,DBusList * link)304 _dbus_list_append_link (DBusList **list,
305 DBusList *link)
306 {
307 _dbus_list_prepend_link (list, link);
308
309 /* Now cycle the list forward one so the prepended node is the tail */
310 *list = (*list)->next;
311 }
312
313 /**
314 * Prepends a link to the list.
315 * Cannot fail due to out of memory.
316 * This is a constant-time operation.
317 *
318 * @param list address of the list head.
319 * @param link the link to prepend.
320 */
321 void
_dbus_list_prepend_link(DBusList ** list,DBusList * link)322 _dbus_list_prepend_link (DBusList **list,
323 DBusList *link)
324 {
325 link_before (list, *list, link);
326 }
327
328 /**
329 * Inserts data into the list after the given existing link.
330 *
331 * @param list the list to modify
332 * @param after_this_link existing link to insert after, or #NULL to prepend
333 * @param data the value to insert
334 * @returns #TRUE on success, #FALSE if memory allocation fails
335 */
336 dbus_bool_t
_dbus_list_insert_after(DBusList ** list,DBusList * after_this_link,void * data)337 _dbus_list_insert_after (DBusList **list,
338 DBusList *after_this_link,
339 void *data)
340 {
341 DBusList *link;
342
343 if (after_this_link == NULL)
344 return _dbus_list_prepend (list, data);
345 else
346 {
347 link = alloc_link (data);
348 if (link == NULL)
349 return FALSE;
350
351 link_after (list, after_this_link, link);
352 }
353
354 return TRUE;
355 }
356
357 /**
358 * Inserts a link into the list before the given existing link.
359 *
360 * @param list the list to modify
361 * @param before_this_link existing link to insert before, or #NULL to append
362 * @param link the link to insert
363 */
364 void
_dbus_list_insert_before_link(DBusList ** list,DBusList * before_this_link,DBusList * link)365 _dbus_list_insert_before_link (DBusList **list,
366 DBusList *before_this_link,
367 DBusList *link)
368 {
369 if (before_this_link == NULL)
370 _dbus_list_append_link (list, link);
371 else
372 link_before (list, before_this_link, link);
373 }
374
375 /**
376 * Inserts a link into the list after the given existing link.
377 *
378 * @param list the list to modify
379 * @param after_this_link existing link to insert after, or #NULL to prepend
380 * @param link the link to insert
381 */
382 void
_dbus_list_insert_after_link(DBusList ** list,DBusList * after_this_link,DBusList * link)383 _dbus_list_insert_after_link (DBusList **list,
384 DBusList *after_this_link,
385 DBusList *link)
386 {
387 if (after_this_link == NULL)
388 _dbus_list_prepend_link (list, link);
389 else
390 link_after (list, after_this_link, link);
391 }
392
393 /**
394 * Removes a value from the list. Only removes the
395 * first value equal to the given data pointer,
396 * even if multiple values exist which match.
397 * This is a linear-time operation.
398 *
399 * @param list address of the list head.
400 * @param data the value to remove.
401 * @returns #TRUE if a value was found to remove.
402 */
403 dbus_bool_t
_dbus_list_remove(DBusList ** list,void * data)404 _dbus_list_remove (DBusList **list,
405 void *data)
406 {
407 DBusList *link;
408
409 link = *list;
410 while (link != NULL)
411 {
412 if (link->data == data)
413 {
414 _dbus_list_remove_link (list, link);
415 return TRUE;
416 }
417
418 link = _dbus_list_get_next_link (list, link);
419 }
420
421 return FALSE;
422 }
423
424 /**
425 * Removes a value from the list. Only removes the
426 * last value equal to the given data pointer,
427 * even if multiple values exist which match.
428 * This is a linear-time operation.
429 *
430 * @param list address of the list head.
431 * @param data the value to remove.
432 * @returns #TRUE if a value was found to remove.
433 */
434 dbus_bool_t
_dbus_list_remove_last(DBusList ** list,void * data)435 _dbus_list_remove_last (DBusList **list,
436 void *data)
437 {
438 DBusList *link;
439
440 link = _dbus_list_find_last (list, data);
441 if (link)
442 {
443 _dbus_list_remove_link (list, link);
444 return TRUE;
445 }
446 else
447 return FALSE;
448 }
449
450 /**
451 * Finds a value in the list. Returns the last link
452 * with value equal to the given data pointer.
453 * This is a linear-time operation.
454 * Returns #NULL if no value found that matches.
455 *
456 * @param list address of the list head.
457 * @param data the value to find.
458 * @returns the link if found
459 */
460 DBusList*
_dbus_list_find_last(DBusList ** list,void * data)461 _dbus_list_find_last (DBusList **list,
462 void *data)
463 {
464 DBusList *link;
465
466 link = _dbus_list_get_last_link (list);
467
468 while (link != NULL)
469 {
470 if (link->data == data)
471 return link;
472
473 link = _dbus_list_get_prev_link (list, link);
474 }
475
476 return NULL;
477 }
478
479 /**
480 * Removes the given link from the list, but doesn't
481 * free it. _dbus_list_remove_link() both removes the
482 * link and also frees it.
483 *
484 * @param list the list
485 * @param link the link in the list
486 */
487 void
_dbus_list_unlink(DBusList ** list,DBusList * link)488 _dbus_list_unlink (DBusList **list,
489 DBusList *link)
490 {
491 if (link->next == link)
492 {
493 /* one-element list */
494 *list = NULL;
495 }
496 else
497 {
498 link->prev->next = link->next;
499 link->next->prev = link->prev;
500
501 if (*list == link)
502 *list = link->next;
503 }
504
505 link->next = NULL;
506 link->prev = NULL;
507 }
508
509 /**
510 * Removes a link from the list. This is a constant-time operation.
511 *
512 * @param list address of the list head.
513 * @param link the list link to remove.
514 */
515 void
_dbus_list_remove_link(DBusList ** list,DBusList * link)516 _dbus_list_remove_link (DBusList **list,
517 DBusList *link)
518 {
519 _dbus_list_unlink (list, link);
520 free_link (link);
521 }
522
523 /**
524 * Frees all links in the list and sets the list head to #NULL. Does
525 * not free the data in each link, for obvious reasons. This is a
526 * linear-time operation.
527 *
528 * @param list address of the list head.
529 */
530 void
_dbus_list_clear(DBusList ** list)531 _dbus_list_clear (DBusList **list)
532 {
533 DBusList *link;
534
535 link = *list;
536 while (link != NULL)
537 {
538 DBusList *next = _dbus_list_get_next_link (list, link);
539
540 free_link (link);
541
542 link = next;
543 }
544
545 *list = NULL;
546 }
547
548 /**
549 * Gets the first link in the list.
550 * This is a constant-time operation.
551 *
552 * @param list address of the list head.
553 * @returns the first link, or #NULL for an empty list.
554 */
555 DBusList*
_dbus_list_get_first_link(DBusList ** list)556 _dbus_list_get_first_link (DBusList **list)
557 {
558 return *list;
559 }
560
561 /**
562 * Gets the last link in the list.
563 * This is a constant-time operation.
564 *
565 * @param list address of the list head.
566 * @returns the last link, or #NULL for an empty list.
567 */
568 DBusList*
_dbus_list_get_last_link(DBusList ** list)569 _dbus_list_get_last_link (DBusList **list)
570 {
571 if (*list == NULL)
572 return NULL;
573 else
574 return (*list)->prev;
575 }
576
577 /**
578 * Gets the last data in the list.
579 * This is a constant-time operation.
580 *
581 * @param list address of the list head.
582 * @returns the last data in the list, or #NULL for an empty list.
583 */
584 void*
_dbus_list_get_last(DBusList ** list)585 _dbus_list_get_last (DBusList **list)
586 {
587 if (*list == NULL)
588 return NULL;
589 else
590 return (*list)->prev->data;
591 }
592
593 /**
594 * Gets the first data in the list.
595 * This is a constant-time operation.
596 *
597 * @param list address of the list head.
598 * @returns the first data in the list, or #NULL for an empty list.
599 */
600 void*
_dbus_list_get_first(DBusList ** list)601 _dbus_list_get_first (DBusList **list)
602 {
603 if (*list == NULL)
604 return NULL;
605 else
606 return (*list)->data;
607 }
608
609 /**
610 * Removes the first link in the list and returns it. This is a
611 * constant-time operation.
612 *
613 * @param list address of the list head.
614 * @returns the first link in the list, or #NULL for an empty list.
615 */
616 DBusList*
_dbus_list_pop_first_link(DBusList ** list)617 _dbus_list_pop_first_link (DBusList **list)
618 {
619 DBusList *link;
620
621 link = _dbus_list_get_first_link (list);
622 if (link == NULL)
623 return NULL;
624
625 _dbus_list_unlink (list, link);
626
627 return link;
628 }
629
630 /**
631 * Removes the first value in the list and returns it. This is a
632 * constant-time operation.
633 *
634 * @param list address of the list head.
635 * @returns the first data in the list, or #NULL for an empty list.
636 */
637 void*
_dbus_list_pop_first(DBusList ** list)638 _dbus_list_pop_first (DBusList **list)
639 {
640 DBusList *link;
641 void *data;
642
643 link = _dbus_list_get_first_link (list);
644 if (link == NULL)
645 return NULL;
646
647 data = link->data;
648 _dbus_list_remove_link (list, link);
649
650 return data;
651 }
652
653 /**
654 * Removes the last value in the list and returns it. This is a
655 * constant-time operation.
656 *
657 * @param list address of the list head.
658 * @returns the last data in the list, or #NULL for an empty list.
659 */
660 void*
_dbus_list_pop_last(DBusList ** list)661 _dbus_list_pop_last (DBusList **list)
662 {
663 DBusList *link;
664 void *data;
665
666 link = _dbus_list_get_last_link (list);
667 if (link == NULL)
668 return NULL;
669
670 data = link->data;
671 _dbus_list_remove_link (list, link);
672
673 return data;
674 }
675
676 /**
677 * Copies a list. This is a linear-time operation. If there isn't
678 * enough memory to copy the entire list, the destination list will be
679 * set to #NULL.
680 *
681 * @param list address of the head of the list to copy.
682 * @param dest address where the copied list should be placed.
683 * @returns #TRUE on success, #FALSE if not enough memory.
684 */
685 dbus_bool_t
_dbus_list_copy(DBusList ** list,DBusList ** dest)686 _dbus_list_copy (DBusList **list,
687 DBusList **dest)
688 {
689 DBusList *link;
690
691 _dbus_assert (list != dest);
692
693 *dest = NULL;
694
695 link = *list;
696 while (link != NULL)
697 {
698 if (!_dbus_list_append (dest, link->data))
699 {
700 /* free what we have so far */
701 _dbus_list_clear (dest);
702 return FALSE;
703 }
704
705 link = _dbus_list_get_next_link (list, link);
706 }
707
708 return TRUE;
709 }
710
711 /**
712 * Gets the length of a list. This is a linear-time
713 * operation.
714 *
715 * @param list address of the head of the list
716 * @returns number of elements in the list.
717 */
718 int
_dbus_list_get_length(DBusList ** list)719 _dbus_list_get_length (DBusList **list)
720 {
721 DBusList *link;
722 int length;
723
724 length = 0;
725
726 link = *list;
727 while (link != NULL)
728 {
729 ++length;
730
731 link = _dbus_list_get_next_link (list, link);
732 }
733
734 return length;
735 }
736
737 /**
738 * Calls the given function for each element in the list. The
739 * function is passed the list element as its first argument, and the
740 * given data as its second argument.
741 *
742 * @param list address of the head of the list.
743 * @param function function to call for each element.
744 * @param data extra data for the function.
745 *
746 */
747 void
_dbus_list_foreach(DBusList ** list,DBusForeachFunction function,void * data)748 _dbus_list_foreach (DBusList **list,
749 DBusForeachFunction function,
750 void *data)
751 {
752 DBusList *link;
753
754 link = *list;
755 while (link != NULL)
756 {
757 DBusList *next = _dbus_list_get_next_link (list, link);
758
759 (* function) (link->data, data);
760
761 link = next;
762 }
763 }
764
765 /**
766 * Check whether length is exactly one.
767 *
768 * @param list the list
769 * @returns #TRUE if length is exactly one
770 */
771 dbus_bool_t
_dbus_list_length_is_one(DBusList ** list)772 _dbus_list_length_is_one (DBusList **list)
773 {
774 return (*list != NULL &&
775 (*list)->next == *list);
776 }
777
778 /** @} */
779
780 #ifdef DBUS_BUILD_TESTS
781 #include "dbus-test.h"
782 #include <stdio.h>
783
784 static void
verify_list(DBusList ** list)785 verify_list (DBusList **list)
786 {
787 DBusList *link;
788 int length;
789
790 link = *list;
791
792 if (link == NULL)
793 return;
794
795 if (link->next == link)
796 {
797 _dbus_assert (link->prev == link);
798 _dbus_assert (*list == link);
799 return;
800 }
801
802 length = 0;
803 do
804 {
805 length += 1;
806 _dbus_assert (link->prev->next == link);
807 _dbus_assert (link->next->prev == link);
808 link = link->next;
809 }
810 while (link != *list);
811
812 _dbus_assert (length == _dbus_list_get_length (list));
813
814 if (length == 1)
815 _dbus_assert (_dbus_list_length_is_one (list));
816 else
817 _dbus_assert (!_dbus_list_length_is_one (list));
818 }
819
820 static dbus_bool_t
is_ascending_sequence(DBusList ** list)821 is_ascending_sequence (DBusList **list)
822 {
823 DBusList *link;
824 int prev;
825
826 prev = _DBUS_INT_MIN;
827
828 link = _dbus_list_get_first_link (list);
829 while (link != NULL)
830 {
831 int v = _DBUS_POINTER_TO_INT (link->data);
832
833 if (v <= prev)
834 return FALSE;
835
836 prev = v;
837
838 link = _dbus_list_get_next_link (list, link);
839 }
840
841 return TRUE;
842 }
843
844 static dbus_bool_t
is_descending_sequence(DBusList ** list)845 is_descending_sequence (DBusList **list)
846 {
847 DBusList *link;
848 int prev;
849
850 prev = _DBUS_INT_MAX;
851
852 link = _dbus_list_get_first_link (list);
853 while (link != NULL)
854 {
855 int v = _DBUS_POINTER_TO_INT (link->data);
856
857 if (v >= prev)
858 return FALSE;
859
860 prev = v;
861
862 link = _dbus_list_get_next_link (list, link);
863 }
864
865 return TRUE;
866 }
867
868 static dbus_bool_t
all_even_values(DBusList ** list)869 all_even_values (DBusList **list)
870 {
871 DBusList *link;
872
873 link = _dbus_list_get_first_link (list);
874 while (link != NULL)
875 {
876 int v = _DBUS_POINTER_TO_INT (link->data);
877
878 if ((v % 2) != 0)
879 return FALSE;
880
881 link = _dbus_list_get_next_link (list, link);
882 }
883
884 return TRUE;
885 }
886
887 static dbus_bool_t
all_odd_values(DBusList ** list)888 all_odd_values (DBusList **list)
889 {
890 DBusList *link;
891
892 link = _dbus_list_get_first_link (list);
893 while (link != NULL)
894 {
895 int v = _DBUS_POINTER_TO_INT (link->data);
896
897 if ((v % 2) == 0)
898 return FALSE;
899
900 link = _dbus_list_get_next_link (list, link);
901 }
902
903 return TRUE;
904 }
905
906 static dbus_bool_t
lists_equal(DBusList ** list1,DBusList ** list2)907 lists_equal (DBusList **list1,
908 DBusList **list2)
909 {
910 DBusList *link1;
911 DBusList *link2;
912
913 link1 = _dbus_list_get_first_link (list1);
914 link2 = _dbus_list_get_first_link (list2);
915 while (link1 && link2)
916 {
917 if (link1->data != link2->data)
918 return FALSE;
919
920 link1 = _dbus_list_get_next_link (list1, link1);
921 link2 = _dbus_list_get_next_link (list2, link2);
922 }
923
924 if (link1 || link2)
925 return FALSE;
926
927 return TRUE;
928 }
929
930 /**
931 * @ingroup DBusListInternals
932 * Unit test for DBusList
933 * @returns #TRUE on success.
934 */
935 dbus_bool_t
_dbus_list_test(void)936 _dbus_list_test (void)
937 {
938 DBusList *list1;
939 DBusList *list2;
940 DBusList *link1;
941 DBusList *link2;
942 DBusList *copy1;
943 DBusList *copy2;
944 int i;
945
946 list1 = NULL;
947 list2 = NULL;
948
949 /* Test append and prepend */
950
951 i = 0;
952 while (i < 10)
953 {
954 if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)))
955 _dbus_assert_not_reached ("could not allocate for append");
956
957 if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)))
958 _dbus_assert_not_reached ("count not allocate for prepend");
959 ++i;
960
961 verify_list (&list1);
962 verify_list (&list2);
963
964 _dbus_assert (_dbus_list_get_length (&list1) == i);
965 _dbus_assert (_dbus_list_get_length (&list2) == i);
966 }
967
968 _dbus_assert (is_ascending_sequence (&list1));
969 _dbus_assert (is_descending_sequence (&list2));
970
971 /* Test list clear */
972 _dbus_list_clear (&list1);
973 _dbus_list_clear (&list2);
974
975 verify_list (&list1);
976 verify_list (&list2);
977
978 /* Test get_first, get_last, pop_first, pop_last */
979
980 i = 0;
981 while (i < 10)
982 {
983 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
984 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
985 ++i;
986 }
987
988 --i;
989 while (i >= 0)
990 {
991 void *got_data1;
992 void *got_data2;
993
994 void *data1;
995 void *data2;
996
997 got_data1 = _dbus_list_get_last (&list1);
998 got_data2 = _dbus_list_get_first (&list2);
999
1000 data1 = _dbus_list_pop_last (&list1);
1001 data2 = _dbus_list_pop_first (&list2);
1002
1003 _dbus_assert (got_data1 == data1);
1004 _dbus_assert (got_data2 == data2);
1005
1006 _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
1007 _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
1008
1009 verify_list (&list1);
1010 verify_list (&list2);
1011
1012 _dbus_assert (is_ascending_sequence (&list1));
1013 _dbus_assert (is_descending_sequence (&list2));
1014
1015 --i;
1016 }
1017
1018 _dbus_assert (list1 == NULL);
1019 _dbus_assert (list2 == NULL);
1020
1021 /* Test get_first_link, get_last_link, pop_first_link, pop_last_link */
1022
1023 i = 0;
1024 while (i < 10)
1025 {
1026 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1027 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1028 ++i;
1029 }
1030
1031 --i;
1032 while (i >= 0)
1033 {
1034 DBusList *got_link1;
1035 DBusList *got_link2;
1036
1037 DBusList *link2;
1038
1039 void *data1_indirect;
1040 void *data1;
1041 void *data2;
1042
1043 got_link1 = _dbus_list_get_last_link (&list1);
1044 got_link2 = _dbus_list_get_first_link (&list2);
1045
1046 link2 = _dbus_list_pop_first_link (&list2);
1047
1048 _dbus_assert (got_link2 == link2);
1049
1050 data1_indirect = got_link1->data;
1051 /* this call makes got_link1 invalid */
1052 data1 = _dbus_list_pop_last (&list1);
1053 _dbus_assert (data1 == data1_indirect);
1054 data2 = link2->data;
1055
1056 _dbus_list_free_link (link2);
1057
1058 _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
1059 _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
1060
1061 verify_list (&list1);
1062 verify_list (&list2);
1063
1064 _dbus_assert (is_ascending_sequence (&list1));
1065 _dbus_assert (is_descending_sequence (&list2));
1066
1067 --i;
1068 }
1069
1070 _dbus_assert (list1 == NULL);
1071 _dbus_assert (list2 == NULL);
1072
1073 /* Test iteration */
1074
1075 i = 0;
1076 while (i < 10)
1077 {
1078 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1079 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1080 ++i;
1081
1082 verify_list (&list1);
1083 verify_list (&list2);
1084
1085 _dbus_assert (_dbus_list_get_length (&list1) == i);
1086 _dbus_assert (_dbus_list_get_length (&list2) == i);
1087 }
1088
1089 _dbus_assert (is_ascending_sequence (&list1));
1090 _dbus_assert (is_descending_sequence (&list2));
1091
1092 --i;
1093 link2 = _dbus_list_get_first_link (&list2);
1094 while (link2 != NULL)
1095 {
1096 verify_list (&link2); /* pretend this link is the head */
1097
1098 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
1099
1100 link2 = _dbus_list_get_next_link (&list2, link2);
1101 --i;
1102 }
1103
1104 i = 0;
1105 link1 = _dbus_list_get_first_link (&list1);
1106 while (link1 != NULL)
1107 {
1108 verify_list (&link1); /* pretend this link is the head */
1109
1110 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
1111
1112 link1 = _dbus_list_get_next_link (&list1, link1);
1113 ++i;
1114 }
1115
1116 --i;
1117 link1 = _dbus_list_get_last_link (&list1);
1118 while (link1 != NULL)
1119 {
1120 verify_list (&link1); /* pretend this link is the head */
1121
1122 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
1123
1124 link1 = _dbus_list_get_prev_link (&list1, link1);
1125 --i;
1126 }
1127
1128 _dbus_list_clear (&list1);
1129 _dbus_list_clear (&list2);
1130
1131 /* Test remove */
1132
1133 i = 0;
1134 while (i < 10)
1135 {
1136 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1137 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1138 ++i;
1139 }
1140
1141 --i;
1142 while (i >= 0)
1143 {
1144 if ((i % 2) == 0)
1145 {
1146 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
1147 _dbus_assert_not_reached ("element should have been in list");
1148 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
1149 _dbus_assert_not_reached ("element should have been in list");
1150
1151 verify_list (&list1);
1152 verify_list (&list2);
1153 }
1154 --i;
1155 }
1156
1157 _dbus_assert (all_odd_values (&list1));
1158 _dbus_assert (all_odd_values (&list2));
1159
1160 _dbus_list_clear (&list1);
1161 _dbus_list_clear (&list2);
1162
1163 /* test removing the other half of the elements */
1164
1165 i = 0;
1166 while (i < 10)
1167 {
1168 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1169 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1170 ++i;
1171 }
1172
1173 --i;
1174 while (i >= 0)
1175 {
1176 if ((i % 2) != 0)
1177 {
1178 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
1179 _dbus_assert_not_reached ("element should have been in list");
1180 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
1181 _dbus_assert_not_reached ("element should have been in list");
1182
1183 verify_list (&list1);
1184 verify_list (&list2);
1185 }
1186 --i;
1187 }
1188
1189 _dbus_assert (all_even_values (&list1));
1190 _dbus_assert (all_even_values (&list2));
1191
1192 /* clear list using remove_link */
1193 while (list1 != NULL)
1194 {
1195 _dbus_list_remove_link (&list1, list1);
1196 verify_list (&list1);
1197 }
1198 while (list2 != NULL)
1199 {
1200 _dbus_list_remove_link (&list2, list2);
1201 verify_list (&list2);
1202 }
1203
1204 /* Test remove link more generally */
1205 i = 0;
1206 while (i < 10)
1207 {
1208 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1209 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1210 ++i;
1211 }
1212
1213 --i;
1214 link2 = _dbus_list_get_first_link (&list2);
1215 while (link2 != NULL)
1216 {
1217 DBusList *next = _dbus_list_get_next_link (&list2, link2);
1218
1219 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
1220
1221 if ((i % 2) == 0)
1222 _dbus_list_remove_link (&list2, link2);
1223
1224 verify_list (&list2);
1225
1226 link2 = next;
1227 --i;
1228 }
1229
1230 _dbus_assert (all_odd_values (&list2));
1231 _dbus_list_clear (&list2);
1232
1233 i = 0;
1234 link1 = _dbus_list_get_first_link (&list1);
1235 while (link1 != NULL)
1236 {
1237 DBusList *next = _dbus_list_get_next_link (&list1, link1);
1238
1239 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
1240
1241 if ((i % 2) != 0)
1242 _dbus_list_remove_link (&list1, link1);
1243
1244 verify_list (&list1);
1245
1246 link1 = next;
1247 ++i;
1248 }
1249
1250 _dbus_assert (all_even_values (&list1));
1251 _dbus_list_clear (&list1);
1252
1253 /* Test copying a list */
1254 i = 0;
1255 while (i < 10)
1256 {
1257 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1258 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1259 ++i;
1260 }
1261
1262 /* bad pointers, because they are allowed in the copy dest */
1263 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1264 copy2 = _DBUS_INT_TO_POINTER (23);
1265
1266 _dbus_list_copy (&list1, ©1);
1267 verify_list (&list1);
1268 verify_list (©1);
1269 _dbus_assert (lists_equal (&list1, ©1));
1270
1271 _dbus_list_copy (&list2, ©2);
1272 verify_list (&list2);
1273 verify_list (©2);
1274 _dbus_assert (lists_equal (&list2, ©2));
1275
1276 /* Now test copying empty lists */
1277 _dbus_list_clear (&list1);
1278 _dbus_list_clear (&list2);
1279 _dbus_list_clear (©1);
1280 _dbus_list_clear (©2);
1281
1282 /* bad pointers, because they are allowed in the copy dest */
1283 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1284 copy2 = _DBUS_INT_TO_POINTER (23);
1285
1286 _dbus_list_copy (&list1, ©1);
1287 verify_list (&list1);
1288 verify_list (©1);
1289 _dbus_assert (lists_equal (&list1, ©1));
1290
1291 _dbus_list_copy (&list2, ©2);
1292 verify_list (&list2);
1293 verify_list (©2);
1294 _dbus_assert (lists_equal (&list2, ©2));
1295
1296 _dbus_list_clear (&list1);
1297 _dbus_list_clear (&list2);
1298
1299 /* insert_after on empty list */
1300 _dbus_list_insert_after (&list1, NULL,
1301 _DBUS_INT_TO_POINTER (0));
1302 verify_list (&list1);
1303
1304 /* inserting after first element */
1305 _dbus_list_insert_after (&list1, list1,
1306 _DBUS_INT_TO_POINTER (1));
1307 verify_list (&list1);
1308 _dbus_assert (is_ascending_sequence (&list1));
1309
1310 /* inserting at the end */
1311 _dbus_list_insert_after (&list1, list1->next,
1312 _DBUS_INT_TO_POINTER (2));
1313 verify_list (&list1);
1314 _dbus_assert (is_ascending_sequence (&list1));
1315
1316 /* using insert_after to prepend */
1317 _dbus_list_insert_after (&list1, NULL,
1318 _DBUS_INT_TO_POINTER (-1));
1319 verify_list (&list1);
1320 _dbus_assert (is_ascending_sequence (&list1));
1321
1322 _dbus_list_clear (&list1);
1323
1324 /* using remove_last */
1325 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (2));
1326 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (1));
1327 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (3));
1328
1329 _dbus_list_remove_last (&list1, _DBUS_INT_TO_POINTER (2));
1330
1331 verify_list (&list1);
1332 _dbus_assert (is_ascending_sequence (&list1));
1333
1334 _dbus_list_clear (&list1);
1335
1336 return TRUE;
1337 }
1338
1339 #endif
1340