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