first commit

This commit is contained in:
wenbo13579
2024-02-24 10:39:51 +08:00
commit 1ec0a5e336
18 changed files with 4092 additions and 0 deletions

597
ebtn/bit_array.h Normal file
View File

@@ -0,0 +1,597 @@
#ifndef _BIT_ARRAY_H_
#define _BIT_ARRAY_H_
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
// #define BIT_ARRAY_CONFIG_64
// here can change to uint64_t, if you system is 64bit.
#ifdef BIT_ARRAY_CONFIG_64
typedef uint64_t bit_array_t;
#define BIT_ARRAY_BIT(n) (1ULL << (n))
#else
typedef uint32_t bit_array_t;
#define BIT_ARRAY_BIT(n) (1UL << (n))
#endif
typedef bit_array_t bit_array_val_t;
#define BIT_ARRAY_BITS (sizeof(bit_array_val_t) * 8)
#define BIT_ARRAY_BIT_WORD(bit) ((bit) / BIT_ARRAY_BITS)
#define BIT_ARRAY_BIT_INDEX(bit) ((bit_array_val_t)(bit) & (BIT_ARRAY_BITS - 1U))
#define BIT_ARRAY_MASK(bit) BIT_ARRAY_BIT(BIT_ARRAY_BIT_INDEX(bit))
#define BIT_ARRAY_ELEM(addr, bit) ((addr)[BIT_ARRAY_BIT_WORD(bit)])
// word of all 1s
#define BIT_ARRAY_WORD_MAX (~(bit_array_val_t)0)
#define BIT_ARRAY_SUB_MASK(nbits) ((nbits) ? BIT_ARRAY_WORD_MAX >> (BIT_ARRAY_BITS - (nbits)) : (bit_array_val_t)0)
// A possibly faster way to combine two words with a mask
// #define bitmask_merge(a,b,abits) ((a & abits) | (b & ~abits))
#define bitmask_merge(a, b, abits) (b ^ ((a ^ b) & abits))
/**
* @brief This macro computes the number of bit array variables necessary to
* represent a bitmap with @a num_bits.
*
* @param num_bits Number of bits.
*/
#define BIT_ARRAY_BITMAP_SIZE(num_bits) (1 + ((num_bits)-1) / BIT_ARRAY_BITS)
/**
* @brief Define an array of bit array variables.
*
* This macro defines an array of bit array variables containing at least
* @a num_bits bits.
*
* @note
* If used from file scope, the bits of the array are initialized to zero;
* if used from within a function, the bits are left uninitialized.
*
* @cond INTERNAL_HIDDEN
* @note
* This macro should be replicated in the PREDEFINED field of the documentation
* Doxyfile.
* @endcond
*
* @param name Name of array of bit array variables.
* @param num_bits Number of bits needed.
*/
#define BIT_ARRAY_DEFINE(name, num_bits) bit_array_t name[BIT_ARRAY_BITMAP_SIZE(num_bits)]
#if 1
// See http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
static inline bit_array_val_t _windows_popcount(bit_array_val_t w)
{
w = w - ((w >> 1) & (bit_array_val_t) ~(bit_array_val_t)0 / 3);
w = (w & (bit_array_val_t) ~(bit_array_val_t)0 / 15 * 3) + ((w >> 2) & (bit_array_val_t) ~(bit_array_val_t)0 / 15 * 3);
w = (w + (w >> 4)) & (bit_array_val_t) ~(bit_array_val_t)0 / 255 * 15;
return (bit_array_val_t)(w * ((bit_array_val_t) ~(bit_array_val_t)0 / 255)) >> (sizeof(bit_array_val_t) - 1) * 8;
}
#define POPCOUNT(x) _windows_popcount(x)
#else
#define POPCOUNT(x) (unsigned)__builtin_popcountll(x)
#endif
#define bits_in_top_word(nbits) ((nbits) ? BIT_ARRAY_BIT_INDEX((nbits)-1) + 1 : 0)
static inline void _bit_array_mask_top_word(bit_array_t *target, int num_bits)
{
// Mask top word
int num_of_words = BIT_ARRAY_BITMAP_SIZE(num_bits);
int bits_active = bits_in_top_word(num_bits);
target[num_of_words - 1] &= BIT_ARRAY_SUB_MASK(bits_active);
}
/**
* @brief Bit Array test a bit.
*
* This routine tests whether bit number @a bit of @a target is set or not.
*
* @param target Address of bit array variable or array.
* @param bit Bit number (starting from 0).
*
* @return true if the bit was set, false if it wasn't.
*/
static inline int bit_array_get(const bit_array_t *target, int bit)
{
bit_array_val_t val = BIT_ARRAY_ELEM(target, bit);
return (1 & (val >> (bit & (BIT_ARRAY_BITS - 1)))) != 0;
}
/**
* @brief Bit Array clear a bit.
*
* Bit Array clear bit number @a bit of @a target.
*
* @param target Address of bit array variable or array.
* @param bit Bit number (starting from 0).
*/
static inline void bit_array_clear(bit_array_t *target, int bit)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
BIT_ARRAY_ELEM(target, bit) &= ~mask;
}
/**
* @brief Bit Array set a bit.
*
* Bit Array set bit number @a bit of @a target.
*
* @param target Address of bit array variable or array.
* @param bit Bit number (starting from 0).
*/
static inline void bit_array_set(bit_array_t *target, int bit)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
BIT_ARRAY_ELEM(target, bit) |= mask;
}
/**
* @brief Bit Array toggle a bit.
*
* Bit Array toggle bit number @a bit of @a target.
*
* @param target Address of bit array variable or array.
* @param bit Bit number (starting from 0).
*/
static inline void bit_array_toggle(bit_array_t *target, int bit)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
BIT_ARRAY_ELEM(target, bit) ^= mask;
}
/**
* @brief Bit Array set a bit to a given value.
*
* Bit Array set bit number @a bit of @a target to value @a val.
*
* @param target Address of bit array variable or array.
* @param bit Bit number (starting from 0).
* @param val true for 1, false for 0.
*/
static inline void bit_array_assign(bit_array_t *target, int bit, int val)
{
bit_array_val_t mask = BIT_ARRAY_MASK(bit);
if (val)
{
BIT_ARRAY_ELEM(target, bit) |= mask;
}
else
{
BIT_ARRAY_ELEM(target, bit) &= ~mask;
}
}
static inline void bit_array_clear_all(bit_array_t *target, int num_bits)
{
memset((void *)target, 0, BIT_ARRAY_BITMAP_SIZE(num_bits) * sizeof(bit_array_val_t));
}
static inline void bit_array_set_all(bit_array_t *target, int num_bits)
{
memset((void *)target, 0xff, BIT_ARRAY_BITMAP_SIZE(num_bits) * sizeof(bit_array_val_t));
_bit_array_mask_top_word(target, num_bits);
}
static inline void bit_array_toggle_all(bit_array_t *target, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
target[i] ^= BIT_ARRAY_WORD_MAX;
}
_bit_array_mask_top_word(target, num_bits);
}
//
// Strings and printing
//
// Construct a BIT_ARRAY from a substring with given on and off characters.
// From string method
static inline void bit_array_from_str(bit_array_t *bitarr, const char *str)
{
int i, index;
int space = 0;
int len = strlen(str);
for (i = 0; i < len; i++)
{
index = i - space;
if (strchr("1", str[i]) != NULL)
{
bit_array_set(bitarr, index);
}
else if (strchr("0", str[i]) != NULL)
{
bit_array_clear(bitarr, index);
}
else
{
// error.
space++;
}
}
}
// Takes a char array to write to. `str` must be bitarr->num_of_bits+1 in length
// Terminates string with '\0'
static inline char *bit_array_to_str(const bit_array_t *bitarr, int num_bits, char *str)
{
int i;
for (i = 0; i < num_bits; i++)
{
str[i] = bit_array_get(bitarr, i) ? '1' : '0';
}
str[num_bits] = '\0';
return str;
}
// Takes a char array to write to. `str` must be bitarr->num_of_bits+1 in length
// Terminates string with '\0'
static inline char *bit_array_to_str_8(const bit_array_t *bitarr, int num_bits, char *str)
{
int i;
int space = 0;
for (i = 0; i < num_bits; i++)
{
str[i + space] = bit_array_get(bitarr, i) ? '1' : '0';
if ((i + 1) % 8 == 0)
{
space++;
str[i + space] = ' ';
}
}
str[num_bits + space] = '\0';
return str;
}
//
// Get and set words (internal use only -- no bounds checking)
//
static inline bit_array_val_t _bit_array_get_word(const bit_array_t *target, int num_bits, int start)
{
int word_index = BIT_ARRAY_BIT_WORD(start);
int word_offset = BIT_ARRAY_BIT_INDEX(start);
bit_array_val_t result = target[word_index] >> word_offset;
int bits_taken = BIT_ARRAY_BITS - word_offset;
// word_offset is now the number of bits we need from the next word
// Check the next word has at least some bits
if (word_offset > 0 && start + bits_taken < num_bits)
{
result |= target[word_index + 1] << (BIT_ARRAY_BITS - word_offset);
}
return result;
}
// Set 64 bits from a particular start position
// Doesn't extend bit array
static inline void _bit_array_set_word(bit_array_t *target, int num_bits, int start, bit_array_val_t word)
{
int word_index = BIT_ARRAY_BIT_WORD(start);
int word_offset = BIT_ARRAY_BIT_INDEX(start);
if (word_offset == 0)
{
target[word_index] = word;
}
else
{
target[word_index] = (word << word_offset) | (target[word_index] & BIT_ARRAY_SUB_MASK(word_offset));
if (word_index + 1 < BIT_ARRAY_BITMAP_SIZE(num_bits))
{
target[word_index + 1] = (word >> (BIT_ARRAY_BITS - word_offset)) | (target[word_index + 1] & (BIT_ARRAY_WORD_MAX << word_offset));
}
}
// Mask top word
_bit_array_mask_top_word(target, num_bits);
}
//
// Fill a region (internal use only)
//
// FillAction is fill with 0 or 1 or toggle
typedef enum
{
ZERO_REGION,
FILL_REGION,
SWAP_REGION
} FillAction;
static inline void _bit_array_set_region(bit_array_t *target, int start, int length, FillAction action)
{
if (length == 0)
return;
int first_word = BIT_ARRAY_BIT_WORD(start);
int last_word = BIT_ARRAY_BIT_WORD(start + length - 1);
int foffset = BIT_ARRAY_BIT_INDEX(start);
int loffset = BIT_ARRAY_BIT_INDEX(start + length - 1);
if (first_word == last_word)
{
bit_array_val_t mask = BIT_ARRAY_SUB_MASK(length) << foffset;
switch (action)
{
case ZERO_REGION:
target[first_word] &= ~mask;
break;
case FILL_REGION:
target[first_word] |= mask;
break;
case SWAP_REGION:
target[first_word] ^= mask;
break;
}
}
else
{
// Set first word
switch (action)
{
case ZERO_REGION:
target[first_word] &= BIT_ARRAY_SUB_MASK(foffset);
break;
case FILL_REGION:
target[first_word] |= ~BIT_ARRAY_SUB_MASK(foffset);
break;
case SWAP_REGION:
target[first_word] ^= ~BIT_ARRAY_SUB_MASK(foffset);
break;
}
int i;
// Set whole words
switch (action)
{
case ZERO_REGION:
for (i = first_word + 1; i < last_word; i++)
target[i] = (bit_array_val_t)0;
break;
case FILL_REGION:
for (i = first_word + 1; i < last_word; i++)
target[i] = BIT_ARRAY_WORD_MAX;
break;
case SWAP_REGION:
for (i = first_word + 1; i < last_word; i++)
target[i] ^= BIT_ARRAY_WORD_MAX;
break;
}
// Set last word
switch (action)
{
case ZERO_REGION:
target[last_word] &= ~BIT_ARRAY_SUB_MASK(loffset + 1);
break;
case FILL_REGION:
target[last_word] |= BIT_ARRAY_SUB_MASK(loffset + 1);
break;
case SWAP_REGION:
target[last_word] ^= BIT_ARRAY_SUB_MASK(loffset + 1);
break;
}
}
}
// Get the number of bits set (hamming weight)
static inline int bit_array_num_bits_set(bit_array_t *target, int num_bits)
{
int i;
int num_of_bits_set = 0;
for (i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
if (target[i] > 0)
{
num_of_bits_set += POPCOUNT(target[i]);
}
}
return num_of_bits_set;
}
// Get the number of bits not set (1 - hamming weight)
static inline int bit_array_num_bits_cleared(bit_array_t *target, int num_bits)
{
return num_bits - bit_array_num_bits_set(target, num_bits);
}
// Copy bits from one array to another
// Note: use MACRO bit_array_copy
// Destination and source can be the same bit_array and
// src/dst regions can overlap
static inline void bit_array_copy(bit_array_t *dst, int dstindx, const bit_array_t *src, int srcindx, int length, int src_num_bits, int dst_num_bits)
{
// Num of full words to copy
int num_of_full_words = length / BIT_ARRAY_BITS;
int i;
int bits_in_last_word = bits_in_top_word(length);
if (dst == src && srcindx > dstindx)
{
// Work left to right
for (i = 0; i < num_of_full_words; i++)
{
bit_array_val_t word = _bit_array_get_word(src, src_num_bits, srcindx + i * BIT_ARRAY_BITS);
_bit_array_set_word(dst, dst_num_bits, dstindx + i * BIT_ARRAY_BITS, word);
}
if (bits_in_last_word > 0)
{
bit_array_val_t src_word = _bit_array_get_word(src, src_num_bits, srcindx + i * BIT_ARRAY_BITS);
bit_array_val_t dst_word = _bit_array_get_word(dst, dst_num_bits, dstindx + i * BIT_ARRAY_BITS);
bit_array_val_t mask = BIT_ARRAY_SUB_MASK(bits_in_last_word);
bit_array_val_t word = bitmask_merge(src_word, dst_word, mask);
_bit_array_set_word(dst, dst_num_bits, dstindx + num_of_full_words * BIT_ARRAY_BITS, word);
}
}
else
{
// Work right to left
for (i = 0; i < num_of_full_words; i++)
{
bit_array_val_t word = _bit_array_get_word(src, src_num_bits, srcindx + length - (i + 1) * BIT_ARRAY_BITS);
_bit_array_set_word(dst, dst_num_bits, dstindx + length - (i + 1) * BIT_ARRAY_BITS, word);
}
if (bits_in_last_word > 0)
{
bit_array_val_t src_word = _bit_array_get_word(src, src_num_bits, srcindx);
bit_array_val_t dst_word = _bit_array_get_word(dst, dst_num_bits, dstindx);
bit_array_val_t mask = BIT_ARRAY_SUB_MASK(bits_in_last_word);
bit_array_val_t word = bitmask_merge(src_word, dst_word, mask);
_bit_array_set_word(dst, dst_num_bits, dstindx, word);
}
}
_bit_array_mask_top_word(dst, dst_num_bits);
}
// copy all of src to dst. dst is resized to match src.
static inline void bit_array_copy_all(bit_array_t *dst, const bit_array_t *src, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dst[i] = src[i];
}
}
//
// Logic operators
//
// Destination can be the same as one or both of the sources
static inline void bit_array_and(bit_array_t *dest, const bit_array_t *src1, const bit_array_t *src2, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = src1[i] & src2[i];
}
}
static inline void bit_array_or(bit_array_t *dest, const bit_array_t *src1, const bit_array_t *src2, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = src1[i] | src2[i];
}
}
static inline void bit_array_xor(bit_array_t *dest, const bit_array_t *src1, const bit_array_t *src2, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = src1[i] ^ src2[i];
}
}
static inline void bit_array_not(bit_array_t *dest, const bit_array_t *src, int num_bits)
{
for (int i = 0; i < BIT_ARRAY_BITMAP_SIZE(num_bits); i++)
{
dest[i] = ~src[i];
}
}
//
// Shift array left/right. If fill is zero, filled with 0, otherwise 1
//
// Shift towards LSB / lower index
static inline void bit_array_shift_right(bit_array_t *target, int num_bits, int shift_dist, int fill)
{
if (shift_dist >= num_bits)
{
fill ? bit_array_set_all(target, num_bits) : bit_array_clear_all(target, num_bits);
return;
}
else if (shift_dist == 0)
{
return;
}
FillAction action = fill ? FILL_REGION : ZERO_REGION;
int cpy_length = num_bits - shift_dist;
bit_array_copy(target, 0, target, shift_dist, cpy_length, num_bits, num_bits);
_bit_array_set_region(target, cpy_length, shift_dist, action);
}
// Shift towards MSB / higher index
static inline void bit_array_shift_left(bit_array_t *target, int num_bits, int shift_dist, int fill)
{
if (shift_dist >= num_bits)
{
fill ? bit_array_set_all(target, num_bits) : bit_array_clear_all(target, num_bits);
return;
}
else if (shift_dist == 0)
{
return;
}
FillAction action = fill ? FILL_REGION : ZERO_REGION;
int cpy_length = num_bits - shift_dist;
bit_array_copy(target, shift_dist, target, 0, cpy_length, num_bits, num_bits);
_bit_array_set_region(target, 0, shift_dist, action);
}
//
// Comparisons
//
// Compare two bit arrays by value stored, with index 0 being the Least
// Significant Bit (LSB). Arrays must have the same length.
// returns:
// >0 iff bitarr1 > bitarr2
// 0 iff bitarr1 == bitarr2
// <0 iff bitarr1 < bitarr2
static inline int bit_array_cmp(const bit_array_t *bitarr1, const bit_array_t *bitarr2, int num_bits)
{
return memcmp(bitarr1, bitarr2, BIT_ARRAY_BITMAP_SIZE(num_bits) * sizeof(bit_array_val_t));
}
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _BIT_ARRAY_H_ */