util: add a bitmask_t type for bit masks

Previously we used uint32_t for bitmasks but having a custom type means
we're less likely to confuse an int value with a mask type.

Two types of API here, the u32 api for passing in masks and a bit API
for passing in single bits.

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1218>
This commit is contained in:
Peter Hutterer 2025-03-14 10:58:40 +10:00
parent 905b4c6a4c
commit 8974a15178
2 changed files with 326 additions and 0 deletions

View file

@ -28,8 +28,10 @@
#include "config.h"
#include <assert.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#define bit(x_) (1UL << (x_))
#define NBITS(b) (b * 8)
@ -98,3 +100,128 @@ long_any_bit_set(unsigned long *array, size_t size)
return true;
return false;
}
/* A wrapper around a bit mask to avoid type confusion */
typedef struct {
uint32_t mask;
} bitmask_t;
static inline uint32_t
bitmask_as_u32(bitmask_t mask)
{
return mask.mask;
}
static inline bool
bitmask_is_empty(bitmask_t mask) {
return mask.mask == 0;
}
static inline bool
bitmask_any(bitmask_t mask, bitmask_t bits) {
return !!(mask.mask & bits.mask);
}
static inline bool
bitmask_all(bitmask_t mask, bitmask_t bits) {
return bits.mask != 0 && (mask.mask & bits.mask) == bits.mask;
}
static inline bool
bitmask_merge(bitmask_t *mask, bitmask_t bits) {
bool all = bitmask_all(*mask, bits);
mask->mask |= bits.mask;
return all;
}
static inline bool
bitmask_clear(bitmask_t *mask, bitmask_t bits) {
bool all = bitmask_all(*mask, bits);
mask->mask &= ~bits.mask;
return all;
}
static inline bool
bitmask_bit_is_set(bitmask_t mask, unsigned int bit) {
return !!(mask.mask & bit(bit));
}
static inline bool
bitmask_set_bit(bitmask_t *mask, unsigned int bit) {
bool isset = bitmask_bit_is_set(*mask, bit);
mask->mask |= bit(bit);
return isset;
}
static inline bool
bitmask_clear_bit(bitmask_t *mask, unsigned int bit) {
bool isset = bitmask_bit_is_set(*mask, bit);
mask->mask &= ~bit(bit);
return isset;
}
static inline bitmask_t
bitmask_new(void) {
bitmask_t m = {0};
return m;
}
static inline bitmask_t
bitmask_from_bit(unsigned int bit) {
bitmask_t m = {
.mask = bit(bit)
};
return m;
}
static inline bitmask_t
bitmask_from_u32(uint32_t mask) {
bitmask_t m = {
.mask = mask
};
return m;
}
static inline bitmask_t
_bitmask_from_masks(uint32_t mask1, ...)
{
uint32_t mask = mask1;
va_list args;
va_start(args, mask1);
uint32_t v = va_arg(args, unsigned int);
while (v != 0) {
mask |= v;
v = va_arg(args, unsigned int);
}
va_end(args);
return bitmask_from_u32(mask);
}
#define bitmask_from_masks(...) \
_bitmask_from_masks(__VA_ARGS__, 0)
static inline bitmask_t
_bitmask_from_bits(unsigned int bit1, ...)
{
uint32_t mask = bit(bit1);
va_list args;
va_start(args, bit1);
uint32_t v = va_arg(args, unsigned int);
while (v < 32) {
mask |= bit(v);
v = va_arg(args, unsigned int);
}
va_end(args);
return bitmask_from_u32(mask);
}
#define bitmask_from_bits(...) \
_bitmask_from_bits(__VA_ARGS__, 32)

View file

