1 /*
2 * bitset.c
3 * Plain old bitset data type.
4 *
5 * Copyright (C) 2001-2017 Cosmin Truta.
6 *
7 * This software is distributed under the zlib license.
8 * Please see the accompanying LICENSE file.
9 */
10
11 #include "bitset.h"
12
13 #include <ctype.h>
14 #include <errno.h>
15 #include <stddef.h>
16
17
18 /*
19 * Returns the minimum of two given values.
20 */
21 #define opng__MIN__(a, b) \
22 ((a) < (b) ? (a) : (b))
23
24 /*
25 * Returns the maximum of two given values.
26 */
27 #define opng__MAX__(a, b) \
28 ((a) > (b) ? (a) : (b))
29
30 /*
31 * Spans the given pointer past the elements that satisfy the given predicate.
32 * E.g., opng__SPAN__(str, isspace) moves str past the leading whitespace.
33 */
34 #define opng__SPAN__(ptr, predicate) \
35 { \
36 while ((predicate)(*(ptr))) \
37 ++(ptr); \
38 }
39
40
41 /*
42 * Counts the number of elements in a bitset.
43 */
44 unsigned int
opng_bitset_count(opng_bitset_t set)45 opng_bitset_count(opng_bitset_t set)
46 {
47 unsigned int result;
48
49 /* Apply Wegner's method. */
50 result = 0;
51 while (set != 0)
52 {
53 set &= (set - 1);
54 ++result;
55 }
56 return result;
57 }
58
59 /*
60 * Finds the first element in a bitset.
61 */
62 int
opng_bitset_find_first(opng_bitset_t set)63 opng_bitset_find_first(opng_bitset_t set)
64 {
65 int i;
66
67 for (i = 0; i <= OPNG_BITSET_ELT_MAX; ++i)
68 {
69 if (opng_bitset_test(set, i))
70 return i;
71 }
72 return -1;
73 }
74
75 /*
76 * Finds the next element in a bitset.
77 */
78 int
opng_bitset_find_next(opng_bitset_t set,int elt)79 opng_bitset_find_next(opng_bitset_t set, int elt)
80 {
81 int i;
82
83 for (i = opng__MAX__(elt, -1) + 1; i <= OPNG_BITSET_ELT_MAX; ++i)
84 {
85 if (opng_bitset_test(set, i))
86 return i;
87 }
88 return -1;
89 }
90
91 /*
92 * Finds the last element in a bitset.
93 */
94 int
opng_bitset_find_last(opng_bitset_t set)95 opng_bitset_find_last(opng_bitset_t set)
96 {
97 int i;
98
99 for (i = OPNG_BITSET_ELT_MAX; i >= 0; --i)
100 {
101 if (opng_bitset_test(set, i))
102 return i;
103 }
104 return -1;
105 }
106
107 /*
108 * Finds the previous element in a bitset.
109 */
110 int
opng_bitset_find_prev(opng_bitset_t set,int elt)111 opng_bitset_find_prev(opng_bitset_t set, int elt)
112 {
113 int i;
114
115 for (i = opng__MIN__(elt, OPNG_BITSET_ELT_MAX + 1) - 1; i >= 0; --i)
116 {
117 if (opng_bitset_test(set, i))
118 return i;
119 }
120 return -1;
121 }
122
123 /*
124 * Parses a rangeset string and converts the result to a bitset.
125 */
126 int
opng_strparse_rangeset_to_bitset(opng_bitset_t * out_set,const char * rangeset_str,opng_bitset_t mask_set)127 opng_strparse_rangeset_to_bitset(opng_bitset_t *out_set,
128 const char *rangeset_str,
129 opng_bitset_t mask_set)
130 {
131 opng_bitset_t result;
132 const char *ptr;
133 int state;
134 int num, num1, num2;
135 int err_invalid, err_range;
136
137 result = OPNG_BITSET_EMPTY;
138 ptr = rangeset_str;
139 state = 0;
140 err_invalid = err_range = 0;
141 num1 = num2 = -1;
142
143 for ( ; ; )
144 {
145 opng__SPAN__(ptr, isspace);
146 switch (state)
147 {
148 case 0: /* "" */
149 case 2: /* "N-" */
150 /* Expecting number; go to next state. */
151 if (*ptr >= '0' && *ptr <= '9')
152 {
153 num = 0;
154 do
155 {
156 num = 10 * num + (*ptr - '0');
157 if (num > OPNG_BITSET_ELT_MAX)
158 {
159 num = OPNG_BITSET_ELT_MAX;
160 err_range = 1;
161 }
162 ++ptr;
163 } while (*ptr >= '0' && *ptr <= '9');
164 if (!opng_bitset_test(mask_set, num))
165 err_range = 1;
166 if (state == 0)
167 num1 = num;
168 num2 = num;
169 ++state;
170 continue;
171 }
172 break;
173 case 1: /* "N" */
174 /* Expecting range operator; go to next state. */
175 if (*ptr == '-')
176 {
177 ++ptr;
178 num2 = OPNG_BITSET_ELT_MAX;
179 ++state;
180 continue;
181 }
182 break;
183 }
184
185 if (state > 0) /* "N", "N-" or "N-N" */
186 {
187 /* Store the partial result; go to state 0. */
188 if (num1 <= num2)
189 {
190 opng_bitset_set_range(&result, num1, num2);
191 result &= mask_set;
192 }
193 else
194 {
195 /* Incorrect range operands. */
196 err_range = 1;
197 }
198 state = 0;
199 }
200
201 if (*ptr == ',' || *ptr == ';')
202 {
203 /* Separator: continue the loop. */
204 ++ptr;
205 continue;
206 }
207 else if (*ptr == '-')
208 {
209 /* Unexpected range operator: invalidate and exit the loop. */
210 err_invalid = 1;
211 break;
212 }
213 else
214 {
215 /* Unexpected character or end of string: exit the loop. */
216 break;
217 }
218 }
219
220 opng__SPAN__(ptr, isspace);
221 if (*ptr != '\0')
222 {
223 /* Unexpected trailing character: invalidate. */
224 err_invalid = 1;
225 }
226
227 if (err_invalid)
228 {
229 /* Invalid input error. */
230 #ifdef EINVAL
231 errno = EINVAL;
232 #endif
233 *out_set = OPNG_BITSET_EMPTY;
234 return -1;
235 }
236 else if (err_range)
237 {
238 /* Range error. */
239 #ifdef ERANGE
240 errno = ERANGE;
241 #endif
242 *out_set = OPNG_BITSET_FULL;
243 return -1;
244 }
245 else
246 {
247 /* Success. */
248 *out_set = result;
249 return 0;
250 }
251 }
252
253 /*
254 * Formats a bitset using the rangeset string representation.
255 */
256 size_t
257 opng_strformat_bitset_as_rangeset(char *out_buf,
258 size_t out_buf_size,
259 opng_bitset_t bitset);
260 /* TODO */
261