iscsi tools: handle compile warnings about unused variables
[open-iscsi.git] / utils / open-isns / bitvector.c
blob3e23f269c9ce91c28adffc3a1d7c6ae663cec479
1 /*
2 * Handle bit vector as a run length encoded array of
3 * 32bit words.
5 * Copyright (C) 2007 Olaf Kirch <olaf.kirch@oracle.com>
6 */
8 #include <stdlib.h>
9 #include <string.h>
10 #include "isns.h"
11 #include "util.h"
13 struct isns_bitvector {
14 unsigned int ib_count;
15 uint32_t * ib_words;
18 void
19 isns_bitvector_init(isns_bitvector_t *bv)
21 memset(bv, 0, sizeof(*bv));
24 void
25 isns_bitvector_destroy(isns_bitvector_t *bv)
27 isns_free(bv->ib_words);
28 memset(bv, 0, sizeof(*bv));
31 isns_bitvector_t *
32 isns_bitvector_alloc(void)
34 return isns_calloc(1, sizeof(isns_bitvector_t));
37 void
38 isns_bitvector_free(isns_bitvector_t *bv)
40 if (bv) {
41 isns_free(bv->ib_words);
42 memset(bv, 0xa5, sizeof(*bv));
43 isns_free(bv);
48 * Helper function to locate bit
50 uint32_t *
51 __isns_bitvector_find_word(const isns_bitvector_t *bv, unsigned int bit)
53 uint32_t *wp, *end;
55 if (bv->ib_words == NULL)
56 return NULL;
58 wp = bv->ib_words;
59 end = wp + bv->ib_count;
60 while (wp < end) {
61 unsigned int base, rlen;
63 base = wp[0];
64 rlen = wp[1];
66 isns_assert(!(base % 32));
67 if (base <= bit && bit < base + rlen * 32)
68 return wp + 2 + ((bit - base) / 32);
70 wp += 2 + rlen;
71 isns_assert(wp <= end);
74 return NULL;
78 * Insert words in the middle of the array
80 static inline void
81 __isns_bitvector_insert_words(isns_bitvector_t *bv,
82 unsigned int offset, unsigned int count)
84 bv->ib_words = isns_realloc(bv->ib_words,
85 (bv->ib_count + count) * sizeof(uint32_t));
87 /* If we insert in the middle, shift out the tail
88 * to make room for the new range. */
89 isns_assert(offset <= bv->ib_count);
90 if (offset < bv->ib_count) {
91 memmove(bv->ib_words + offset + count,
92 bv->ib_words + offset,
93 (bv->ib_count - offset) * sizeof(uint32_t));
96 memset(bv->ib_words + offset, 0, count * sizeof(uint32_t));
97 bv->ib_count += count;
101 * Insert a new range
103 static inline uint32_t *
104 __isns_bitvector_insert_range(isns_bitvector_t *bv,
105 unsigned int offset, unsigned int base)
107 uint32_t *pos;
109 __isns_bitvector_insert_words(bv, offset, 3);
111 pos = bv->ib_words + offset;
113 *pos++ = base & ~31;
114 *pos++ = 1;
116 return pos;
120 * Extend an existing range
121 * @offset marks the beginning of the existing range.
123 static inline uint32_t *
124 __isns_bitvector_extend_range(isns_bitvector_t *bv,
125 unsigned int offset, unsigned int count)
127 uint32_t *pos, rlen;
129 /* Find the end of the range */
130 pos = bv->ib_words + offset;
131 rlen = pos[1];
133 __isns_bitvector_insert_words(bv, offset + 2 + rlen, count);
135 pos = bv->ib_words + offset;
136 pos[1] += count;
138 /* Return pointer to the last word of the new range. */
139 return pos + 2 + rlen + count - 1;
143 * Find a suitable range for insertion
145 static uint32_t *
146 __isns_bitvector_find_insert_word(isns_bitvector_t *bv, unsigned int bit)
148 uint32_t *wp, *end;
150 if (bv->ib_words == NULL)
151 return __isns_bitvector_insert_range(bv, 0, bit);
153 wp = bv->ib_words;
154 end = wp + bv->ib_count;
155 while (wp < end) {
156 unsigned int base, rlen, distance;
158 base = wp[0];
159 rlen = wp[1];
161 isns_assert(!(base % 32));
163 if (bit < base) {
164 return __isns_bitvector_insert_range(bv,
165 wp - bv->ib_words, bit);
168 distance = (bit - base) / 32;
169 if (distance < rlen) {
170 /* This bit is within range */
171 return wp + 2 + distance;
174 /* Is it efficient to extend this range?
175 * The break even point is if we have to add
176 * 3 words to extend the range, because a new
177 * range would be at least that much.
179 if (distance + 1 <= rlen + 3) {
180 return __isns_bitvector_extend_range(bv,
181 wp - bv->ib_words,
182 distance + 1 - rlen);
185 wp += 2 + rlen;
186 isns_assert(wp <= end);
189 /* No suitable range found. Append one at the end */
190 return __isns_bitvector_insert_range(bv,
191 bv->ib_count, bit);
195 * After clearing a bit, check if the bitvector can be
196 * compacted.
198 static void
199 __isns_bitvector_compact(isns_bitvector_t *bv)
201 uint32_t *src, *dst, *end;
202 unsigned int dst_base = 0, dst_len = 0;
204 if (bv->ib_words == NULL)
205 return;
207 src = dst = bv->ib_words;
208 end = src + bv->ib_count;
209 while (src < end) {
210 unsigned int base, rlen;
212 base = *src++;
213 rlen = *src++;
215 /* Consume leading NUL words */
216 while (rlen && *src == 0) {
217 base += 32;
218 src++;
219 rlen--;
222 /* Consume trailing NUL words */
223 while (rlen && src[rlen-1] == 0)
224 rlen--;
226 if (rlen != 0) {
227 if (dst_len && dst_base + 32 * dst_len == base) {
228 /* We can extend the previous run */
229 } else {
230 /* New run. Close off the previous one,
231 * if we had one. */
232 if (dst_len != 0) {
233 dst[0] = dst_base;
234 dst[1] = dst_len;
235 dst += 2 + dst_len;
238 dst_base = base;
239 dst_len = 0;
242 while (rlen--)
243 dst[2 + dst_len++] = *src++;
246 isns_assert(src <= end);
250 if (dst_len != 0) {
251 dst[0] = dst_base;
252 dst[1] = dst_len;
253 dst += 2 + dst_len;
256 bv->ib_count = dst - bv->ib_words;
257 if (bv->ib_count == 0)
258 isns_bitvector_destroy(bv);
262 * Test the value of a single bit
265 isns_bitvector_test_bit(const isns_bitvector_t *bv, unsigned int bit)
267 const uint32_t *pos;
268 uint32_t mask;
270 pos = __isns_bitvector_find_word(bv, bit);
271 if (pos == NULL)
272 return 0;
274 mask = 1 << (bit % 32);
275 return !!(*pos & mask);
279 isns_bitvector_clear_bit(isns_bitvector_t *bv, unsigned int bit)
281 uint32_t *pos, oldval, mask;
283 pos = __isns_bitvector_find_word(bv, bit);
284 if (pos == NULL)
285 return 0;
287 mask = 1 << (bit % 32);
288 oldval = *pos;
289 *pos &= ~mask;
291 __isns_bitvector_compact(bv);
292 return !!(oldval & mask);
296 isns_bitvector_set_bit(isns_bitvector_t *bv, unsigned int bit)
298 uint32_t *pos, oldval = 0, mask;
300 mask = 1 << (bit % 32);
302 pos = __isns_bitvector_find_insert_word(bv, bit);
303 if (pos != NULL) {
304 oldval = *pos;
305 *pos |= mask;
307 return !!(oldval & mask);
310 return 0;
314 isns_bitvector_is_empty(const isns_bitvector_t *bv)
316 uint32_t *wp, *end;
318 if (bv == NULL || bv->ib_count == 0)
319 return 1;
321 /* In theory, we should never have a non-compacted
322 * empty bitvector, as the only way to get one
323 * is through clear_bit.
324 * Better safe than sorry...
327 wp = bv->ib_words;
328 end = wp + bv->ib_count;
329 while (wp < end) {
330 unsigned int base, rlen;
332 base = *wp++;
333 rlen = *wp++;
335 while (rlen--) {
336 if (*wp++)
337 return 0;
339 isns_assert(wp <= end);
342 return 1;
346 isns_bitvector_intersect(const isns_bitvector_t *a,
347 const isns_bitvector_t *b,
348 isns_bitvector_t *result)
350 const uint32_t *runa, *runb, *enda, *endb;
351 const uint32_t *wpa = NULL, *wpb = NULL;
352 uint32_t bita = 0, lena = 0, bitb = 0, lenb = 0;
353 int found = -1;
355 if (a == NULL || b == NULL)
356 return -1;
358 /* Returning the intersect is not implemented yet. */
359 isns_assert(result == NULL);
361 runa = a->ib_words;
362 enda = runa + a->ib_count;
363 runb = b->ib_words;
364 endb = runb + b->ib_count;
366 while (1) {
367 unsigned int skip;
369 if (lena == 0) {
370 next_a:
371 if (runa >= enda)
372 break;
373 bita = *runa++;
374 lena = *runa++;
375 wpa = runa;
376 runa += lena;
377 lena *= 32;
380 if (lenb == 0) {
381 next_b:
382 if (runb >= endb)
383 break;
384 bitb = *runb++;
385 lenb = *runb++;
386 wpb = runb;
387 runb += lenb;
388 lenb *= 32;
391 if (bita < bitb) {
392 skip = bitb - bita;
394 /* range A ends before range B starts.
395 * Proceed to next run in vector A. */
396 if (skip >= lena)
397 goto next_a;
399 bita += skip;
400 lena -= skip;
401 wpa += skip / 32;
402 } else
403 if (bitb < bita) {
404 skip = bita - bitb;
406 /* range B ends before range A starts.
407 * Proceed to next run in vector B. */
408 if (skip >= lenb)
409 goto next_b;
411 bitb += skip;
412 lenb -= skip;
413 wpb += skip / 32;
416 isns_assert(bita == bitb);
418 while (lena && lenb) {
419 uint32_t intersect;
421 intersect = *wpa & *wpb;
423 if (!intersect)
424 goto next_word;
426 /* Find the bit */
427 if (found < 0) {
428 uint32_t mask = intersect;
430 found = bita;
431 while (!(mask & 1)) {
432 found++;
433 mask >>= 1;
437 if (result == NULL)
438 return found;
440 /* Append to result vector */
441 /* FIXME: TBD */
443 next_word:
444 bita += 32; lena -= 32; wpa++;
445 bitb += 32; lenb -= 32; wpb++;
449 return found;
453 * Iterate over the bit vector
455 void
456 isns_bitvector_foreach(const isns_bitvector_t *bv,
457 int (*cb)(uint32_t, void *),
458 void *user_data)
460 uint32_t *wp, *end;
462 wp = bv->ib_words;
463 end = wp + bv->ib_count;
464 while (wp < end) {
465 unsigned int base, rlen, bits;
467 base = wp[0];
468 rlen = wp[1];
469 bits = rlen * 32;
470 wp += 2;
472 while (rlen--) {
473 uint32_t mask, word;
475 word = *wp++;
476 for (mask = 1; mask; mask <<= 1, ++base) {
477 if (word & mask)
478 cb(base, user_data);
481 isns_assert(wp <= end);
485 void
486 isns_bitvector_dump(const isns_bitvector_t *bv, isns_print_fn_t *fn)
488 uint32_t *wp, *end;
490 fn("Bit Vector %p (%u words):", bv, bv->ib_count);
492 wp = bv->ib_words;
493 end = wp + bv->ib_count;
494 while (wp < end) {
495 unsigned int base, rlen, bits;
497 base = wp[0];
498 rlen = wp[1];
499 bits = rlen * 32;
500 wp += 2;
502 fn(" <%u:", base);
503 while (rlen--)
504 fn(" 0x%x", *wp++);
505 fn(">");
507 isns_assert(wp <= end);
510 if (bv->ib_count == 0)
511 fn("<empty>");
512 fn("\n");
515 static inline void
516 __isns_bitvector_print_next(uint32_t first, uint32_t last,
517 isns_print_fn_t *fn)
519 switch (last - first) {
520 case 0:
521 return;
522 case 1:
523 fn(", %u", last);
524 break;
525 default:
526 fn("-%u", last);
527 break;
531 void
532 isns_bitvector_print(const isns_bitvector_t *bv,
533 isns_print_fn_t *fn)
535 uint32_t *wp, *end, first = 0, next = 0;
536 const char *sepa = "";
538 wp = bv->ib_words;
539 end = wp + bv->ib_count;
540 while (wp < end) {
541 unsigned int base, rlen, bits;
543 base = wp[0];
544 rlen = wp[1];
545 bits = rlen * 32;
546 wp += 2;
548 while (rlen--) {
549 uint32_t mask, word;
551 word = *wp++;
552 for (mask = 1; mask; mask <<= 1, ++base) {
553 if (word & mask) {
554 if (next++)
555 continue;
556 fn("%s%u", sepa, base);
557 sepa = ", ";
558 first = base;
559 next = base + 1;
560 } else {
561 if (next)
562 __isns_bitvector_print_next(first, next - 1, fn);
563 first = next = 0;
567 isns_assert(wp <= end);
570 if (next)
571 __isns_bitvector_print_next(first, next - 1, fn);
573 if (*sepa == '\0')
574 fn("<empty>");
575 fn("\n");
578 #ifdef TEST
580 main(void)
582 isns_bitvector_t a, b;
583 int i;
585 isns_bitvector_init(&a);
586 isns_bitvector_set_bit(&a, 0);
587 isns_bitvector_dump(&a, isns_print_stdout);
588 isns_bitvector_set_bit(&a, 1);
589 isns_bitvector_set_bit(&a, 16);
590 isns_bitvector_set_bit(&a, 32);
591 isns_bitvector_set_bit(&a, 64);
592 isns_bitvector_dump(&a, isns_print_stdout);
593 isns_bitvector_set_bit(&a, 8192);
594 isns_bitvector_set_bit(&a, 8196);
595 isns_bitvector_set_bit(&a, 8194);
596 isns_bitvector_dump(&a, isns_print_stdout);
597 isns_bitvector_set_bit(&a, 2052);
598 isns_bitvector_set_bit(&a, 2049);
599 isns_bitvector_set_bit(&a, 2051);
600 isns_bitvector_set_bit(&a, 2050);
601 isns_bitvector_dump(&a, isns_print_stdout);
602 isns_bitvector_print(&a, isns_print_stdout);
603 isns_bitvector_destroy(&a);
605 isns_bitvector_init(&a);
606 for (i = 127; i >= 0; --i)
607 isns_bitvector_set_bit(&a, i);
608 isns_bitvector_dump(&a, isns_print_stdout);
609 printf("[Compacting]\n");
610 __isns_bitvector_compact(&a);
611 isns_bitvector_dump(&a, isns_print_stdout);
612 isns_bitvector_print(&a, isns_print_stdout);
613 isns_bitvector_destroy(&a);
615 isns_bitvector_init(&a);
616 for (i = 0; i < 128; ++i)
617 isns_bitvector_set_bit(&a, i);
618 isns_bitvector_dump(&a, isns_print_stdout);
619 isns_bitvector_print(&a, isns_print_stdout);
620 isns_bitvector_destroy(&a);
622 isns_bitvector_init(&a);
623 isns_bitvector_init(&b);
624 isns_bitvector_set_bit(&a, 0);
625 isns_bitvector_set_bit(&a, 77);
626 isns_bitvector_set_bit(&a, 249);
627 isns_bitvector_set_bit(&a, 102);
629 isns_bitvector_set_bit(&b, 1);
630 isns_bitvector_set_bit(&b, 76);
631 isns_bitvector_set_bit(&b, 250);
632 isns_bitvector_set_bit(&b, 102);
633 i = isns_bitvector_intersect(&a, &b, NULL);
634 if (i != 102)
635 fprintf(stderr, "*** BAD: Intersect should return 102 (got %d)! ***\n", i);
636 else
637 printf("Intersect okay: %d\n", i);
638 isns_bitvector_destroy(&a);
639 isns_bitvector_destroy(&b);
641 isns_bitvector_init(&a);
642 isns_bitvector_set_bit(&a, 0);
643 isns_bitvector_set_bit(&a, 1);
644 isns_bitvector_clear_bit(&a, 1);
645 isns_bitvector_clear_bit(&a, 0);
646 isns_bitvector_dump(&a, isns_print_stdout);
647 isns_bitvector_print(&a, isns_print_stdout);
648 isns_bitvector_destroy(&a);
649 return 0;
651 #endif