@ -317,6 +317,204 @@ START_TEST(bitfield_helpers)
}
END_TEST
START_TEST(bitmask_test)
{
{
bitmask_t mask1 = bitmask_from_u32(0x12345678U);
litest_assert(bitmask_as_u32(mask1) == 0x12345678U);
bitmask_t mask2 = bitmask_from_u32(0);
litest_assert_int_eq(bitmask_as_u32(mask2), 0U);
bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert_int_eq(bitmask_as_u32(mask3), 0xFFFFFFFFU);
}
{
bitmask_t mask1 = bitmask_new();
litest_assert(bitmask_is_empty(mask1));
bitmask_t mask2 = bitmask_from_u32(0x00000001U);
litest_assert(!bitmask_is_empty(mask2));
bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert(!bitmask_is_empty(mask3));
}
{
bitmask_t mask1 = bitmask_from_u32(0x0000000FU);
bitmask_t bits1 = bitmask_from_u32(0x00000003U);
litest_assert(bitmask_any(mask1, bits1));
bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
bitmask_t bits2 = bitmask_from_u32(0x000000F0U);
litest_assert(!bitmask_any(mask2, bits2));
bitmask_t mask3 = bitmask_from_u32(0x00000000U);
bitmask_t bits3 = bitmask_from_u32(0x00000001U);
litest_assert(!bitmask_any(mask3, bits3));
bitmask_t mask4 = bitmask_from_u32(0xFFFFFFFFU);
bitmask_t bits4 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert(bitmask_any(mask4, bits4));
bitmask_t mask5 = bitmask_from_u32(0x10000000U);
bitmask_t bits5 = bitmask_from_u32(0x10000000U);
litest_assert(bitmask_any(mask5, bits5));
}
{
bitmask_t mask1 = bitmask_from_u32(0x0000000FU);
bitmask_t bits1 = bitmask_from_u32(0x00000003U);
litest_assert(bitmask_all(mask1, bits1));
litest_assert(!bitmask_all(bits1, mask1));
bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
bitmask_t bits2 = bitmask_from_u32(0x0000000FU);
litest_assert(bitmask_all(mask2, bits2));
litest_assert(bitmask_all(bits2, mask2));
bitmask_t mask3 = bitmask_from_u32(0x00000000U);
bitmask_t bits3 = bitmask_from_u32(0x00000000U);
litest_assert(!bitmask_all(mask3, bits3)); /* zero is special */
bitmask_t mask4 = bitmask_from_u32(0xFFFFFFFFU);
bitmask_t bits4 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert(bitmask_all(mask4, bits4));
bitmask_t mask5 = bitmask_from_u32(0x10000000U);
bitmask_t bits5 = bitmask_from_u32(0x10000000U);
litest_assert(bitmask_all(mask5, bits5));
}
{
bitmask_t mask1 = bitmask_from_u32(0x0000000FU);
bitmask_t bits1 = bitmask_from_u32(0x000000F0U);
litest_assert(!bitmask_merge(&mask1, bits1));
litest_assert_int_eq(mask1.mask, 0x000000FFU);
bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
bitmask_t bits2 = bitmask_from_u32(0x0000000FU);
litest_assert(bitmask_merge(&mask2, bits2));
litest_assert_int_eq(mask2.mask, 0x0000000FU);
bitmask_t mask3 = bitmask_new();
bitmask_t bits3 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert(!bitmask_merge(&mask3, bits3));
litest_assert_int_eq(mask3.mask, 0xFFFFFFFFU);
bitmask_t mask4 = bitmask_from_u32(0x80000000U);
bitmask_t bits4 = bitmask_from_u32(0x00000001U);
litest_assert(!bitmask_merge(&mask4, bits4));
litest_assert_int_eq(mask4.mask, 0x80000001U);
}
{
bitmask_t mask1 = bitmask_from_u32(0x000000FFU);
bitmask_t bits1 = bitmask_from_u32(0x0000000FU);
litest_assert(bitmask_clear(&mask1, bits1));
litest_assert_int_eq(mask1.mask, 0x000000F0U);
bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
bitmask_t bits2 = bitmask_from_u32(0x0000000FU);
litest_assert(bitmask_clear(&mask2, bits2));
litest_assert_int_eq(mask2.mask, 0x00000000U);
bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
bitmask_t bits3 = bitmask_from_u32(0x00000000U);
litest_assert(!bitmask_clear(&mask3, bits3)); /* zero is special */
litest_assert_int_eq(mask3.mask, 0xFFFFFFFFU);
bitmask_t mask4 = bitmask_from_u32(0xFFFFFFFFU);
bitmask_t bits4 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert(bitmask_clear(&mask4, bits4));
litest_assert_int_eq(mask4.mask, 0x0U);
}
{
bitmask_t mask1 = bitmask_from_u32(0x00000001U);
litest_assert(bitmask_bit_is_set(mask1, 0));
litest_assert(!bitmask_bit_is_set(mask1, 1));
bitmask_t mask2 = bitmask_from_u32(0x80000000U);
litest_assert(bitmask_bit_is_set(mask2, 31));
litest_assert(!bitmask_bit_is_set(mask2, 0));
bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert(bitmask_bit_is_set(mask3, 0));
litest_assert(bitmask_bit_is_set(mask3, 31));
litest_assert(bitmask_bit_is_set(mask3, 16));
bitmask_t mask4 = bitmask_new();
litest_assert(!bitmask_bit_is_set(mask4, 0));
litest_assert(!bitmask_bit_is_set(mask4, 1));
}
{
bitmask_t mask1 = bitmask_new();
litest_assert(!bitmask_set_bit(&mask1, 0));
litest_assert_int_eq(mask1.mask, 0x00000001U);
litest_assert(bitmask_set_bit(&mask1, 0));
litest_assert_int_eq(mask1.mask, 0x00000001U);
litest_assert(!bitmask_set_bit(&mask1, 31));
litest_assert_int_eq(mask1.mask, 0x80000001U);
bitmask_t mask2 = bitmask_from_u32(0x0000000FU);
litest_assert(!bitmask_set_bit(&mask2, 4));
litest_assert_int_eq(mask2.mask, 0x0000001FU);
litest_assert(bitmask_set_bit(&mask2, 4));
litest_assert_int_eq(mask2.mask, 0x0000001FU);
}
{
bitmask_t mask1 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert(bitmask_clear_bit(&mask1, 0));
litest_assert_int_eq(mask1.mask, 0xFFFFFFFEU);
litest_assert(!bitmask_clear_bit(&mask1, 0));
litest_assert_int_eq(mask1.mask, 0xFFFFFFFEU);
litest_assert(bitmask_clear_bit(&mask1, 31));
litest_assert_int_eq(mask1.mask, 0x7FFFFFFEU);
bitmask_t mask2 = bitmask_from_u32(0x0000001FU);
litest_assert(bitmask_clear_bit(&mask2, 4));
litest_assert_int_eq(mask2.mask, 0x0000000FU);
litest_assert(!bitmask_clear_bit(&mask2, 4));
litest_assert_int_eq(mask2.mask, 0x0000000FU);
}
{
bitmask_t mask1 = bitmask_from_bit(0);
litest_assert_int_eq(mask1.mask, 0x00000001U);
bitmask_t mask2 = bitmask_from_bit(31);
litest_assert_int_eq(mask2.mask, 0x80000000U);
bitmask_t mask3 = bitmask_from_bit(16);
litest_assert_int_eq(mask3.mask, 0x00010000U);
}
{
bitmask_t mask1 = bitmask_from_u32(0x12345678U);
litest_assert_int_eq(mask1.mask, 0x12345678U);
bitmask_t mask2 = bitmask_from_u32(0);
litest_assert_int_eq(mask2.mask, 0U);
bitmask_t mask3 = bitmask_from_u32(0xFFFFFFFFU);
litest_assert_int_eq(mask3.mask, 0xFFFFFFFFU);
}
{
bitmask_t mask1 = bitmask_from_bits(1, 2, 5);
litest_assert_int_eq(mask1.mask, bit(1) | bit(2) | bit(5));
bitmask_t mask2 = bitmask_from_bits(0);
litest_assert_int_eq(mask2.mask, bit(0));
}
{
bitmask_t mask1 = bitmask_from_masks(0x1, 0x2, 0x8);
litest_assert_int_eq(mask1.mask, 0x0000000BU);
bitmask_t mask2 = bitmask_from_masks(0x0);
litest_assert_int_eq(mask2.mask, 0x00000000U);
}
}
END_TEST
START_TEST(matrix_helpers)
{
struct matrix m1, m2, m3;
@ -2719,6 +2917,7 @@ int main(void)
ADD_TEST(array_for_each);
ADD_TEST(bitfield_helpers);
ADD_TEST(bitmask_test);
ADD_TEST(matrix_helpers);
ADD_TEST(ratelimit_helpers);
ADD_TEST(dpi_parser);