| // Copyright 2023 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UI_ACCESSIBILITY_AX_BITSET_H_ |
| #define UI_ACCESSIBILITY_AX_BITSET_H_ |
| |
| #include <stdint.h> |
| |
| #include <bit> |
| #include <optional> |
| |
| #include "base/functional/function_ref.h" |
| |
| namespace ui { |
| |
| // A helper class to store AX-related boolean enums. |
| // IMPORTANT: This AXBitset implementation uses single uint32_t bitmasks and is |
| // therefore limited to managing enums whose underlying integer values are |
| // strictly less than 32 (i.e., in the range [0, 31]). Enum values outside |
| // this range will lead to incorrect behavior or will be ignored. |
| template <typename T> |
| class AXBitset { |
| public: |
| AXBitset() = default; |
| AXBitset(uint32_t initial_set_bits, uint32_t initial_values) |
| : set_bits_(initial_set_bits), values_(initial_values) {} |
| ~AXBitset() = default; |
| |
| uint32_t GetSetBits() const { return set_bits_; } |
| uint32_t GetValues() const { return values_; } |
| |
| // Returns whether enum T at |value| is set to true, false or unset. |
| std::optional<bool> Get(T enum_value) const { |
| uint32_t index = static_cast<uint32_t>(enum_value); |
| uint32_t mask = 1u << index; |
| // Check if the value is set. |
| if (set_bits_ & mask) { |
| return values_ & mask; |
| } |
| return std::nullopt; |
| } |
| |
| // Sets the enum T at |enum_value| to true or false. |
| void Set(T enum_value, bool bool_value) { |
| uint32_t index = static_cast<uint32_t>(enum_value); |
| uint32_t mask = 1u << index; |
| // Mark as set. |
| set_bits_ |= mask; |
| if (bool_value) { |
| // Set the value bit to 1 for true. |
| values_ |= mask; |
| } else { |
| // Clear the value bit to 0 for false. |
| values_ &= ~mask; |
| } |
| } |
| |
| void Unset(T enum_value) { |
| uint32_t index = static_cast<uint32_t>(enum_value); |
| uint32_t mask = 1u << index; |
| // Mark as not set. |
| set_bits_ &= ~mask; |
| } |
| |
| // Iterates over each attribute that is currently "set" (i.e., has been |
| // explicitly set to true or false and not subsequently unset) and invokes |
| // the provided 'function' with the attribute and its boolean value. |
| // The order of iteration is from the least significant bit (lowest enum |
| // value) to the most significant bit (highest enum value) among the set |
| // attributes. |
| void ForEach( |
| base::FunctionRef<void(T attribute, bool value)> function) const { |
| uint32_t remainder = set_bits_; |
| |
| while (remainder) { |
| // Find the index (0-31) of the least significant bit that is set to 1 |
| // in 'remainder'. This corresponds to the enum's integer value. |
| // std::countr_zero counts trailing zeros; e.g., for 0b...1000, it |
| // returns 3. |
| int index = std::countr_zero(remainder); |
| |
| T attribute = static_cast<T>(index); |
| uint32_t mask = 1u << index; |
| bool attribute_value = static_cast<bool>(values_ & mask); |
| |
| function(attribute, attribute_value); |
| |
| // Clear the least significant set bit in 'remainder' to prepare for the |
| // next iteration. This ensures that each set bit is processed exactly |
| // once. |
| remainder &= remainder - 1; |
| } |
| } |
| |
| // Merges the set attributes from another AXBitset into this one. |
| void Append(const AXBitset<T>& other) { |
| // Clear positions in 'this->values_' that will be overridden by 'other'. |
| // These are positions where 'other.set_bits_' has a '1'. |
| // `~other.set_bits_` has '0's at these positions, so ANDing clears them in |
| // `this->values_`. |
| values_ &= ~other.set_bits_; |
| |
| // OR in the relevant values from 'other'. |
| // `(other.values_ & other.set_bits_)` isolates T/F values only for |
| // attributes actually set in 'other'. |
| values_ |= (other.values_ & other.set_bits_); |
| |
| // Ensure attributes set in 'other' are now also marked as set in 'this'. |
| set_bits_ |= other.set_bits_; |
| } |
| |
| // Returns the number of attributes that are currently explicitly set |
| // (i.e., have been Set to true or false and not subsequently Unset). |
| size_t Size() const { return std::popcount(set_bits_); } |
| |
| template <typename U> |
| friend bool operator==(const AXBitset<U>& lhs, const AXBitset<U>& rhs); |
| |
| private: |
| uint32_t set_bits_ = 0; |
| uint32_t values_ = 0; |
| }; |
| |
| template <typename T> |
| bool operator==(const AXBitset<T>& lhs, const AXBitset<T>& rhs) { |
| // Check if the set of active attributes is the same. |
| if (lhs.set_bits_ != rhs.set_bits_) { |
| return false; |
| } |
| |
| // If the set_bits_ are identical, then compare the values for the bits that |
| // are actually set. |
| return (lhs.values_ & lhs.set_bits_) == (rhs.values_ & lhs.set_bits_); |
| } |
| |
| } // namespace ui |
| |
| #endif // UI_ACCESSIBILITY_AX_BITSET_H_ |