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