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