#ifndef MULTIBIT_H
#define MULTIBIT_H
#include "config.h"
#include "ue2common.h"
#include "bitutils.h"
#include "partial_store.h"
#include "unaligned.h"
#include "multibit_internal.h"
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
#define MMB_ONE (1ULL)
#define MMB_ALL_ONES (0xffffffffffffffffULL)
#define MMB_KEY_BITS (sizeof(MMB_TYPE) * 8)
#define MMB_KEY_MASK (MMB_KEY_BITS - 1)
#define MMB_KEY_SHIFT 6
#define MMB_FLAT_MAX_BITS 256
extern const u8 mmbit_keyshift_lut[32];
extern const u8 mmbit_maxlevel_from_keyshift_lut[32];
extern const u8 mmbit_maxlevel_direct_lut[32];
extern const u32 mmbit_root_offset_from_level[7];
extern const u64a mmbit_zero_to_lut[65];
static really_inline
MMB_TYPE mmb_load(const u8 * bits) {
return unaligned_load_u64a(bits);
}
static really_inline
void mmb_store(u8 *bits, MMB_TYPE val) {
unaligned_store_u64a(bits, val);
}
static really_inline
void mmb_store_partial(u8 *bits, MMB_TYPE val, u32 block_bits) {
assert(block_bits <= MMB_KEY_BITS);
partial_store_u64a(bits, val, ROUNDUP_N(block_bits, 8U) / 8U);
}
static really_inline
MMB_TYPE mmb_single_bit(u32 bit) {
assert(bit < MMB_KEY_BITS);
return MMB_ONE << bit;
}
static really_inline
MMB_TYPE mmb_mask_zero_to(u32 bit) {
assert(bit <= MMB_KEY_BITS);
#ifdef ARCH_32_BIT
return mmbit_zero_to_lut[bit];
#else
if (bit == MMB_KEY_BITS) {
return MMB_ALL_ONES;
} else {
return mmb_single_bit(bit) - MMB_ONE;
}
#endif
}
static really_inline
MMB_TYPE mmb_mask_zero_to_nocheck(u32 bit) {
assert(bit < MMB_KEY_BITS);
#ifdef ARCH_32_BIT
return mmbit_zero_to_lut[bit];
#else
return mmb_single_bit(bit) - MMB_ONE;
#endif
}
static really_inline
u32 mmb_test(MMB_TYPE val, u32 bit) {
assert(bit < MMB_KEY_BITS);
return (val >> bit) & MMB_ONE;
}
static really_inline
void mmb_set(MMB_TYPE * val, u32 bit) {
assert(bit < MMB_KEY_BITS);
*val |= mmb_single_bit(bit);
}
static really_inline
void mmb_clear(MMB_TYPE * val, u32 bit) {
assert(bit < MMB_KEY_BITS);
*val &= ~mmb_single_bit(bit);
}
static really_inline
u32 mmb_ctz(MMB_TYPE val) {
return ctz64(val);
}
static really_inline
u32 mmb_popcount(MMB_TYPE val) {
return popcount64(val);
}
#ifndef MMMB_DEBUG
#define MDEBUG_PRINTF(x, ...) do { } while(0)
#else
#define MDEBUG_PRINTF DEBUG_PRINTF
#endif
#ifdef MMB_TRACE_WRITES
#define MMB_TRACE(format, ...) \
printf("mmb [%u bits @ %p] " format, total_bits, bits, ##__VA_ARGS__)
#else
#define MMB_TRACE(format, ...) \
do { \
} while (0)
#endif
static really_inline
u32 mmbit_keyshift(u32 total_bits) {
assert(total_bits > 1);
u32 n = clz32(total_bits - 1); return mmbit_keyshift_lut[n];
}
static really_inline
u32 mmbit_maxlevel(u32 total_bits) {
assert(total_bits > 1);
u32 n = clz32(total_bits - 1); u32 max_level = mmbit_maxlevel_direct_lut[n];
assert(max_level <= MMB_MAX_LEVEL);
return max_level;
}
static really_inline
u32 mmbit_maxlevel_from_keyshift(u32 ks) {
assert(ks <= 30);
assert(ks % MMB_KEY_SHIFT == 0);
u32 max_level = mmbit_maxlevel_from_keyshift_lut[ks];
assert(max_level <= MMB_MAX_LEVEL);
return max_level;
}
static really_inline
u32 mmbit_get_ks(u32 max_level, u32 level) {
assert(max_level <= MMB_MAX_LEVEL);
assert(level <= max_level);
return (max_level - level) * MMB_KEY_SHIFT;
}
static really_inline
u32 mmbit_get_key_val(u32 max_level, u32 level, u32 key) {
return (key >> mmbit_get_ks(max_level, level)) & MMB_KEY_MASK;
}
static really_inline
u8 *mmbit_get_level_root(u8 *bits, u32 level) {
assert(level < ARRAY_LENGTH(mmbit_root_offset_from_level));
return bits + mmbit_root_offset_from_level[level] * sizeof(MMB_TYPE);
}
static really_inline
const u8 *mmbit_get_level_root_const(const u8 *bits, u32 level) {
assert(level < ARRAY_LENGTH(mmbit_root_offset_from_level));
return bits + mmbit_root_offset_from_level[level] * sizeof(MMB_TYPE);
}
static really_inline
u8 *mmbit_get_block_ptr(u8 *bits, u32 max_level, u32 level, u32 key) {
u8 *level_root = mmbit_get_level_root(bits, level);
u32 ks = mmbit_get_ks(max_level, level);
return level_root + ((u64a)key >> (ks + MMB_KEY_SHIFT)) * sizeof(MMB_TYPE);
}
static really_inline
const u8 *mmbit_get_block_ptr_const(const u8 *bits, u32 max_level, u32 level,
u32 key) {
const u8 *level_root = mmbit_get_level_root_const(bits, level);
u32 ks = mmbit_get_ks(max_level, level);
return level_root + ((u64a)key >> (ks + MMB_KEY_SHIFT)) * sizeof(MMB_TYPE);
}
static really_inline
u8 *mmbit_get_byte_ptr(u8 *bits, u32 max_level, u32 level, u32 key) {
u8 *level_root = mmbit_get_level_root(bits, level);
u32 ks = mmbit_get_ks(max_level, level);
return level_root + ((u64a)key >> (ks + MMB_KEY_SHIFT - 3));
}
static really_inline
u32 mmbit_get_key_val_byte(u32 max_level, u32 level, u32 key) {
return (key >> (mmbit_get_ks(max_level, level))) & 0x7;
}
static really_inline
MMB_TYPE mmbit_get_flat_block(const u8 *bits, u32 n_bits) {
assert(n_bits <= MMB_KEY_BITS);
u32 n_bytes = ROUNDUP_N(n_bits, 8) / 8;
switch (n_bytes) {
case 1:
return *bits;
case 2:
return unaligned_load_u16(bits);
case 3:
case 4: {
u32 rv;
assert(n_bytes <= sizeof(rv));
memcpy(&rv, bits + n_bytes - sizeof(rv), sizeof(rv));
rv >>= (sizeof(rv) - n_bytes) * 8;
assert(rv == partial_load_u32(bits, n_bytes));
return rv;
}
default: {
u64a rv;
assert(n_bytes <= sizeof(rv));
memcpy(&rv, bits + n_bytes - sizeof(rv), sizeof(rv));
rv >>= (sizeof(rv) - n_bytes) * 8;
assert(rv == partial_load_u64a(bits, n_bytes));
return rv;
}
}
}
static really_inline
u32 mmbit_is_flat_model(u32 total_bits) {
return total_bits <= MMB_FLAT_MAX_BITS;
}
static really_inline
u32 mmbit_flat_size(u32 total_bits) {
assert(mmbit_is_flat_model(total_bits));
return ROUNDUP_N(total_bits, 8) / 8;
}
static really_inline
u32 mmbit_flat_select_byte(u32 key, UNUSED u32 total_bits) {
return key / 8;
}
static really_inline
u32 mmbit_mask_index(u32 bit, MMB_TYPE mask) {
assert(bit < MMB_KEY_BITS);
assert(mmb_test(mask, bit));
mask &= mmb_mask_zero_to(bit);
if (mask == 0ULL) {
return 0; }
return mmb_popcount(mask);
}
static really_inline
void mmbit_clear(u8 *bits, u32 total_bits) {
MDEBUG_PRINTF("%p total_bits %u\n", bits, total_bits);
MMB_TRACE("CLEAR\n");
if (!total_bits) {
return;
}
if (mmbit_is_flat_model(total_bits)) {
memset(bits, 0, mmbit_flat_size(total_bits));
return;
}
mmb_store(bits, 0);
}
static really_inline
char mmbit_set_flat(u8 *bits, u32 total_bits, u32 key) {
bits += mmbit_flat_select_byte(key, total_bits);
u8 mask = 1U << (key % 8);
char was_set = !!(*bits & mask);
*bits |= mask;
return was_set;
}
static really_inline
char mmbit_set_big(u8 *bits, u32 total_bits, u32 key) {
const u32 max_level = mmbit_maxlevel(total_bits);
u32 level = 0;
do {
u8 * byte_ptr = mmbit_get_byte_ptr(bits, max_level, level, key);
u8 keymask = 1U << mmbit_get_key_val_byte(max_level, level, key);
u8 byte = *byte_ptr;
if (likely(!(byte & keymask))) {
*byte_ptr = byte | keymask;
while (level++ != max_level) {
u8 *block_ptr_1 = mmbit_get_block_ptr(bits, max_level, level, key);
MMB_TYPE keymask_1 = mmb_single_bit(mmbit_get_key_val(max_level, level, key));
mmb_store(block_ptr_1, keymask_1);
}
return 0;
}
} while (level++ != max_level);
return 1;
}
static really_inline
char mmbit_set_i(u8 *bits, u32 total_bits, u32 key) {
assert(key < total_bits);
if (mmbit_is_flat_model(total_bits)) {
return mmbit_set_flat(bits, total_bits, key);
} else {
return mmbit_set_big(bits, total_bits, key);
}
}
static really_inline
char mmbit_isset(const u8 *bits, u32 total_bits, u32 key);
static really_inline
char mmbit_set(u8 *bits, u32 total_bits, u32 key) {
MDEBUG_PRINTF("%p total_bits %u key %u\n", bits, total_bits, key);
char status = mmbit_set_i(bits, total_bits, key);
MMB_TRACE("SET %u (prev status: %d)\n", key, (int)status);
assert(mmbit_isset(bits, total_bits, key));
return status;
}
static really_inline
char mmbit_isset_flat(const u8 *bits, u32 total_bits, u32 key) {
bits += mmbit_flat_select_byte(key, total_bits);
return !!(*bits & (1U << (key % 8U)));
}
static really_inline
char mmbit_isset_big(const u8 *bits, u32 total_bits, u32 key) {
const u32 max_level = mmbit_maxlevel(total_bits);
u32 level = 0;
do {
const u8 *block_ptr = mmbit_get_block_ptr_const(bits, max_level, level, key);
MMB_TYPE block = mmb_load(block_ptr);
if (!mmb_test(block, mmbit_get_key_val(max_level, level, key))) {
return 0;
}
} while (level++ != max_level);
return 1;
}
static really_inline
char mmbit_isset(const u8 *bits, u32 total_bits, u32 key) {
MDEBUG_PRINTF("%p total_bits %u key %u\n", bits, total_bits, key);
assert(key < total_bits);
if (mmbit_is_flat_model(total_bits)) {
return mmbit_isset_flat(bits, total_bits, key);
} else {
return mmbit_isset_big(bits, total_bits, key);
}
}
static really_inline
void mmbit_unset_flat(u8 *bits, u32 total_bits, u32 key) {
bits += mmbit_flat_select_byte(key, total_bits);
*bits &= ~(1U << (key % 8U));
}
static really_inline
void mmbit_unset_big(u8 *bits, u32 total_bits, u32 key) {
const u32 max_level = mmbit_maxlevel(total_bits);
u32 level = 0;
do {
u8 *block_ptr = mmbit_get_block_ptr(bits, max_level, level, key);
u32 key_val = mmbit_get_key_val(max_level, level, key);
MMB_TYPE block = mmb_load(block_ptr);
if (!mmb_test(block, key_val)) {
return;
}
if (level == max_level) {
mmb_clear(&block, key_val);
mmb_store(block_ptr, block);
}
} while (level++ != max_level);
}
static really_inline
void mmbit_unset(u8 *bits, u32 total_bits, u32 key) {
MDEBUG_PRINTF("%p total_bits %u key %u\n", bits, total_bits, key);
assert(key < total_bits);
MMB_TRACE("UNSET %u (prev status: %d)\n", key,
(int)mmbit_isset(bits, total_bits, key));
if (mmbit_is_flat_model(total_bits)) {
mmbit_unset_flat(bits, total_bits, key);
} else {
mmbit_unset_big(bits, total_bits, key);
}
}
static really_inline
u32 mmbit_iterate_flat(const u8 *bits, u32 total_bits, u32 it_in) {
if (total_bits <= MMB_KEY_BITS) {
MMB_TYPE block = mmbit_get_flat_block(bits, total_bits);
if (it_in != MMB_INVALID) {
it_in++;
assert(it_in < total_bits);
block &= ~mmb_mask_zero_to(it_in);
}
if (block) {
return mmb_ctz(block);
}
return MMB_INVALID;
}
const u32 last_block = total_bits / MMB_KEY_BITS;
u32 start;
if (it_in != MMB_INVALID) {
it_in++;
assert(it_in < total_bits);
start = (ROUNDUP_N(it_in, MMB_KEY_BITS) / MMB_KEY_BITS) - 1;
u32 start_key = start * MMB_KEY_BITS;
u32 block_size = MIN(MMB_KEY_BITS, total_bits - start_key);
MMB_TYPE block =
mmbit_get_flat_block(bits + (start * sizeof(MMB_TYPE)), block_size);
block &= ~mmb_mask_zero_to(it_in - start_key);
if (block) {
return start_key + mmb_ctz(block);
} else if (start_key + MMB_KEY_BITS >= total_bits) {
return MMB_INVALID; }
start++;
} else {
start = 0;
}
for (; start < last_block; start++) {
MMB_TYPE block = mmb_load(bits + (start * sizeof(MMB_TYPE)));
if (block) {
return (start * MMB_KEY_BITS) + mmb_ctz(block);
}
}
if (total_bits % MMB_KEY_BITS) {
u32 start_key = start * MMB_KEY_BITS;
u32 block_size = MIN(MMB_KEY_BITS, total_bits - start_key);
MMB_TYPE block =
mmbit_get_flat_block(bits + (start * sizeof(MMB_TYPE)), block_size);
if (block) {
return start_key + mmb_ctz(block);
}
}
return MMB_INVALID;
}
static really_inline
u32 mmbit_iterate_big(const u8 * bits, u32 total_bits, u32 it_in) {
const u32 max_level = mmbit_maxlevel(total_bits);
u32 level = 0;
u32 key = 0;
u32 key_rem = 0;
if (it_in != MMB_INVALID) {
key = it_in >> MMB_KEY_SHIFT;
key_rem = (it_in & MMB_KEY_MASK) + 1;
level = max_level;
}
while (1) {
if (key_rem < MMB_KEY_BITS) {
const u8 *block_ptr = mmbit_get_level_root_const(bits, level) +
key * sizeof(MMB_TYPE);
MMB_TYPE block
= mmb_load(block_ptr) & ~mmb_mask_zero_to_nocheck(key_rem);
if (block) {
key = (key << MMB_KEY_SHIFT) + mmb_ctz(block);
if (level++ == max_level) {
break;
}
key_rem = 0;
continue; }
}
if (level-- == 0) {
return MMB_INVALID; }
key_rem = (key & MMB_KEY_MASK) + 1;
key >>= MMB_KEY_SHIFT;
}
assert(key < total_bits);
assert(mmbit_isset(bits, total_bits, key));
return key;
}
static really_inline
u32 mmbit_iterate(const u8 *bits, u32 total_bits, u32 it_in) {
MDEBUG_PRINTF("%p total_bits %u it_in %u\n", bits, total_bits, it_in);
assert(it_in < total_bits || it_in == MMB_INVALID);
if (!total_bits) {
return MMB_INVALID;
}
if (it_in == total_bits - 1) {
return MMB_INVALID; }
u32 key;
if (mmbit_is_flat_model(total_bits)) {
key = mmbit_iterate_flat(bits, total_bits, it_in);
} else {
key = mmbit_iterate_big(bits, total_bits, it_in);
}
assert(key == MMB_INVALID || mmbit_isset(bits, total_bits, key));
return key;
}
static really_inline
char mmbit_any_flat(const u8 *bits, u32 total_bits) {
if (total_bits <= MMB_KEY_BITS) {
return !!mmbit_get_flat_block(bits, total_bits);
}
const u8 *end = bits + mmbit_flat_size(total_bits);
for (const u8 *last = end - sizeof(MMB_TYPE); bits < last;
bits += sizeof(MMB_TYPE)) {
if (mmb_load(bits)) {
return 1;
}
}
return !!mmb_load(end - sizeof(MMB_TYPE));
}
static really_inline
char mmbit_any(const u8 *bits, u32 total_bits) {
MDEBUG_PRINTF("%p total_bits %u\n", bits, total_bits);
if (!total_bits) {
return 0;
}
if (mmbit_is_flat_model(total_bits)) {
return mmbit_any_flat(bits, total_bits);
}
return !!mmb_load(bits);
}
static really_inline
char mmbit_any_precise(const u8 *bits, u32 total_bits) {
MDEBUG_PRINTF("%p total_bits %u\n", bits, total_bits);
if (!total_bits) {
return 0;
}
if (mmbit_is_flat_model(total_bits)) {
return mmbit_any_flat(bits, total_bits);
}
return mmbit_iterate_big(bits, total_bits, MMB_INVALID) != MMB_INVALID;
}
static really_inline
char mmbit_all_flat(const u8 *bits, u32 total_bits) {
while (total_bits > MMB_KEY_BITS) {
if (mmb_load(bits) != MMB_ALL_ONES) {
return 0;
}
bits += sizeof(MMB_TYPE);
total_bits -= MMB_KEY_BITS;
}
while (total_bits > 8) {
if (*bits != 0xff) {
return 0;
}
bits++;
total_bits -= 8;
}
u8 mask = (u8)mmb_mask_zero_to_nocheck(total_bits);
return (*bits & mask) == mask;
}
static really_inline
char mmbit_all_big(const u8 *bits, u32 total_bits) {
u32 ks = mmbit_keyshift(total_bits);
u32 level = 0;
for (;;) {
u32 level_bits;
if (ks != 0) {
u32 next_level_width = MMB_KEY_BITS << (ks - MMB_KEY_SHIFT);
level_bits = ROUNDUP_N(total_bits, next_level_width) >> ks;
} else {
level_bits = total_bits;
}
const u8 *block_ptr = mmbit_get_level_root_const(bits, level);
while (level_bits >= MMB_KEY_BITS) {
MMB_TYPE block = mmb_load(block_ptr);
if (block != MMB_ALL_ONES) {
return 0;
}
block_ptr += sizeof(MMB_TYPE);
level_bits -= MMB_KEY_BITS;
}
if (level_bits > 0) {
MMB_TYPE block = mmb_load(block_ptr);
MMB_TYPE mask = mmb_mask_zero_to_nocheck(level_bits);
if ((block & mask) != mask) {
return 0;
}
}
if (ks == 0) {
break;
}
ks -= MMB_KEY_SHIFT;
level++;
}
return 1;
}
static really_inline
char mmbit_all(const u8 *bits, u32 total_bits) {
MDEBUG_PRINTF("%p total_bits %u\n", bits, total_bits);
if (mmbit_is_flat_model(total_bits)) {
return mmbit_all_flat(bits, total_bits);
}
return mmbit_all_big(bits, total_bits);
}
static really_inline
MMB_TYPE get_flat_masks(u32 base, u32 it_start, u32 it_end) {
if (it_end <= base) {
return 0;
}
u32 udiff = it_end - base;
MMB_TYPE mask = udiff < 64 ? mmb_mask_zero_to_nocheck(udiff) : MMB_ALL_ONES;
if (it_start >= base) {
u32 ldiff = it_start - base;
MMB_TYPE lmask = ldiff < 64 ? ~mmb_mask_zero_to_nocheck(ldiff) : 0;
mask &= lmask;
}
return mask;
}
static really_inline
u32 mmbit_iterate_bounded_flat(const u8 *bits, u32 total_bits, u32 begin,
u32 end) {
if (total_bits <= MMB_KEY_BITS) {
MMB_TYPE block = mmbit_get_flat_block(bits, total_bits);
block &= get_flat_masks(0, begin, end);
if (block) {
return mmb_ctz(block);
}
return MMB_INVALID;
}
const u32 last_block = ROUNDDOWN_N(total_bits, MMB_KEY_BITS);
for (u32 i = ROUNDDOWN_N(begin, MMB_KEY_BITS), e = MIN(end, last_block);
i < e; i += MMB_KEY_BITS) {
const u8 *block_ptr = bits + i / 8;
MMB_TYPE block = mmb_load(block_ptr);
block &= get_flat_masks(i, begin, end);
if (block) {
return i + mmb_ctz(block);
}
}
if (end > last_block) {
const u8 *block_ptr = bits + last_block / 8;
u32 num_bits = total_bits - last_block;
MMB_TYPE block = mmbit_get_flat_block(block_ptr, num_bits);
block &= get_flat_masks(last_block, begin, end);
if (block) {
return last_block + mmb_ctz(block);
}
}
return MMB_INVALID;
}
static really_inline
MMB_TYPE get_lowhi_masks(u32 level, u32 max_level, u64a block_min, u64a block_max,
u64a block_base) {
const u32 level_shift = (max_level - level) * MMB_KEY_SHIFT;
u64a lshift = (block_min - block_base) >> level_shift;
u64a ushift = (block_max - block_base) >> level_shift;
MMB_TYPE lmask = lshift < 64 ? ~mmb_mask_zero_to_nocheck(lshift) : 0;
MMB_TYPE umask =
ushift < 63 ? mmb_mask_zero_to_nocheck(ushift + 1) : MMB_ALL_ONES;
return lmask & umask;
}
static really_inline
u32 mmbit_iterate_bounded_big(const u8 *bits, u32 total_bits, u32 it_start, u32 it_end) {
u64a key = 0;
u32 ks = mmbit_keyshift(total_bits);
const u32 max_level = mmbit_maxlevel_from_keyshift(ks);
u32 level = 0;
--it_end; for (;;) {
assert(level <= max_level);
u64a block_width = MMB_KEY_BITS << ks;
u64a block_base = key * block_width;
u64a block_min = MAX(it_start, block_base);
u64a block_max = MIN(it_end, block_base + block_width - 1);
const u8 *block_ptr =
mmbit_get_level_root_const(bits, level) + key * sizeof(MMB_TYPE);
MMB_TYPE block = mmb_load(block_ptr);
block &= get_lowhi_masks(level, max_level, block_min, block_max, block_base);
if (block) {
key = (key << MMB_KEY_SHIFT) + mmb_ctz(block);
if (level++ == max_level) {
return key;
}
ks -= MMB_KEY_SHIFT;
} else {
u64a next_start = block_base + block_width;
if (next_start > it_end) {
break;
}
if (level-- == 0) {
break;
}
it_start = next_start;
key >>= MMB_KEY_SHIFT;
ks += MMB_KEY_SHIFT;
}
}
return MMB_INVALID;
}
static really_inline
u32 mmbit_iterate_bounded(const u8 *bits, u32 total_bits, u32 it_start,
u32 it_end) {
MDEBUG_PRINTF("%p total_bits %u it_start %u it_end %u\n", bits, total_bits,
it_start, it_end);
assert(it_start <= it_end);
assert(it_end <= total_bits);
if (!total_bits || it_end == it_start) {
return MMB_INVALID;
}
assert(it_start < total_bits);
u32 key;
if (mmbit_is_flat_model(total_bits)) {
key = mmbit_iterate_bounded_flat(bits, total_bits, it_start, it_end);
} else {
key = mmbit_iterate_bounded_big(bits, total_bits, it_start, it_end);
}
assert(key == MMB_INVALID || mmbit_isset(bits, total_bits, key));
return key;
}
static really_inline
void mmbit_unset_range_flat(u8 *bits, u32 total_bits, u32 begin, u32 end) {
const u32 last_block = ROUNDDOWN_N(total_bits, MMB_KEY_BITS);
for (u32 i = ROUNDDOWN_N(begin, MMB_KEY_BITS), e = MIN(end, last_block);
i < e; i += MMB_KEY_BITS) {
u8 *block_ptr = bits + i / 8;
MMB_TYPE block = mmb_load(block_ptr);
MMB_TYPE mask = get_flat_masks(i, begin, end);
mmb_store(block_ptr, block & ~mask);
}
if (end > last_block) {
u8 *block_ptr = bits + last_block / 8;
u32 num_bits = total_bits - last_block;
MMB_TYPE block = mmbit_get_flat_block(block_ptr, num_bits);
MMB_TYPE mask = get_flat_masks(last_block, begin, end);
mmb_store_partial(block_ptr, block & ~mask, num_bits);
}
}
static really_inline
void mmbit_unset_range_big(u8 *bits, const u32 total_bits, u32 begin,
u32 end) {
u32 i = begin;
for (;;) {
i = mmbit_iterate_bounded(bits, total_bits, i, end);
if (i == MMB_INVALID) {
break;
}
mmbit_unset_big(bits, total_bits, i);
if (++i == end) {
break;
}
}
}
static really_inline
void mmbit_unset_range(u8 *bits, const u32 total_bits, u32 begin, u32 end) {
MDEBUG_PRINTF("%p total_bits %u begin %u end %u\n", bits, total_bits, begin,
end);
assert(begin <= end);
assert(end <= total_bits);
if (mmbit_is_flat_model(total_bits)) {
mmbit_unset_range_flat(bits, total_bits, begin, end);
} else {
mmbit_unset_range_big(bits, total_bits, begin, end);
}
assert(MMB_INVALID == mmbit_iterate_bounded(bits, total_bits, begin, end));
}
static really_inline
void mmbit_init_range_flat(u8 *bits, const u32 total_bits, u32 begin, u32 end) {
const u32 last_block = ROUNDDOWN_N(total_bits, MMB_KEY_BITS);
for (u32 i = 0; i < last_block; i += MMB_KEY_BITS) {
mmb_store(bits + i / 8, get_flat_masks(i, begin, end));
}
if (total_bits % MMB_KEY_BITS) {
u32 num_bits = total_bits - last_block;
MMB_TYPE block = get_flat_masks(last_block, begin, end);
mmb_store_partial(bits + last_block / 8, block, num_bits);
}
}
static really_inline
void mmbit_init_range_big(u8 *bits, const u32 total_bits, u32 begin, u32 end) {
u32 ks = mmbit_keyshift(total_bits);
u32 level = 0;
for (;;) {
u8 *block = mmbit_get_level_root(bits, level);
u32 k1 = begin >> ks, k2 = end >> ks;
if ((k2 << ks) != end) {
k2++;
}
block += (k1 / MMB_KEY_BITS) * sizeof(MMB_TYPE);
if (k1 % MMB_KEY_BITS) {
u32 idx = k1 / MMB_KEY_BITS;
u32 block_end = (idx + 1) * MMB_KEY_BITS;
MMB_TYPE mask = MMB_ALL_ONES << (k1 % MMB_KEY_BITS);
if (k2 < block_end) {
assert(k2 % MMB_KEY_BITS);
mask &= mmb_mask_zero_to_nocheck(k2 % MMB_KEY_BITS);
mmb_store(block, mask);
goto next_level;
} else {
mmb_store(block, mask);
k1 = block_end;
block += sizeof(MMB_TYPE);
}
}
for (; k1 < (k2 & ~MMB_KEY_MASK); k1 += MMB_KEY_BITS) {
mmb_store(block, MMB_ALL_ONES);
block += sizeof(MMB_TYPE);
}
if (likely(k1 < k2)) {
assert(k2 % MMB_KEY_BITS);
MMB_TYPE mask = mmb_mask_zero_to_nocheck(k2 % MMB_KEY_BITS);
mmb_store(block, mask);
}
next_level:
if (ks == 0) {
break; }
ks -= MMB_KEY_SHIFT;
level++;
}
}
static really_inline
void mmbit_init_range(u8 *bits, const u32 total_bits, u32 begin, u32 end) {
MDEBUG_PRINTF("%p total_bits %u begin %u end %u\n", bits, total_bits, begin,
end);
assert(begin <= end);
assert(end <= total_bits);
if (!total_bits) {
return;
}
if (begin == end) {
mmbit_clear(bits, total_bits);
return;
}
if (mmbit_is_flat_model(total_bits)) {
mmbit_init_range_flat(bits, total_bits, begin, end);
} else {
mmbit_init_range_big(bits, total_bits, begin, end);
}
assert(begin == end ||
mmbit_iterate(bits, total_bits, MMB_INVALID) == begin);
assert(!end || begin == end ||
mmbit_iterate(bits, total_bits, end - 1) == MMB_INVALID);
}
static really_inline
u32 mmbit_sparse_iter_state_size(u32 total_bits) {
if (mmbit_is_flat_model(total_bits)) {
return 2;
}
u32 levels = mmbit_maxlevel(total_bits);
return levels + 1;
}
#ifdef DUMP_SUPPORT
void mmbit_sparse_iter_dump(const struct mmbit_sparse_iter *it, u32 total_bits);
#endif
static really_inline
u32 mmbit_sparse_iter_exec(const u8 *bits, u32 key, u32 *idx, u32 level,
const u32 max_level, struct mmbit_sparse_state *s,
const struct mmbit_sparse_iter *it_root,
const struct mmbit_sparse_iter *it) {
for (;;) {
MMB_TYPE block = s[level].mask;
if (block) {
u32 bit = mmb_ctz(block);
key = (key << MMB_KEY_SHIFT) + bit;
u32 bit_idx = mmbit_mask_index(bit, it->mask);
if (level++ == max_level) {
*idx = it->val + bit_idx;
return key;
} else {
u32 iter_key = it->val + bit_idx;
it = it_root + iter_key;
MMB_TYPE nextblock =
mmb_load(mmbit_get_level_root_const(bits, level) +
key * sizeof(MMB_TYPE));
s[level].mask = nextblock & it->mask;
s[level].itkey = iter_key;
}
} else {
if (level-- == 0) {
break; }
key >>= MMB_KEY_SHIFT;
s[level].mask &= (s[level].mask - 1);
it = it_root + s[level].itkey;
}
}
return MMB_INVALID;
}
static really_inline
u32 mmbit_sparse_iter_begin_big(const u8 *bits, u32 total_bits, u32 *idx,
const struct mmbit_sparse_iter *it_root,
struct mmbit_sparse_state *s) {
const struct mmbit_sparse_iter *it = it_root;
u32 key = 0;
MMB_TYPE block = mmb_load(bits) & it->mask;
if (!block) {
return MMB_INVALID;
}
const u32 max_level = mmbit_maxlevel(total_bits);
s[0].mask = block;
s[0].itkey = 0;
return mmbit_sparse_iter_exec(bits, key, idx, 0, max_level,
s, it_root, it);
}
static really_inline
u32 mmbit_sparse_iter_begin_flat(const u8 *bits, u32 total_bits, u32 *idx,
const struct mmbit_sparse_iter *it_root,
struct mmbit_sparse_state *s) {
if (total_bits <= MMB_KEY_BITS) {
MMB_TYPE block = mmbit_get_flat_block(bits, total_bits);
block &= it_root->mask;
if (!block) {
return MMB_INVALID;
}
s->mask = block;
u32 key = mmb_ctz(block);
*idx = mmbit_mask_index(key, it_root->mask);
return key;
}
assert(mmbit_maxlevel(total_bits) == 1); MMB_TYPE root = it_root->mask;
for (; root; root &= (root - 1)) {
u32 bit = mmb_ctz(root);
u32 bit_idx = mmbit_mask_index(bit, it_root->mask);
u32 iter_key = it_root->val + bit_idx;
const struct mmbit_sparse_iter *it = it_root + iter_key;
u32 block_key_min = bit * MMB_KEY_BITS;
u32 block_key_max = block_key_min + MMB_KEY_BITS;
MMB_TYPE block;
if (block_key_max > total_bits) {
block_key_max = total_bits;
block = mmbit_get_flat_block(bits + (bit * sizeof(MMB_TYPE)),
block_key_max - block_key_min);
} else {
block = mmb_load(bits + (bit * sizeof(MMB_TYPE)));
}
block &= it->mask;
if (block) {
s[0].mask = root;
s[1].mask = block;
s[1].itkey = iter_key;
u32 key = mmb_ctz(block);
*idx = it->val + mmbit_mask_index(key, it->mask);
return key + block_key_min;
}
}
return MMB_INVALID;
}
static really_inline
u32 mmbit_sparse_iter_begin(const u8 *bits, u32 total_bits, u32 *idx,
const struct mmbit_sparse_iter *it_root,
struct mmbit_sparse_state *s) {
assert(ISALIGNED_N(it_root, alignof(struct mmbit_sparse_iter)));
#ifndef _WIN32
assert(ISALIGNED_N(s, alignof(struct mmbit_sparse_state)));
#else
assert(ISALIGNED_N(s, 4));
#endif
MDEBUG_PRINTF("%p total_bits %u\n", bits, total_bits);
assert(it_root->mask != 0);
u32 key;
if (mmbit_is_flat_model(total_bits)) {
key = mmbit_sparse_iter_begin_flat(bits, total_bits, idx, it_root, s);
} else {
key = mmbit_sparse_iter_begin_big(bits, total_bits, idx, it_root, s);
}
if (key != MMB_INVALID) {
assert(key < total_bits);
assert(mmbit_isset(bits, total_bits, key));
}
return key;
}
static really_inline
u32 mmbit_sparse_iter_next_big(const u8 *bits, u32 total_bits, u32 last_key,
u32 *idx,
const struct mmbit_sparse_iter *it_root,
struct mmbit_sparse_state *s) {
const u32 max_level = mmbit_maxlevel(total_bits);
u32 key = last_key >> MMB_KEY_SHIFT;
s[max_level].mask &= (s[max_level].mask - 1);
const struct mmbit_sparse_iter *it = it_root + s[max_level].itkey;
return mmbit_sparse_iter_exec(bits, key, idx, max_level, max_level, s,
it_root, it);
}
static really_inline
u32 mmbit_sparse_iter_next_flat(const u8 *bits, const u32 total_bits, u32 *idx,
const struct mmbit_sparse_iter *it_root,
struct mmbit_sparse_state *s) {
if (total_bits <= MMB_KEY_BITS) {
s->mask &= (s->mask - 1);
if (s->mask) {
u32 key = mmb_ctz(s->mask);
*idx = mmbit_mask_index(key, it_root->mask);
return key;
}
} else {
assert(s[0].mask);
s[1].mask &= (s[1].mask - 1); u32 bit = mmb_ctz(s[0].mask);
for (;;) {
if (s[1].mask) {
u32 key = mmb_ctz(s[1].mask);
const struct mmbit_sparse_iter *it = it_root + s[1].itkey;
*idx = it->val + mmbit_mask_index(key, it->mask);
key += (bit * MMB_KEY_BITS);
return key;
}
s[0].mask &= s[0].mask - 1;
if (!s[0].mask) {
break;
}
bit = mmb_ctz(s[0].mask);
u32 bit_idx = mmbit_mask_index(bit, it_root->mask);
u32 iter_key = it_root->val + bit_idx;
const struct mmbit_sparse_iter *it = it_root + iter_key;
u32 block_key_min = bit * MMB_KEY_BITS;
u32 block_key_max = block_key_min + MMB_KEY_BITS;
MMB_TYPE block;
if (block_key_max > total_bits) {
block_key_max = total_bits;
block = mmbit_get_flat_block(bits + (bit * sizeof(MMB_TYPE)),
block_key_max - block_key_min);
} else {
block = mmb_load(bits + (bit * sizeof(MMB_TYPE)));
}
s[1].mask = block & it->mask;
s[1].itkey = iter_key;
}
}
return MMB_INVALID;
}
static really_inline
u32 mmbit_sparse_iter_next(const u8 *bits, u32 total_bits, u32 last_key,
u32 *idx, const struct mmbit_sparse_iter *it_root,
struct mmbit_sparse_state *s) {
assert(ISALIGNED_N(it_root, alignof(struct mmbit_sparse_iter)));
#ifndef _WIN32
assert(ISALIGNED_N(s, alignof(struct mmbit_sparse_state)));
#else
assert(ISALIGNED_N(s, 4));
#endif
MDEBUG_PRINTF("%p total_bits %u\n", bits, total_bits);
MDEBUG_PRINTF("NEXT (total_bits=%u, last_key=%u)\n", total_bits, last_key);
UNUSED u32 last_idx = *idx; assert(it_root->mask != 0);
assert(last_key < total_bits);
u32 key;
if (mmbit_is_flat_model(total_bits)) {
key = mmbit_sparse_iter_next_flat(bits, total_bits, idx, it_root, s);
} else {
key = mmbit_sparse_iter_next_big(bits, total_bits, last_key, idx,
it_root, s);
}
if (key != MMB_INVALID) {
MDEBUG_PRINTF("END NEXT: key=%u, idx=%u\n", key, *idx);
assert(key < total_bits);
assert(key > last_key);
assert(mmbit_isset(bits, total_bits, key));
assert(*idx > last_idx);
} else {
MDEBUG_PRINTF("END NEXT: no more keys\n");
}
return key;
}
static really_inline
void mmbit_sparse_iter_unset_flat(u8 *bits, u32 total_bits,
const struct mmbit_sparse_iter *it_root) {
if (total_bits <= MMB_KEY_BITS) {
MMB_TYPE block = mmbit_get_flat_block(bits, total_bits);
block &= ~it_root->mask;
mmb_store_partial(bits, block, total_bits);
return;
}
u32 bit_idx = 0;
for (MMB_TYPE root = it_root->mask; root; root &= (root - 1), bit_idx++) {
u32 bit = mmb_ctz(root);
u32 block_key_min = bit * MMB_KEY_BITS;
u32 block_key_max = block_key_min + MMB_KEY_BITS;
u8 *block_ptr = bits + (bit * sizeof(MMB_TYPE));
u32 iter_key = it_root->val + bit_idx;
const struct mmbit_sparse_iter *it = it_root + iter_key;
if (block_key_max <= total_bits) {
MMB_TYPE block = mmb_load(block_ptr);
block &= ~it->mask;
mmb_store(block_ptr, block);
} else {
u32 num_bits = total_bits - block_key_min;
MMB_TYPE block = mmbit_get_flat_block(block_ptr, num_bits);
block &= ~it->mask;
mmb_store_partial(block_ptr, block, num_bits);
break; }
}
}
static really_inline
void mmbit_sparse_iter_unset_big(u8 *bits, u32 total_bits,
const struct mmbit_sparse_iter *it_root,
struct mmbit_sparse_state *s) {
const struct mmbit_sparse_iter *it = it_root;
MMB_TYPE block = mmb_load(bits) & it->mask;
if (!block) {
return;
}
u32 key = 0;
const u32 max_level = mmbit_maxlevel(total_bits);
u32 level = 0;
s[level].mask = block;
s[level].itkey = 0;
for (;;) {
block = s[level].mask;
if (block) {
if (level == max_level) {
u8 *block_ptr =
mmbit_get_level_root(bits, level) + key * sizeof(MMB_TYPE);
MMB_TYPE real_block = mmb_load(block_ptr);
real_block &= ~(it->mask);
mmb_store(block_ptr, real_block);
goto uplevel; } else {
u32 bit = mmb_ctz(block);
key = (key << MMB_KEY_SHIFT) + bit;
level++;
u32 iter_key = it->val + mmbit_mask_index(bit, it->mask);
it = it_root + iter_key;
MMB_TYPE nextblock =
mmb_load(mmbit_get_level_root_const(bits, level) +
key * sizeof(MMB_TYPE));
s[level].mask = nextblock & it->mask;
s[level].itkey = iter_key;
}
} else {
uplevel:
if (level == 0) {
return; }
u8 *block_ptr =
mmbit_get_level_root(bits, level) + key * sizeof(MMB_TYPE);
MMB_TYPE real_block = mmb_load(block_ptr);
key >>= MMB_KEY_SHIFT;
level--;
if (real_block == 0) {
u32 bit = mmb_ctz(s[level].mask);
u8 *parent_ptr =
mmbit_get_level_root(bits, level) + key * sizeof(MMB_TYPE);
MMB_TYPE parent_block = mmb_load(parent_ptr);
mmb_clear(&parent_block, bit);
mmb_store(parent_ptr, parent_block);
}
s[level].mask &= (s[level].mask - 1);
it = it_root + s[level].itkey;
}
}
}
static really_inline
void mmbit_sparse_iter_unset(u8 *bits, u32 total_bits,
const struct mmbit_sparse_iter *it,
struct mmbit_sparse_state *s) {
assert(ISALIGNED_N(it, alignof(struct mmbit_sparse_iter)));
#ifndef _WIN32
assert(ISALIGNED_N(s, alignof(struct mmbit_sparse_state)));
#else
assert(ISALIGNED_N(s, 4));
#endif
MDEBUG_PRINTF("%p total_bits %u\n", bits, total_bits);
#ifdef MMB_TRACE_WRITES
MMB_TRACE("ITER-UNSET iter=[");
mmbit_sparse_iter_dump(it, total_bits);
printf("] actually on=[");
struct mmbit_sparse_state tmp[MAX_SPARSE_ITER_STATES];
u32 idx = 0;
u32 i = mmbit_sparse_iter_begin(bits, total_bits, &idx, it, tmp);
for (; i != MMB_INVALID;
i = mmbit_sparse_iter_next(bits, total_bits, i, &idx, it, tmp)) {
printf(" %u", i);
}
printf("]\n");
#endif
if (mmbit_is_flat_model(total_bits)) {
mmbit_sparse_iter_unset_flat(bits, total_bits, it);
} else {
mmbit_sparse_iter_unset_big(bits, total_bits, it, s);
}
}
#ifdef __cplusplus
} #endif
#endif