mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 15:00:05 +01:00
util: add an infmask_t type for an infinitely-sized bitmask
Using the bitmask-size underneath but this mask can grow to any number of bits requested. Notably, the sized of the mask is the nearest 4-byte multiple for the highest bit ever set in the mask. Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1271>
This commit is contained in:
parent
4c4d5f33ee
commit
d45f4493f1
2 changed files with 300 additions and 0 deletions
202
src/util-bits.h
202
src/util-bits.h
|
|
@ -28,10 +28,14 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "util-macros.h"
|
||||||
|
|
||||||
#define bit(x_) (1UL << (x_))
|
#define bit(x_) (1UL << (x_))
|
||||||
#define NBITS(b) (b * 8)
|
#define NBITS(b) (b * 8)
|
||||||
|
|
@ -238,3 +242,201 @@ _bitmask_from_bits(unsigned int bit1, ...)
|
||||||
|
|
||||||
#define bitmask_from_bits(...) \
|
#define bitmask_from_bits(...) \
|
||||||
_bitmask_from_bits(__VA_ARGS__, 32)
|
_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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3054,6 +3054,102 @@ START_TEST(evdev_frames)
|
||||||
}
|
}
|
||||||
END_TEST
|
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
|
int
|
||||||
main(void)
|
main(void)
|
||||||
{
|
{
|
||||||
|
|
@ -3140,6 +3236,8 @@ main(void)
|
||||||
|
|
||||||
ADD_TEST(evdev_frames);
|
ADD_TEST(evdev_frames);
|
||||||
|
|
||||||
|
ADD_TEST(infmask_test);
|
||||||
|
|
||||||
enum litest_runner_result result = litest_runner_run_tests(runner);
|
enum litest_runner_result result = litest_runner_run_tests(runner);
|
||||||
litest_runner_destroy(runner);
|
litest_runner_destroy(runner);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue