| /* |
| * Copyright © 2025 Valve Corporation |
| * |
| * SPDX-License-Identifier: MIT |
| */ |
| |
| #ifndef _UTIL_SPARSE_BITSET_H |
| #define _UTIL_SPARSE_BITSET_H |
| |
| #include "rb_tree.h" |
| #include "bitset.h" |
| #include "ralloc.h" |
| |
| #define U_SPARSE_BITSET_LOG2_BITS_PER_NODE 10 |
| #define U_SPARSE_BITSET_BITS_PER_NODE (1u << U_SPARSE_BITSET_LOG2_BITS_PER_NODE) |
| #define U_SPARSE_BITSET_BIT_INDEX_MASK (U_SPARSE_BITSET_BITS_PER_NODE - 1) |
| #define U_SPARSE_BITSET_OFFSET_MASK (~U_SPARSE_BITSET_BIT_INDEX_MASK) |
| |
| /* Sets under this # of bits use small-set optimization */ |
| #define U_SPARSE_BITSET_SMALL_SET_THRESHOLD 0 |
| |
| struct u_sparse_bitset_node { |
| struct rb_node node; |
| |
| /* The first bit covered by this node */ |
| unsigned offset; |
| BITSET_DECLARE(vals, U_SPARSE_BITSET_BITS_PER_NODE); |
| }; |
| |
| #define to_u_sparse_bitset_node(rb_node) \ |
| rb_node_data(struct u_sparse_bitset_node, rb_node, node) |
| |
| /* |
| * sparse_bitset wraps around a rb_tree of bitset nodes. |
| * |
| * Using sparse_bitset over a regular bitset is advantageous when you have a |
| * large number of potentially-set bits, but expect most of them to be zero |
| * (with the set bits mostly being within small, scattered regions). |
| * |
| * By default, bits are assumed to be unset. Areas that have set bits are |
| * represented by nodes in the rb_tree. One node represents a fixed-size bit |
| * range (internally with a non-sparse bitset of U_SPARSE_BITSET_BITS_PER_NODE). |
| */ |
| struct u_sparse_bitset { |
| union { |
| /* Large set - rb_tree of bitset nodes */ |
| struct { |
| void *mem_ctx; |
| struct rb_tree tree; |
| }; |
| |
| /* Small set optimization - bitset on heap */ |
| BITSET_WORD *vals; |
| }; |
| |
| /* Capacity of a small set, or 0 to indicate a large set */ |
| unsigned capacity; |
| }; |
| |
| static inline bool |
| _u_sparse_bitset_is_small(const struct u_sparse_bitset *s) |
| { |
| return s->capacity != 0; |
| } |
| |
| static inline void |
| u_sparse_bitset_init(struct u_sparse_bitset *s, unsigned capacity, |
| void *mem_ctx) |
| { |
| if (capacity && capacity < U_SPARSE_BITSET_SMALL_SET_THRESHOLD) { |
| s->vals = BITSET_RZALLOC(mem_ctx, capacity); |
| s->capacity = capacity; |
| } else { |
| rb_tree_init(&s->tree); |
| s->mem_ctx = mem_ctx; |
| s->capacity = 0; |
| } |
| } |
| |
| static inline int |
| _u_sparse_bitset_node_comparator(const struct rb_node *a, unsigned offset_b) |
| { |
| unsigned offset_a = to_u_sparse_bitset_node(a)->offset; |
| return (offset_a < offset_b) - (offset_a > offset_b); |
| } |
| |
| static inline int |
| _u_sparse_bitset_node_compare(const struct rb_node *a, const struct rb_node *b) |
| { |
| unsigned offs_b = to_u_sparse_bitset_node(b)->offset; |
| return _u_sparse_bitset_node_comparator(a, offs_b); |
| } |
| |
| static inline int |
| _u_sparse_bitset_node_search(const struct rb_node *node, const void *key) |
| { |
| return _u_sparse_bitset_node_comparator(node, (unsigned)(uintptr_t)key); |
| } |
| |
| static inline struct u_sparse_bitset_node * |
| _u_sparse_bitset_get_node(struct u_sparse_bitset *s, unsigned offset) |
| { |
| assert(!_u_sparse_bitset_is_small(s)); |
| |
| struct rb_node *node = rb_tree_search(&s->tree, (void *)(uintptr_t)offset, |
| _u_sparse_bitset_node_search); |
| if (!node) |
| return NULL; |
| |
| return rb_node_data(struct u_sparse_bitset_node, node, node); |
| } |
| |
| static inline struct u_sparse_bitset_node * |
| _u_sparse_bitset_get_or_add_node(struct u_sparse_bitset *s, unsigned offset) |
| { |
| assert(!_u_sparse_bitset_is_small(s)); |
| assert((offset & U_SPARSE_BITSET_BIT_INDEX_MASK) == 0); |
| |
| struct u_sparse_bitset_node *node = _u_sparse_bitset_get_node(s, offset); |
| if (!node) { |
| node = rzalloc(s->mem_ctx, struct u_sparse_bitset_node); |
| node->offset = offset; |
| rb_tree_insert(&s->tree, &node->node, _u_sparse_bitset_node_compare); |
| } |
| |
| return node; |
| } |
| |
| static inline void |
| u_sparse_bitset_set(struct u_sparse_bitset *s, unsigned bit) |
| { |
| if (_u_sparse_bitset_is_small(s)) { |
| assert(bit < s->capacity); |
| BITSET_SET(s->vals, bit); |
| } else { |
| struct u_sparse_bitset_node *node = |
| _u_sparse_bitset_get_or_add_node(s, bit & U_SPARSE_BITSET_OFFSET_MASK); |
| |
| BITSET_SET(node->vals, bit & U_SPARSE_BITSET_BIT_INDEX_MASK); |
| } |
| } |
| |
| static inline void |
| u_sparse_bitset_clear(struct u_sparse_bitset *s, unsigned bit) |
| { |
| if (_u_sparse_bitset_is_small(s)) { |
| assert(bit < s->capacity); |
| BITSET_CLEAR(s->vals, bit); |
| } else { |
| struct u_sparse_bitset_node *node = |
| _u_sparse_bitset_get_node(s, bit & U_SPARSE_BITSET_OFFSET_MASK); |
| |
| if (node) |
| BITSET_CLEAR(node->vals, bit & U_SPARSE_BITSET_BIT_INDEX_MASK); |
| } |
| } |
| |
| static inline bool |
| u_sparse_bitset_test(struct u_sparse_bitset *s, unsigned bit) |
| { |
| if (_u_sparse_bitset_is_small(s)) { |
| assert(bit < s->capacity); |
| return BITSET_TEST(s->vals, bit); |
| } else { |
| struct u_sparse_bitset_node *node = |
| _u_sparse_bitset_get_node(s, bit & U_SPARSE_BITSET_OFFSET_MASK); |
| |
| return node != NULL && |
| BITSET_TEST(node->vals, bit & U_SPARSE_BITSET_BIT_INDEX_MASK); |
| } |
| } |
| |
| static inline int |
| u_sparse_bitset_cmp(struct u_sparse_bitset *a, struct u_sparse_bitset *b) |
| { |
| assert(a->capacity == b->capacity); |
| |
| if (_u_sparse_bitset_is_small(a)) { |
| return memcmp(a->vals, b->vals, BITSET_BYTES(a->capacity)); |
| } |
| |
| struct rb_node *a_iter = rb_tree_first(&a->tree); |
| struct rb_node *b_iter = rb_tree_first(&b->tree); |
| |
| while (a_iter && b_iter) { |
| struct u_sparse_bitset_node *node_a = to_u_sparse_bitset_node(a_iter); |
| struct u_sparse_bitset_node *node_b = to_u_sparse_bitset_node(b_iter); |
| |
| int node_cmp = _u_sparse_bitset_node_compare(a_iter, b_iter); |
| if (node_cmp) |
| return node_cmp; |
| |
| int cmp_res = memcmp(node_a->vals, node_b->vals, sizeof(node_a->vals)); |
| if (cmp_res) |
| return cmp_res; |
| |
| a_iter = rb_node_next(a_iter); |
| b_iter = rb_node_next(b_iter); |
| } |
| |
| return (a_iter != NULL) - (b_iter != NULL); |
| } |
| |
| static inline void |
| u_sparse_bitset_dup_with_ctx(struct u_sparse_bitset *dst, |
| struct u_sparse_bitset *src, void *mem_ctx) |
| { |
| u_sparse_bitset_init(dst, src->capacity, mem_ctx); |
| |
| if (_u_sparse_bitset_is_small(src)) { |
| memcpy(dst->vals, src->vals, BITSET_BYTES(src->capacity)); |
| return; |
| } |
| |
| rb_tree_foreach(struct u_sparse_bitset_node, node, &src->tree, node) { |
| if (BITSET_IS_EMPTY(node->vals)) |
| continue; |
| |
| struct u_sparse_bitset_node *dst_node = |
| (struct u_sparse_bitset_node *)ralloc_memdup( |
| mem_ctx, node, sizeof(struct u_sparse_bitset_node)); |
| |
| rb_tree_insert(&dst->tree, &dst_node->node, |
| _u_sparse_bitset_node_compare); |
| } |
| } |
| |
| static inline void |
| u_sparse_bitset_dup(struct u_sparse_bitset *dst, struct u_sparse_bitset *src) |
| { |
| u_sparse_bitset_dup_with_ctx(dst, src, src->mem_ctx); |
| } |
| |
| static inline bool |
| _u_bitset_merge(BITSET_WORD *dst, const BITSET_WORD *src, unsigned words) |
| { |
| bool changed = false; |
| |
| for (unsigned i = 0; i < words; i++) { |
| changed |= (bool)(src[i] & ~dst[i]); |
| dst[i] |= src[i]; |
| } |
| |
| return changed; |
| } |
| |
| static inline bool |
| u_sparse_bitset_merge(struct u_sparse_bitset *dst, struct u_sparse_bitset *src) |
| { |
| bool changed = false; |
| assert(dst->capacity == src->capacity); |
| |
| if (_u_sparse_bitset_is_small(src)) { |
| return _u_bitset_merge(dst->vals, src->vals, BITSET_WORDS(src->capacity)); |
| } |
| |
| rb_tree_foreach(struct u_sparse_bitset_node, node, &src->tree, node) { |
| if (!BITSET_IS_EMPTY(node->vals)) { |
| struct u_sparse_bitset_node *dst_node = |
| _u_sparse_bitset_get_or_add_node(dst, node->offset); |
| |
| changed |= |
| _u_bitset_merge(dst_node->vals, node->vals, ARRAY_SIZE(node->vals)); |
| } |
| } |
| |
| return changed; |
| } |
| |
| static inline unsigned |
| u_sparse_bitset_count(struct u_sparse_bitset *s) |
| { |
| if (_u_sparse_bitset_is_small(s)) { |
| return __bitset_count(s->vals, s->capacity); |
| } else { |
| unsigned sum = 0; |
| |
| rb_tree_foreach_safe(struct u_sparse_bitset_node, node, &s->tree, node) { |
| sum += BITSET_COUNT(node->vals); |
| } |
| |
| return sum; |
| } |
| } |
| |
| static inline void |
| u_sparse_bitset_free(struct u_sparse_bitset *s) |
| { |
| if (_u_sparse_bitset_is_small(s)) { |
| ralloc_free(s->vals); |
| } else { |
| rb_tree_foreach_safe(struct u_sparse_bitset_node, node, &s->tree, node) { |
| rb_tree_remove(&s->tree, &node->node); |
| ralloc_free(node); |
| } |
| } |
| } |
| |
| static inline unsigned |
| _u_sparse_bitset_next_set_dense(BITSET_WORD *set, unsigned size, unsigned from) |
| { |
| /* Check if there even is a first node */ |
| if (from >= size) { |
| return UINT_MAX; |
| } |
| |
| unsigned i = BITSET_BITWORD(from); |
| unsigned offset = from % BITSET_WORDBITS; |
| |
| /* Check for a next bit in the first node */ |
| if (set[i] >> offset) { |
| return from + ffs(set[i] >> offset) - 1; |
| } |
| |
| /* Else look for the next node */ |
| for (i++; i < BITSET_WORDS(size); ++i) { |
| if (set[i]) { |
| return (i * BITSET_WORDBITS) + ffs(set[i]) - 1; |
| } |
| } |
| |
| return UINT_MAX; |
| } |
| |
| static inline unsigned |
| _u_sparse_bitset_next_set(const struct u_sparse_bitset *s, uintptr_t *node_, |
| const unsigned from) |
| { |
| unsigned ret = UINT_MAX; |
| |
| if (_u_sparse_bitset_is_small(s)) { |
| unsigned i = _u_sparse_bitset_next_set_dense(s->vals, s->capacity, from); |
| if (i < s->capacity) { |
| ret = i; |
| } |
| } else { |
| unsigned node_from = from; |
| |
| /* Look for the next set bit in the current node */ |
| for (struct rb_node *rb = (struct rb_node *)*node_; rb != NULL; |
| rb = rb_node_next(rb), node_from = 0) { |
| |
| struct u_sparse_bitset_node *it = to_u_sparse_bitset_node(rb); |
| unsigned num = U_SPARSE_BITSET_BITS_PER_NODE; |
| |
| /* Deal with from at the end of the node */ |
| if (node_from != 0 && |
| (node_from - it->offset) >= U_SPARSE_BITSET_BITS_PER_NODE) { |
| continue; |
| } |
| |
| node_from &= U_SPARSE_BITSET_BIT_INDEX_MASK; |
| |
| unsigned i = _u_sparse_bitset_next_set_dense(it->vals, num, node_from); |
| |
| if (i < num) { |
| *node_ = (uintptr_t)rb; |
| ret = it->offset + i; |
| break; |
| } |
| } |
| } |
| |
| assert(from <= ret); |
| return ret; |
| } |
| |
| static inline unsigned |
| _u_sparse_bitset_first_set(struct u_sparse_bitset *s, uintptr_t *node_) |
| { |
| uintptr_t node = 0; |
| if (!_u_sparse_bitset_is_small(s)) { |
| node = (uintptr_t)rb_tree_first(&s->tree); |
| } |
| |
| unsigned ret = _u_sparse_bitset_next_set(s, &node, 0); |
| *node_ = node; |
| return ret; |
| } |
| |
| #define U_SPARSE_BITSET_FOREACH_SET(s, it) \ |
| for (uintptr_t _node, it = _u_sparse_bitset_first_set((s), &_node); \ |
| it != UINT_MAX; it = _u_sparse_bitset_next_set((s), &_node, it + 1)) |
| |
| #endif //_UTIL_SPARSE_BITSET_H |