forked from systemd/systemd-stable
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
basic/bitfield: add bitfield operations
Add macros to manage bits in a bitfield (e.g. uint32_t, uint64_t, etc), such as setting, clearing, checking bits, and iterating all set bits. These are similiar to the bitmap operations, but operate on basic types instead of requiring a Bitmap object. (cherry picked from commit 33d9bee)
- Loading branch information
Showing
1 changed file
with
73 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* SPDX-License-Identifier: LGPL-2.1-or-later */ | ||
#pragma once | ||
|
||
#include "macro.h" | ||
|
||
/* Bit index (0-based) to mask of specified type. Assertion failure if index is out of range. */ | ||
#define _INDEX_TO_MASK(type, i, uniq) \ | ||
({ \ | ||
int UNIQ_T(_i, uniq) = (i); \ | ||
assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \ | ||
((type)1) << UNIQ_T(_i, uniq); \ | ||
}) | ||
#define INDEX_TO_MASK(type, i) \ | ||
({ \ | ||
assert_cc(sizeof(type) <= sizeof(unsigned long long)); \ | ||
assert_cc(__builtin_choose_expr(__builtin_constant_p(i), i, 0) < (int)(sizeof(type) * 8)); \ | ||
__builtin_choose_expr(__builtin_constant_p(i), \ | ||
((type)1) << (i), \ | ||
_INDEX_TO_MASK(type, i, UNIQ)); \ | ||
}) | ||
|
||
/* Builds a mask of specified type with multiple bits set. Note the result will not be constant, even if all | ||
* indexes are constant. */ | ||
#define INDEXES_TO_MASK(type, ...) \ | ||
UNIQ_INDEXES_TO_MASK(type, UNIQ, ##__VA_ARGS__) | ||
#define UNIQ_INDEXES_TO_MASK(type, uniq, ...) \ | ||
({ \ | ||
typeof(type) UNIQ_T(_mask, uniq) = (type)0; \ | ||
int UNIQ_T(_i, uniq); \ | ||
VA_ARGS_FOREACH(UNIQ_T(_i, uniq), ##__VA_ARGS__) \ | ||
UNIQ_T(_mask, uniq) |= INDEX_TO_MASK(type, UNIQ_T(_i, uniq)); \ | ||
UNIQ_T(_mask, uniq); \ | ||
}) | ||
|
||
/* Same as the FLAG macros, but accept a 0-based bit index instead of a mask. Results in assertion failure if | ||
* index is out of range for the type. */ | ||
#define SET_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), true) | ||
#define CLEAR_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), false) | ||
#define BIT_SET(bits, i) FLAGS_SET(bits, INDEX_TO_MASK(typeof(bits), i)) | ||
|
||
/* As above, but accepts multiple indexes. Note the result will not be constant, even if all indexes are | ||
* constant. */ | ||
#define SET_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), true) | ||
#define CLEAR_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), false) | ||
#define BITS_SET(bits, ...) FLAGS_SET(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__)) | ||
|
||
/* Iterate through each set bit. Index is 0-based and type int. */ | ||
#define BIT_FOREACH(index, bits) _BIT_FOREACH(index, bits, UNIQ) | ||
#define _BIT_FOREACH(index, bits, uniq) \ | ||
for (int UNIQ_T(_last, uniq) = -1, index; \ | ||
(index = BIT_NEXT_SET(bits, UNIQ_T(_last, uniq))) >= 0; \ | ||
UNIQ_T(_last, uniq) = index) | ||
|
||
/* Find the next set bit after 0-based index 'prev'. Result is 0-based index of next set bit, or -1 if no | ||
* more bits are set. */ | ||
#define BIT_FIRST_SET(bits) BIT_NEXT_SET(bits, -1) | ||
#define BIT_NEXT_SET(bits, prev) \ | ||
UNIQ_BIT_NEXT_SET(bits, prev, UNIQ) | ||
#define UNIQ_BIT_NEXT_SET(bits, prev, uniq) \ | ||
({ \ | ||
typeof(bits) UNIQ_T(_bits, uniq) = (bits); \ | ||
int UNIQ_T(_prev, uniq) = (prev); \ | ||
int UNIQ_T(_next, uniq); \ | ||
_BIT_NEXT_SET(UNIQ_T(_bits, uniq), \ | ||
UNIQ_T(_prev, uniq), \ | ||
UNIQ_T(_next, uniq)); \ | ||
}) | ||
#define _BIT_NEXT_SET(bits, prev, next) \ | ||
((int)(prev + 1) == (int)sizeof(bits) * 8 \ | ||
? -1 /* Prev index was msb. */ \ | ||
: ((next = __builtin_ffsll(((unsigned long long)(bits)) >> (prev + 1))) == 0 \ | ||
? -1 /* No more bits set. */ \ | ||
: prev + next)) |