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, &copy1);
1267   verify_list (&list1);
1268   verify_list (&copy1);
1269   _dbus_assert (lists_equal (&list1, &copy1));
1270 
1271   _dbus_list_copy (&list2, &copy2);
1272   verify_list (&list2);
1273   verify_list (&copy2);
1274   _dbus_assert (lists_equal (&list2, &copy2));
1275 
1276   /* Now test copying empty lists */
1277   _dbus_list_clear (&list1);
1278   _dbus_list_clear (&list2);
1279   _dbus_list_clear (&copy1);
1280   _dbus_list_clear (&copy2);
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, &copy1);
1287   verify_list (&list1);
1288   verify_list (&copy1);
1289   _dbus_assert (lists_equal (&list1, &copy1));
1290 
1291   _dbus_list_copy (&list2, &copy2);
1292   verify_list (&list2);
1293   verify_list (&copy2);
1294   _dbus_assert (lists_equal (&list2, &copy2));
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