minor optimization.
[platform/upstream/glib.git] / glib / gslist.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GLib Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 /* 
28  * MT safe
29  */
30
31 #include "glib.h"
32
33
34 struct _GAllocator /* from gmem.c */
35 {
36   gchar         *name;
37   guint16        n_preallocs;
38   guint          is_unused : 1;
39   guint          type : 4;
40   GAllocator    *last;
41   GMemChunk     *mem_chunk;
42   GSList        *free_lists; /* implementation specific */
43 };
44
45 G_LOCK_DEFINE_STATIC (current_allocator);
46 static GAllocator       *current_allocator = NULL;
47
48 /* HOLDS: current_allocator_lock */
49 static void
50 g_slist_validate_allocator (GAllocator *allocator)
51 {
52   g_return_if_fail (allocator != NULL);
53   g_return_if_fail (allocator->is_unused == TRUE);
54
55   if (allocator->type != G_ALLOCATOR_SLIST)
56     {
57       allocator->type = G_ALLOCATOR_SLIST;
58       if (allocator->mem_chunk)
59         {
60           g_mem_chunk_destroy (allocator->mem_chunk);
61           allocator->mem_chunk = NULL;
62         }
63     }
64
65   if (!allocator->mem_chunk)
66     {
67       allocator->mem_chunk = g_mem_chunk_new (allocator->name,
68                                               sizeof (GSList),
69                                               sizeof (GSList) * allocator->n_preallocs,
70                                               G_ALLOC_ONLY);
71       allocator->free_lists = NULL;
72     }
73
74   allocator->is_unused = FALSE;
75 }
76
77 void
78 g_slist_push_allocator (GAllocator *allocator)
79 {
80   G_LOCK (current_allocator);
81   g_slist_validate_allocator (allocator);
82   allocator->last = current_allocator;
83   current_allocator = allocator;
84   G_UNLOCK (current_allocator);
85 }
86
87 void
88 g_slist_pop_allocator (void)
89 {
90   G_LOCK (current_allocator);
91   if (current_allocator)
92     {
93       GAllocator *allocator;
94
95       allocator = current_allocator;
96       current_allocator = allocator->last;
97       allocator->last = NULL;
98       allocator->is_unused = TRUE;
99     }
100   G_UNLOCK (current_allocator);
101 }
102
103 static inline GSList*
104 _g_slist_alloc (void)
105 {
106   GSList *list;
107
108   G_LOCK (current_allocator);
109   if (!current_allocator)
110     {
111       GAllocator *allocator = g_allocator_new ("GLib default GSList allocator",
112                                                128);
113       g_slist_validate_allocator (allocator);
114       allocator->last = NULL;
115       current_allocator = allocator; 
116     }
117   if (!current_allocator->free_lists)
118     {
119       list = g_chunk_new (GSList, current_allocator->mem_chunk);
120       list->data = NULL;
121     }
122   else
123     {
124       if (current_allocator->free_lists->data)
125         {
126           list = current_allocator->free_lists->data;
127           current_allocator->free_lists->data = list->next;
128           list->data = NULL;
129         }
130       else
131         {
132           list = current_allocator->free_lists;
133           current_allocator->free_lists = list->next;
134         }
135     }
136   G_UNLOCK (current_allocator);
137   
138   list->next = NULL;
139
140   return list;
141 }
142
143 GSList*
144 g_slist_alloc (void)
145 {
146   return _g_slist_alloc ();
147 }
148
149 void
150 g_slist_free (GSList *list)
151 {
152   if (list)
153     {
154       list->data = list->next;
155       G_LOCK (current_allocator);
156       list->next = current_allocator->free_lists;
157       current_allocator->free_lists = list;
158       G_UNLOCK (current_allocator);
159     }
160 }
161
162 static inline void
163 _g_slist_free_1 (GSList *list)
164 {
165   if (list)
166     {
167       list->data = NULL;
168       G_LOCK (current_allocator);
169       list->next = current_allocator->free_lists;
170       current_allocator->free_lists = list;
171       G_UNLOCK (current_allocator);
172     }
173 }
174
175 void
176 g_slist_free_1 (GSList *list)
177 {
178   _g_slist_free_1 (list);
179 }
180
181 GSList*
182 g_slist_append (GSList   *list,
183                 gpointer  data)
184 {
185   GSList *new_list;
186   GSList *last;
187
188   new_list = _g_slist_alloc ();
189   new_list->data = data;
190
191   if (list)
192     {
193       last = g_slist_last (list);
194       /* g_assert (last != NULL); */
195       last->next = new_list;
196
197       return list;
198     }
199   else
200       return new_list;
201 }
202
203 GSList*
204 g_slist_prepend (GSList   *list,
205                  gpointer  data)
206 {
207   GSList *new_list;
208
209   new_list = _g_slist_alloc ();
210   new_list->data = data;
211   new_list->next = list;
212
213   return new_list;
214 }
215
216 GSList*
217 g_slist_insert (GSList   *list,
218                 gpointer  data,
219                 gint      position)
220 {
221   GSList *prev_list;
222   GSList *tmp_list;
223   GSList *new_list;
224
225   if (position < 0)
226     return g_slist_append (list, data);
227   else if (position == 0)
228     return g_slist_prepend (list, data);
229
230   new_list = _g_slist_alloc ();
231   new_list->data = data;
232
233   if (!list)
234     return new_list;
235
236   prev_list = NULL;
237   tmp_list = list;
238
239   while ((position-- > 0) && tmp_list)
240     {
241       prev_list = tmp_list;
242       tmp_list = tmp_list->next;
243     }
244
245   if (prev_list)
246     {
247       new_list->next = prev_list->next;
248       prev_list->next = new_list;
249     }
250   else
251     {
252       new_list->next = list;
253       list = new_list;
254     }
255
256   return list;
257 }
258
259 GSList *
260 g_slist_concat (GSList *list1, GSList *list2)
261 {
262   if (list2)
263     {
264       if (list1)
265         g_slist_last (list1)->next = list2;
266       else
267         list1 = list2;
268     }
269
270   return list1;
271 }
272
273 GSList*
274 g_slist_remove (GSList   *list,
275                 gpointer  data)
276 {
277   GSList *tmp;
278   GSList *prev;
279
280   prev = NULL;
281   tmp = list;
282
283   while (tmp)
284     {
285       if (tmp->data == data)
286         {
287           if (prev)
288             prev->next = tmp->next;
289           if (list == tmp)
290             list = list->next;
291
292           tmp->next = NULL;
293           g_slist_free (tmp);
294
295           break;
296         }
297
298       prev = tmp;
299       tmp = tmp->next;
300     }
301
302   return list;
303 }
304
305 static inline GSList*
306 _g_slist_remove_link (GSList *list,
307                       GSList *link)
308 {
309   GSList *tmp;
310   GSList *prev;
311
312   prev = NULL;
313   tmp = list;
314
315   while (tmp)
316     {
317       if (tmp == link)
318         {
319           if (prev)
320             prev->next = tmp->next;
321           if (list == tmp)
322             list = list->next;
323
324           tmp->next = NULL;
325           break;
326         }
327
328       prev = tmp;
329       tmp = tmp->next;
330     }
331
332   return list;
333 }
334
335 GSList* 
336 g_slist_remove_link (GSList *list,
337                      GSList *link)
338 {
339   return _g_slist_remove_link (list, link);
340 }
341
342 GSList*
343 g_slist_delete_link (GSList *list,
344                      GSList *link)
345 {
346   list = _g_slist_remove_link (list, link);
347   _g_slist_free_1 (link);
348
349   return list;
350 }
351
352 GSList*
353 g_slist_copy (GSList *list)
354 {
355   GSList *new_list = NULL;
356
357   if (list)
358     {
359       GSList *last;
360
361       new_list = _g_slist_alloc ();
362       new_list->data = list->data;
363       last = new_list;
364       list = list->next;
365       while (list)
366         {
367           last->next = _g_slist_alloc ();
368           last = last->next;
369           last->data = list->data;
370           list = list->next;
371         }
372     }
373
374   return new_list;
375 }
376
377 GSList*
378 g_slist_reverse (GSList *list)
379 {
380   GSList *prev = NULL;
381   GSList *next = NULL;
382   
383   while (list)
384     {
385       next = list->next;
386       list->next = prev;
387       
388       prev = list;
389       list = next;
390     }
391   
392   return prev;
393 }
394
395 GSList*
396 g_slist_nth (GSList *list,
397              guint   n)
398 {
399   while (n-- > 0 && list)
400     list = list->next;
401
402   return list;
403 }
404
405 gpointer
406 g_slist_nth_data (GSList   *list,
407                   guint     n)
408 {
409   while (n-- > 0 && list)
410     list = list->next;
411
412   return list ? list->data : NULL;
413 }
414
415 GSList*
416 g_slist_find (GSList   *list,
417               gpointer  data)
418 {
419   while (list)
420     {
421       if (list->data == data)
422         break;
423       list = list->next;
424     }
425
426   return list;
427 }
428
429 GSList*
430 g_slist_find_custom (GSList      *list,
431                      gpointer     data,
432                      GCompareFunc func)
433 {
434   g_return_val_if_fail (func != NULL, list);
435
436   while (list)
437     {
438       if (! func (list->data, data))
439         return list;
440       list = list->next;
441     }
442
443   return NULL;
444 }
445
446 gint
447 g_slist_position (GSList *list,
448                   GSList *link)
449 {
450   gint i;
451
452   i = 0;
453   while (list)
454     {
455       if (list == link)
456         return i;
457       i++;
458       list = list->next;
459     }
460
461   return -1;
462 }
463
464 gint
465 g_slist_index (GSList   *list,
466                gpointer data)
467 {
468   gint i;
469
470   i = 0;
471   while (list)
472     {
473       if (list->data == data)
474         return i;
475       i++;
476       list = list->next;
477     }
478
479   return -1;
480 }
481
482 GSList*
483 g_slist_last (GSList *list)
484 {
485   if (list)
486     {
487       while (list->next)
488         list = list->next;
489     }
490
491   return list;
492 }
493
494 guint
495 g_slist_length (GSList *list)
496 {
497   guint length;
498
499   length = 0;
500   while (list)
501     {
502       length++;
503       list = list->next;
504     }
505
506   return length;
507 }
508
509 void
510 g_slist_foreach (GSList   *list,
511                  GFunc     func,
512                  gpointer  user_data)
513 {
514   while (list)
515     {
516       (*func) (list->data, user_data);
517       list = list->next;
518     }
519 }
520
521 GSList*
522 g_slist_insert_sorted (GSList       *list,
523                        gpointer      data,
524                        GCompareFunc  func)
525 {
526   GSList *tmp_list = list;
527   GSList *prev_list = NULL;
528   GSList *new_list;
529   gint cmp;
530  
531   g_return_val_if_fail (func != NULL, list);
532
533   if (!list)
534     {
535       new_list = _g_slist_alloc ();
536       new_list->data = data;
537       return new_list;
538     }
539  
540   cmp = (*func) (data, tmp_list->data);
541  
542   while ((tmp_list->next) && (cmp > 0))
543     {
544       prev_list = tmp_list;
545       tmp_list = tmp_list->next;
546       cmp = (*func) (data, tmp_list->data);
547     }
548
549   new_list = _g_slist_alloc ();
550   new_list->data = data;
551
552   if ((!tmp_list->next) && (cmp > 0))
553     {
554       tmp_list->next = new_list;
555       return list;
556     }
557   
558   if (prev_list)
559     {
560       prev_list->next = new_list;
561       new_list->next = tmp_list;
562       return list;
563     }
564   else
565     {
566       new_list->next = list;
567       return new_list;
568     }
569 }
570
571 static GSList* 
572 g_slist_sort_merge  (GSList      *l1, 
573                      GSList      *l2,
574                      GCompareFunc compare_func)
575 {
576   GSList list, *l;
577
578   l=&list;
579
580   while (l1 && l2)
581     {
582       if (compare_func(l1->data,l2->data) < 0)
583         {
584           l=l->next=l1;
585           l1=l1->next;
586         } 
587       else 
588         {
589           l=l->next=l2;
590           l2=l2->next;
591         }
592     }
593   l->next= l1 ? l1 : l2;
594   
595   return list.next;
596 }
597
598 GSList* 
599 g_slist_sort (GSList       *list,
600               GCompareFunc compare_func)
601 {
602   GSList *l1, *l2;
603
604   if (!list) 
605     return NULL;
606   if (!list->next) 
607     return list;
608
609   l1 = list; 
610   l2 = list->next;
611
612   while ((l2 = l2->next) != NULL)
613     {
614       if ((l2 = l2->next) == NULL) 
615         break;
616       l1=l1->next;
617     }
618   l2 = l1->next; 
619   l1->next = NULL;
620
621   return g_slist_sort_merge (g_slist_sort (list, compare_func),
622                              g_slist_sort (l2,   compare_func),
623                              compare_func);
624 }