/* * M*LIB - BITSET module * * Copyright (c) 2017-2023, Patrick Pelissier * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * + Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MSTARLIB_BITSET_H #define MSTARLIB_BITSET_H #include #include "m-core.h" /********************************** INTERNAL ************************************/ M_BEGIN_PROTECTED_CODE // Define the basic limb of a bitset typedef uint64_t m_b1tset_limb_ct; // And its size in bits #define M_B1TSET_LIMB_BIT (sizeof(m_b1tset_limb_ct) * CHAR_BIT) // bitset grow policy. n is limb size #define M_B1TSET_INC_ALLOC_SIZE(n) ((n) < 4 ? 4 : (n) * 2) // Compute the number of allocated limbs needed to handle 'n' bits. #define M_B1TSET_TO_ALLOC(n) (((n) + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT) // Compute the number of bits available from the allocated size in limbs #define M_B1TSET_FROM_ALLOC(n) ((n) * M_B1TSET_LIMB_BIT) // Contract of a bitset #define M_B1TSET_CONTRACT(t) do { \ M_ASSERT (t != NULL); \ M_ASSERT (t->size <= M_B1TSET_FROM_ALLOC (t->alloc)); \ M_ASSERT (t->alloc <= ((size_t)-1) / M_B1TSET_LIMB_BIT); \ M_ASSERT (t->size < ((size_t)-1) - M_B1TSET_LIMB_BIT); \ M_ASSERT (t->size == 0 || t->ptr != NULL); \ M_ASSERT (t->alloc == 0 || t->ptr != NULL); \ M_ASSERT ((t->size % M_B1TSET_LIMB_BIT) == 0 || (t->ptr[ (t->size-1) / M_B1TSET_LIMB_BIT] & ~(((((m_b1tset_limb_ct)1)<<(t->size % M_B1TSET_LIMB_BIT))<<1)-1)) == 0); \ } while (0) /********************************** EXTERNAL ************************************/ /* Define a type of variable 'bits' or array of packed booleans */ typedef struct m_bitset_s { size_t size; // Size is the number of bits size_t alloc; // Alloc is the number of allocated limbs m_b1tset_limb_ct *ptr; // Pointer to the allocated limbs } m_bitset_t[1]; /* Pointer to a m_bitset_t */ typedef struct m_bitset_s *m_bitset_ptr; /* Constant Pointer to a m_bitset_t */ typedef const struct m_bitset_s *m_bitset_srcptr; /* Iterator on a bitset */ typedef struct m_bitset_it_s { size_t index; // index to the array of bit bool value; // value used for _ref & _cref to store the value struct m_bitset_s *set; // the associated bitset } m_bitset_it_t[1]; /* Initialize a bitset (CONSTRUCTOR) */ M_INLINE void m_bitset_init(m_bitset_t t) { M_ASSERT (t != NULL); M_STATIC_ASSERT (M_POWEROF2_P(M_B1TSET_LIMB_BIT), MLIB_INTERNAL, "M*LIB: BITSET LIMB shall be a power of 2."); t->size = 0; t->alloc = 0; t->ptr = NULL; M_B1TSET_CONTRACT(t); } /* Clean a bitset */ M_INLINE void m_bitset_reset(m_bitset_t t) { M_B1TSET_CONTRACT(t); t->size = 0; } /* Clear a bitset (DESTRUCTOR) */ M_INLINE void m_bitset_clear(m_bitset_t t) { m_bitset_reset(t); M_MEMORY_FREE(t->ptr); // This is not really needed, but is safer // This representation is invalid and will be detected by the contract. // A C compiler should be able to optimize out theses initializations. t->alloc = 1; t->ptr = NULL; } /* Set a bitset to another one */ M_INLINE void m_bitset_set(m_bitset_t d, const m_bitset_t s) { M_B1TSET_CONTRACT(d); M_B1TSET_CONTRACT(s); if (M_UNLIKELY (d == s)) return; const size_t needAlloc = M_B1TSET_TO_ALLOC (s->size); if (M_LIKELY (s->size > 0)) { // Test if enough space in target if (s->size > M_B1TSET_FROM_ALLOC (d->alloc)) { m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, d->ptr, needAlloc); if (M_UNLIKELY_NOMEM (ptr == NULL)) { M_MEMORY_FULL(needAlloc); return ; } d->ptr = ptr; d->alloc = needAlloc; } M_ASSERT(d->ptr != NULL); M_ASSERT(s->ptr != NULL); memcpy (d->ptr, s->ptr, needAlloc * sizeof(m_b1tset_limb_ct) ); } d->size = s->size; M_B1TSET_CONTRACT(d); } /* Initialize & set a bitset to another one (CONSTRUCTOR) */ M_INLINE void m_bitset_init_set(m_bitset_t d, const m_bitset_t s) { M_ASSERT (d != s); m_bitset_init(d); m_bitset_set(d, s); } /* Initialize & move a bitset (CONSTRUCTOR) from another one (DESTRUCTOR) */ M_INLINE void m_bitset_init_move(m_bitset_t d, m_bitset_t s) { M_B1TSET_CONTRACT(s); d->size = s->size; d->alloc = s->alloc; d->ptr = s->ptr; // Illegal representation of a bitset, to be detectable s->alloc = 1; s->ptr = NULL; M_B1TSET_CONTRACT(d); } /* Move a bitset from another one (DESTRUCTOR) */ M_INLINE void m_bitset_move(m_bitset_t d, m_bitset_t s) { m_bitset_clear(d); m_bitset_init_move (d, s); } /* Set the bit 'i' in the bitset to the value 'x' */ M_INLINE void m_bitset_set_at(m_bitset_t v, size_t i, bool x) { M_B1TSET_CONTRACT(v); M_ASSERT (v->ptr != NULL); M_ASSERT_INDEX(i, v->size); const size_t offset = i / M_B1TSET_LIMB_BIT; const size_t index = i % M_B1TSET_LIMB_BIT; // This is a branchless version as x can only be 0 or 1 with only one variable shift. const m_b1tset_limb_ct mask = ((m_b1tset_limb_ct)1)<ptr[offset] = (v->ptr[offset] & ~mask) | (mask & (0-(m_b1tset_limb_ct)x)); M_B1TSET_CONTRACT (v); } /* Flip the bit 'i' in the bitset */ M_INLINE void m_bitset_flip_at(m_bitset_t v, size_t i) { M_B1TSET_CONTRACT(v); M_ASSERT (v->ptr != NULL); M_ASSERT_INDEX(i, v->size); size_t offset = i / M_B1TSET_LIMB_BIT; size_t index = i % M_B1TSET_LIMB_BIT; v->ptr[offset] ^= ((m_b1tset_limb_ct)1)<size >= M_B1TSET_FROM_ALLOC (v->alloc))) { // Compute the needed allocation. const size_t needAlloc = M_B1TSET_INC_ALLOC_SIZE(v->alloc); // Check for integer overflow if (M_UNLIKELY_NOMEM (needAlloc <= v->alloc)) { M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct)); return; } // Alloc memory m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, needAlloc); // Check if success if (M_UNLIKELY_NOMEM (ptr == NULL) ) { M_MEMORY_FULL(needAlloc * sizeof(m_b1tset_limb_ct)); return; } v->ptr = ptr; v->alloc = needAlloc; } M_ASSERT(v->ptr != NULL); const size_t i = v->size; const size_t offset = i / M_B1TSET_LIMB_BIT; const size_t index = i % M_B1TSET_LIMB_BIT; if (M_UNLIKELY(index == 0)) { // A new limb if used. Clear it before using it. v->ptr[offset] = 0; } // This is a branchless version as x can only be 0 or 1 with only one variable shift. const m_b1tset_limb_ct mask = ((m_b1tset_limb_ct)1)<ptr[offset] = (v->ptr[offset] & ~mask) | (mask & (0-(m_b1tset_limb_ct)x)); v->size ++; M_B1TSET_CONTRACT (v); } /* Resize the bitset to have exactly 'size' bits */ M_INLINE void m_bitset_resize (m_bitset_t v, size_t size) { M_B1TSET_CONTRACT (v); // Check for overflow if (M_UNLIKELY_NOMEM (size >= ((size_t)-1) - M_B1TSET_LIMB_BIT)) { M_MEMORY_FULL((size_t) -1); return; } // Compute the needed allocation. size_t newAlloc = M_B1TSET_TO_ALLOC (size); if (newAlloc > v->alloc) { // Allocate more limbs to store the bitset. m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc); if (M_UNLIKELY_NOMEM (ptr == NULL) ) { M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct)); return; } v->ptr = ptr; v->alloc = newAlloc; } // Resize the bitsets const size_t old_size = v->size; const size_t offset = size / M_B1TSET_LIMB_BIT; const size_t index = size % M_B1TSET_LIMB_BIT; const m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1)<ptr[offset] &= mask; } } else if (size > old_size) { // Resize up the bitset: set to 0 new bits. const size_t old_offset = (old_size + M_B1TSET_LIMB_BIT - 1)/ M_B1TSET_LIMB_BIT; for(size_t i = old_offset ; i < offset; i++) { v->ptr[i] = 0; } if (M_LIKELY(index != 0)) { // Mask the last limb to clear the last bits v->ptr[offset] = 0; } } v->size = size; M_B1TSET_CONTRACT (v); } /* Reserve allocation in the bitset to accomodate at least 'size' bits without reallocation */ M_INLINE void m_bitset_reserve (m_bitset_t v, size_t alloc) { M_B1TSET_CONTRACT (v); size_t oldAlloc = M_B1TSET_TO_ALLOC (v->size); size_t newAlloc = M_B1TSET_TO_ALLOC (alloc); // We refuse to reduce allocation below current size if (oldAlloc > newAlloc) { newAlloc = oldAlloc; } if (M_UNLIKELY (newAlloc == 0)) { // Free all memory used by the bitsets M_MEMORY_FREE (v->ptr); v->size = v->alloc = 0; v->ptr = NULL; } else { // Allocate more memory or reduce memory usage m_b1tset_limb_ct *ptr = M_MEMORY_REALLOC (m_b1tset_limb_ct, v->ptr, newAlloc); if (M_UNLIKELY_NOMEM (ptr == NULL) ) { M_MEMORY_FULL(newAlloc * sizeof(m_b1tset_limb_ct)); return; } v->ptr = ptr; v->alloc = newAlloc; } M_B1TSET_CONTRACT (v); } /* Return the value of the boolean at index 'i'. * NOTE: Interface is a little bit different: * It doesn't return a pointer to the data, but the data itself. */ M_INLINE bool m_bitset_get(const m_bitset_t v, size_t i) { M_B1TSET_CONTRACT(v); M_ASSERT (v->ptr != NULL); M_ASSERT_INDEX(i, v->size); size_t offset = i / M_B1TSET_LIMB_BIT; size_t index = i % M_B1TSET_LIMB_BIT; return ( v->ptr[offset] & (((m_b1tset_limb_ct)1) << index) ) != 0; } /* m_bitset_cget is the exact same service than m_bitset_get */ #define m_bitset_cget m_bitset_get /* Pop back the last bit in the bitset */ M_INLINE void m_bitset_pop_back(bool *dest, m_bitset_t v) { M_B1TSET_CONTRACT (v); M_ASSERT_INDEX (0, v->size); // Remove one item from the bitset v->size--; // Prepare clearing popped bit const size_t offset = v->size / M_B1TSET_LIMB_BIT; const size_t index = v->size % M_B1TSET_LIMB_BIT; const m_b1tset_limb_ct mask = ((m_b1tset_limb_ct)1)<ptr[offset] & mask) != 0; } v->ptr[offset] &= mask-1; M_B1TSET_CONTRACT (v); } /* Return the front bit value in the bitset */ M_INLINE bool m_bitset_front(m_bitset_t v) { M_B1TSET_CONTRACT (v); M_ASSERT_INDEX (0, v->size); return m_bitset_get(v, 0); } /* Return the back bit value in the bitset */ M_INLINE bool m_bitset_back(m_bitset_t v) { M_B1TSET_CONTRACT (v); M_ASSERT_INDEX (0, v->size); return m_bitset_get(v, v->size-1); } /* Test if the bitset is empty (no bits stored)*/ M_INLINE bool m_bitset_empty_p(m_bitset_t v) { M_B1TSET_CONTRACT (v); return v->size == 0; } /* Return the number of bits of the bitset */ M_INLINE size_t m_bitset_size(m_bitset_t v) { M_B1TSET_CONTRACT (v); return v->size; } /* Return the capacity in limbs of the bitset */ M_INLINE size_t m_bitset_capacity(m_bitset_t v) { M_B1TSET_CONTRACT (v); return M_B1TSET_FROM_ALLOC (v->alloc); } /* Swap the bit at index i and j of the bitset */ M_INLINE void m_bitset_swap_at (m_bitset_t v, size_t i, size_t j) { M_ASSERT_INDEX(i, v->size); M_ASSERT_INDEX(j, v->size); bool i_val = m_bitset_get(v, i); bool j_val = m_bitset_get(v, j); m_bitset_set_at (v, i, j_val); m_bitset_set_at (v, j, i_val); } /* Swap the bitsets */ M_INLINE void m_bitset_swap (m_bitset_t v1, m_bitset_t v2) { M_B1TSET_CONTRACT (v1); M_B1TSET_CONTRACT (v2); M_SWAP (size_t, v1->size, v2->size); M_SWAP (size_t, v1->alloc, v2->alloc); M_SWAP (m_b1tset_limb_ct *, v1->ptr, v2->ptr); M_B1TSET_CONTRACT (v1); M_B1TSET_CONTRACT (v2); } /* (INTERNAL) Left shift of the bitset (ptr+size) by 1 bit, * integrating the carry in the lowest position. * Return the new carry. */ M_INLINE m_b1tset_limb_ct m_b1tset_lshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry) { for(size_t i = 0; i < n; i++) { m_b1tset_limb_ct v = ptr[i]; ptr[i] = (v << 1) | carry; carry = (v >> (M_B1TSET_LIMB_BIT-1) ); } return carry; } /* (INTERNAL) Right shift of the bitset (ptr+size) by 1 bit, * integrating the carry in the lowest position. * Return the new carry. */ M_INLINE m_b1tset_limb_ct m_b1tset_rshift(m_b1tset_limb_ct ptr[], size_t n, m_b1tset_limb_ct carry) { for(size_t i = n - 1; i < n; i--) { m_b1tset_limb_ct v = ptr[i]; ptr[i] = (v >> 1) | (carry << (M_B1TSET_LIMB_BIT-1) ); carry = v & 1; } return carry; } /* Insert a new bit at position 'key' of value 'value' in the bitset 'set' shifting the set accordingly */ M_INLINE void m_bitset_push_at(m_bitset_t set, size_t key, bool value) { M_B1TSET_CONTRACT (set); // First push another value to extend the array to the right size m_bitset_push_back(set, false); M_ASSERT (set->ptr != NULL); M_ASSERT_INDEX(key, set->size); // Then shift it size_t offset = key / M_B1TSET_LIMB_BIT; size_t index = key % M_B1TSET_LIMB_BIT; m_b1tset_limb_ct v = set->ptr[offset]; m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1)<> (M_B1TSET_LIMB_BIT-1) ); v = (v & mask) | ((unsigned int) value << index) | ((v & ~mask) << 1); set->ptr[offset] = v; size_t size = (set->size + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; M_ASSERT (size >= offset + 1); v = m_b1tset_lshift(&set->ptr[offset+1], size - offset - 1, carry); // v is unused as it should be zero. M_ASSERT(v == 0); (void) v; M_B1TSET_CONTRACT (set); } /* Pop a new bit at position 'key' in the bitset * and return in *dest its value if *dest exists */ M_INLINE void m_bitset_pop_at(bool *dest, m_bitset_t set, size_t key) { M_B1TSET_CONTRACT (set); M_ASSERT (set->ptr != NULL); M_ASSERT_INDEX(key, set->size); if (dest) { *dest = m_bitset_get (set, key); } // Shift it size_t offset = key / M_B1TSET_LIMB_BIT; size_t index = key % M_B1TSET_LIMB_BIT; size_t size = (set->size + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; m_b1tset_limb_ct v, mask, carry; carry = m_b1tset_rshift(&set->ptr[offset+1], size - offset - 1, false); v = set->ptr[offset]; mask = (((m_b1tset_limb_ct)1)<>1) & ~mask) | (carry << (M_B1TSET_LIMB_BIT-1)) ; set->ptr[offset] = v; // Decrease size set->size --; M_B1TSET_CONTRACT (set); } /* Test if two bitsets are equal */ M_INLINE bool m_bitset_equal_p (const m_bitset_t set1, const m_bitset_t set2) { M_B1TSET_CONTRACT (set1); M_B1TSET_CONTRACT (set2); if (set1->size != set2->size) return false; /* We won't compare each bit individualy, but instead compare them per limb */ const size_t limbSize = (set1->size + M_B1TSET_LIMB_BIT -1) / M_B1TSET_LIMB_BIT; for(size_t i = 0 ; i < limbSize;i++) if (set1->ptr[i] != set2->ptr[i]) return false; return true; } /* Initialize an iterator to the first bit of the biset */ M_INLINE void m_bitset_it(m_bitset_it_t it, m_bitset_t set) { M_B1TSET_CONTRACT (set); it->index = 0; it->set = set; } /* Initialize an iterator to reference the same bit as the given one*/ M_INLINE void m_bitset_it_set(m_bitset_it_t it, const m_bitset_it_t itorg) { M_ASSERT (it != NULL && itorg != NULL); it->index = itorg->index; it->set = itorg->set; } /* Initialize an iterator to reference the last bit of the bitset*/ M_INLINE void m_bitset_it_last(m_bitset_it_t it, m_bitset_t set) { M_B1TSET_CONTRACT (set); it->index = set->size-1; it->set = set; } /* Initialize an iterator to reference no valid bit of the bitset*/ M_INLINE void m_bitset_it_end(m_bitset_it_t it, m_bitset_t set) { M_B1TSET_CONTRACT (set); it->index = set->size; it->set = set; } /* Test if an iterator references no valid bit of the bitset anymore */ M_INLINE bool m_bitset_end_p(const m_bitset_it_t it) { M_ASSERT (it != NULL && it->set != NULL); return (it->index) >= (it->set->size); } /* Test if an iterator references the last (or end) bit of the bitset anymore */ M_INLINE bool m_bitset_last_p(const m_bitset_it_t it) { M_ASSERT (it != NULL && it->set != NULL); /* NOTE: Can not compute 'size-1' due to potential overflow if size is 0 */ return (it->index+1) >= (it->set->size); } /* Test if both iterators reference the same bit */ M_INLINE bool m_bitset_it_equal_p(const m_bitset_it_t it1, const m_bitset_it_t it2) { M_ASSERT (it1 != NULL && it2 != NULL); return it1->index == it2->index && it1->set == it2->set; } /* Move the iterator to the next bit */ M_INLINE void m_bitset_next(m_bitset_it_t it) { M_ASSERT (it != NULL && it->set != NULL); it->index++; } /* Move the iterator to the previous bit */ M_INLINE void m_bitset_previous(m_bitset_it_t it) { M_ASSERT (it != NULL && it->set != NULL); it->index--; } // There is no _ref as it is not possible to modify the value using the IT interface /* Return a pointer to the bit referenced by the iterator * Only one reference is possible at a time per iterator */ M_INLINE const bool * m_bitset_cref(m_bitset_it_t it) { M_ASSERT (it != NULL && it->set != NULL); it->value = m_bitset_get(it->set, it->index); return &it->value; } /* Output the bitset as a formatted text in a FILE */ M_INLINE void m_bitset_out_str(FILE *file, const m_bitset_t set) { M_B1TSET_CONTRACT (set); M_ASSERT(file != NULL); fputc ('[', file); for(size_t i = 0; i < set->size; i++) { const bool b = m_bitset_get (set, i); const char c = b ? '1' : '0'; fputc (c, file); } fputc (']', file); } /* Input the bitset from a formatted text in a FILE */ M_INLINE bool m_bitset_in_str(m_bitset_t set, FILE *file) { M_B1TSET_CONTRACT (set); M_ASSERT(file != NULL); m_bitset_reset(set); int c = fgetc(file); if (M_UNLIKELY (c != '[')) return false; c = fgetc(file); while (c == '0' || c == '1') { const bool b = (c == '1'); m_bitset_push_back (set, b); c = fgetc(file); } M_B1TSET_CONTRACT (set); return c == ']'; } /* Parse the bitset from a formatted text in a C string */ M_INLINE bool m_bitset_parse_str(m_bitset_t set, const char str[], const char **endptr) { M_B1TSET_CONTRACT (set); M_ASSERT(str != NULL); bool success = false; m_bitset_reset(set); char c = *str++; if (M_UNLIKELY(c != '[')) goto exit; c = *str++; do { if (M_UNLIKELY(c != '0' && c != '1')) goto exit; const bool b = (c == '1'); m_bitset_push_back (set, b); c = *str++; } while (c != ']' && c != 0); M_B1TSET_CONTRACT (set); success = (c == ']'); exit: if (endptr) *endptr = str; return success; } /* Set the bitset from a formatted text in a C string */ M_INLINE bool m_bitset_set_str(m_bitset_t dest, const char str[]) { return m_bitset_parse_str(dest, str, NULL); } /* Perform an AND operation between the bitsets, * up to the minimum size of both bitsets */ M_INLINE void m_bitset_and(m_bitset_t dest, const m_bitset_t src) { M_B1TSET_CONTRACT(dest); M_B1TSET_CONTRACT(src); size_t s = M_MIN(dest->size, src->size); size_t n = (s + M_B1TSET_LIMB_BIT -1) / M_B1TSET_LIMB_BIT; for(size_t i = 0 ; i < n; i++) dest->ptr[i] &= src->ptr[i]; // Reduce the dest size to the minimum size between both dest->size = s; M_B1TSET_CONTRACT(dest); } /* Perform an OR operation between the bitsets, * up to the minimum size of both bitsets */ M_INLINE void m_bitset_or(m_bitset_t dest, const m_bitset_t src) { M_B1TSET_CONTRACT(dest); M_B1TSET_CONTRACT(src); size_t s = M_MIN(dest->size, src->size); size_t n = (s + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; for(size_t i = 0 ; i < n; i++) dest->ptr[i] |= src->ptr[i]; // Reduce the dest size to the minimum size between both dest->size = s; M_B1TSET_CONTRACT(dest); } /* Perform an XOR operation between the bitsets, * up to the minimum size of both bitsets */ M_INLINE void m_bitset_xor(m_bitset_t dest, const m_bitset_t src) { M_B1TSET_CONTRACT(dest); M_B1TSET_CONTRACT(src); size_t s = M_MIN(dest->size, src->size); size_t n = s / M_B1TSET_LIMB_BIT; size_t m = s % M_B1TSET_LIMB_BIT; for(size_t i = 0 ; i < n; i++) dest->ptr[i] ^= src->ptr[i]; if (M_LIKELY(m)) { // Last limb needs to be masked too m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; dest->ptr[n] = (dest->ptr[n] ^ src->ptr[n]) & mask; } // Reduce the dest size to the minimum size between both dest->size = s; M_B1TSET_CONTRACT(dest); } /* Perform a NOT operation of the bitset */ M_INLINE void m_bitset_not(m_bitset_t dest) { M_B1TSET_CONTRACT(dest); size_t s = dest->size; size_t n = s / M_B1TSET_LIMB_BIT; size_t m = s % M_B1TSET_LIMB_BIT; for(size_t i = 0 ; i < n; i++) dest->ptr[i] = ~ (dest->ptr[i]); if (M_LIKELY(m)) { // Last limb needs to be masked too m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; dest->ptr[n] = (~ dest->ptr[n]) & mask; } M_B1TSET_CONTRACT(dest); } /* Copute a hash of the bitset */ M_INLINE size_t m_bitset_hash(const m_bitset_t set) { M_B1TSET_CONTRACT(set); size_t s = set->size; size_t n = (s + M_B1TSET_LIMB_BIT-1) / M_B1TSET_LIMB_BIT; M_HASH_DECL(hash); for(size_t i = 0 ; i < n; i++) M_HASH_UP(hash, set->ptr[i]); return M_HASH_FINAL (hash); } /* Count the number of leading zero */ M_INLINE size_t m_bitset_clz(const m_bitset_t set) { M_B1TSET_CONTRACT(set); size_t s = set->size; if (M_UNLIKELY (s == 0)) { return 0; } size_t n = (s -1) / M_B1TSET_LIMB_BIT; size_t m = s % M_B1TSET_LIMB_BIT; m_b1tset_limb_ct limb = set->ptr[n]; if (m) { m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; limb &= mask; } else { m = M_B1TSET_LIMB_BIT; } s = 0; while (limb == 0 && n > 0) { s += m; limb = set->ptr[--n]; m = M_B1TSET_LIMB_BIT; } s += m_core_clz64(limb) - (M_B1TSET_LIMB_BIT - m); return s; } /* Count the number of trailing zero */ M_INLINE size_t m_bitset_ctz(const m_bitset_t set) { M_B1TSET_CONTRACT(set); size_t s = set->size; if (M_UNLIKELY (s == 0)) { return 0; } size_t i = 0, n = (s -1) / M_B1TSET_LIMB_BIT; size_t m = s % M_B1TSET_LIMB_BIT; m_b1tset_limb_ct limb = set->ptr[0]; s = 0; while (limb == 0 && i < n) { s += M_B1TSET_LIMB_BIT; limb = set->ptr[++i]; } if (i == n && m != 0) { m_b1tset_limb_ct mask = (((m_b1tset_limb_ct)1) << m) - 1; limb &= mask; } unsigned ctz = m_core_ctz64(limb); s += (ctz == 64) ? m : ctz; return s; } // For GCC or CLANG or ICC #if defined(__GNUC__) M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb) { return (size_t) __builtin_popcountll(limb); } #else // MSVC __popcnt64 may not exist on the target architecture (no emulation layer) // Use emulation layer: https://en.wikipedia.org/wiki/Hamming_weight M_INLINE size_t m_b1tset_popcount64(m_b1tset_limb_ct limb) { limb = limb - ((limb >> 1) & 0x5555555555555555ULL); limb = (limb & 0x3333333333333333ULL) + ((limb >> 2) & 0x3333333333333333ULL); limb = (limb + (limb >> 4)) & 0x0f0f0f0f0f0f0f0fULL; return (limb * 0x0101010101010101ULL) >> 56; } #endif /* Count the number of 1 */ M_INLINE size_t m_bitset_popcount(const m_bitset_t set) { M_B1TSET_CONTRACT(set); size_t s = 0; size_t n = (set->size + M_B1TSET_LIMB_BIT - 1) / M_B1TSET_LIMB_BIT; for(size_t i = 0 ; i < n; i++) s += m_b1tset_popcount64(set->ptr[i]); return s; } /* Oplist for a bitset */ #define M_BITSET_OPLIST \ (INIT(m_bitset_init) \ ,INIT_SET(m_bitset_init_set) \ ,INIT_WITH(API_1(M_INIT_VAI)) \ ,SET(m_bitset_set) \ ,CLEAR(m_bitset_clear) \ ,INIT_MOVE(m_bitset_init_move) \ ,MOVE(m_bitset_move) \ ,SWAP(m_bitset_swap) \ ,TYPE(m_bitset_t) \ ,SUBTYPE(bool) \ ,EMPTY_P(m_bitset_empty_p), \ ,GET_SIZE(m_bitset_size) \ ,IT_TYPE(m_bitset_it_t) \ ,IT_FIRST(m_bitset_it) \ ,IT_SET(m_bitset_it_set) \ ,IT_LAST(m_bitset_it_last) \ ,IT_END(m_bitset_it_end) \ ,IT_END_P(m_bitset_end_p) \ ,IT_LAST_P(m_bitset_last_p) \ ,IT_EQUAL_P(m_bitset_it_equal_p) \ ,IT_NEXT(m_bitset_next) \ ,IT_PREVIOUS(m_bitset_previous) \ ,IT_CREF(m_bitset_cref) \ ,RESET(m_bitset_reset) \ ,PUSH(m_bitset_push_back) \ ,POP(m_bitset_pop_back) \ ,HASH(m_bitset_hash) \ ,GET_STR(m_bitset_get_str) \ ,OUT_STR(m_bitset_out_str) \ ,PARSE_STR(m_bitset_parse_str) \ ,IN_STR(m_bitset_in_str) \ ,EQUAL(m_bitset_equal_p) \ ) /* Register the OPLIST as a global one */ #define M_OPL_m_bitset_t() M_BITSET_OPLIST // TODO: set_at2, insert_v, remove_v #if M_USE_SMALL_NAME #define bitset_s m_bitset_s #define bitset_t m_bitset_t #define bitset_ptr m_bitset_ptr #define bitset_srcptr m_bitset_srcptr #define bitset_it_s m_bitset_it_s #define bitset_it_t m_bitset_it_t #define bitset_init m_bitset_init #define bitset_reset m_bitset_reset #define bitset_clear m_bitset_clear #define bitset_set m_bitset_set #define bitset_init_set m_bitset_init_set #define bitset_init_move m_bitset_init_move #define bitset_move m_bitset_move #define bitset_set_at m_bitset_set_at #define bitset_flip_at m_bitset_flip_at #define bitset_push_back m_bitset_push_back #define bitset_resize m_bitset_resize #define bitset_reserve m_bitset_reserve #define bitset_get m_bitset_get #define bitset_pop_back m_bitset_pop_back #define bitset_front m_bitset_front #define bitset_back m_bitset_back #define bitset_empty_p m_bitset_empty_p #define bitset_size m_bitset_size #define bitset_capacity m_bitset_capacity #define bitset_swap_at m_bitset_swap_at #define bitset_swap m_bitset_swap #define bitset_push_at m_bitset_push_at #define bitset_pop_at m_bitset_pop_at #define bitset_equal_p m_bitset_equal_p #define bitset_it m_bitset_it #define bitset_it_set m_bitset_it_set #define bitset_it_last m_bitset_it_last #define bitset_it_end m_bitset_it_end #define bitset_end_p m_bitset_end_p #define bitset_last_p m_bitset_last_p #define bitset_it_equal_p m_bitset_it_equal_p #define bitset_next m_bitset_next #define bitset_previous m_bitset_previous #define bitset_cref m_bitset_cref #define bitset_out_str m_bitset_out_str #define bitset_in_str m_bitset_in_str #define bitset_parse_str m_bitset_parse_str #define bitset_set_str m_bitset_set_str #define bitset_and m_bitset_and #define bitset_or m_bitset_or #define bitset_xor m_bitset_xor #define bitset_not m_bitset_not #define bitset_hash m_bitset_hash #define bitset_clz m_bitset_clz #define bitset_ctz m_bitset_ctz #define bitset_popcount m_bitset_popcount #define bitset_get_str m_bitset_get_str #define BITSET_OPLIST M_BITSET_OPLIST #define M_OPL_bitset_t M_OPL_m_bitset_t #endif M_END_PROTECTED_CODE #endif // NOTE: Define this function only if m-string has been included #if !defined(MSTARLIB_BITSET_STRING_H) && defined(MSTARLIB_STRING_H) #define MSTARLIB_BITSET_STRING_H M_BEGIN_PROTECTED_CODE /* Output to a m_string_t 'str' the formatted text representation of the bitset 'set' or append it to the strinf (append=true) */ M_INLINE void m_bitset_get_str(m_string_t str, const m_bitset_t set, bool append) { M_B1TSET_CONTRACT (set); M_ASSERT(str != NULL); (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); for(size_t i = 0; i < set->size; i++) { const bool b = m_bitset_get (set, i); const char c = b ? '1' : '0'; m_string_push_back (str, c); } m_string_push_back (str, ']'); } M_END_PROTECTED_CODE #endif