mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Extract ColorModeMask into EnumBitmask helper
This commit is contained in:
		| @@ -1,6 +1,7 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <cstdint> | #include <cstdint> | ||||||
|  | #include "esphome/core/enum_bitmask.h" | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace light { | namespace light { | ||||||
| @@ -104,16 +105,16 @@ constexpr ColorModeHelper operator|(ColorModeHelper lhs, ColorMode rhs) { | |||||||
|   return static_cast<ColorMode>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs)); |   return static_cast<ColorMode>(static_cast<uint8_t>(lhs) | static_cast<uint8_t>(rhs)); | ||||||
| } | } | ||||||
|  |  | ||||||
| // Type alias for raw color mode bitmask values | // Type alias for raw color mode bitmask values (retained for compatibility) | ||||||
| using color_mode_bitmask_t = uint16_t; | using color_mode_bitmask_t = uint16_t; | ||||||
|  |  | ||||||
| // Constants for ColorMode count and bit range | // Number of ColorMode enum values | ||||||
| static constexpr int COLOR_MODE_COUNT = 10;                             // UNKNOWN through RGB_COLD_WARM_WHITE | constexpr int COLOR_MODE_BITMASK_SIZE = 10; | ||||||
| static constexpr int MAX_BIT_INDEX = sizeof(color_mode_bitmask_t) * 8;  // Number of bits in bitmask type |  | ||||||
|  |  | ||||||
| // Compile-time array of all ColorMode values in declaration order | // Shared lookup table for ColorMode bit mapping | ||||||
| // Bit positions (0-9) map directly to enum declaration order | // This array defines the canonical order of color modes (bit 0-9) | ||||||
| static constexpr ColorMode COLOR_MODES[COLOR_MODE_COUNT] = { | // Declared early so it can be used by constexpr functions | ||||||
|  | constexpr ColorMode COLOR_MODE_LOOKUP[COLOR_MODE_BITMASK_SIZE] = { | ||||||
|     ColorMode::UNKNOWN,                // bit 0 |     ColorMode::UNKNOWN,                // bit 0 | ||||||
|     ColorMode::ON_OFF,                 // bit 1 |     ColorMode::ON_OFF,                 // bit 1 | ||||||
|     ColorMode::BRIGHTNESS,             // bit 2 |     ColorMode::BRIGHTNESS,             // bit 2 | ||||||
| @@ -126,33 +127,20 @@ static constexpr ColorMode COLOR_MODES[COLOR_MODE_COUNT] = { | |||||||
|     ColorMode::RGB_COLD_WARM_WHITE,    // bit 9 |     ColorMode::RGB_COLD_WARM_WHITE,    // bit 9 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// Map ColorMode enum values to bit positions (0-9) | // Type alias for ColorMode bitmask using generic EnumBitmask template | ||||||
| /// Bit positions follow the enum declaration order | using ColorModeMask = EnumBitmask<ColorMode, COLOR_MODE_BITMASK_SIZE>; | ||||||
| static constexpr int mode_to_bit(ColorMode mode) { |  | ||||||
|   // Linear search through COLOR_MODES array |  | ||||||
|   // Compiler optimizes this to efficient code since array is constexpr |  | ||||||
|   for (int i = 0; i < COLOR_MODE_COUNT; ++i) { |  | ||||||
|     if (COLOR_MODES[i] == mode) |  | ||||||
|       return i; |  | ||||||
|   } |  | ||||||
|   return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Map bit positions (0-9) to ColorMode enum values | // Number of ColorCapability enum values | ||||||
| /// Bit positions follow the enum declaration order | constexpr int COLOR_CAPABILITY_COUNT = 6; | ||||||
| static constexpr ColorMode bit_to_mode(int bit) { |  | ||||||
|   // Direct lookup in COLOR_MODES array |  | ||||||
|   return (bit >= 0 && bit < COLOR_MODE_COUNT) ? COLOR_MODES[bit] : ColorMode::UNKNOWN; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /// Helper to compute capability bitmask at compile time | /// Helper to compute capability bitmask at compile time | ||||||
| static constexpr color_mode_bitmask_t compute_capability_bitmask(ColorCapability capability) { | constexpr uint16_t compute_capability_bitmask(ColorCapability capability) { | ||||||
|   color_mode_bitmask_t mask = 0; |   uint16_t mask = 0; | ||||||
|   uint8_t cap_bit = static_cast<uint8_t>(capability); |   uint8_t cap_bit = static_cast<uint8_t>(capability); | ||||||
|  |  | ||||||
|   // Check each ColorMode to see if it has this capability |   // Check each ColorMode to see if it has this capability | ||||||
|   for (int bit = 0; bit < COLOR_MODE_COUNT; ++bit) { |   for (int bit = 0; bit < COLOR_MODE_BITMASK_SIZE; ++bit) { | ||||||
|     uint8_t mode_val = static_cast<uint8_t>(bit_to_mode(bit)); |     uint8_t mode_val = static_cast<uint8_t>(COLOR_MODE_LOOKUP[bit]); | ||||||
|     if ((mode_val & cap_bit) != 0) { |     if ((mode_val & cap_bit) != 0) { | ||||||
|       mask |= (1 << bit); |       mask |= (1 << bit); | ||||||
|     } |     } | ||||||
| @@ -160,12 +148,9 @@ static constexpr color_mode_bitmask_t compute_capability_bitmask(ColorCapability | |||||||
|   return mask; |   return mask; | ||||||
| } | } | ||||||
|  |  | ||||||
| // Number of ColorCapability enum values |  | ||||||
| static constexpr int COLOR_CAPABILITY_COUNT = 6; |  | ||||||
|  |  | ||||||
| /// Compile-time lookup table mapping ColorCapability to bitmask | /// Compile-time lookup table mapping ColorCapability to bitmask | ||||||
| /// This array is computed at compile time using constexpr | /// This array is computed at compile time using constexpr | ||||||
| static constexpr color_mode_bitmask_t CAPABILITY_BITMASKS[] = { | constexpr uint16_t CAPABILITY_BITMASKS[] = { | ||||||
|     compute_capability_bitmask(ColorCapability::ON_OFF),             // 1 << 0 |     compute_capability_bitmask(ColorCapability::ON_OFF),             // 1 << 0 | ||||||
|     compute_capability_bitmask(ColorCapability::BRIGHTNESS),         // 1 << 1 |     compute_capability_bitmask(ColorCapability::BRIGHTNESS),         // 1 << 1 | ||||||
|     compute_capability_bitmask(ColorCapability::WHITE),              // 1 << 2 |     compute_capability_bitmask(ColorCapability::WHITE),              // 1 << 2 | ||||||
| @@ -174,105 +159,9 @@ static constexpr color_mode_bitmask_t CAPABILITY_BITMASKS[] = { | |||||||
|     compute_capability_bitmask(ColorCapability::RGB),                // 1 << 5 |     compute_capability_bitmask(ColorCapability::RGB),                // 1 << 5 | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// Bitmask for storing a set of ColorMode values efficiently. |  | ||||||
| /// Replaces std::set<ColorMode> to eliminate red-black tree overhead (~586 bytes). |  | ||||||
| class ColorModeMask { |  | ||||||
|  public: |  | ||||||
|   constexpr ColorModeMask() = default; |  | ||||||
|  |  | ||||||
|   /// Support initializer list syntax: {ColorMode::RGB, ColorMode::WHITE} |  | ||||||
|   constexpr ColorModeMask(std::initializer_list<ColorMode> modes) { |  | ||||||
|     for (auto mode : modes) { |  | ||||||
|       this->add(mode); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   constexpr void add(ColorMode mode) { this->mask_ |= (1 << mode_to_bit(mode)); } |  | ||||||
|  |  | ||||||
|   /// Add multiple modes at once using initializer list |  | ||||||
|   constexpr void add(std::initializer_list<ColorMode> modes) { |  | ||||||
|     for (auto mode : modes) { |  | ||||||
|       this->add(mode); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   constexpr bool contains(ColorMode mode) const { return (this->mask_ & (1 << mode_to_bit(mode))) != 0; } |  | ||||||
|  |  | ||||||
|   constexpr size_t size() const { |  | ||||||
|     // Count set bits using Brian Kernighan's algorithm |  | ||||||
|     // More efficient for sparse bitmasks (typical case: 2-4 modes out of 10) |  | ||||||
|     uint16_t n = this->mask_; |  | ||||||
|     size_t count = 0; |  | ||||||
|     while (n) { |  | ||||||
|       n &= n - 1;  // Clear the least significant set bit |  | ||||||
|       count++; |  | ||||||
|     } |  | ||||||
|     return count; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   constexpr bool empty() const { return this->mask_ == 0; } |  | ||||||
|  |  | ||||||
|   /// Iterator support for API encoding |  | ||||||
|   class Iterator { |  | ||||||
|    public: |  | ||||||
|     using iterator_category = std::forward_iterator_tag; |  | ||||||
|     using value_type = ColorMode; |  | ||||||
|     using difference_type = std::ptrdiff_t; |  | ||||||
|     using pointer = const ColorMode *; |  | ||||||
|     using reference = ColorMode; |  | ||||||
|  |  | ||||||
|     constexpr Iterator(color_mode_bitmask_t mask, int bit) : mask_(mask), bit_(bit) { advance_to_next_set_bit_(); } |  | ||||||
|  |  | ||||||
|     constexpr ColorMode operator*() const { return bit_to_mode(bit_); } |  | ||||||
|  |  | ||||||
|     constexpr Iterator &operator++() { |  | ||||||
|       ++bit_; |  | ||||||
|       advance_to_next_set_bit_(); |  | ||||||
|       return *this; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     constexpr bool operator==(const Iterator &other) const { return bit_ == other.bit_; } |  | ||||||
|  |  | ||||||
|     constexpr bool operator!=(const Iterator &other) const { return !(*this == other); } |  | ||||||
|  |  | ||||||
|    private: |  | ||||||
|     constexpr void advance_to_next_set_bit_() { bit_ = ColorModeMask::find_next_set_bit(mask_, bit_); } |  | ||||||
|  |  | ||||||
|     color_mode_bitmask_t mask_; |  | ||||||
|     int bit_; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   constexpr Iterator begin() const { return Iterator(mask_, 0); } |  | ||||||
|   constexpr Iterator end() const { return Iterator(mask_, MAX_BIT_INDEX); } |  | ||||||
|  |  | ||||||
|   /// Get the raw bitmask value for API encoding |  | ||||||
|   constexpr color_mode_bitmask_t get_mask() const { return this->mask_; } |  | ||||||
|  |  | ||||||
|   /// Find the next set bit in a bitmask starting from a given position |  | ||||||
|   /// Returns the bit position, or MAX_BIT_INDEX if no more bits are set |  | ||||||
|   static constexpr int find_next_set_bit(color_mode_bitmask_t mask, int start_bit) { |  | ||||||
|     int bit = start_bit; |  | ||||||
|     while (bit < MAX_BIT_INDEX && !(mask & (1 << bit))) { |  | ||||||
|       ++bit; |  | ||||||
|     } |  | ||||||
|     return bit; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Find the first set bit in a bitmask and return the corresponding ColorMode |  | ||||||
|   /// Used for optimizing compute_color_mode_() intersection logic |  | ||||||
|   static constexpr ColorMode first_mode_from_mask(color_mode_bitmask_t mask) { |  | ||||||
|     return bit_to_mode(find_next_set_bit(mask, 0)); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   /// Check if a ColorMode is present in a raw bitmask value |  | ||||||
|   /// Useful for checking intersection results without creating a temporary ColorModeMask |  | ||||||
|   static constexpr bool mask_contains(color_mode_bitmask_t mask, ColorMode mode) { |  | ||||||
|     return (mask & (1 << mode_to_bit(mode))) != 0; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
| /// Check if any mode in the bitmask has a specific capability | /// Check if any mode in the bitmask has a specific capability | ||||||
| /// Used for checking if a light supports a capability (e.g., BRIGHTNESS, RGB) | /// Used for checking if a light supports a capability (e.g., BRIGHTNESS, RGB) | ||||||
|   bool has_capability(ColorCapability capability) const { | inline bool has_capability(const ColorModeMask &mask, ColorCapability capability) { | ||||||
|   // Lookup the pre-computed bitmask for this capability and check intersection with our mask |   // Lookup the pre-computed bitmask for this capability and check intersection with our mask | ||||||
|   // ColorCapability values: 1, 2, 4, 8, 16, 32 -> array indices: 0, 1, 2, 3, 4, 5 |   // ColorCapability values: 1, 2, 4, 8, 16, 32 -> array indices: 0, 1, 2, 3, 4, 5 | ||||||
|   // We need to convert the power-of-2 value to an index |   // We need to convert the power-of-2 value to an index | ||||||
| @@ -288,16 +177,33 @@ class ColorModeMask { | |||||||
|     ++index; |     ++index; | ||||||
|   } |   } | ||||||
| #endif | #endif | ||||||
|     return (this->mask_ & CAPABILITY_BITMASKS[index]) != 0; |   return (mask.get_mask() & CAPABILITY_BITMASKS[index]) != 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  private: |  | ||||||
|   // Using uint16_t instead of uint32_t for more efficient iteration (fewer bits to scan). |  | ||||||
|   // Currently only 10 ColorMode values exist, so 16 bits is sufficient. |  | ||||||
|   // Can be changed to uint32_t if more than 16 color modes are needed in the future. |  | ||||||
|   // Note: Due to struct padding, uint16_t and uint32_t result in same LightTraits size (12 bytes). |  | ||||||
|   color_mode_bitmask_t mask_{0}; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| }  // namespace light | }  // namespace light | ||||||
| }  // namespace esphome | }  // namespace esphome | ||||||
|  |  | ||||||
|  | // Template specializations for ColorMode must be in global namespace | ||||||
|  |  | ||||||
|  | /// Map ColorMode enum values to bit positions (0-9) | ||||||
|  | /// Bit positions follow the enum declaration order | ||||||
|  | template<> | ||||||
|  | constexpr int esphome::EnumBitmask<esphome::light::ColorMode, esphome::light::COLOR_MODE_BITMASK_SIZE>::enum_to_bit( | ||||||
|  |     esphome::light::ColorMode mode) { | ||||||
|  |   // Linear search through COLOR_MODE_LOOKUP array | ||||||
|  |   // Compiler optimizes this to efficient code since array is constexpr | ||||||
|  |   for (int i = 0; i < esphome::light::COLOR_MODE_BITMASK_SIZE; ++i) { | ||||||
|  |     if (esphome::light::COLOR_MODE_LOOKUP[i] == mode) | ||||||
|  |       return i; | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Map bit positions (0-9) to ColorMode enum values | ||||||
|  | /// Bit positions follow the enum declaration order | ||||||
|  | template<> | ||||||
|  | inline esphome::light::ColorMode esphome::EnumBitmask<esphome::light::ColorMode, | ||||||
|  |                                                       esphome::light::COLOR_MODE_BITMASK_SIZE>::bit_to_enum(int bit) { | ||||||
|  |   return (bit >= 0 && bit < esphome::light::COLOR_MODE_BITMASK_SIZE) ? esphome::light::COLOR_MODE_LOOKUP[bit] | ||||||
|  |                                                                      : esphome::light::ColorMode::UNKNOWN; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -437,7 +437,7 @@ ColorMode LightCall::compute_color_mode_() { | |||||||
|  |  | ||||||
|   // Use the preferred suitable mode. |   // Use the preferred suitable mode. | ||||||
|   if (intersection != 0) { |   if (intersection != 0) { | ||||||
|     ColorMode mode = ColorModeMask::first_mode_from_mask(intersection); |     ColorMode mode = ColorModeMask::first_value_from_mask(intersection); | ||||||
|     ESP_LOGI(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(), |     ESP_LOGI(TAG, "'%s': color mode not specified; using %s", this->parent_->get_name().c_str(), | ||||||
|              LOG_STR_ARG(color_mode_to_human(mode))); |              LOG_STR_ARG(color_mode_to_human(mode))); | ||||||
|     return mode; |     return mode; | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ class LightTraits { | |||||||
|  |  | ||||||
|   bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.contains(color_mode); } |   bool supports_color_mode(ColorMode color_mode) const { return this->supported_color_modes_.contains(color_mode); } | ||||||
|   bool supports_color_capability(ColorCapability color_capability) const { |   bool supports_color_capability(ColorCapability color_capability) const { | ||||||
|     return this->supported_color_modes_.has_capability(color_capability); |     return has_capability(this->supported_color_modes_, color_capability); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   float get_min_mireds() const { return this->min_mireds_; } |   float get_min_mireds() const { return this->min_mireds_; } | ||||||
|   | |||||||
							
								
								
									
										155
									
								
								esphome/core/enum_bitmask.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								esphome/core/enum_bitmask.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <cstddef> | ||||||
|  | #include <cstdint> | ||||||
|  | #include <initializer_list> | ||||||
|  | #include <iterator> | ||||||
|  | #include <type_traits> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  |  | ||||||
|  | /// Generic bitmask for storing a set of enum values efficiently. | ||||||
|  | /// Replaces std::set<EnumType> to eliminate red-black tree overhead (~586 bytes per instantiation). | ||||||
|  | /// | ||||||
|  | /// Template parameters: | ||||||
|  | ///   EnumType: The enum type to store (must be uint8_t-based) | ||||||
|  | ///   MaxBits: Maximum number of bits needed (auto-selects uint8_t/uint16_t/uint32_t) | ||||||
|  | /// | ||||||
|  | /// Requirements: | ||||||
|  | ///   - EnumType must be an enum with sequential values starting from 0 | ||||||
|  | ///   - Specialization must provide enum_to_bit() and bit_to_enum() static methods | ||||||
|  | ///   - MaxBits must be sufficient to hold all enum values | ||||||
|  | /// | ||||||
|  | /// Example usage: | ||||||
|  | ///   using ClimateModeMask = EnumBitmask<ClimateMode, 8>; | ||||||
|  | ///   ClimateModeMask modes({CLIMATE_MODE_HEAT, CLIMATE_MODE_COOL}); | ||||||
|  | ///   if (modes.contains(CLIMATE_MODE_HEAT)) { ... } | ||||||
|  | ///   for (auto mode : modes) { ... }  // Iterate over set bits | ||||||
|  | /// | ||||||
|  | /// Design notes: | ||||||
|  | ///   - Uses compile-time type selection for optimal size (uint8_t/uint16_t/uint32_t) | ||||||
|  | ///   - Iterator converts bit positions to actual enum values during traversal | ||||||
|  | ///   - All operations are constexpr-compatible for compile-time initialization | ||||||
|  | ///   - Drop-in replacement for std::set<EnumType> with simpler API | ||||||
|  | /// | ||||||
|  | template<typename EnumType, int MaxBits = 16> class EnumBitmask { | ||||||
|  |  public: | ||||||
|  |   // Automatic bitmask type selection based on MaxBits | ||||||
|  |   // ≤8 bits: uint8_t, ≤16 bits: uint16_t, otherwise: uint32_t | ||||||
|  |   using bitmask_t = | ||||||
|  |       typename std::conditional<(MaxBits <= 8), uint8_t, | ||||||
|  |                                 typename std::conditional<(MaxBits <= 16), uint16_t, uint32_t>::type>::type; | ||||||
|  |  | ||||||
|  |   constexpr EnumBitmask() = default; | ||||||
|  |  | ||||||
|  |   /// Construct from initializer list: {VALUE1, VALUE2, ...} | ||||||
|  |   constexpr EnumBitmask(std::initializer_list<EnumType> values) { | ||||||
|  |     for (auto value : values) { | ||||||
|  |       this->add(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Add a single enum value to the set | ||||||
|  |   constexpr void add(EnumType value) { this->mask_ |= (static_cast<bitmask_t>(1) << enum_to_bit(value)); } | ||||||
|  |  | ||||||
|  |   /// Add multiple enum values from initializer list | ||||||
|  |   constexpr void add(std::initializer_list<EnumType> values) { | ||||||
|  |     for (auto value : values) { | ||||||
|  |       this->add(value); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Remove an enum value from the set | ||||||
|  |   constexpr void remove(EnumType value) { this->mask_ &= ~(static_cast<bitmask_t>(1) << enum_to_bit(value)); } | ||||||
|  |  | ||||||
|  |   /// Clear all values from the set | ||||||
|  |   constexpr void clear() { this->mask_ = 0; } | ||||||
|  |  | ||||||
|  |   /// Check if the set contains a specific enum value | ||||||
|  |   constexpr bool contains(EnumType value) const { | ||||||
|  |     return (this->mask_ & (static_cast<bitmask_t>(1) << enum_to_bit(value))) != 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Count the number of enum values in the set | ||||||
|  |   constexpr size_t size() const { | ||||||
|  |     // Brian Kernighan's algorithm - efficient for sparse bitmasks | ||||||
|  |     // Typical case: 2-4 modes out of 10 possible | ||||||
|  |     bitmask_t n = this->mask_; | ||||||
|  |     size_t count = 0; | ||||||
|  |     while (n) { | ||||||
|  |       n &= n - 1;  // Clear the least significant set bit | ||||||
|  |       count++; | ||||||
|  |     } | ||||||
|  |     return count; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Check if the set is empty | ||||||
|  |   constexpr bool empty() const { return this->mask_ == 0; } | ||||||
|  |  | ||||||
|  |   /// Iterator support for range-based for loops and API encoding | ||||||
|  |   /// Iterates over set bits and converts bit positions to enum values | ||||||
|  |   class Iterator { | ||||||
|  |    public: | ||||||
|  |     using iterator_category = std::forward_iterator_tag; | ||||||
|  |     using value_type = EnumType; | ||||||
|  |     using difference_type = std::ptrdiff_t; | ||||||
|  |     using pointer = const EnumType *; | ||||||
|  |     using reference = EnumType; | ||||||
|  |  | ||||||
|  |     constexpr Iterator(bitmask_t mask, int bit) : mask_(mask), bit_(bit) { advance_to_next_set_bit_(); } | ||||||
|  |  | ||||||
|  |     constexpr EnumType operator*() const { return bit_to_enum(bit_); } | ||||||
|  |  | ||||||
|  |     constexpr Iterator &operator++() { | ||||||
|  |       ++bit_; | ||||||
|  |       advance_to_next_set_bit_(); | ||||||
|  |       return *this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     constexpr bool operator==(const Iterator &other) const { return bit_ == other.bit_; } | ||||||
|  |  | ||||||
|  |     constexpr bool operator!=(const Iterator &other) const { return !(*this == other); } | ||||||
|  |  | ||||||
|  |    private: | ||||||
|  |     constexpr void advance_to_next_set_bit_() { bit_ = find_next_set_bit(mask_, bit_); } | ||||||
|  |  | ||||||
|  |     bitmask_t mask_; | ||||||
|  |     int bit_; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |   constexpr Iterator begin() const { return Iterator(mask_, 0); } | ||||||
|  |   constexpr Iterator end() const { return Iterator(mask_, MaxBits); } | ||||||
|  |  | ||||||
|  |   /// Get the raw bitmask value for optimized operations | ||||||
|  |   constexpr bitmask_t get_mask() const { return this->mask_; } | ||||||
|  |  | ||||||
|  |   /// Check if a specific enum value is present in a raw bitmask | ||||||
|  |   /// Useful for checking intersection results without creating temporary objects | ||||||
|  |   static constexpr bool mask_contains(bitmask_t mask, EnumType value) { | ||||||
|  |     return (mask & (static_cast<bitmask_t>(1) << enum_to_bit(value))) != 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /// Get the first enum value from a raw bitmask | ||||||
|  |   /// Used for optimizing intersection logic (e.g., "pick first suitable mode") | ||||||
|  |   static constexpr EnumType first_value_from_mask(bitmask_t mask) { return bit_to_enum(find_next_set_bit(mask, 0)); } | ||||||
|  |  | ||||||
|  |   /// Find the next set bit in a bitmask starting from a given position | ||||||
|  |   /// Returns the bit position, or MaxBits if no more bits are set | ||||||
|  |   static constexpr int find_next_set_bit(bitmask_t mask, int start_bit) { | ||||||
|  |     int bit = start_bit; | ||||||
|  |     while (bit < MaxBits && !(mask & (static_cast<bitmask_t>(1) << bit))) { | ||||||
|  |       ++bit; | ||||||
|  |     } | ||||||
|  |     return bit; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   // Must be provided by template specialization | ||||||
|  |   // These convert between enum values and bit positions (0, 1, 2, ...) | ||||||
|  |   static constexpr int enum_to_bit(EnumType value); | ||||||
|  |   static EnumType bit_to_enum(int bit);  // Not constexpr due to static array limitation in C++20 | ||||||
|  |  | ||||||
|  |   bitmask_t mask_{0}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace esphome | ||||||
		Reference in New Issue
	
	Block a user