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);
83 dbus_mutex_unlock (list_pool_lock);
89 free_link (DBusList *link)
91 dbus_mutex_lock (list_pool_lock);
92 _dbus_mem_pool_dealloc (list_pool, link);
93 dbus_mutex_unlock (list_pool_lock);
97 link_before (DBusList **list,
98 DBusList *before_this_link,
109 link->next = before_this_link;
110 link->prev = before_this_link->prev;
111 before_this_link->prev = link;
112 link->prev->next = link;
114 if (before_this_link == *list)
120 link_after (DBusList **list,
121 DBusList *after_this_link,
132 link->prev = after_this_link;
133 link->next = after_this_link->next;
134 after_this_link->next = link;
135 link->next->prev = link;
142 * @addtogroup DBusList
149 * A node in a linked list.
151 * DBusList is a circular list; that is, the tail of the list
152 * points back to the head of the list. The empty list is
153 * represented by a #NULL pointer.
157 * @def _dbus_list_get_next_link
159 * Gets the next link in the list, or #NULL if
160 * there are no more links. Used for iteration.
164 * link = _dbus_list_get_first_link (&list);
165 * while (link != NULL)
167 * printf ("value is %p\n", link->data);
168 * link = _dbus_list_get_next_link (&link);
172 * @param list address of the list head.
173 * @param link current link.
174 * @returns the next link, or %NULL if none.
179 * @def _dbus_list_get_prev_link
181 * Gets the previous link in the list, or #NULL if
182 * there are no more links. Used for iteration.
186 * link = _dbus_list_get_last_link (&list);
187 * while (link != NULL)
189 * printf ("value is %p\n", link->data);
190 * link = _dbus_list_get_prev_link (&link);
194 * @param list address of the list head.
195 * @param link current link.
196 * @returns the previous link, or %NULL if none.
201 * Allocates a linked list node. Useful for preallocating
202 * nodes and using _dbus_list_append_link() to avoid
205 * @param data the value to store in the link.
206 * @returns a newly allocated link.
209 _dbus_list_alloc_link (void *data)
211 return alloc_link (data);
215 * Frees a linked list node allocated with _dbus_list_alloc_link.
216 * Does not free the data in the node.
218 * @param link the list node
221 _dbus_list_free_link (DBusList *link)
228 * Appends a value to the list. May return #FALSE
229 * if insufficient memory exists to add a list link.
230 * This is a constant-time operation.
232 * @param list address of the list head.
233 * @param data the value to append.
234 * @returns #TRUE on success.
237 _dbus_list_append (DBusList **list,
240 if (!_dbus_list_prepend (list, data))
243 /* Now cycle the list forward one so the prepended node is the tail */
244 *list = (*list)->next;
250 * Prepends 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.
254 * @param list address of the list head.
255 * @param data the value to prepend.
256 * @returns #TRUE on success.
259 _dbus_list_prepend (DBusList **list,
264 link = alloc_link (data);
268 link_before (list, *list, link);
274 * Appends a link to the list.
275 * Cannot fail due to out of memory.
276 * This is a constant-time operation.
278 * @param list address of the list head.
279 * @param link the link to append.
282 _dbus_list_append_link (DBusList **list,
285 _dbus_list_prepend_link (list, link);
287 /* Now cycle the list forward one so the prepended node is the tail */
288 *list = (*list)->next;
294 * Prepends a link to the list.
295 * Cannot fail due to out of memory.
296 * This is a constant-time operation.
298 * @param list address of the list head.
299 * @param link the link to prepend.
302 _dbus_list_prepend_link (DBusList **list,
305 link_before (list, *list, link);
311 * Inserts data into the list before the given existing link.
313 * @param list the list to modify
314 * @param before_this_link existing link to insert before, or #NULL to append
315 * @param data the value to insert
316 * @returns #TRUE on success, #FALSE if memory allocation fails
319 _dbus_list_insert_before (DBusList **list,
320 DBusList *before_this_link,
325 if (before_this_link == NULL)
326 return _dbus_list_append (list, data);
329 link = alloc_link (data);
333 link_before (list, before_this_link, link);
340 * Inserts data into the list after the given existing link.
342 * @param list the list to modify
343 * @param after_this_link existing link to insert after, or #NULL to prepend
344 * @param data the value to insert
345 * @returns #TRUE on success, #FALSE if memory allocation fails
348 _dbus_list_insert_after (DBusList **list,
349 DBusList *after_this_link,
354 if (after_this_link == NULL)
355 return _dbus_list_prepend (list, data);
358 link = alloc_link (data);
362 link_after (list, after_this_link, link);
369 * Removes a value from the list. Only removes the
370 * first value equal to the given data pointer,
371 * even if multiple values exist which match.
372 * This is a linear-time operation.
374 * @param list address of the list head.
375 * @param data the value to remove.
376 * @returns #TRUE if a value was found to remove.
379 _dbus_list_remove (DBusList **list,
387 if (link->data == data)
389 _dbus_list_remove_link (list, link);
393 link = _dbus_list_get_next_link (list, link);
400 * Removes a value from the list. Only removes the
401 * last value equal to the given data pointer,
402 * even if multiple values exist which match.
403 * This is a linear-time operation.
405 * @param list address of the list head.
406 * @param data the value to remove.
407 * @returns #TRUE if a value was found to remove.
410 _dbus_list_remove_last (DBusList **list,
415 link = _dbus_list_get_last_link (list);
419 if (link->data == data)
421 _dbus_list_remove_link (list, link);
425 link = _dbus_list_get_prev_link (list, link);
432 * Removes a link from the list. This is a constant-time operation.
434 * @param list address of the list head.
435 * @param link the list link to remove.
438 _dbus_list_remove_link (DBusList **list,
441 if (link->next == link)
443 /* one-element list */
449 link->prev->next = link->next;
450 link->next->prev = link->prev;
460 * Frees all links in the list and sets the list head to #NULL. Does
461 * not free the data in each link, for obvious reasons. This is a
462 * linear-time operation.
464 * @param list address of the list head.
467 _dbus_list_clear (DBusList **list)
474 DBusList *next = _dbus_list_get_next_link (list, link);
485 * Gets the first link in the list.
486 * This is a constant-time operation.
488 * @param list address of the list head.
489 * @returns the first link, or #NULL for an empty list.
492 _dbus_list_get_first_link (DBusList **list)
498 * Gets the last link in the list.
499 * This is a constant-time operation.
501 * @param list address of the list head.
502 * @returns the last link, or #NULL for an empty list.
505 _dbus_list_get_last_link (DBusList **list)
510 return (*list)->prev;
514 * Gets the last data in the list.
515 * This is a constant-time operation.
517 * @param list address of the list head.
518 * @returns the last data in the list, or #NULL for an empty list.
521 _dbus_list_get_last (DBusList **list)
526 return (*list)->prev->data;
530 * Gets the first data in the list.
531 * This is a constant-time operation.
533 * @param list address of the list head.
534 * @returns the first data in the list, or #NULL for an empty list.
537 _dbus_list_get_first (DBusList **list)
542 return (*list)->data;
546 * Removes the first value in the list and returns it. This is a
547 * constant-time operation.
549 * @param list address of the list head.
550 * @returns the first data in the list, or #NULL for an empty list.
553 _dbus_list_pop_first (DBusList **list)
558 link = _dbus_list_get_first_link (list);
563 _dbus_list_remove_link (list, link);
569 * Removes the last value in the list and returns it. This is a
570 * constant-time operation.
572 * @param list address of the list head.
573 * @returns the last data in the list, or #NULL for an empty list.
576 _dbus_list_pop_last (DBusList **list)
581 link = _dbus_list_get_last_link (list);
586 _dbus_list_remove_link (list, link);
592 * Copies a list. This is a linear-time operation. If there isn't
593 * enough memory to copy the entire list, the destination list will be
596 * @param list address of the head of the list to copy.
597 * @param dest address where the copied list should be placed.
598 * @returns #TRUE on success, #FALSE if not enough memory.
601 _dbus_list_copy (DBusList **list,
606 _dbus_assert (list != dest);
613 if (!_dbus_list_append (dest, link->data))
615 /* free what we have so far */
616 _dbus_list_clear (dest);
620 link = _dbus_list_get_next_link (list, link);
627 * Gets the length of a list. This is a linear-time
630 * @param list address of the head of the list
631 * @returns number of elements in the list.
634 _dbus_list_get_length (DBusList **list)
646 link = _dbus_list_get_next_link (list, link);
653 * Calls the given function for each element in the list. The
654 * function is passed the list element as its first argument, and the
655 * given data as its second argument.
657 * @param list address of the head of the list.
658 * @param function function to call for each element.
659 * @param data extra data for the function.
663 _dbus_list_foreach (DBusList **list,
664 DBusForeachFunction function,
672 DBusList *next = _dbus_list_get_next_link (list, link);
674 (* function) (link->data, data);
682 #ifdef DBUS_BUILD_TESTS
683 #include "dbus-test.h"
687 verify_list (DBusList **list)
697 if (link->next == link)
699 _dbus_assert (link->prev == link);
700 _dbus_assert (*list == link);
708 _dbus_assert (link->prev->next == link);
709 _dbus_assert (link->next->prev == link);
712 while (link != *list);
714 _dbus_assert (length == _dbus_list_get_length (list));
718 is_ascending_sequence (DBusList **list)
723 prev = _DBUS_INT_MIN;
725 link = _dbus_list_get_first_link (list);
728 int v = _DBUS_POINTER_TO_INT (link->data);
735 link = _dbus_list_get_next_link (list, link);
742 is_descending_sequence (DBusList **list)
747 prev = _DBUS_INT_MAX;
749 link = _dbus_list_get_first_link (list);
752 int v = _DBUS_POINTER_TO_INT (link->data);
759 link = _dbus_list_get_next_link (list, link);
766 all_even_values (DBusList **list)
770 link = _dbus_list_get_first_link (list);
773 int v = _DBUS_POINTER_TO_INT (link->data);
778 link = _dbus_list_get_next_link (list, link);
785 all_odd_values (DBusList **list)
789 link = _dbus_list_get_first_link (list);
792 int v = _DBUS_POINTER_TO_INT (link->data);
797 link = _dbus_list_get_next_link (list, link);
804 lists_equal (DBusList **list1,
810 link1 = _dbus_list_get_first_link (list1);
811 link2 = _dbus_list_get_first_link (list2);
812 while (link1 && link2)
814 if (link1->data != link2->data)
817 link1 = _dbus_list_get_next_link (list1, link1);
818 link2 = _dbus_list_get_next_link (list2, link2);
828 * @ingroup DBusListInternals
829 * Unit test for DBusList
830 * @returns #TRUE on success.
833 _dbus_list_test (void)
846 /* Test append and prepend */
851 if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)))
852 _dbus_assert_not_reached ("could not allocate for append");
854 if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)))
855 _dbus_assert_not_reached ("count not allocate for prepend");
858 verify_list (&list1);
859 verify_list (&list2);
861 _dbus_assert (_dbus_list_get_length (&list1) == i);
862 _dbus_assert (_dbus_list_get_length (&list2) == i);
865 _dbus_assert (is_ascending_sequence (&list1));
866 _dbus_assert (is_descending_sequence (&list2));
868 /* Test list clear */
869 _dbus_list_clear (&list1);
870 _dbus_list_clear (&list2);
872 verify_list (&list1);
873 verify_list (&list2);
875 /* Test get_first, get_last, pop_first, pop_last */
880 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
881 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
894 got_data1 = _dbus_list_get_last (&list1);
895 got_data2 = _dbus_list_get_first (&list2);
897 data1 = _dbus_list_pop_last (&list1);
898 data2 = _dbus_list_pop_first (&list2);
900 _dbus_assert (got_data1 == data1);
901 _dbus_assert (got_data2 == data2);
903 _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
904 _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
906 verify_list (&list1);
907 verify_list (&list2);
909 _dbus_assert (is_ascending_sequence (&list1));
910 _dbus_assert (is_descending_sequence (&list2));
915 _dbus_assert (list1 == NULL);
916 _dbus_assert (list2 == NULL);
923 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
924 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
927 verify_list (&list1);
928 verify_list (&list2);
930 _dbus_assert (_dbus_list_get_length (&list1) == i);
931 _dbus_assert (_dbus_list_get_length (&list2) == i);
934 _dbus_assert (is_ascending_sequence (&list1));
935 _dbus_assert (is_descending_sequence (&list2));
938 link2 = _dbus_list_get_first_link (&list2);
939 while (link2 != NULL)
941 verify_list (&link2); /* pretend this link is the head */
943 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
945 link2 = _dbus_list_get_next_link (&list2, link2);
950 link1 = _dbus_list_get_first_link (&list1);
951 while (link1 != NULL)
953 verify_list (&link1); /* pretend this link is the head */
955 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
957 link1 = _dbus_list_get_next_link (&list1, link1);
962 link1 = _dbus_list_get_last_link (&list1);
963 while (link1 != NULL)
965 verify_list (&link1); /* pretend this link is the head */
967 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
969 link1 = _dbus_list_get_prev_link (&list1, link1);
973 _dbus_list_clear (&list1);
974 _dbus_list_clear (&list2);
981 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
982 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
991 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
992 _dbus_assert_not_reached ("element should have been in list");
993 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
994 _dbus_assert_not_reached ("element should have been in list");
996 verify_list (&list1);
997 verify_list (&list2);
1002 _dbus_assert (all_odd_values (&list1));
1003 _dbus_assert (all_odd_values (&list2));
1005 _dbus_list_clear (&list1);
1006 _dbus_list_clear (&list2);
1008 /* test removing the other half of the elements */
1013 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1014 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1023 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
1024 _dbus_assert_not_reached ("element should have been in list");
1025 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
1026 _dbus_assert_not_reached ("element should have been in list");
1028 verify_list (&list1);
1029 verify_list (&list2);
1034 _dbus_assert (all_even_values (&list1));
1035 _dbus_assert (all_even_values (&list2));
1037 /* clear list using remove_link */
1038 while (list1 != NULL)
1040 _dbus_list_remove_link (&list1, list1);
1041 verify_list (&list1);
1043 while (list2 != NULL)
1045 _dbus_list_remove_link (&list2, list2);
1046 verify_list (&list2);
1049 /* Test remove link more generally */
1053 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1054 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1059 link2 = _dbus_list_get_first_link (&list2);
1060 while (link2 != NULL)
1062 DBusList *next = _dbus_list_get_next_link (&list2, link2);
1064 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
1067 _dbus_list_remove_link (&list2, link2);
1069 verify_list (&list2);
1075 _dbus_assert (all_odd_values (&list2));
1076 _dbus_list_clear (&list2);
1079 link1 = _dbus_list_get_first_link (&list1);
1080 while (link1 != NULL)
1082 DBusList *next = _dbus_list_get_next_link (&list1, link1);
1084 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
1087 _dbus_list_remove_link (&list1, link1);
1089 verify_list (&list1);
1095 _dbus_assert (all_even_values (&list1));
1096 _dbus_list_clear (&list1);
1098 /* Test copying a list */
1102 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1103 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1107 /* bad pointers, because they are allowed in the copy dest */
1108 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1109 copy2 = _DBUS_INT_TO_POINTER (23);
1111 _dbus_list_copy (&list1, ©1);
1112 verify_list (&list1);
1113 verify_list (©1);
1114 _dbus_assert (lists_equal (&list1, ©1));
1116 _dbus_list_copy (&list2, ©2);
1117 verify_list (&list2);
1118 verify_list (©2);
1119 _dbus_assert (lists_equal (&list2, ©2));
1121 /* Now test copying empty lists */
1122 _dbus_list_clear (&list1);
1123 _dbus_list_clear (&list2);
1124 _dbus_list_clear (©1);
1125 _dbus_list_clear (©2);
1127 /* bad pointers, because they are allowed in the copy dest */
1128 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1129 copy2 = _DBUS_INT_TO_POINTER (23);
1131 _dbus_list_copy (&list1, ©1);
1132 verify_list (&list1);
1133 verify_list (©1);
1134 _dbus_assert (lists_equal (&list1, ©1));
1136 _dbus_list_copy (&list2, ©2);
1137 verify_list (&list2);
1138 verify_list (©2);
1139 _dbus_assert (lists_equal (&list2, ©2));
1141 _dbus_list_clear (&list1);
1142 _dbus_list_clear (&list2);
1144 /* insert_before on empty list */
1145 _dbus_list_insert_before (&list1, NULL,
1146 _DBUS_INT_TO_POINTER (0));
1147 verify_list (&list1);
1149 /* inserting before first element */
1150 _dbus_list_insert_before (&list1, list1,
1151 _DBUS_INT_TO_POINTER (2));
1152 verify_list (&list1);
1153 _dbus_assert (is_descending_sequence (&list1));
1155 /* inserting in the middle */
1156 _dbus_list_insert_before (&list1, list1->next,
1157 _DBUS_INT_TO_POINTER (1));
1158 verify_list (&list1);
1159 _dbus_assert (is_descending_sequence (&list1));
1161 /* using insert_before to append */
1162 _dbus_list_insert_before (&list1, NULL,
1163 _DBUS_INT_TO_POINTER (-1));
1164 verify_list (&list1);
1165 _dbus_assert (is_descending_sequence (&list1));
1167 _dbus_list_clear (&list1);
1169 /* insert_after on empty list */
1170 _dbus_list_insert_after (&list1, NULL,
1171 _DBUS_INT_TO_POINTER (0));
1172 verify_list (&list1);
1174 /* inserting after first element */
1175 _dbus_list_insert_after (&list1, list1,
1176 _DBUS_INT_TO_POINTER (1));
1177 verify_list (&list1);
1178 _dbus_assert (is_ascending_sequence (&list1));
1180 /* inserting at the end */
1181 _dbus_list_insert_after (&list1, list1->next,
1182 _DBUS_INT_TO_POINTER (2));
1183 verify_list (&list1);
1184 _dbus_assert (is_ascending_sequence (&list1));
1186 /* using insert_after to prepend */
1187 _dbus_list_insert_after (&list1, NULL,
1188 _DBUS_INT_TO_POINTER (-1));
1189 verify_list (&list1);
1190 _dbus_assert (is_ascending_sequence (&list1));
1192 _dbus_list_clear (&list1);
1194 /* using remove_last */
1195 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (2));
1196 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (1));
1197 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (3));
1199 _dbus_list_remove_last (&list1, _DBUS_INT_TO_POINTER (2));
1201 verify_list (&list1);
1202 _dbus_assert (is_ascending_sequence (&list1));
1204 _dbus_list_clear (&list1);