Use spaces for indentation, not tabs.
[gnulib.git] / lib / gl_array_oset.c
1 /* Ordered set data type implemented by an array.
2    Copyright (C) 2006-2007 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2006.
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19
20 /* Specification.  */
21 #include "gl_array_oset.h"
22
23 #include <stdlib.h>
24
25 #include "xalloc.h"
26
27 /* Checked size_t computations.  */
28 #include "xsize.h"
29
30 /* -------------------------- gl_oset_t Data Type -------------------------- */
31
32 /* Concrete gl_oset_impl type, valid for this file only.  */
33 struct gl_oset_impl
34 {
35   struct gl_oset_impl_base base;
36   /* An array of ALLOCATED elements, of which the first COUNT are used.
37      0 <= COUNT <= ALLOCATED.  */
38   const void **elements;
39   size_t count;
40   size_t allocated;
41 };
42
43 static gl_oset_t
44 gl_array_create_empty (gl_oset_implementation_t implementation,
45                        gl_setelement_compar_fn compar_fn,
46                        gl_setelement_dispose_fn dispose_fn)
47 {
48   struct gl_oset_impl *set = XMALLOC (struct gl_oset_impl);
49
50   set->base.vtable = implementation;
51   set->base.compar_fn = compar_fn;
52   set->base.dispose_fn = dispose_fn;
53   set->elements = NULL;
54   set->count = 0;
55   set->allocated = 0;
56
57   return set;
58 }
59
60 static size_t
61 gl_array_size (gl_oset_t set)
62 {
63   return set->count;
64 }
65
66 static size_t
67 gl_array_indexof (gl_oset_t set, const void *elt)
68 {
69   size_t count = set->count;
70
71   if (count > 0)
72     {
73       gl_setelement_compar_fn compar = set->base.compar_fn;
74       size_t low = 0;
75       size_t high = count;
76
77       /* At each loop iteration, low < high; for indices < low the values
78          are smaller than ELT; for indices >= high the values are greater
79          than ELT.  So, if the element occurs in the list, it is at
80          low <= position < high.  */
81       do
82         {
83           size_t mid = low + (high - low) / 2; /* low <= mid < high */
84           int cmp = (compar != NULL
85                      ? compar (set->elements[mid], elt)
86                      : (set->elements[mid] > elt ? 1 :
87                         set->elements[mid] < elt ? -1 : 0));
88
89           if (cmp < 0)
90             low = mid + 1;
91           else if (cmp > 0)
92             high = mid;
93           else /* cmp == 0 */
94             /* We have an element equal to ELT at index MID.  */
95             return mid;
96         }
97       while (low < high);
98     }
99   return (size_t)(-1);
100 }
101
102 static bool
103 gl_array_search (gl_oset_t set, const void *elt)
104 {
105   return gl_array_indexof (set, elt) != (size_t)(-1);
106 }
107
108 static bool
109 gl_array_search_atleast (gl_oset_t set,
110                          gl_setelement_threshold_fn threshold_fn,
111                          const void *threshold,
112                          const void **eltp)
113 {
114   size_t count = set->count;
115
116   if (count > 0)
117     {
118       size_t low = 0;
119       size_t high = count;
120
121       /* At each loop iteration, low < high; for indices < low the values are
122          smaller than THRESHOLD; for indices >= high the values are nonexistent.
123          So, if an element >= THRESHOLD occurs in the list, it is at
124          low <= position < high.  */
125       do
126         {
127           size_t mid = low + (high - low) / 2; /* low <= mid < high */
128
129           if (! threshold_fn (set->elements[mid], threshold))
130             low = mid + 1;
131           else
132             {
133               /* We have an element >= THRESHOLD at index MID.  But we need the
134                  minimal such index.  */
135               high = mid;
136               /* At each loop iteration, low <= high and
137                    compar (list->elements[high], value) >= 0,
138                  and we know that the first occurrence of the element is at
139                  low <= position <= high.  */
140               while (low < high)
141                 {
142                   size_t mid2 = low + (high - low) / 2; /* low <= mid2 < high */
143
144                   if (! threshold_fn (set->elements[mid2], threshold))
145                     low = mid2 + 1;
146                   else
147                     high = mid2;
148                 }
149               *eltp = set->elements[low];
150               return true;
151             }
152         }
153       while (low < high);
154     }
155   return false;
156 }
157
158 /* Ensure that set->allocated > set->count.  */
159 static void
160 grow (gl_oset_t set)
161 {
162   size_t new_allocated;
163   size_t memory_size;
164   const void **memory;
165
166   new_allocated = xtimes (set->allocated, 2);
167   new_allocated = xsum (new_allocated, 1);
168   memory_size = xtimes (new_allocated, sizeof (const void *));
169   if (size_overflow_p (memory_size))
170     /* Overflow, would lead to out of memory.  */
171     xalloc_die ();
172   memory = (const void **) xrealloc (set->elements, memory_size);
173   if (memory == NULL)
174     /* Out of memory.  */
175     xalloc_die ();
176   set->elements = memory;
177   set->allocated = new_allocated;
178 }
179
180 /* Add the given element ELT at the given position,
181    0 <= position <= gl_oset_size (set).  */
182 static inline void
183 gl_array_add_at (gl_oset_t set, size_t position, const void *elt)
184 {
185   size_t count = set->count;
186   const void **elements;
187   size_t i;
188
189   if (count == set->allocated)
190     grow (set);
191   elements = set->elements;
192   for (i = count; i > position; i--)
193     elements[i] = elements[i - 1];
194   elements[position] = elt;
195   set->count = count + 1;
196 }
197
198 /* Remove the element at the given position,
199    0 <= position < gl_oset_size (set).  */
200 static inline void
201 gl_array_remove_at (gl_oset_t set, size_t position)
202 {
203   size_t count = set->count;
204   const void **elements;
205   size_t i;
206
207   elements = set->elements;
208   if (set->base.dispose_fn != NULL)
209     set->base.dispose_fn (elements[position]);
210   for (i = position + 1; i < count; i++)
211     elements[i - 1] = elements[i];
212   set->count = count - 1;
213 }
214
215 static bool
216 gl_array_add (gl_oset_t set, const void *elt)
217 {
218   size_t count = set->count;
219   size_t low = 0;
220
221   if (count > 0)
222     {
223       gl_setelement_compar_fn compar = set->base.compar_fn;
224       size_t high = count;
225
226       /* At each loop iteration, low < high; for indices < low the values
227          are smaller than ELT; for indices >= high the values are greater
228          than ELT.  So, if the element occurs in the list, it is at
229          low <= position < high.  */
230       do
231         {
232           size_t mid = low + (high - low) / 2; /* low <= mid < high */
233           int cmp = (compar != NULL
234                      ? compar (set->elements[mid], elt)
235                      : (set->elements[mid] > elt ? 1 :
236                         set->elements[mid] < elt ? -1 : 0));
237
238           if (cmp < 0)
239             low = mid + 1;
240           else if (cmp > 0)
241             high = mid;
242           else /* cmp == 0 */
243             return false;
244         }
245       while (low < high);
246     }
247   gl_array_add_at (set, low, elt);
248   return true;
249 }
250
251 static bool
252 gl_array_remove (gl_oset_t set, const void *elt)
253 {
254   size_t index = gl_array_indexof (set, elt);
255   if (index != (size_t)(-1))
256     {
257       gl_array_remove_at (set, index);
258       return true;
259     }
260   else
261     return false;
262 }
263
264 static void
265 gl_array_free (gl_oset_t set)
266 {
267   if (set->elements != NULL)
268     {
269       if (set->base.dispose_fn != NULL)
270         {
271           size_t count = set->count;
272
273           if (count > 0)
274             {
275               gl_setelement_dispose_fn dispose = set->base.dispose_fn;
276               const void **elements = set->elements;
277
278               do
279                 dispose (*elements++);
280               while (--count > 0);
281             }
282         }
283       free (set->elements);
284     }
285   free (set);
286 }
287
288 /* --------------------- gl_oset_iterator_t Data Type --------------------- */
289
290 static gl_oset_iterator_t
291 gl_array_iterator (gl_oset_t set)
292 {
293   gl_oset_iterator_t result;
294
295   result.vtable = set->base.vtable;
296   result.set = set;
297   result.count = set->count;
298   result.p = set->elements + 0;
299   result.q = set->elements + set->count;
300 #ifdef lint
301   result.i = 0;
302   result.j = 0;
303 #endif
304
305   return result;
306 }
307
308 static bool
309 gl_array_iterator_next (gl_oset_iterator_t *iterator, const void **eltp)
310 {
311   gl_oset_t set = iterator->set;
312   if (iterator->count != set->count)
313     {
314       if (iterator->count != set->count + 1)
315         /* Concurrent modifications were done on the set.  */
316         abort ();
317       /* The last returned element was removed.  */
318       iterator->count--;
319       iterator->p = (const void **) iterator->p - 1;
320       iterator->q = (const void **) iterator->q - 1;
321     }
322   if (iterator->p < iterator->q)
323     {
324       const void **p = (const void **) iterator->p;
325       *eltp = *p;
326       iterator->p = p + 1;
327       return true;
328     }
329   else
330     return false;
331 }
332
333 static void
334 gl_array_iterator_free (gl_oset_iterator_t *iterator)
335 {
336 }
337
338
339 const struct gl_oset_implementation gl_array_oset_implementation =
340   {
341     gl_array_create_empty,
342     gl_array_size,
343     gl_array_search,
344     gl_array_search_atleast,
345     gl_array_add,
346     gl_array_remove,
347     gl_array_free,
348     gl_array_iterator,
349     gl_array_iterator_next,
350     gl_array_iterator_free
351   };