910e62b5创建于 1月15日历史提交
// 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_