| /* | |
| * Copyright 2026 Rive | |
| */ | |
| #ifndef _RIVE_BITWISE_HPP_ | |
| #define _RIVE_BITWISE_HPP_ | |
| #include <type_traits> | |
| #include "rive/rive_types.hpp" | |
| #include "rive/enums.hpp" | |
| #include "rive/math/math_types.hpp" | |
| namespace rive::math | |
| { | |
| // Attempt to generate a "clz" assembly instruction. | |
| RIVE_ALWAYS_INLINE static int clz32(uint32_t x) | |
| { | |
| assert(x != 0); | |
| #if __has_builtin(__builtin_clz) | |
| return __builtin_clz(x); | |
| #else | |
| uint64_t doubleBits = bit_cast<uint64_t>(static_cast<double>(x)); | |
| return 1054 - (doubleBits >> 52); | |
| #endif | |
| } | |
| RIVE_ALWAYS_INLINE static int clz64(uint64_t x) | |
| { | |
| assert(x != 0); | |
| #if __has_builtin(__builtin_clzll) | |
| return __builtin_clzll(x); | |
| #else | |
| uint32_t hi32 = x >> 32; | |
| return hi32 != 0 ? clz32(hi32) : 32 + clz32(x & 0xffffffff); | |
| #endif | |
| } | |
| // Returns the 1-based index of the most significat bit in x. | |
| // | |
| // 0 -> 0 | |
| // 1 -> 1 | |
| // 2..3 -> 2 | |
| // 4..7 -> 3 | |
| // ... | |
| // | |
| RIVE_ALWAYS_INLINE static uint32_t msb(uint32_t x) | |
| { | |
| return x != 0 ? 32 - clz32(x) : 0; | |
| } | |
| // Attempt to generate a "rotl" (rotate-left) assembly instruction. | |
| RIVE_ALWAYS_INLINE static uint32_t rotateleft32(uint32_t x, int y) | |
| { | |
| #if __has_builtin(__builtin_rotateleft32) | |
| return __builtin_rotateleft32(x, y); | |
| #else | |
| return (x << y) | (x >> (32 - y)); | |
| #endif | |
| } | |
| namespace internal | |
| { | |
| // For compilers where there isn't a built-in popcount function, here's the | |
| // simplest efficient implementation. This is done here in a namespace so that | |
| // it can be unit tested even on systems that have built-in popcount support. | |
| template <typename I> constexpr uint32_t count_set_bits_fallback(I i) noexcept | |
| { | |
| uint32_t count = 0; | |
| while (i != 0) | |
| { | |
| count++; | |
| // remove the lowest set bit from the given value | |
| i &= i - 1; | |
| } | |
| return count; | |
| } | |
| } // namespace internal | |
| template <typename T> constexpr uint32_t count_set_bits(T i) noexcept | |
| { | |
| #if __has_builtin(__builtin_popcount) | |
| static_assert(std::is_integral_v<T>); | |
| if constexpr (sizeof(T) == sizeof(uint64_t)) | |
| { | |
| return uint32_t(__builtin_popcountll(uint64_t(i))); | |
| } | |
| else | |
| { | |
| return uint32_t(__builtin_popcount(uint32_t(i))); | |
| } | |
| #else | |
| return internal::count_set_bits_fallback(i); | |
| #endif | |
| } | |
| // Give a value with bits set within a given mask, compact the bits down to the | |
| // minimum number needed to represent the values that are set in the mask. | |
| // For example: a mask of 11010001 has 4 set bits, and so a value of 10x0xxx1 | |
| // (where x is any bit value not within the mask) would be compacted to 1001 | |
| constexpr uint32_t compact_bitmask_value(uint32_t value, uint32_t mask) noexcept | |
| { | |
| uint32_t compacted = 0; | |
| for (int32_t inBitIndex = 31; inBitIndex >= 0; inBitIndex--) | |
| { | |
| auto bit = 1u << inBitIndex; | |
| if ((mask & bit) != 0) | |
| { | |
| compacted <<= 1; | |
| compacted |= ((value & bit) != 0) ? 1 : 0; | |
| } | |
| } | |
| return compacted; | |
| } | |
| // Undo the operation that compactBitmaskValue does. | |
| // For example: a mask of 11010001, with a given compacted vaue of 1001 would | |
| // expand to 10000001 (where each 1 in "compacted" means setting the | |
| // corresponding bit from mask) | |
| constexpr uint32_t expand_compacted_bitmask_value(uint32_t compacted, | |
| uint32_t mask) noexcept | |
| { | |
| uint32_t expanded = 0; | |
| for (int32_t maskBitIndex = 0; maskBitIndex < 32; maskBitIndex++) | |
| { | |
| auto maskBit = 1u << maskBitIndex; | |
| if ((mask & maskBit) != 0) | |
| { | |
| if (compacted & 1) | |
| { | |
| expanded |= maskBit; | |
| } | |
| compacted >>= 1; | |
| } | |
| } | |
| return expanded; | |
| } | |
| // Iteratable returned from iterateBitCombinationsInMask | |
| template <typename T> class BitCombinationIterable | |
| { | |
| public: | |
| explicit constexpr BitCombinationIterable(T mask) : m_mask(mask) {} | |
| class Iterator | |
| { | |
| public: | |
| explicit constexpr Iterator(T mask) noexcept : | |
| m_currentValue(mask), m_mask(mask) | |
| {} | |
| constexpr static Iterator MakeEnd(T mask) noexcept | |
| { | |
| Iterator it{mask}; | |
| it.m_wasAdvanced = true; | |
| return it; | |
| } | |
| constexpr Iterator& operator++() noexcept | |
| { | |
| m_wasAdvanced = true; | |
| // Do this operation as an unsigned integral version of the type. | |
| // For enums this allows the subtraction below to work, and for | |
| // signed integers this allows us to do wrapping subtraction | |
| // (subtracting from 0) without it being undefined behavior. | |
| // NOTE: The odd "enable_if" in there is a trick to delay evaluation | |
| // of underlying_type<T> until the conditional gets chosen, because | |
| // otherwise this won't compile for non-enum types | |
| using U = std::make_unsigned_t< | |
| typename std::conditional_t<std::is_enum_v<T>, | |
| std::underlying_type<T>, | |
| std::enable_if<true, T>>::type>; | |
| // We actually iterate through these combinations from most bits to | |
| // least bits because it's simpler. (Iterating forward can be | |
| // accomplished by starting at 0, and xoring with the mask before | |
| // and after this decrement, but it seemed unnecessary since it's | |
| // unlikely that the ordering matters anywhere) | |
| // This subtraction and mask effectively removes the lowest set bit | |
| // in the mask from the current value (and re-sets and mask bits | |
| // below it), that is with a mask of 01001010 and a current value of | |
| // 01001000, subtracting would give 01000111, then the mask leaves | |
| // it as 01000010, which is the next-lowest valid combination of | |
| // mask bits from where we were. | |
| m_currentValue = T(U(m_currentValue) - 1) & m_mask; | |
| return *this; | |
| } | |
| constexpr Iterator operator++(int) noexcept | |
| { | |
| auto prev = *this; | |
| ++(*this); | |
| return prev; | |
| } | |
| constexpr T operator*() const noexcept { return m_currentValue; } | |
| constexpr bool operator==(const Iterator& other) const noexcept | |
| { | |
| assert(m_mask == other.m_mask); | |
| return (m_currentValue == other.m_currentValue && | |
| m_wasAdvanced == other.m_wasAdvanced); | |
| } | |
| constexpr bool operator!=(const Iterator& other) const noexcept | |
| { | |
| return !(*this == other); | |
| } | |
| private: | |
| T m_currentValue{}; | |
| T m_mask{}; | |
| bool m_wasAdvanced = false; | |
| }; | |
| constexpr Iterator begin() const noexcept { return Iterator{m_mask}; } | |
| constexpr Iterator end() const noexcept | |
| { | |
| return Iterator::MakeEnd(m_mask); | |
| } | |
| private: | |
| T m_mask; | |
| }; | |
| // Iterate through all combinations of set/unset bits for a given mask. | |
| // That is, for a mask that is 101011, it would iterate through: | |
| // 101011, 101010, 101001, 101000, 100011, ..., 000001, 000000 | |
| template <typename T> | |
| BitCombinationIterable<T> iterate_bit_combinations_in_mask(T mask) | |
| { | |
| return BitCombinationIterable{mask}; | |
| } | |
| // Shift bits into the bottom of a key value (ensuring that the key value does | |
| // not overflow and the value fits within the specified bit count) | |
| template <typename Key, typename Bits> | |
| [[nodiscard]] Key add_bits_to_key(Key key, | |
| Bits bits, | |
| uint32_t bitCount) noexcept | |
| { | |
| static_assert(sizeof(Bits) <= sizeof(Key)); | |
| assert((key << bitCount) >> bitCount == key); | |
| assert((Key(bits) & ((1ull << bitCount) - 1)) == Key(bits)); | |
| return (key << bitCount) | Key(bits); | |
| } | |
| } // namespace rive::math | |
| #endif |