summaryrefslogtreecommitdiff
path: root/accel-pppd/extra/bitpool.h
blob: fa739a2874ac9e5e6776da33ea4c43db1cdf2265 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#ifndef __BITPOOL_H
#define __BITPOOL_H

#include <stdint.h>
#include <stddef.h>

/*
 * Fixed-size bit array shared by the IPv4/IPv6 address pool allocators.
 *
 * Semantics: bit == 1 => allocated/unavailable, bit == 0 => free. A freshly
 * calloc'd map is therefore all-free. The caller is responsible for pre-setting
 * to 1 any bit it never wants handed out:
 *   - "remainder" bits in the final word (indices >= count), and
 *   - reserved in-range bits (e.g. the p2p gateway collision),
 * so the word-at-a-time scan can never return a non-existent unit.
 */

typedef unsigned long bm_word_t;
#define BM_WORD_BITS  (8 * sizeof(bm_word_t))
#define BM_NWORDS(n)  (((n) + BM_WORD_BITS - 1) / BM_WORD_BITS)
#define BM_INVALID    ((uint64_t)-1)

static inline void bm_set(bm_word_t *bm, uint64_t i)
{
	bm[i / BM_WORD_BITS] |= (bm_word_t)1 << (i % BM_WORD_BITS);
}

static inline void bm_clear(bm_word_t *bm, uint64_t i)
{
	bm[i / BM_WORD_BITS] &= ~((bm_word_t)1 << (i % BM_WORD_BITS));
}

static inline int bm_test(const bm_word_t *bm, uint64_t i)
{
	return (bm[i / BM_WORD_BITS] >> (i % BM_WORD_BITS)) & 1;
}

/*
 * Find the first free (0) bit at or after `from`, wrapping once back to 0.
 * `count` is the number of valid bits; remainder bits in the final word must
 * already be set to 1 by the caller so they are never returned. Returns the
 * bit index in [0,count), or BM_INVALID if every bit is used.
 *
 * Word-at-a-time: a fully-used word (~w == 0) is skipped in one branch, so the
 * scan is O(1) amortized for a sparse pool and O(count/word) worst case.
 */
static inline uint64_t bm_find_free(const bm_word_t *bm, uint64_t count, uint64_t from)
{
	uint64_t nwords, fw, i;
	unsigned fb;

	if (!count)
		return BM_INVALID;
	if (from >= count)
		from = 0;

	nwords = BM_NWORDS(count);
	fw = from / BM_WORD_BITS;
	fb = from % BM_WORD_BITS;

	/* Probe nwords+1 times: the start word is examined first with its low bits
	 * (< from) masked off, and once more at the end with only those low bits,
	 * so every bit is considered exactly once across the wrap. */
	for (i = 0; i <= nwords; i++) {
		uint64_t word = fw + i;
		bm_word_t inv;

		if (word >= nwords)
			word -= nwords;

		inv = ~bm[word];

		if (i == 0)
			inv &= ~(((bm_word_t)1 << fb) - 1);	/* skip bits below `from` */
		else if (i == nwords)
			inv &= ((bm_word_t)1 << fb) - 1;	/* wrapped: only those skipped bits */

		if (inv) {
			uint64_t bit = word * BM_WORD_BITS + __builtin_ctzl(inv);
			if (bit < count)
				return bit;
		}
	}

	return BM_INVALID;
}

#endif