// 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_