Include <config.h> unconditionally.
[gnulib.git] / lib / gl_array_oset.c
1 /* Ordered set data type implemented by an array.
2    Copyright (C) 2006 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 {
48   struct gl_oset_impl *set =
49     (struct gl_oset_impl *) xmalloc (sizeof (struct gl_oset_impl));
50
51   set->base.vtable = implementation;
52   set->base.compar_fn = compar_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, 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 /* Ensure that set->allocated > set->count.  */
109 static void
110 grow (gl_oset_t set)
111 {
112   size_t new_allocated;
113   size_t memory_size;
114   const void **memory;
115
116   new_allocated = xtimes (set->allocated, 2);
117   new_allocated = xsum (new_allocated, 1);
118   memory_size = xtimes (new_allocated, sizeof (const void *));
119   if (size_overflow_p (memory_size))
120     /* Overflow, would lead to out of memory.  */
121     xalloc_die ();
122   memory = (const void **) xrealloc (set->elements, memory_size);
123   if (memory == NULL)
124     /* Out of memory.  */
125     xalloc_die ();
126   set->elements = memory;
127   set->allocated = new_allocated;
128 }
129
130 /* Add the given element ELT at the given position,
131    0 <= position <= gl_oset_size (set).  */
132 static inline void
133 gl_array_add_at (gl_oset_t set, size_t position, const void *elt)
134 {
135   size_t count = set->count;
136   const void **elements;
137   size_t i;
138
139   if (count == set->allocated)
140     grow (set);
141   elements = set->elements;
142   for (i = count; i > position; i--)
143     elements[i] = elements[i - 1];
144   elements[position] = elt;
145   set->count = count + 1;
146 }
147
148 /* Remove the element at the given position,
149    0 <= position < gl_oset_size (set).  */
150 static inline void
151 gl_array_remove_at (gl_oset_t set, size_t position)
152 {
153   size_t count = set->count;
154   const void **elements;
155   size_t i;
156
157   elements = set->elements;
158   for (i = position + 1; i < count; i++)
159     elements[i - 1] = elements[i];
160   set->count = count - 1;
161 }
162
163 static bool
164 gl_array_add (gl_oset_t set, const void *elt)
165 {
166   size_t count = set->count;
167   size_t low = 0;
168
169   if (count > 0)
170     {
171       gl_setelement_compar_fn compar = set->base.compar_fn;
172       size_t high = count;
173
174       /* At each loop iteration, low < high; for indices < low the values
175          are smaller than ELT; for indices >= high the values are greater
176          than ELT.  So, if the element occurs in the list, is at
177          low <= position < high.  */
178       do
179         {
180           size_t mid = low + (high - low) / 2; /* low <= mid < high */
181           int cmp = (compar != NULL
182                      ? compar (set->elements[mid], elt)
183                      : (set->elements[mid] > elt ? 1 :
184                         set->elements[mid] < elt ? -1 : 0));
185
186           if (cmp < 0)
187             low = mid + 1;
188           else if (cmp > 0)
189             high = mid;
190           else /* cmp == 0 */
191             return false;
192         }
193       while (low < high);
194     }
195   gl_array_add_at (set, low, elt);
196   return true;
197 }
198
199 static bool
200 gl_array_remove (gl_oset_t set, const void *elt)
201 {
202   size_t index = gl_array_indexof (set, elt);
203   if (index != (size_t)(-1))
204     {
205       gl_array_remove_at (set, index);
206       return true;
207     }
208   else
209     return false;
210 }
211
212 static void
213 gl_array_free (gl_oset_t set)
214 {
215   if (set->elements != NULL)
216     free (set->elements);
217   free (set);
218 }
219
220 /* --------------------- gl_oset_iterator_t Data Type --------------------- */
221
222 static gl_oset_iterator_t
223 gl_array_iterator (gl_oset_t set)
224 {
225   gl_oset_iterator_t result;
226
227   result.vtable = set->base.vtable;
228   result.set = set;
229   result.count = set->count;
230   result.p = set->elements + 0;
231   result.q = set->elements + set->count;
232
233   return result;
234 }
235
236 static bool
237 gl_array_iterator_next (gl_oset_iterator_t *iterator, const void **eltp)
238 {
239   gl_oset_t set = iterator->set;
240   if (iterator->count != set->count)
241     {
242       if (iterator->count != set->count + 1)
243         /* Concurrent modifications were done on the set.  */
244         abort ();
245       /* The last returned element was removed.  */
246       iterator->count--;
247       iterator->p--;
248       iterator->q--;
249     }
250   if (iterator->p < iterator->q)
251     {
252       const void **p = (const void **) iterator->p;
253       *eltp = *p;
254       iterator->p = p + 1;
255       return true;
256     }
257   else
258     return false;
259 }
260
261 static void
262 gl_array_iterator_free (gl_oset_iterator_t *iterator)
263 {
264 }
265
266
267 const struct gl_oset_implementation gl_array_oset_implementation =
268   {
269     gl_array_create_empty,
270     gl_array_size,
271     gl_array_search,
272     gl_array_add,
273     gl_array_remove,
274     gl_array_free,
275     gl_array_iterator,
276     gl_array_iterator_next,
277     gl_array_iterator_free
278   };