1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-list.c Generic linked list utility (internal to D-BUS implementation)
4 * Copyright (C) 2002 Red Hat, Inc.
6 * Licensed under the Academic Free License version 1.2
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "dbus-internals.h"
25 #include "dbus-list.h"
28 * @defgroup DBusList Linked list
29 * @ingroup DBusInternals
30 * @brief DBusList data structure
32 * Types and functions related to DBusList.
36 * @defgroup DBusListInternals Linked list implementation details
37 * @ingroup DBusInternals
38 * @brief DBusList implementation details
40 * The guts of DBusList.
42 * @todo should use a memory pool for the list nodes, to avoid
43 * a memory allocation for every link.
48 alloc_link (void *data)
52 link = dbus_new0 (DBusList, 1);
59 free_link (DBusList *link)
65 link_before (DBusList **list,
66 DBusList *before_this_link,
77 link->next = before_this_link;
78 link->prev = before_this_link->prev;
79 before_this_link->prev = link;
80 link->prev->next = link;
82 if (before_this_link == *list)
88 link_after (DBusList **list,
89 DBusList *after_this_link,
100 link->prev = after_this_link;
101 link->next = after_this_link->next;
102 after_this_link->next = link;
103 link->next->prev = link;
110 * @addtogroup DBusList
117 * A node in a linked list.
119 * DBusList is a circular list; that is, the tail of the list
120 * points back to the head of the list. The empty list is
121 * represented by a #NULL pointer.
125 * @def _dbus_list_get_next_link
127 * Gets the next link in the list, or #NULL if
128 * there are no more links. Used for iteration.
132 * link = _dbus_list_get_first_link (&list);
133 * while (link != NULL)
135 * printf ("value is %p\n", link->data);
136 * link = _dbus_list_get_next_link (&link);
140 * @param list address of the list head.
141 * @param link current link.
142 * @returns the next link, or %NULL if none.
147 * @def _dbus_list_get_prev_link
149 * Gets the previous link in the list, or #NULL if
150 * there are no more links. Used for iteration.
154 * link = _dbus_list_get_last_link (&list);
155 * while (link != NULL)
157 * printf ("value is %p\n", link->data);
158 * link = _dbus_list_get_prev_link (&link);
162 * @param list address of the list head.
163 * @param link current link.
164 * @returns the previous link, or %NULL if none.
169 * Appends a value to the list. May return #FALSE
170 * if insufficient memory exists to add a list link.
171 * This is a constant-time operation.
173 * @param list address of the list head.
174 * @param data the value to append.
175 * @returns #TRUE on success.
178 _dbus_list_append (DBusList **list,
181 if (!_dbus_list_prepend (list, data))
184 /* Now cycle the list forward one so the prepended node is the tail */
185 *list = (*list)->next;
191 * Prepends a value to the list. May return #FALSE
192 * if insufficient memory exists to add a list link.
193 * This is a constant-time operation.
195 * @param list address of the list head.
196 * @param data the value to prepend.
197 * @returns #TRUE on success.
200 _dbus_list_prepend (DBusList **list,
205 link = alloc_link (data);
209 link_before (list, *list, link);
215 * Inserts data into the list before the given existing link.
217 * @param list the list to modify
218 * @param before_this_link existing link to insert before, or #NULL to append
219 * @param data the value to insert
220 * @returns #TRUE on success, #FALSE if memory allocation fails
223 _dbus_list_insert_before (DBusList **list,
224 DBusList *before_this_link,
229 if (before_this_link == NULL)
230 return _dbus_list_append (list, data);
233 link = alloc_link (data);
237 link_before (list, before_this_link, link);
244 * Inserts data into the list after the given existing link.
246 * @param list the list to modify
247 * @param after_this_link existing link to insert after, or #NULL to prepend
248 * @param data the value to insert
249 * @returns #TRUE on success, #FALSE if memory allocation fails
252 _dbus_list_insert_after (DBusList **list,
253 DBusList *after_this_link,
258 if (after_this_link == NULL)
259 return _dbus_list_prepend (list, data);
262 link = alloc_link (data);
266 link_after (list, after_this_link, link);
273 * Removes a value from the list. Only removes the
274 * first value equal to the given data pointer,
275 * even if multiple values exist which match.
276 * This is a linear-time operation.
278 * @param list address of the list head.
279 * @param data the value to remove.
280 * @returns #TRUE if a value was found to remove.
283 _dbus_list_remove (DBusList **list,
291 if (link->data == data)
293 _dbus_list_remove_link (list, link);
297 link = _dbus_list_get_next_link (list, link);
304 * Removes a value from the list. Only removes the
305 * last value equal to the given data pointer,
306 * even if multiple values exist which match.
307 * This is a linear-time operation.
309 * @param list address of the list head.
310 * @param data the value to remove.
311 * @returns #TRUE if a value was found to remove.
314 _dbus_list_remove_last (DBusList **list,
319 link = _dbus_list_get_last_link (list);
322 if (link->data == data)
324 _dbus_list_remove_link (list, link);
328 link = _dbus_list_get_prev_link (list, link);
335 * Removes a link from the list. This is a constant-time operation.
337 * @param list address of the list head.
338 * @param link the list link to remove.
341 _dbus_list_remove_link (DBusList **list,
344 if (link->next == link)
346 /* one-element list */
352 link->prev->next = link->next;
353 link->next->prev = link->prev;
363 * Frees all links in the list and sets the list head to #NULL. Does
364 * not free the data in each link, for obvious reasons. This is a
365 * linear-time operation.
367 * @param list address of the list head.
370 _dbus_list_clear (DBusList **list)
377 DBusList *next = _dbus_list_get_next_link (list, link);
388 * Gets the first link in the list.
389 * This is a constant-time operation.
391 * @param list address of the list head.
392 * @returns the first link, or #NULL for an empty list.
395 _dbus_list_get_first_link (DBusList **list)
401 * Gets the last link in the list.
402 * This is a constant-time operation.
404 * @param list address of the list head.
405 * @returns the last link, or #NULL for an empty list.
408 _dbus_list_get_last_link (DBusList **list)
413 return (*list)->prev;
417 * Gets the last data in the list.
418 * This is a constant-time operation.
420 * @param list address of the list head.
421 * @returns the last data in the list, or #NULL for an empty list.
424 _dbus_list_get_last (DBusList **list)
429 return (*list)->prev->data;
433 * Gets the first data in the list.
434 * This is a constant-time operation.
436 * @param list address of the list head.
437 * @returns the first data in the list, or #NULL for an empty list.
440 _dbus_list_get_first (DBusList **list)
445 return (*list)->data;
449 * Removes the first value in the list and returns it. This is a
450 * constant-time operation.
452 * @param list address of the list head.
453 * @returns the first data in the list, or #NULL for an empty list.
456 _dbus_list_pop_first (DBusList **list)
461 link = _dbus_list_get_first_link (list);
466 _dbus_list_remove_link (list, link);
472 * Removes the last value in the list and returns it. This is a
473 * constant-time operation.
475 * @param list address of the list head.
476 * @returns the last data in the list, or #NULL for an empty list.
479 _dbus_list_pop_last (DBusList **list)
484 link = _dbus_list_get_last_link (list);
489 _dbus_list_remove_link (list, link);
495 * Copies a list. This is a linear-time operation. If there isn't
496 * enough memory to copy the entire list, the destination list will be
499 * @param list address of the head of the list to copy.
500 * @param dest address where the copied list should be placed.
501 * @returns #TRUE on success, #FALSE if not enough memory.
504 _dbus_list_copy (DBusList **list,
509 _dbus_assert (list != dest);
516 if (!_dbus_list_append (dest, link->data))
518 /* free what we have so far */
519 _dbus_list_clear (dest);
523 link = _dbus_list_get_next_link (list, link);
530 * Gets the length of a list. This is a linear-time
533 * @param list address of the head of the list
534 * @returns number of elements in the list.
537 _dbus_list_get_length (DBusList **list)
549 link = _dbus_list_get_next_link (list, link);
556 * Calls the given function for each element in the list. The
557 * function is passed the list element as its first argument, and the
558 * given data as its second argument.
560 * @param list address of the head of the list.
561 * @param function function to call for each element.
562 * @param data extra data for the function.
566 _dbus_list_foreach (DBusList **list,
567 DBusForeachFunction function,
575 DBusList *next = _dbus_list_get_next_link (list, link);
577 (* function) (link->data, data);
585 #ifdef DBUS_BUILD_TESTS
586 #include "dbus-test.h"
590 verify_list (DBusList **list)
600 if (link->next == link)
602 _dbus_assert (link->prev == link);
603 _dbus_assert (*list == link);
611 _dbus_assert (link->prev->next == link);
612 _dbus_assert (link->next->prev == link);
615 while (link != *list);
617 _dbus_assert (length == _dbus_list_get_length (list));
621 is_ascending_sequence (DBusList **list)
626 prev = _DBUS_INT_MIN;
628 link = _dbus_list_get_first_link (list);
631 int v = _DBUS_POINTER_TO_INT (link->data);
638 link = _dbus_list_get_next_link (list, link);
645 is_descending_sequence (DBusList **list)
650 prev = _DBUS_INT_MAX;
652 link = _dbus_list_get_first_link (list);
655 int v = _DBUS_POINTER_TO_INT (link->data);
662 link = _dbus_list_get_next_link (list, link);
669 all_even_values (DBusList **list)
673 link = _dbus_list_get_first_link (list);
676 int v = _DBUS_POINTER_TO_INT (link->data);
681 link = _dbus_list_get_next_link (list, link);
688 all_odd_values (DBusList **list)
692 link = _dbus_list_get_first_link (list);
695 int v = _DBUS_POINTER_TO_INT (link->data);
700 link = _dbus_list_get_next_link (list, link);
707 lists_equal (DBusList **list1,
713 link1 = _dbus_list_get_first_link (list1);
714 link2 = _dbus_list_get_first_link (list2);
715 while (link1 && link2)
717 if (link1->data != link2->data)
720 link1 = _dbus_list_get_next_link (list1, link1);
721 link2 = _dbus_list_get_next_link (list2, link2);
731 * @ingroup DBusListInternals
732 * Unit test for DBusList
733 * @returns #TRUE on success.
736 _dbus_list_test (void)
749 /* Test append and prepend */
754 if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)))
755 _dbus_assert_not_reached ("could not allocate for append");
757 if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)))
758 _dbus_assert_not_reached ("count not allocate for prepend");
761 verify_list (&list1);
762 verify_list (&list2);
764 _dbus_assert (_dbus_list_get_length (&list1) == i);
765 _dbus_assert (_dbus_list_get_length (&list2) == i);
768 _dbus_assert (is_ascending_sequence (&list1));
769 _dbus_assert (is_descending_sequence (&list2));
771 /* Test list clear */
772 _dbus_list_clear (&list1);
773 _dbus_list_clear (&list2);
775 verify_list (&list1);
776 verify_list (&list2);
778 /* Test get_first, get_last, pop_first, pop_last */
783 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
784 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
797 got_data1 = _dbus_list_get_last (&list1);
798 got_data2 = _dbus_list_get_first (&list2);
800 data1 = _dbus_list_pop_last (&list1);
801 data2 = _dbus_list_pop_first (&list2);
803 _dbus_assert (got_data1 == data1);
804 _dbus_assert (got_data2 == data2);
806 _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
807 _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
809 verify_list (&list1);
810 verify_list (&list2);
812 _dbus_assert (is_ascending_sequence (&list1));
813 _dbus_assert (is_descending_sequence (&list2));
818 _dbus_assert (list1 == NULL);
819 _dbus_assert (list2 == NULL);
826 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
827 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
830 verify_list (&list1);
831 verify_list (&list2);
833 _dbus_assert (_dbus_list_get_length (&list1) == i);
834 _dbus_assert (_dbus_list_get_length (&list2) == i);
837 _dbus_assert (is_ascending_sequence (&list1));
838 _dbus_assert (is_descending_sequence (&list2));
841 link2 = _dbus_list_get_first_link (&list2);
842 while (link2 != NULL)
844 verify_list (&link2); /* pretend this link is the head */
846 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
848 link2 = _dbus_list_get_next_link (&list2, link2);
853 link1 = _dbus_list_get_first_link (&list1);
854 while (link1 != NULL)
856 verify_list (&link1); /* pretend this link is the head */
858 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
860 link1 = _dbus_list_get_next_link (&list1, link1);
864 _dbus_list_clear (&list1);
865 _dbus_list_clear (&list2);
872 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
873 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
882 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
883 _dbus_assert_not_reached ("element should have been in list");
884 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
885 _dbus_assert_not_reached ("element should have been in list");
887 verify_list (&list1);
888 verify_list (&list2);
893 _dbus_assert (all_odd_values (&list1));
894 _dbus_assert (all_odd_values (&list2));
896 _dbus_list_clear (&list1);
897 _dbus_list_clear (&list2);
899 /* test removing the other half of the elements */
904 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
905 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
914 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
915 _dbus_assert_not_reached ("element should have been in list");
916 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
917 _dbus_assert_not_reached ("element should have been in list");
919 verify_list (&list1);
920 verify_list (&list2);
925 _dbus_assert (all_even_values (&list1));
926 _dbus_assert (all_even_values (&list2));
928 /* clear list using remove_link */
929 while (list1 != NULL)
931 _dbus_list_remove_link (&list1, list1);
932 verify_list (&list1);
934 while (list2 != NULL)
936 _dbus_list_remove_link (&list2, list2);
937 verify_list (&list2);
940 /* Test remove link more generally */
944 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
945 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
950 link2 = _dbus_list_get_first_link (&list2);
951 while (link2 != NULL)
953 DBusList *next = _dbus_list_get_next_link (&list2, link2);
955 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
958 _dbus_list_remove_link (&list2, link2);
960 verify_list (&list2);
966 _dbus_assert (all_odd_values (&list2));
967 _dbus_list_clear (&list2);
970 link1 = _dbus_list_get_first_link (&list1);
971 while (link1 != NULL)
973 DBusList *next = _dbus_list_get_next_link (&list1, link1);
975 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
978 _dbus_list_remove_link (&list1, link1);
980 verify_list (&list1);
986 _dbus_assert (all_even_values (&list1));
987 _dbus_list_clear (&list1);
989 /* Test copying a list */
993 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
994 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
998 /* bad pointers, because they are allowed in the copy dest */
999 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1000 copy2 = _DBUS_INT_TO_POINTER (23);
1002 _dbus_list_copy (&list1, ©1);
1003 verify_list (&list1);
1004 verify_list (©1);
1005 _dbus_assert (lists_equal (&list1, ©1));
1007 _dbus_list_copy (&list2, ©2);
1008 verify_list (&list2);
1009 verify_list (©2);
1010 _dbus_assert (lists_equal (&list2, ©2));
1012 /* Now test copying empty lists */
1013 _dbus_list_clear (&list1);
1014 _dbus_list_clear (&list2);
1015 _dbus_list_clear (©1);
1016 _dbus_list_clear (©2);
1018 /* bad pointers, because they are allowed in the copy dest */
1019 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1020 copy2 = _DBUS_INT_TO_POINTER (23);
1022 _dbus_list_copy (&list1, ©1);
1023 verify_list (&list1);
1024 verify_list (©1);
1025 _dbus_assert (lists_equal (&list1, ©1));
1027 _dbus_list_copy (&list2, ©2);
1028 verify_list (&list2);
1029 verify_list (©2);
1030 _dbus_assert (lists_equal (&list2, ©2));
1032 _dbus_list_clear (&list1);
1033 _dbus_list_clear (&list2);
1035 /* insert_before on empty list */
1036 _dbus_list_insert_before (&list1, NULL,
1037 _DBUS_INT_TO_POINTER (0));
1038 verify_list (&list1);
1040 /* inserting before first element */
1041 _dbus_list_insert_before (&list1, list1,
1042 _DBUS_INT_TO_POINTER (2));
1043 verify_list (&list1);
1044 _dbus_assert (is_descending_sequence (&list1));
1046 /* inserting in the middle */
1047 _dbus_list_insert_before (&list1, list1->next,
1048 _DBUS_INT_TO_POINTER (1));
1049 verify_list (&list1);
1050 _dbus_assert (is_descending_sequence (&list1));
1052 /* using insert_before to append */
1053 _dbus_list_insert_before (&list1, NULL,
1054 _DBUS_INT_TO_POINTER (-1));
1055 verify_list (&list1);
1056 _dbus_assert (is_descending_sequence (&list1));
1058 _dbus_list_clear (&list1);
1060 /* insert_after on empty list */
1061 _dbus_list_insert_after (&list1, NULL,
1062 _DBUS_INT_TO_POINTER (0));
1063 verify_list (&list1);
1065 /* inserting after first element */
1066 _dbus_list_insert_after (&list1, list1,
1067 _DBUS_INT_TO_POINTER (1));
1068 verify_list (&list1);
1069 _dbus_assert (is_ascending_sequence (&list1));
1071 /* inserting at the end */
1072 _dbus_list_insert_after (&list1, list1->next,
1073 _DBUS_INT_TO_POINTER (2));
1074 verify_list (&list1);
1075 _dbus_assert (is_ascending_sequence (&list1));
1077 /* using insert_after to prepend */
1078 _dbus_list_insert_after (&list1, NULL,
1079 _DBUS_INT_TO_POINTER (-1));
1080 verify_list (&list1);
1081 _dbus_assert (is_ascending_sequence (&list1));
1083 _dbus_list_clear (&list1);