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"
26 #include "dbus-mempool.h"
27 #include "dbus-threads.h"
30 * @defgroup DBusList Linked list
31 * @ingroup DBusInternals
32 * @brief DBusList data structure
34 * Types and functions related to DBusList.
37 static DBusMemPool *list_pool;
38 static DBusMutex *list_pool_lock = NULL;
40 DBusMutex *_dbus_list_init_lock (void);
42 _dbus_list_init_lock (void)
44 list_pool_lock = dbus_mutex_new ();
45 return list_pool_lock;
49 * @defgroup DBusListInternals Linked list implementation details
50 * @ingroup DBusInternals
51 * @brief DBusList implementation details
53 * The guts of DBusList.
58 /* the mem pool is probably a speed hit, with the thread
59 * lock, though it does still save memory - unknown.
62 alloc_link (void *data)
66 if (!dbus_mutex_lock (list_pool_lock))
71 list_pool = _dbus_mem_pool_new (sizeof (DBusList), TRUE);
73 if (list_pool == NULL)
75 dbus_mutex_unlock (list_pool_lock);
80 link = _dbus_mem_pool_alloc (list_pool);
84 dbus_mutex_unlock (list_pool_lock);
90 free_link (DBusList *link)
92 dbus_mutex_lock (list_pool_lock);
93 _dbus_mem_pool_dealloc (list_pool, link);
94 dbus_mutex_unlock (list_pool_lock);
98 link_before (DBusList **list,
99 DBusList *before_this_link,
110 link->next = before_this_link;
111 link->prev = before_this_link->prev;
112 before_this_link->prev = link;
113 link->prev->next = link;
115 if (before_this_link == *list)
121 link_after (DBusList **list,
122 DBusList *after_this_link,
133 link->prev = after_this_link;
134 link->next = after_this_link->next;
135 after_this_link->next = link;
136 link->next->prev = link;
143 * @addtogroup DBusList
150 * A node in a linked list.
152 * DBusList is a circular list; that is, the tail of the list
153 * points back to the head of the list. The empty list is
154 * represented by a #NULL pointer.
158 * @def _dbus_list_get_next_link
160 * Gets the next link in the list, or #NULL if
161 * there are no more links. Used for iteration.
165 * link = _dbus_list_get_first_link (&list);
166 * while (link != NULL)
168 * printf ("value is %p\n", link->data);
169 * link = _dbus_list_get_next_link (&link);
173 * @param list address of the list head.
174 * @param link current link.
175 * @returns the next link, or %NULL if none.
180 * @def _dbus_list_get_prev_link
182 * Gets the previous link in the list, or #NULL if
183 * there are no more links. Used for iteration.
187 * link = _dbus_list_get_last_link (&list);
188 * while (link != NULL)
190 * printf ("value is %p\n", link->data);
191 * link = _dbus_list_get_prev_link (&link);
195 * @param list address of the list head.
196 * @param link current link.
197 * @returns the previous link, or %NULL if none.
202 * Allocates a linked list node. Useful for preallocating
203 * nodes and using _dbus_list_append_link() to avoid
206 * @param data the value to store in the link.
207 * @returns a newly allocated link.
210 _dbus_list_alloc_link (void *data)
212 return alloc_link (data);
216 * Frees a linked list node allocated with _dbus_list_alloc_link.
217 * Does not free the data in the node.
219 * @param link the list node
222 _dbus_list_free_link (DBusList *link)
229 * Appends a value to the list. May return #FALSE
230 * if insufficient memory exists to add a list link.
231 * This is a constant-time operation.
233 * @param list address of the list head.
234 * @param data the value to append.
235 * @returns #TRUE on success.
238 _dbus_list_append (DBusList **list,
241 if (!_dbus_list_prepend (list, data))
244 /* Now cycle the list forward one so the prepended node is the tail */
245 *list = (*list)->next;
251 * Prepends a value to the list. May return #FALSE
252 * if insufficient memory exists to add a list link.
253 * This is a constant-time operation.
255 * @param list address of the list head.
256 * @param data the value to prepend.
257 * @returns #TRUE on success.
260 _dbus_list_prepend (DBusList **list,
265 link = alloc_link (data);
269 link_before (list, *list, link);
275 * Appends a link to the list.
276 * Cannot fail due to out of memory.
277 * This is a constant-time operation.
279 * @param list address of the list head.
280 * @param link the link to append.
283 _dbus_list_append_link (DBusList **list,
286 _dbus_list_prepend_link (list, link);
288 /* Now cycle the list forward one so the prepended node is the tail */
289 *list = (*list)->next;
293 * Prepends a link to the list.
294 * Cannot fail due to out of memory.
295 * This is a constant-time operation.
297 * @param list address of the list head.
298 * @param link the link to prepend.
301 _dbus_list_prepend_link (DBusList **list,
304 link_before (list, *list, link);
308 * Inserts data into the list before the given existing link.
310 * @param list the list to modify
311 * @param before_this_link existing link to insert before, or #NULL to append
312 * @param data the value to insert
313 * @returns #TRUE on success, #FALSE if memory allocation fails
316 _dbus_list_insert_before (DBusList **list,
317 DBusList *before_this_link,
322 if (before_this_link == NULL)
323 return _dbus_list_append (list, data);
326 link = alloc_link (data);
330 link_before (list, before_this_link, link);
337 * Inserts data into the list after the given existing link.
339 * @param list the list to modify
340 * @param after_this_link existing link to insert after, or #NULL to prepend
341 * @param data the value to insert
342 * @returns #TRUE on success, #FALSE if memory allocation fails
345 _dbus_list_insert_after (DBusList **list,
346 DBusList *after_this_link,
351 if (after_this_link == NULL)
352 return _dbus_list_prepend (list, data);
355 link = alloc_link (data);
359 link_after (list, after_this_link, link);
366 * Removes a value from the list. Only removes the
367 * first value equal to the given data pointer,
368 * even if multiple values exist which match.
369 * This is a linear-time operation.
371 * @param list address of the list head.
372 * @param data the value to remove.
373 * @returns #TRUE if a value was found to remove.
376 _dbus_list_remove (DBusList **list,
384 if (link->data == data)
386 _dbus_list_remove_link (list, link);
390 link = _dbus_list_get_next_link (list, link);
397 * Removes a value from the list. Only removes the
398 * last value equal to the given data pointer,
399 * even if multiple values exist which match.
400 * This is a linear-time operation.
402 * @param list address of the list head.
403 * @param data the value to remove.
404 * @returns #TRUE if a value was found to remove.
407 _dbus_list_remove_last (DBusList **list,
412 link = _dbus_list_get_last_link (list);
416 if (link->data == data)
418 _dbus_list_remove_link (list, link);
422 link = _dbus_list_get_prev_link (list, link);
429 * Removes a link from the list. This is a constant-time operation.
431 * @param list address of the list head.
432 * @param link the list link to remove.
435 _dbus_list_remove_link (DBusList **list,
438 if (link->next == link)
440 /* one-element list */
446 link->prev->next = link->next;
447 link->next->prev = link->prev;
457 * Frees all links in the list and sets the list head to #NULL. Does
458 * not free the data in each link, for obvious reasons. This is a
459 * linear-time operation.
461 * @param list address of the list head.
464 _dbus_list_clear (DBusList **list)
471 DBusList *next = _dbus_list_get_next_link (list, link);
482 * Gets the first link in the list.
483 * This is a constant-time operation.
485 * @param list address of the list head.
486 * @returns the first link, or #NULL for an empty list.
489 _dbus_list_get_first_link (DBusList **list)
495 * Gets the last link in the list.
496 * This is a constant-time operation.
498 * @param list address of the list head.
499 * @returns the last link, or #NULL for an empty list.
502 _dbus_list_get_last_link (DBusList **list)
507 return (*list)->prev;
511 * Gets the last data in the list.
512 * This is a constant-time operation.
514 * @param list address of the list head.
515 * @returns the last data in the list, or #NULL for an empty list.
518 _dbus_list_get_last (DBusList **list)
523 return (*list)->prev->data;
527 * Gets the first data in the list.
528 * This is a constant-time operation.
530 * @param list address of the list head.
531 * @returns the first data in the list, or #NULL for an empty list.
534 _dbus_list_get_first (DBusList **list)
539 return (*list)->data;
543 * Removes the first value in the list and returns it. This is a
544 * constant-time operation.
546 * @param list address of the list head.
547 * @returns the first data in the list, or #NULL for an empty list.
550 _dbus_list_pop_first (DBusList **list)
555 link = _dbus_list_get_first_link (list);
560 _dbus_list_remove_link (list, link);
566 * Removes the last value in the list and returns it. This is a
567 * constant-time operation.
569 * @param list address of the list head.
570 * @returns the last data in the list, or #NULL for an empty list.
573 _dbus_list_pop_last (DBusList **list)
578 link = _dbus_list_get_last_link (list);
583 _dbus_list_remove_link (list, link);
589 * Copies a list. This is a linear-time operation. If there isn't
590 * enough memory to copy the entire list, the destination list will be
593 * @param list address of the head of the list to copy.
594 * @param dest address where the copied list should be placed.
595 * @returns #TRUE on success, #FALSE if not enough memory.
598 _dbus_list_copy (DBusList **list,
603 _dbus_assert (list != dest);
610 if (!_dbus_list_append (dest, link->data))
612 /* free what we have so far */
613 _dbus_list_clear (dest);
617 link = _dbus_list_get_next_link (list, link);
624 * Gets the length of a list. This is a linear-time
627 * @param list address of the head of the list
628 * @returns number of elements in the list.
631 _dbus_list_get_length (DBusList **list)
643 link = _dbus_list_get_next_link (list, link);
650 * Calls the given function for each element in the list. The
651 * function is passed the list element as its first argument, and the
652 * given data as its second argument.
654 * @param list address of the head of the list.
655 * @param function function to call for each element.
656 * @param data extra data for the function.
660 _dbus_list_foreach (DBusList **list,
661 DBusForeachFunction function,
669 DBusList *next = _dbus_list_get_next_link (list, link);
671 (* function) (link->data, data);
679 #ifdef DBUS_BUILD_TESTS
680 #include "dbus-test.h"
684 verify_list (DBusList **list)
694 if (link->next == link)
696 _dbus_assert (link->prev == link);
697 _dbus_assert (*list == link);
705 _dbus_assert (link->prev->next == link);
706 _dbus_assert (link->next->prev == link);
709 while (link != *list);
711 _dbus_assert (length == _dbus_list_get_length (list));
715 is_ascending_sequence (DBusList **list)
720 prev = _DBUS_INT_MIN;
722 link = _dbus_list_get_first_link (list);
725 int v = _DBUS_POINTER_TO_INT (link->data);
732 link = _dbus_list_get_next_link (list, link);
739 is_descending_sequence (DBusList **list)
744 prev = _DBUS_INT_MAX;
746 link = _dbus_list_get_first_link (list);
749 int v = _DBUS_POINTER_TO_INT (link->data);
756 link = _dbus_list_get_next_link (list, link);
763 all_even_values (DBusList **list)
767 link = _dbus_list_get_first_link (list);
770 int v = _DBUS_POINTER_TO_INT (link->data);
775 link = _dbus_list_get_next_link (list, link);
782 all_odd_values (DBusList **list)
786 link = _dbus_list_get_first_link (list);
789 int v = _DBUS_POINTER_TO_INT (link->data);
794 link = _dbus_list_get_next_link (list, link);
801 lists_equal (DBusList **list1,
807 link1 = _dbus_list_get_first_link (list1);
808 link2 = _dbus_list_get_first_link (list2);
809 while (link1 && link2)
811 if (link1->data != link2->data)
814 link1 = _dbus_list_get_next_link (list1, link1);
815 link2 = _dbus_list_get_next_link (list2, link2);
825 * @ingroup DBusListInternals
826 * Unit test for DBusList
827 * @returns #TRUE on success.
830 _dbus_list_test (void)
843 /* Test append and prepend */
848 if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)))
849 _dbus_assert_not_reached ("could not allocate for append");
851 if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)))
852 _dbus_assert_not_reached ("count not allocate for prepend");
855 verify_list (&list1);
856 verify_list (&list2);
858 _dbus_assert (_dbus_list_get_length (&list1) == i);
859 _dbus_assert (_dbus_list_get_length (&list2) == i);
862 _dbus_assert (is_ascending_sequence (&list1));
863 _dbus_assert (is_descending_sequence (&list2));
865 /* Test list clear */
866 _dbus_list_clear (&list1);
867 _dbus_list_clear (&list2);
869 verify_list (&list1);
870 verify_list (&list2);
872 /* Test get_first, get_last, pop_first, pop_last */
877 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
878 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
891 got_data1 = _dbus_list_get_last (&list1);
892 got_data2 = _dbus_list_get_first (&list2);
894 data1 = _dbus_list_pop_last (&list1);
895 data2 = _dbus_list_pop_first (&list2);
897 _dbus_assert (got_data1 == data1);
898 _dbus_assert (got_data2 == data2);
900 _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
901 _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
903 verify_list (&list1);
904 verify_list (&list2);
906 _dbus_assert (is_ascending_sequence (&list1));
907 _dbus_assert (is_descending_sequence (&list2));
912 _dbus_assert (list1 == NULL);
913 _dbus_assert (list2 == NULL);
920 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
921 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
924 verify_list (&list1);
925 verify_list (&list2);
927 _dbus_assert (_dbus_list_get_length (&list1) == i);
928 _dbus_assert (_dbus_list_get_length (&list2) == i);
931 _dbus_assert (is_ascending_sequence (&list1));
932 _dbus_assert (is_descending_sequence (&list2));
935 link2 = _dbus_list_get_first_link (&list2);
936 while (link2 != NULL)
938 verify_list (&link2); /* pretend this link is the head */
940 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
942 link2 = _dbus_list_get_next_link (&list2, link2);
947 link1 = _dbus_list_get_first_link (&list1);
948 while (link1 != NULL)
950 verify_list (&link1); /* pretend this link is the head */
952 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
954 link1 = _dbus_list_get_next_link (&list1, link1);
959 link1 = _dbus_list_get_last_link (&list1);
960 while (link1 != NULL)
962 verify_list (&link1); /* pretend this link is the head */
964 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
966 link1 = _dbus_list_get_prev_link (&list1, link1);
970 _dbus_list_clear (&list1);
971 _dbus_list_clear (&list2);
978 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
979 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
988 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
989 _dbus_assert_not_reached ("element should have been in list");
990 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
991 _dbus_assert_not_reached ("element should have been in list");
993 verify_list (&list1);
994 verify_list (&list2);
999 _dbus_assert (all_odd_values (&list1));
1000 _dbus_assert (all_odd_values (&list2));
1002 _dbus_list_clear (&list1);
1003 _dbus_list_clear (&list2);
1005 /* test removing the other half of the elements */
1010 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1011 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1020 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
1021 _dbus_assert_not_reached ("element should have been in list");
1022 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
1023 _dbus_assert_not_reached ("element should have been in list");
1025 verify_list (&list1);
1026 verify_list (&list2);
1031 _dbus_assert (all_even_values (&list1));
1032 _dbus_assert (all_even_values (&list2));
1034 /* clear list using remove_link */
1035 while (list1 != NULL)
1037 _dbus_list_remove_link (&list1, list1);
1038 verify_list (&list1);
1040 while (list2 != NULL)
1042 _dbus_list_remove_link (&list2, list2);
1043 verify_list (&list2);
1046 /* Test remove link more generally */
1050 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1051 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1056 link2 = _dbus_list_get_first_link (&list2);
1057 while (link2 != NULL)
1059 DBusList *next = _dbus_list_get_next_link (&list2, link2);
1061 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
1064 _dbus_list_remove_link (&list2, link2);
1066 verify_list (&list2);
1072 _dbus_assert (all_odd_values (&list2));
1073 _dbus_list_clear (&list2);
1076 link1 = _dbus_list_get_first_link (&list1);
1077 while (link1 != NULL)
1079 DBusList *next = _dbus_list_get_next_link (&list1, link1);
1081 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
1084 _dbus_list_remove_link (&list1, link1);
1086 verify_list (&list1);
1092 _dbus_assert (all_even_values (&list1));
1093 _dbus_list_clear (&list1);
1095 /* Test copying a list */
1099 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1100 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1104 /* bad pointers, because they are allowed in the copy dest */
1105 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1106 copy2 = _DBUS_INT_TO_POINTER (23);
1108 _dbus_list_copy (&list1, ©1);
1109 verify_list (&list1);
1110 verify_list (©1);
1111 _dbus_assert (lists_equal (&list1, ©1));
1113 _dbus_list_copy (&list2, ©2);
1114 verify_list (&list2);
1115 verify_list (©2);
1116 _dbus_assert (lists_equal (&list2, ©2));
1118 /* Now test copying empty lists */
1119 _dbus_list_clear (&list1);
1120 _dbus_list_clear (&list2);
1121 _dbus_list_clear (©1);
1122 _dbus_list_clear (©2);
1124 /* bad pointers, because they are allowed in the copy dest */
1125 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1126 copy2 = _DBUS_INT_TO_POINTER (23);
1128 _dbus_list_copy (&list1, ©1);
1129 verify_list (&list1);
1130 verify_list (©1);
1131 _dbus_assert (lists_equal (&list1, ©1));
1133 _dbus_list_copy (&list2, ©2);
1134 verify_list (&list2);
1135 verify_list (©2);
1136 _dbus_assert (lists_equal (&list2, ©2));
1138 _dbus_list_clear (&list1);
1139 _dbus_list_clear (&list2);
1141 /* insert_before on empty list */
1142 _dbus_list_insert_before (&list1, NULL,
1143 _DBUS_INT_TO_POINTER (0));
1144 verify_list (&list1);
1146 /* inserting before first element */
1147 _dbus_list_insert_before (&list1, list1,
1148 _DBUS_INT_TO_POINTER (2));
1149 verify_list (&list1);
1150 _dbus_assert (is_descending_sequence (&list1));
1152 /* inserting in the middle */
1153 _dbus_list_insert_before (&list1, list1->next,
1154 _DBUS_INT_TO_POINTER (1));
1155 verify_list (&list1);
1156 _dbus_assert (is_descending_sequence (&list1));
1158 /* using insert_before to append */
1159 _dbus_list_insert_before (&list1, NULL,
1160 _DBUS_INT_TO_POINTER (-1));
1161 verify_list (&list1);
1162 _dbus_assert (is_descending_sequence (&list1));
1164 _dbus_list_clear (&list1);
1166 /* insert_after on empty list */
1167 _dbus_list_insert_after (&list1, NULL,
1168 _DBUS_INT_TO_POINTER (0));
1169 verify_list (&list1);
1171 /* inserting after first element */
1172 _dbus_list_insert_after (&list1, list1,
1173 _DBUS_INT_TO_POINTER (1));
1174 verify_list (&list1);
1175 _dbus_assert (is_ascending_sequence (&list1));
1177 /* inserting at the end */
1178 _dbus_list_insert_after (&list1, list1->next,
1179 _DBUS_INT_TO_POINTER (2));
1180 verify_list (&list1);
1181 _dbus_assert (is_ascending_sequence (&list1));
1183 /* using insert_after to prepend */
1184 _dbus_list_insert_after (&list1, NULL,
1185 _DBUS_INT_TO_POINTER (-1));
1186 verify_list (&list1);
1187 _dbus_assert (is_ascending_sequence (&list1));
1189 _dbus_list_clear (&list1);
1191 /* using remove_last */
1192 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (2));
1193 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (1));
1194 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (3));
1196 _dbus_list_remove_last (&list1, _DBUS_INT_TO_POINTER (2));
1198 verify_list (&list1);
1199 _dbus_assert (is_ascending_sequence (&list1));
1201 _dbus_list_clear (&list1);