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