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 DBusStaticMutex list_pool_lock = DBUS_STATIC_MUTEX_INIT;
41 * @defgroup DBusListInternals Linked list implementation details
42 * @ingroup DBusInternals
43 * @brief DBusList implementation details
45 * The guts of DBusList.
50 /* the mem pool is probably a speed hit, with the thread
51 * lock, though it does still save memory - unknown.
54 alloc_link (void *data)
58 if (!dbus_static_mutex_lock (&list_pool_lock))
63 list_pool = _dbus_mem_pool_new (sizeof (DBusList), TRUE);
65 if (list_pool == NULL)
67 dbus_static_mutex_unlock (&list_pool_lock);
72 link = _dbus_mem_pool_alloc (list_pool);
75 dbus_static_mutex_unlock (&list_pool_lock);
81 free_link (DBusList *link)
83 dbus_static_mutex_lock (&list_pool_lock);
84 _dbus_mem_pool_dealloc (list_pool, link);
85 dbus_static_mutex_unlock (&list_pool_lock);
89 link_before (DBusList **list,
90 DBusList *before_this_link,
101 link->next = before_this_link;
102 link->prev = before_this_link->prev;
103 before_this_link->prev = link;
104 link->prev->next = link;
106 if (before_this_link == *list)
112 link_after (DBusList **list,
113 DBusList *after_this_link,
124 link->prev = after_this_link;
125 link->next = after_this_link->next;
126 after_this_link->next = link;
127 link->next->prev = link;
134 * @addtogroup DBusList
141 * A node in a linked list.
143 * DBusList is a circular list; that is, the tail of the list
144 * points back to the head of the list. The empty list is
145 * represented by a #NULL pointer.
149 * @def _dbus_list_get_next_link
151 * Gets the next link in the list, or #NULL if
152 * there are no more links. Used for iteration.
156 * link = _dbus_list_get_first_link (&list);
157 * while (link != NULL)
159 * printf ("value is %p\n", link->data);
160 * link = _dbus_list_get_next_link (&link);
164 * @param list address of the list head.
165 * @param link current link.
166 * @returns the next link, or %NULL if none.
171 * @def _dbus_list_get_prev_link
173 * Gets the previous link in the list, or #NULL if
174 * there are no more links. Used for iteration.
178 * link = _dbus_list_get_last_link (&list);
179 * while (link != NULL)
181 * printf ("value is %p\n", link->data);
182 * link = _dbus_list_get_prev_link (&link);
186 * @param list address of the list head.
187 * @param link current link.
188 * @returns the previous link, or %NULL if none.
193 * Appends a value to the list. May return #FALSE
194 * if insufficient memory exists to add a list link.
195 * This is a constant-time operation.
197 * @param list address of the list head.
198 * @param data the value to append.
199 * @returns #TRUE on success.
202 _dbus_list_append (DBusList **list,
205 if (!_dbus_list_prepend (list, data))
208 /* Now cycle the list forward one so the prepended node is the tail */
209 *list = (*list)->next;
215 * Prepends a value to the list. May return #FALSE
216 * if insufficient memory exists to add a list link.
217 * This is a constant-time operation.
219 * @param list address of the list head.
220 * @param data the value to prepend.
221 * @returns #TRUE on success.
224 _dbus_list_prepend (DBusList **list,
229 link = alloc_link (data);
233 link_before (list, *list, link);
239 * Inserts data into the list before the given existing link.
241 * @param list the list to modify
242 * @param before_this_link existing link to insert before, or #NULL to append
243 * @param data the value to insert
244 * @returns #TRUE on success, #FALSE if memory allocation fails
247 _dbus_list_insert_before (DBusList **list,
248 DBusList *before_this_link,
253 if (before_this_link == NULL)
254 return _dbus_list_append (list, data);
257 link = alloc_link (data);
261 link_before (list, before_this_link, link);
268 * Inserts data into the list after the given existing link.
270 * @param list the list to modify
271 * @param after_this_link existing link to insert after, or #NULL to prepend
272 * @param data the value to insert
273 * @returns #TRUE on success, #FALSE if memory allocation fails
276 _dbus_list_insert_after (DBusList **list,
277 DBusList *after_this_link,
282 if (after_this_link == NULL)
283 return _dbus_list_prepend (list, data);
286 link = alloc_link (data);
290 link_after (list, after_this_link, link);
297 * Removes a value from the list. Only removes the
298 * first value equal to the given data pointer,
299 * even if multiple values exist which match.
300 * This is a linear-time operation.
302 * @param list address of the list head.
303 * @param data the value to remove.
304 * @returns #TRUE if a value was found to remove.
307 _dbus_list_remove (DBusList **list,
315 if (link->data == data)
317 _dbus_list_remove_link (list, link);
321 link = _dbus_list_get_next_link (list, link);
328 * Removes a value from the list. Only removes the
329 * last value equal to the given data pointer,
330 * even if multiple values exist which match.
331 * This is a linear-time operation.
333 * @param list address of the list head.
334 * @param data the value to remove.
335 * @returns #TRUE if a value was found to remove.
338 _dbus_list_remove_last (DBusList **list,
343 link = _dbus_list_get_last_link (list);
347 if (link->data == data)
349 _dbus_list_remove_link (list, link);
353 link = _dbus_list_get_prev_link (list, link);
360 * Removes a link from the list. This is a constant-time operation.
362 * @param list address of the list head.
363 * @param link the list link to remove.
366 _dbus_list_remove_link (DBusList **list,
369 if (link->next == link)
371 /* one-element list */
377 link->prev->next = link->next;
378 link->next->prev = link->prev;
388 * Frees all links in the list and sets the list head to #NULL. Does
389 * not free the data in each link, for obvious reasons. This is a
390 * linear-time operation.
392 * @param list address of the list head.
395 _dbus_list_clear (DBusList **list)
402 DBusList *next = _dbus_list_get_next_link (list, link);
413 * Gets the first link in the list.
414 * This is a constant-time operation.
416 * @param list address of the list head.
417 * @returns the first link, or #NULL for an empty list.
420 _dbus_list_get_first_link (DBusList **list)
426 * Gets the last link in the list.
427 * This is a constant-time operation.
429 * @param list address of the list head.
430 * @returns the last link, or #NULL for an empty list.
433 _dbus_list_get_last_link (DBusList **list)
438 return (*list)->prev;
442 * Gets the last data in the list.
443 * This is a constant-time operation.
445 * @param list address of the list head.
446 * @returns the last data in the list, or #NULL for an empty list.
449 _dbus_list_get_last (DBusList **list)
454 return (*list)->prev->data;
458 * Gets the first data in the list.
459 * This is a constant-time operation.
461 * @param list address of the list head.
462 * @returns the first data in the list, or #NULL for an empty list.
465 _dbus_list_get_first (DBusList **list)
470 return (*list)->data;
474 * Removes the first value in the list and returns it. This is a
475 * constant-time operation.
477 * @param list address of the list head.
478 * @returns the first data in the list, or #NULL for an empty list.
481 _dbus_list_pop_first (DBusList **list)
486 link = _dbus_list_get_first_link (list);
491 _dbus_list_remove_link (list, link);
497 * Removes the last value in the list and returns it. This is a
498 * constant-time operation.
500 * @param list address of the list head.
501 * @returns the last data in the list, or #NULL for an empty list.
504 _dbus_list_pop_last (DBusList **list)
509 link = _dbus_list_get_last_link (list);
514 _dbus_list_remove_link (list, link);
520 * Copies a list. This is a linear-time operation. If there isn't
521 * enough memory to copy the entire list, the destination list will be
524 * @param list address of the head of the list to copy.
525 * @param dest address where the copied list should be placed.
526 * @returns #TRUE on success, #FALSE if not enough memory.
529 _dbus_list_copy (DBusList **list,
534 _dbus_assert (list != dest);
541 if (!_dbus_list_append (dest, link->data))
543 /* free what we have so far */
544 _dbus_list_clear (dest);
548 link = _dbus_list_get_next_link (list, link);
555 * Gets the length of a list. This is a linear-time
558 * @param list address of the head of the list
559 * @returns number of elements in the list.
562 _dbus_list_get_length (DBusList **list)
574 link = _dbus_list_get_next_link (list, link);
581 * Calls the given function for each element in the list. The
582 * function is passed the list element as its first argument, and the
583 * given data as its second argument.
585 * @param list address of the head of the list.
586 * @param function function to call for each element.
587 * @param data extra data for the function.
591 _dbus_list_foreach (DBusList **list,
592 DBusForeachFunction function,
600 DBusList *next = _dbus_list_get_next_link (list, link);
602 (* function) (link->data, data);
610 #ifdef DBUS_BUILD_TESTS
611 #include "dbus-test.h"
615 verify_list (DBusList **list)
625 if (link->next == link)
627 _dbus_assert (link->prev == link);
628 _dbus_assert (*list == link);
636 _dbus_assert (link->prev->next == link);
637 _dbus_assert (link->next->prev == link);
640 while (link != *list);
642 _dbus_assert (length == _dbus_list_get_length (list));
646 is_ascending_sequence (DBusList **list)
651 prev = _DBUS_INT_MIN;
653 link = _dbus_list_get_first_link (list);
656 int v = _DBUS_POINTER_TO_INT (link->data);
663 link = _dbus_list_get_next_link (list, link);
670 is_descending_sequence (DBusList **list)
675 prev = _DBUS_INT_MAX;
677 link = _dbus_list_get_first_link (list);
680 int v = _DBUS_POINTER_TO_INT (link->data);
687 link = _dbus_list_get_next_link (list, link);
694 all_even_values (DBusList **list)
698 link = _dbus_list_get_first_link (list);
701 int v = _DBUS_POINTER_TO_INT (link->data);
706 link = _dbus_list_get_next_link (list, link);
713 all_odd_values (DBusList **list)
717 link = _dbus_list_get_first_link (list);
720 int v = _DBUS_POINTER_TO_INT (link->data);
725 link = _dbus_list_get_next_link (list, link);
732 lists_equal (DBusList **list1,
738 link1 = _dbus_list_get_first_link (list1);
739 link2 = _dbus_list_get_first_link (list2);
740 while (link1 && link2)
742 if (link1->data != link2->data)
745 link1 = _dbus_list_get_next_link (list1, link1);
746 link2 = _dbus_list_get_next_link (list2, link2);
756 * @ingroup DBusListInternals
757 * Unit test for DBusList
758 * @returns #TRUE on success.
761 _dbus_list_test (void)
774 /* Test append and prepend */
779 if (!_dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i)))
780 _dbus_assert_not_reached ("could not allocate for append");
782 if (!_dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i)))
783 _dbus_assert_not_reached ("count not allocate for prepend");
786 verify_list (&list1);
787 verify_list (&list2);
789 _dbus_assert (_dbus_list_get_length (&list1) == i);
790 _dbus_assert (_dbus_list_get_length (&list2) == i);
793 _dbus_assert (is_ascending_sequence (&list1));
794 _dbus_assert (is_descending_sequence (&list2));
796 /* Test list clear */
797 _dbus_list_clear (&list1);
798 _dbus_list_clear (&list2);
800 verify_list (&list1);
801 verify_list (&list2);
803 /* Test get_first, get_last, pop_first, pop_last */
808 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
809 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
822 got_data1 = _dbus_list_get_last (&list1);
823 got_data2 = _dbus_list_get_first (&list2);
825 data1 = _dbus_list_pop_last (&list1);
826 data2 = _dbus_list_pop_first (&list2);
828 _dbus_assert (got_data1 == data1);
829 _dbus_assert (got_data2 == data2);
831 _dbus_assert (_DBUS_POINTER_TO_INT (data1) == i);
832 _dbus_assert (_DBUS_POINTER_TO_INT (data2) == i);
834 verify_list (&list1);
835 verify_list (&list2);
837 _dbus_assert (is_ascending_sequence (&list1));
838 _dbus_assert (is_descending_sequence (&list2));
843 _dbus_assert (list1 == NULL);
844 _dbus_assert (list2 == NULL);
851 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
852 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
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));
866 link2 = _dbus_list_get_first_link (&list2);
867 while (link2 != NULL)
869 verify_list (&link2); /* pretend this link is the head */
871 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
873 link2 = _dbus_list_get_next_link (&list2, link2);
878 link1 = _dbus_list_get_first_link (&list1);
879 while (link1 != NULL)
881 verify_list (&link1); /* pretend this link is the head */
883 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
885 link1 = _dbus_list_get_next_link (&list1, link1);
890 link1 = _dbus_list_get_last_link (&list1);
891 while (link1 != NULL)
893 verify_list (&link1); /* pretend this link is the head */
895 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
897 link1 = _dbus_list_get_prev_link (&list1, link1);
901 _dbus_list_clear (&list1);
902 _dbus_list_clear (&list2);
909 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
910 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
919 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
920 _dbus_assert_not_reached ("element should have been in list");
921 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
922 _dbus_assert_not_reached ("element should have been in list");
924 verify_list (&list1);
925 verify_list (&list2);
930 _dbus_assert (all_odd_values (&list1));
931 _dbus_assert (all_odd_values (&list2));
933 _dbus_list_clear (&list1);
934 _dbus_list_clear (&list2);
936 /* test removing the other half of the elements */
941 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
942 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
951 if (!_dbus_list_remove (&list1, _DBUS_INT_TO_POINTER (i)))
952 _dbus_assert_not_reached ("element should have been in list");
953 if (!_dbus_list_remove (&list2, _DBUS_INT_TO_POINTER (i)))
954 _dbus_assert_not_reached ("element should have been in list");
956 verify_list (&list1);
957 verify_list (&list2);
962 _dbus_assert (all_even_values (&list1));
963 _dbus_assert (all_even_values (&list2));
965 /* clear list using remove_link */
966 while (list1 != NULL)
968 _dbus_list_remove_link (&list1, list1);
969 verify_list (&list1);
971 while (list2 != NULL)
973 _dbus_list_remove_link (&list2, list2);
974 verify_list (&list2);
977 /* Test remove link more generally */
981 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
982 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
987 link2 = _dbus_list_get_first_link (&list2);
988 while (link2 != NULL)
990 DBusList *next = _dbus_list_get_next_link (&list2, link2);
992 _dbus_assert (_DBUS_POINTER_TO_INT (link2->data) == i);
995 _dbus_list_remove_link (&list2, link2);
997 verify_list (&list2);
1003 _dbus_assert (all_odd_values (&list2));
1004 _dbus_list_clear (&list2);
1007 link1 = _dbus_list_get_first_link (&list1);
1008 while (link1 != NULL)
1010 DBusList *next = _dbus_list_get_next_link (&list1, link1);
1012 _dbus_assert (_DBUS_POINTER_TO_INT (link1->data) == i);
1015 _dbus_list_remove_link (&list1, link1);
1017 verify_list (&list1);
1023 _dbus_assert (all_even_values (&list1));
1024 _dbus_list_clear (&list1);
1026 /* Test copying a list */
1030 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (i));
1031 _dbus_list_prepend (&list2, _DBUS_INT_TO_POINTER (i));
1035 /* bad pointers, because they are allowed in the copy dest */
1036 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1037 copy2 = _DBUS_INT_TO_POINTER (23);
1039 _dbus_list_copy (&list1, ©1);
1040 verify_list (&list1);
1041 verify_list (©1);
1042 _dbus_assert (lists_equal (&list1, ©1));
1044 _dbus_list_copy (&list2, ©2);
1045 verify_list (&list2);
1046 verify_list (©2);
1047 _dbus_assert (lists_equal (&list2, ©2));
1049 /* Now test copying empty lists */
1050 _dbus_list_clear (&list1);
1051 _dbus_list_clear (&list2);
1052 _dbus_list_clear (©1);
1053 _dbus_list_clear (©2);
1055 /* bad pointers, because they are allowed in the copy dest */
1056 copy1 = _DBUS_INT_TO_POINTER (0x342234);
1057 copy2 = _DBUS_INT_TO_POINTER (23);
1059 _dbus_list_copy (&list1, ©1);
1060 verify_list (&list1);
1061 verify_list (©1);
1062 _dbus_assert (lists_equal (&list1, ©1));
1064 _dbus_list_copy (&list2, ©2);
1065 verify_list (&list2);
1066 verify_list (©2);
1067 _dbus_assert (lists_equal (&list2, ©2));
1069 _dbus_list_clear (&list1);
1070 _dbus_list_clear (&list2);
1072 /* insert_before on empty list */
1073 _dbus_list_insert_before (&list1, NULL,
1074 _DBUS_INT_TO_POINTER (0));
1075 verify_list (&list1);
1077 /* inserting before first element */
1078 _dbus_list_insert_before (&list1, list1,
1079 _DBUS_INT_TO_POINTER (2));
1080 verify_list (&list1);
1081 _dbus_assert (is_descending_sequence (&list1));
1083 /* inserting in the middle */
1084 _dbus_list_insert_before (&list1, list1->next,
1085 _DBUS_INT_TO_POINTER (1));
1086 verify_list (&list1);
1087 _dbus_assert (is_descending_sequence (&list1));
1089 /* using insert_before to append */
1090 _dbus_list_insert_before (&list1, NULL,
1091 _DBUS_INT_TO_POINTER (-1));
1092 verify_list (&list1);
1093 _dbus_assert (is_descending_sequence (&list1));
1095 _dbus_list_clear (&list1);
1097 /* insert_after on empty list */
1098 _dbus_list_insert_after (&list1, NULL,
1099 _DBUS_INT_TO_POINTER (0));
1100 verify_list (&list1);
1102 /* inserting after first element */
1103 _dbus_list_insert_after (&list1, list1,
1104 _DBUS_INT_TO_POINTER (1));
1105 verify_list (&list1);
1106 _dbus_assert (is_ascending_sequence (&list1));
1108 /* inserting at the end */
1109 _dbus_list_insert_after (&list1, list1->next,
1110 _DBUS_INT_TO_POINTER (2));
1111 verify_list (&list1);
1112 _dbus_assert (is_ascending_sequence (&list1));
1114 /* using insert_after to prepend */
1115 _dbus_list_insert_after (&list1, NULL,
1116 _DBUS_INT_TO_POINTER (-1));
1117 verify_list (&list1);
1118 _dbus_assert (is_ascending_sequence (&list1));
1120 _dbus_list_clear (&list1);
1122 /* using remove_last */
1123 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (2));
1124 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (1));
1125 _dbus_list_append (&list1, _DBUS_INT_TO_POINTER (3));
1127 _dbus_list_remove_last (&list1, _DBUS_INT_TO_POINTER (2));
1129 verify_list (&list1);
1130 _dbus_assert (is_ascending_sequence (&list1));
1132 _dbus_list_clear (&list1);