diff --git a/src/util-bits.h b/src/util-bits.h index 28b9ddcc..3c5a21a9 100644 --- a/src/util-bits.h +++ b/src/util-bits.h @@ -28,10 +28,14 @@ #include "config.h" #include +#include #include #include #include #include +#include + +#include "util-macros.h" #define bit(x_) (1UL << (x_)) #define NBITS(b) (b * 8) @@ -238,3 +242,201 @@ _bitmask_from_bits(unsigned int bit1, ...) #define bitmask_from_bits(...) \ _bitmask_from_bits(__VA_ARGS__, 32) + +/* An infinite bitmask that grows as needed. + * + * Note that unlike the bitmask_t this struct contains + * pointers and some care must be taken when using assign-by-value. + */ +typedef struct { + bitmask_t *mask; + size_t nmasks; +} infmask_t; + +static inline size_t +_infmask_size_for_bit(unsigned int bit) +{ + return (bit / bitmask_size()) + 1; +} + +static inline void +_infmask_ensure_size(infmask_t *mask, unsigned int bit) +{ + size_t required = _infmask_size_for_bit(bit); + if (required > mask->nmasks) { + mask->mask = realloc(mask->mask, required * sizeof(bitmask_t)); + /* Zero out the new memory */ + for (size_t i = mask->nmasks; i < required; i++) + mask->mask[i] = bitmask_new(); + mask->nmasks = required; + } +} + +static inline infmask_t +infmask_new(void) +{ + infmask_t m = { .mask = NULL, .nmasks = 0 }; + return m; +} + +static inline void +infmask_reset(infmask_t *mask) +{ + free(mask->mask); + mask->mask = NULL; + mask->nmasks = 0; +} + +static inline void +infmask_destroy(infmask_t *mask) +{ + infmask_reset(mask); + free(mask); +} + +static inline bool +infmask_is_empty(const infmask_t *mask) +{ + if (!mask->mask) + return true; + + for (size_t i = 0; i < mask->nmasks; i++) + if (!bitmask_is_empty(mask->mask[i])) + return false; + return true; +} + +static inline bool +infmask_any(const infmask_t *mask, const infmask_t *bits) +{ + if (!mask->mask || !bits->mask) + return false; + + size_t min_size = min(mask->nmasks, bits->nmasks); + for (size_t i = 0; i < min_size; i++) + if (bitmask_any(mask->mask[i], bits->mask[i])) + return true; + return false; +} + +static inline bool +infmask_all(const infmask_t *mask, const infmask_t *bits) +{ + if (!bits->mask) + return true; + if (!mask->mask) + return false; + + size_t min_size = min(mask->nmasks, bits->nmasks); + for (size_t i = 0; i < min_size; i++) + if (!bitmask_all(mask->mask[i], bits->mask[i])) + return false; + + /* Check if bits has any bits set beyond min_size */ + for (size_t i = min_size; i < bits->nmasks; i++) + if (!bitmask_is_empty(bits->mask[i])) + return false; + + return true; +} + +static inline bool +infmask_merge(infmask_t *mask, const infmask_t *bits) +{ + if (!bits->mask) + return true; + + _infmask_ensure_size(mask, bits->nmasks * bitmask_size() - 1); + + bool all = true; + + for (size_t i = 0; i < bits->nmasks; i++) { + all = all && bitmask_all(mask->mask[i], bits->mask[i]); + bitmask_merge(&mask->mask[i], bits->mask[i]); + } + return all; +} + +static inline bool +infmask_clear(infmask_t *mask, const infmask_t *bits) +{ + if (!mask->mask || !bits->mask) + return false; + + bool all = infmask_all(mask, bits); + + size_t min_size = min(mask->nmasks, bits->nmasks); + for (size_t i = 0; i < min_size; i++) + bitmask_clear(&mask->mask[i], bits->mask[i]); + + return all; +} + +static inline bool +infmask_bit_is_set(const infmask_t *mask, unsigned int bit) +{ + if (!mask->mask || bit / bitmask_size() >= mask->nmasks) + return false; + + return bitmask_bit_is_set(mask->mask[bit / bitmask_size()], + bit % bitmask_size()); +} + +static inline bool +infmask_set_bit(infmask_t *mask, unsigned int bit) +{ + _infmask_ensure_size(mask, bit); + + bool isset = infmask_bit_is_set(mask, bit); + bitmask_set_bit(&mask->mask[bit / bitmask_size()], bit % bitmask_size()); + return isset; +} + +static inline bool +infmask_clear_bit(infmask_t *mask, unsigned int bit) +{ + if (!mask->mask || bit / bitmask_size() >= mask->nmasks) + return false; + + bool isset = infmask_bit_is_set(mask, bit); + bitmask_clear_bit(&mask->mask[bit / bitmask_size()], bit % bitmask_size()); + return isset; +} + +static inline infmask_t +infmask_from_bit(unsigned int bit) +{ + infmask_t m = infmask_new(); + infmask_set_bit(&m, bit); + return m; +} + +static inline infmask_t +_infmask_from_bits(unsigned int bit1, ...) +{ + infmask_t m = infmask_new(); + infmask_set_bit(&m, bit1); + + va_list args; + va_start(args, bit1); + + unsigned int v = va_arg(args, unsigned int); + while (v < UINT_MAX) { + infmask_set_bit(&m, v); + v = va_arg(args, unsigned int); + } + va_end(args); + + return m; +} + +#define infmask_from_bits(...) \ + _infmask_from_bits(__VA_ARGS__, UINT_MAX) + +static inline infmask_t +infmask_from_u32(uint32_t mask) +{ + infmask_t m = infmask_new(); + infmask_set_bit(&m, mask); + return m; +} diff --git a/test/test-utils.c b/test/test-utils.c index d1a43220..26193d0c 100644 --- a/test/test-utils.c +++ b/test/test-utils.c @@ -3054,6 +3054,102 @@ START_TEST(evdev_frames) } END_TEST +START_TEST(infmask_test) +{ + /* Test empty mask */ + infmask_t empty = infmask_new(); + litest_assert(infmask_is_empty(&empty)); + litest_assert(!infmask_bit_is_set(&empty, 0)); + litest_assert(!infmask_bit_is_set(&empty, 100)); + infmask_reset(&empty); + + /* Test single bit operations */ + infmask_t single = infmask_new(); + litest_assert(!infmask_set_bit(&single, 5)); + litest_assert(infmask_bit_is_set(&single, 5)); + litest_assert(!infmask_bit_is_set(&single, 4)); + litest_assert(!infmask_bit_is_set(&single, 6)); + litest_assert(!infmask_is_empty(&single)); + litest_assert(infmask_clear_bit(&single, 5)); + litest_assert(!infmask_bit_is_set(&single, 5)); + litest_assert(infmask_is_empty(&single)); + infmask_reset(&single); + + /* Test from_bit constructor */ + infmask_t from_bit = infmask_from_bit(7); + litest_assert(infmask_bit_is_set(&from_bit, 7)); + litest_assert(!infmask_bit_is_set(&from_bit, 6)); + litest_assert(!infmask_bit_is_set(&from_bit, 8)); + infmask_reset(&from_bit); + + /* Test from_bits constructor */ + infmask_t from_bits = infmask_from_bits(1, 3, 5); + litest_assert(infmask_bit_is_set(&from_bits, 1)); + litest_assert(!infmask_bit_is_set(&from_bits, 2)); + litest_assert(infmask_bit_is_set(&from_bits, 3)); + litest_assert(!infmask_bit_is_set(&from_bits, 4)); + litest_assert(infmask_bit_is_set(&from_bits, 5)); + infmask_reset(&from_bits); + + /* Test high bit operations */ + infmask_t high = infmask_new(); + litest_assert(!infmask_set_bit(&high, 100)); + litest_assert(infmask_bit_is_set(&high, 100)); + litest_assert(!infmask_bit_is_set(&high, 99)); + litest_assert(!infmask_bit_is_set(&high, 101)); + litest_assert(infmask_clear_bit(&high, 100)); + litest_assert(!infmask_bit_is_set(&high, 100)); + infmask_reset(&high); + + /* Test any/all operations */ + infmask_t mask1 = infmask_from_bits(1, 2, 3); + infmask_t mask2 = infmask_from_bits(2, 3, 4); + infmask_t mask3 = infmask_from_bits(2, 3); + + litest_assert(infmask_any(&mask1, &mask2)); + litest_assert(!infmask_all(&mask1, &mask2)); + litest_assert(infmask_all(&mask1, &mask3)); + litest_assert(infmask_any(&mask1, &mask3)); + + infmask_reset(&mask1); + infmask_reset(&mask2); + infmask_reset(&mask3); + + /* Test merge operation */ + infmask_t merge1 = infmask_from_bits(1, 2); + infmask_t merge2 = infmask_from_bits(2, 3); + litest_assert(!infmask_merge(&merge1, &merge2)); + litest_assert(infmask_bit_is_set(&merge1, 1)); + litest_assert(infmask_bit_is_set(&merge1, 2)); + litest_assert(infmask_bit_is_set(&merge1, 3)); + infmask_reset(&merge1); + infmask_reset(&merge2); + + /* Test clear operation */ + infmask_t clear1 = infmask_from_bits(1, 2, 3); + infmask_t clear2 = infmask_from_bits(2, 3); + litest_assert(infmask_clear(&clear1, &clear2)); + litest_assert(infmask_bit_is_set(&clear1, 1)); + litest_assert(!infmask_bit_is_set(&clear1, 2)); + litest_assert(!infmask_bit_is_set(&clear1, 3)); + infmask_reset(&clear1); + infmask_reset(&clear2); + + /* Test growing behavior */ + infmask_t grow = infmask_new(); + litest_assert(!infmask_set_bit(&grow, 5)); + litest_assert(grow.nmasks == 1); + litest_assert(!infmask_set_bit(&grow, 35)); + litest_assert(grow.nmasks == 2); + litest_assert(!infmask_set_bit(&grow, 65)); + litest_assert(grow.nmasks == 3); + litest_assert(infmask_bit_is_set(&grow, 5)); + litest_assert(infmask_bit_is_set(&grow, 35)); + litest_assert(infmask_bit_is_set(&grow, 65)); + infmask_reset(&grow); +} +END_TEST + int main(void) { @@ -3140,6 +3236,8 @@ main(void) ADD_TEST(evdev_frames); + ADD_TEST(infmask_test); + enum litest_runner_result result = litest_runner_run_tests(runner); litest_runner_destroy(runner);