1/**************************************************************************
2 *
3 * Copyright 2009 VMware, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28/**
29 * @file
30 * Generic bitmask implementation.
31 *
32 * @author Jose Fonseca <jfonseca@vmware.com>
33 */
34
35
36#include "pipe/p_compiler.h"
37#include "util/u_debug.h"
38
39#include "util/u_memory.h"
40#include "util/u_bitmask.h"
41
42
43typedef uint32_t util_bitmask_word;
44
45
46#define UTIL_BITMASK_INITIAL_WORDS 16
47#define UTIL_BITMASK_BITS_PER_BYTE 8
48#define UTIL_BITMASK_BITS_PER_WORD (sizeof(util_bitmask_word) * UTIL_BITMASK_BITS_PER_BYTE)
49
50
51struct util_bitmask
52{
53   util_bitmask_word *words;
54
55   /** Number of bits we can currently hold */
56   unsigned size;
57
58   /** Number of consecutive bits set at the start of the bitmask */
59   unsigned filled;
60};
61
62
63struct util_bitmask *
64util_bitmask_create(void)
65{
66   struct util_bitmask *bm;
67
68   bm = MALLOC_STRUCT(util_bitmask);
69   if (!bm)
70      return NULL;
71
72   bm->words = (util_bitmask_word *)
73      CALLOC(UTIL_BITMASK_INITIAL_WORDS, sizeof(util_bitmask_word));
74   if (!bm->words) {
75      FREE(bm);
76      return NULL;
77   }
78
79   bm->size = UTIL_BITMASK_INITIAL_WORDS * UTIL_BITMASK_BITS_PER_WORD;
80   bm->filled = 0;
81
82   return bm;
83}
84
85
86/**
87 * Resize the bitmask if necessary
88 */
89static inline boolean
90util_bitmask_resize(struct util_bitmask *bm,
91                    unsigned minimum_index)
92{
93   const unsigned minimum_size = minimum_index + 1;
94   unsigned new_size;
95   util_bitmask_word *new_words;
96
97   /* Check integer overflow */
98   if (!minimum_size)
99      return FALSE;
100
101   if (bm->size >= minimum_size)
102      return TRUE;
103
104   assert(bm->size % UTIL_BITMASK_BITS_PER_WORD == 0);
105   new_size = bm->size;
106   while (new_size < minimum_size) {
107      new_size *= 2;
108      /* Check integer overflow */
109      if (new_size < bm->size)
110         return FALSE;
111   }
112   assert(new_size);
113   assert(new_size % UTIL_BITMASK_BITS_PER_WORD == 0);
114
115   new_words = (util_bitmask_word *)
116      REALLOC((void *)bm->words,
117              bm->size / UTIL_BITMASK_BITS_PER_BYTE,
118              new_size / UTIL_BITMASK_BITS_PER_BYTE);
119   if (!new_words)
120      return FALSE;
121
122   memset(new_words + bm->size/UTIL_BITMASK_BITS_PER_WORD,
123          0,
124          (new_size - bm->size)/UTIL_BITMASK_BITS_PER_BYTE);
125
126   bm->size = new_size;
127   bm->words = new_words;
128
129   return TRUE;
130}
131
132
133/**
134 * Check if we can increment the filled counter.
135 */
136static inline void
137util_bitmask_filled_set(struct util_bitmask *bm,
138                        unsigned index)
139{
140   assert(bm->filled <= bm->size);
141   assert(index < bm->size);
142
143   if (index == bm->filled) {
144      ++bm->filled;
145      assert(bm->filled <= bm->size);
146   }
147}
148
149
150/**
151 * Check if we need to decrement the filled counter.
152 */
153static inline void
154util_bitmask_filled_unset(struct util_bitmask *bm,
155                          unsigned index)
156{
157   assert(bm->filled <= bm->size);
158   assert(index < bm->size);
159
160   if (index < bm->filled)
161      bm->filled = index;
162}
163
164
165unsigned
166util_bitmask_add(struct util_bitmask *bm)
167{
168   unsigned word;
169   unsigned bit;
170   util_bitmask_word mask;
171
172   assert(bm);
173
174   /* linear search for an empty index, starting at filled position */
175   word = bm->filled / UTIL_BITMASK_BITS_PER_WORD;
176   bit  = bm->filled % UTIL_BITMASK_BITS_PER_WORD;
177   mask = 1 << bit;
178   while (word < bm->size / UTIL_BITMASK_BITS_PER_WORD) {
179      while (bit < UTIL_BITMASK_BITS_PER_WORD) {
180         if (!(bm->words[word] & mask))
181            goto found;
182         ++bm->filled;
183         ++bit;
184         mask <<= 1;
185      }
186      ++word;
187      bit = 0;
188      mask = 1;
189   }
190found:
191
192   /* grow the bitmask if necessary */
193   if (!util_bitmask_resize(bm, bm->filled))
194      return UTIL_BITMASK_INVALID_INDEX;
195
196   assert(!(bm->words[word] & mask));
197   bm->words[word] |= mask;
198
199   return bm->filled++;
200}
201
202
203unsigned
204util_bitmask_set(struct util_bitmask *bm,
205                 unsigned index)
206{
207   unsigned word;
208   unsigned bit;
209   util_bitmask_word mask;
210
211   assert(bm);
212
213   /* grow the bitmask if necessary */
214   if (!util_bitmask_resize(bm, index))
215      return UTIL_BITMASK_INVALID_INDEX;
216
217   word = index / UTIL_BITMASK_BITS_PER_WORD;
218   bit  = index % UTIL_BITMASK_BITS_PER_WORD;
219   mask = 1 << bit;
220
221   bm->words[word] |= mask;
222
223   util_bitmask_filled_set(bm, index);
224
225   return index;
226}
227
228
229void
230util_bitmask_clear(struct util_bitmask *bm,
231                   unsigned index)
232{
233   unsigned word;
234   unsigned bit;
235   util_bitmask_word mask;
236
237   assert(bm);
238
239   if (index >= bm->size)
240      return;
241
242   word = index / UTIL_BITMASK_BITS_PER_WORD;
243   bit  = index % UTIL_BITMASK_BITS_PER_WORD;
244   mask = 1 << bit;
245
246   bm->words[word] &= ~mask;
247
248   util_bitmask_filled_unset(bm, index);
249}
250
251
252boolean
253util_bitmask_get(struct util_bitmask *bm,
254                 unsigned index)
255{
256   const unsigned word = index / UTIL_BITMASK_BITS_PER_WORD;
257   const unsigned bit  = index % UTIL_BITMASK_BITS_PER_WORD;
258   const util_bitmask_word mask = 1 << bit;
259
260   assert(bm);
261
262   if (index < bm->filled) {
263      assert(bm->words[word] & mask);
264      return TRUE;
265   }
266
267   if (index >= bm->size)
268      return FALSE;
269
270   if (bm->words[word] & mask) {
271      util_bitmask_filled_set(bm, index);
272      return TRUE;
273   }
274   else
275      return FALSE;
276}
277
278
279unsigned
280util_bitmask_get_next_index(struct util_bitmask *bm,
281                            unsigned index)
282{
283   unsigned word = index / UTIL_BITMASK_BITS_PER_WORD;
284   unsigned bit  = index % UTIL_BITMASK_BITS_PER_WORD;
285   util_bitmask_word mask = 1 << bit;
286
287   if (index < bm->filled) {
288      assert(bm->words[word] & mask);
289      return index;
290   }
291
292   if (index >= bm->size) {
293      return UTIL_BITMASK_INVALID_INDEX;
294   }
295
296   /* Do a linear search */
297   while (word < bm->size / UTIL_BITMASK_BITS_PER_WORD) {
298      while (bit < UTIL_BITMASK_BITS_PER_WORD) {
299         if (bm->words[word] & mask) {
300            if (index == bm->filled) {
301               ++bm->filled;
302               assert(bm->filled <= bm->size);
303            }
304            return index;
305         }
306         ++index;
307         ++bit;
308         mask <<= 1;
309      }
310      ++word;
311      bit = 0;
312      mask = 1;
313   }
314
315   return UTIL_BITMASK_INVALID_INDEX;
316}
317
318
319unsigned
320util_bitmask_get_first_index(struct util_bitmask *bm)
321{
322   return util_bitmask_get_next_index(bm, 0);
323}
324
325
326void
327util_bitmask_destroy(struct util_bitmask *bm)
328{
329   if (bm) {
330      FREE(bm->words);
331      FREE(bm);
332   }
333}
334