xref: /illumos-gate/usr/src/uts/common/os/bitset.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/bitset.h>
27 #include <sys/kmem.h>
28 #include <sys/systm.h>
29 #include <sys/cpuvar.h>
30 #include <sys/cmn_err.h>
31 #include <sys/sysmacros.h>
32 
33 /*
34  * Initialize a bitset_t.
35  * After bitset_init(), the bitset will be zero sized.
36  */
37 void
38 bitset_init(bitset_t *b)
39 {
40 	bzero(b, sizeof (bitset_t));
41 }
42 
43 /*
44  * Uninitialize a bitset_t.
45  * This will free the bitset's data, leaving it zero sized.
46  */
47 void
48 bitset_fini(bitset_t *b)
49 {
50 	if (b->bs_words > 0)
51 		kmem_free(b->bs_set, b->bs_words * sizeof (ulong_t));
52 }
53 
54 /*
55  * Resize a bitset to where it can hold sz number of bits.
56  * This can either grow or shrink the bitset holding capacity.
57  * In the case of shrinkage, elements that reside outside the new
58  * holding capacity of the bitset are lost.
59  */
60 void
61 bitset_resize(bitset_t *b, uint_t sz)
62 {
63 	uint_t	nwords;
64 	ulong_t	*bset_new, *bset_tmp;
65 
66 	nwords = BT_BITOUL(sz);
67 	if (b->bs_words == nwords)
68 		return;	/* already properly sized */
69 
70 	/*
71 	 * Allocate the new ulong_t array, and copy the old one, if there
72 	 * was an old one.
73 	 */
74 	if (nwords > 0) {
75 		bset_new = kmem_zalloc(nwords * sizeof (ulong_t), KM_SLEEP);
76 		if (b->bs_words > 0)
77 			bcopy(b->bs_set, bset_new,
78 			    MIN(b->bs_words, nwords) * sizeof (ulong_t));
79 	} else {
80 		bset_new = NULL;
81 	}
82 
83 	/* swap out the old ulong_t array for new one */
84 	bset_tmp = b->bs_set;
85 	b->bs_set = bset_new;
86 
87 	/* free up the old array */
88 	if (b->bs_words > 0)
89 		kmem_free(bset_tmp, b->bs_words * sizeof (ulong_t));
90 	b->bs_words = nwords;
91 }
92 
93 /*
94  * Returns the current holding capacity of the bitset
95  */
96 uint_t
97 bitset_capacity(bitset_t *b)
98 {
99 	return (b->bs_words * BT_NBIPUL);
100 }
101 
102 /*
103  * Add and delete bits in the bitset.
104  *
105  * Adding a bit that is already set, and clearing a bit that's already clear
106  * is legal.
107  *
108  * Adding or deleting an element that falls outside the bitset's current
109  * holding capacity is illegal.
110  */
111 
112 /*
113  * Set a bit
114  */
115 void
116 bitset_add(bitset_t *b, uint_t elt)
117 {
118 	ASSERT(b->bs_words * BT_NBIPUL > elt);
119 
120 	BT_SET(b->bs_set, elt);
121 }
122 
123 /*
124  * Set a bit in an atomically safe way
125  */
126 void
127 bitset_atomic_add(bitset_t *b, uint_t elt)
128 {
129 	ASSERT(b->bs_words * BT_NBIPUL > elt);
130 
131 	BT_ATOMIC_SET(b->bs_set, elt);
132 }
133 
134 /*
135  * Atomically test that a given bit isn't set, and set it.
136  * Returns -1 if the bit was already set.
137  */
138 int
139 bitset_atomic_test_and_add(bitset_t *b, uint_t elt)
140 {
141 	int r;
142 
143 	ASSERT(b->bs_words * BT_NBIPUL > elt);
144 	BT_ATOMIC_SET_EXCL(b->bs_set, elt, r);
145 
146 	return (r);
147 }
148 
149 /*
150  * Clear a bit
151  */
152 void
153 bitset_del(bitset_t *b, uint_t elt)
154 {
155 	ASSERT(b->bs_words * BT_NBIPUL > elt);
156 
157 	BT_CLEAR(b->bs_set, elt);
158 }
159 
160 /*
161  * Clear a bit in an atomically safe way
162  */
163 void
164 bitset_atomic_del(bitset_t *b, uint_t elt)
165 {
166 	ASSERT(b->bs_words * BT_NBIPUL > elt);
167 
168 	BT_ATOMIC_CLEAR(b->bs_set, elt);
169 }
170 
171 /*
172  * Atomically test that a bit is set, and clear it.
173  * Returns -1 if the bit was already clear.
174  */
175 int
176 bitset_atomic_test_and_del(bitset_t *b, uint_t elt)
177 {
178 	int r;
179 
180 	ASSERT(b->bs_words * BT_NBIPUL > elt);
181 	BT_ATOMIC_CLEAR_EXCL(b->bs_set, elt, r);
182 
183 	return (r);
184 }
185 
186 /*
187  * Return non-zero if the bit is present in the set
188  */
189 int
190 bitset_in_set(bitset_t *b, uint_t elt)
191 {
192 	if (elt >= b->bs_words * BT_NBIPUL)
193 		return (0);
194 
195 	return (BT_TEST(b->bs_set, elt));
196 }
197 
198 /*
199  * Return non-zero if the bitset is empty
200  */
201 int
202 bitset_is_null(bitset_t *b)
203 {
204 	int	i;
205 
206 	for (i = 0; i < b->bs_words; i++)
207 		if (b->bs_set[i] != 0)
208 			return (0);
209 	return (1);
210 }
211 
212 /*
213  * Perform a non-victimizing search for a set bit in a word
214  * A "seed" is passed to pseudo-randomize the search.
215  * Return -1 if no set bit was found
216  */
217 static uint_t
218 bitset_find_in_word(ulong_t w, uint_t seed)
219 {
220 	uint_t rotate_bit, elt = (uint_t)-1;
221 	ulong_t rotated_word;
222 
223 	if (w == (ulong_t)0)
224 		return (elt);
225 
226 	rotate_bit = seed % BT_NBIPUL;
227 	rotated_word = (w >> rotate_bit) | (w << (BT_NBIPUL - rotate_bit));
228 	elt = (uint_t)(lowbit(rotated_word) - 1);
229 	if (elt != (uint_t)-1)
230 		elt = ((elt + rotate_bit) % BT_NBIPUL);
231 
232 	return (elt);
233 }
234 
235 /*
236  * Select a bit that is set in the bitset in a non-victimizing fashion
237  * (e.g. doesn't bias the low/high order bits/words).
238  * Return -1 if no set bit was found
239  */
240 uint_t
241 bitset_find(bitset_t *b)
242 {
243 	uint_t start, i;
244 	uint_t elt = (uint_t)-1;
245 	uint_t seed;
246 
247 	seed = CPU_PSEUDO_RANDOM();
248 	start = seed % b->bs_words;
249 
250 	i = start;
251 	do {
252 		elt = bitset_find_in_word(b->bs_set[i], seed);
253 		if (elt != (uint_t)-1) {
254 			elt += i * BT_NBIPUL;
255 			break;
256 		}
257 		if (++i == b->bs_words)
258 			i = 0;
259 	} while (i != start);
260 
261 	return (elt);
262 }
263 
264 /*
265  * AND, OR, and XOR bitset computations
266  * Returns 1 if resulting bitset has any set bits
267  */
268 int
269 bitset_and(bitset_t *bs1, bitset_t *bs2, bitset_t *res)
270 {
271 	int i, anyset;
272 
273 	for (anyset = 0, i = 0; i < bs1->bs_words; i++) {
274 		if ((res->bs_set[i] = (bs1->bs_set[i] & bs2->bs_set[i])) != 0)
275 			anyset = 1;
276 	}
277 	return (anyset);
278 }
279 
280 int
281 bitset_or(bitset_t *bs1, bitset_t *bs2, bitset_t *res)
282 {
283 	int i, anyset;
284 
285 	for (anyset = 0, i = 0; i < bs1->bs_words; i++) {
286 		if ((res->bs_set[i] = (bs1->bs_set[i] | bs2->bs_set[i])) != 0)
287 			anyset = 1;
288 	}
289 	return (anyset);
290 }
291 
292 int
293 bitset_xor(bitset_t *bs1, bitset_t *bs2, bitset_t *res)
294 {
295 	int i;
296 	int anyset = 0;
297 
298 	for (i = 0; i < bs1->bs_words; i++) {
299 		if ((res->bs_set[i] = (bs1->bs_set[i] ^ bs2->bs_set[i])) != 0)
300 			anyset = 1;
301 	}
302 	return (anyset);
303 }
304 
305 /*
306  * return 1 if bitmaps are identical
307  */
308 int
309 bitset_match(bitset_t *bs1, bitset_t *bs2)
310 {
311 	int i;
312 
313 	if (bs1->bs_words != bs2->bs_words)
314 		return (0);
315 
316 	for (i = 0; i < bs1->bs_words; i++)
317 		if (bs1->bs_set[i] != bs2->bs_set[i])
318 			return (0);
319 	return (1);
320 }
321 
322 /*
323  * Zero a bitset_t
324  */
325 void
326 bitset_zero(bitset_t *b)
327 {
328 	bzero(b->bs_set, sizeof (ulong_t) * b->bs_words);
329 }
330 
331 
332 /*
333  * Copy a bitset_t
334  */
335 void
336 bitset_copy(bitset_t *src, bitset_t *dest)
337 {
338 	bcopy(src->bs_set, dest->bs_set, sizeof (ulong_t) * src->bs_words);
339 }
340