mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 15:12:06 +00:00 
			
		
		
		
	[light] Extract ColorModeMask into generic FiniteSetMask helper (#11472)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
This commit is contained in:
		| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstdint> | ||||
| #include "esphome/core/finite_set_mask.h" | ||||
|  | ||||
| namespace esphome { | ||||
| namespace light { | ||||
| @@ -107,13 +108,9 @@ constexpr ColorModeHelper operator|(ColorModeHelper lhs, ColorMode rhs) { | ||||
| // Type alias for raw color mode bitmask values | ||||
| using color_mode_bitmask_t = uint16_t; | ||||
|  | ||||
| // Constants for ColorMode count and bit range | ||||
| static constexpr int COLOR_MODE_COUNT = 10;                             // UNKNOWN through RGB_COLD_WARM_WHITE | ||||
| 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 | ||||
| // Bit positions (0-9) map directly to enum declaration order | ||||
| static constexpr ColorMode COLOR_MODES[COLOR_MODE_COUNT] = { | ||||
| // Lookup table for ColorMode bit mapping | ||||
| // This array defines the canonical order of color modes (bit 0-9) | ||||
| constexpr ColorMode COLOR_MODE_LOOKUP[] = { | ||||
|     ColorMode::UNKNOWN,                // bit 0 | ||||
|     ColorMode::ON_OFF,                 // bit 1 | ||||
|     ColorMode::BRIGHTNESS,             // bit 2 | ||||
| @@ -126,33 +123,42 @@ static constexpr ColorMode COLOR_MODES[COLOR_MODE_COUNT] = { | ||||
|     ColorMode::RGB_COLD_WARM_WHITE,    // bit 9 | ||||
| }; | ||||
|  | ||||
| /// Map ColorMode enum values to bit positions (0-9) | ||||
| /// Bit positions follow the enum declaration order | ||||
| 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; | ||||
| } | ||||
| /// Bit mapping policy for ColorMode | ||||
| /// Uses lookup table for non-contiguous enum values | ||||
| struct ColorModeBitPolicy { | ||||
|   using mask_t = uint16_t;  // 10 bits requires uint16_t | ||||
|   static constexpr int MAX_BITS = sizeof(COLOR_MODE_LOOKUP) / sizeof(COLOR_MODE_LOOKUP[0]); | ||||
|  | ||||
| /// Map bit positions (0-9) to ColorMode enum values | ||||
| /// Bit positions follow the enum declaration order | ||||
| 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; | ||||
| } | ||||
|   static constexpr unsigned to_bit(ColorMode mode) { | ||||
|     // Linear search through lookup table | ||||
|     // Compiler optimizes this to efficient code since array is constexpr | ||||
|     for (int i = 0; i < MAX_BITS; ++i) { | ||||
|       if (COLOR_MODE_LOOKUP[i] == mode) | ||||
|         return i; | ||||
|     } | ||||
|     return 0; | ||||
|   } | ||||
|  | ||||
|   static constexpr ColorMode from_bit(unsigned bit) { | ||||
|     return (bit < MAX_BITS) ? COLOR_MODE_LOOKUP[bit] : ColorMode::UNKNOWN; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // Type alias for ColorMode bitmask using policy-based design | ||||
| using ColorModeMask = FiniteSetMask<ColorMode, ColorModeBitPolicy>; | ||||
|  | ||||
| // Number of ColorCapability enum values | ||||
| constexpr int COLOR_CAPABILITY_COUNT = 6; | ||||
|  | ||||
| /// Helper to compute capability bitmask at compile time | ||||
| static constexpr color_mode_bitmask_t compute_capability_bitmask(ColorCapability capability) { | ||||
|   color_mode_bitmask_t mask = 0; | ||||
| constexpr uint16_t compute_capability_bitmask(ColorCapability capability) { | ||||
|   uint16_t mask = 0; | ||||
|   uint8_t cap_bit = static_cast<uint8_t>(capability); | ||||
|  | ||||
|   // Check each ColorMode to see if it has this capability | ||||
|   for (int bit = 0; bit < COLOR_MODE_COUNT; ++bit) { | ||||
|     uint8_t mode_val = static_cast<uint8_t>(bit_to_mode(bit)); | ||||
|   constexpr int color_mode_count = sizeof(COLOR_MODE_LOOKUP) / sizeof(COLOR_MODE_LOOKUP[0]); | ||||
|   for (int bit = 0; bit < color_mode_count; ++bit) { | ||||
|     uint8_t mode_val = static_cast<uint8_t>(COLOR_MODE_LOOKUP[bit]); | ||||
|     if ((mode_val & cap_bit) != 0) { | ||||
|       mask |= (1 << bit); | ||||
|     } | ||||
| @@ -160,12 +166,9 @@ static constexpr color_mode_bitmask_t compute_capability_bitmask(ColorCapability | ||||
|   return mask; | ||||
| } | ||||
|  | ||||
| // Number of ColorCapability enum values | ||||
| static constexpr int COLOR_CAPABILITY_COUNT = 6; | ||||
|  | ||||
| /// Compile-time lookup table mapping ColorCapability to bitmask | ||||
| /// 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::BRIGHTNESS),         // 1 << 1 | ||||
|     compute_capability_bitmask(ColorCapability::WHITE),              // 1 << 2 | ||||
| @@ -174,130 +177,38 @@ static constexpr color_mode_bitmask_t CAPABILITY_BITMASKS[] = { | ||||
|     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 | ||||
|   /// Used for checking if a light supports a capability (e.g., BRIGHTNESS, RGB) | ||||
|   bool has_capability(ColorCapability capability) const { | ||||
|     // 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 | ||||
|     // We need to convert the power-of-2 value to an index | ||||
|     uint8_t cap_val = static_cast<uint8_t>(capability); | ||||
| /** | ||||
|  * @brief Helper function to convert a power-of-2 ColorCapability value to an array index for CAPABILITY_BITMASKS | ||||
|  * lookup. | ||||
|  * | ||||
|  * This function maps ColorCapability values (1, 2, 4, 8, 16, 32) to array indices (0, 1, 2, 3, 4, 5). | ||||
|  * Used to index into the CAPABILITY_BITMASKS lookup table. | ||||
|  * | ||||
|  * @param capability A ColorCapability enum value (must be a power of 2). | ||||
|  * @return The corresponding array index (0-based). | ||||
|  */ | ||||
| inline int capability_to_index(ColorCapability capability) { | ||||
|   uint8_t cap_val = static_cast<uint8_t>(capability); | ||||
| #if defined(__GNUC__) || defined(__clang__) | ||||
|     // Use compiler intrinsic for efficient bit position lookup (O(1) vs O(log n)) | ||||
|     int index = __builtin_ctz(cap_val); | ||||
|   // Use compiler intrinsic for efficient bit position lookup (O(1) vs O(log n)) | ||||
|   return __builtin_ctz(cap_val); | ||||
| #else | ||||
|     // Fallback for compilers without __builtin_ctz | ||||
|     int index = 0; | ||||
|     while (cap_val > 1) { | ||||
|       cap_val >>= 1; | ||||
|       ++index; | ||||
|     } | ||||
| #endif | ||||
|     return (this->mask_ & CAPABILITY_BITMASKS[index]) != 0; | ||||
|   // Fallback for compilers without __builtin_ctz | ||||
|   int index = 0; | ||||
|   while (cap_val > 1) { | ||||
|     cap_val >>= 1; | ||||
|     ++index; | ||||
|   } | ||||
|   return index; | ||||
| #endif | ||||
| } | ||||
|  | ||||
|  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}; | ||||
| }; | ||||
| /// Check if any mode in the bitmask has a specific capability | ||||
| /// Used for checking if a light supports a capability (e.g., BRIGHTNESS, RGB) | ||||
| inline bool has_capability(const ColorModeMask &mask, ColorCapability capability) { | ||||
|   // Lookup the pre-computed bitmask for this capability and check intersection with our mask | ||||
|   return (mask.get_mask() & CAPABILITY_BITMASKS[capability_to_index(capability)]) != 0; | ||||
| } | ||||
|  | ||||
| }  // namespace light | ||||
| }  // namespace esphome | ||||
|   | ||||
| @@ -437,7 +437,7 @@ ColorMode LightCall::compute_color_mode_() { | ||||
|  | ||||
|   // Use the preferred suitable mode. | ||||
|   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(), | ||||
|              LOG_STR_ARG(color_mode_to_human(mode))); | ||||
|     return mode; | ||||
|   | ||||
| @@ -26,9 +26,9 @@ class LightTraits { | ||||
|     this->supported_color_modes_ = ColorModeMask(modes); | ||||
|   } | ||||
|  | ||||
|   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_.count(color_mode) > 0; } | ||||
|   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_; } | ||||
|   | ||||
							
								
								
									
										171
									
								
								esphome/core/finite_set_mask.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								esphome/core/finite_set_mask.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,171 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstddef> | ||||
| #include <cstdint> | ||||
| #include <initializer_list> | ||||
| #include <iterator> | ||||
| #include <type_traits> | ||||
|  | ||||
| namespace esphome { | ||||
|  | ||||
| /// Default bit mapping policy for contiguous enums starting at 0 | ||||
| /// Provides 1:1 mapping where enum value equals bit position | ||||
| template<typename ValueType, int MaxBits> struct DefaultBitPolicy { | ||||
|   // Automatic bitmask type selection based on MaxBits | ||||
|   // ≤8 bits: uint8_t, ≤16 bits: uint16_t, otherwise: uint32_t | ||||
|   using mask_t = typename std::conditional<(MaxBits <= 8), uint8_t, | ||||
|                                            typename std::conditional<(MaxBits <= 16), uint16_t, uint32_t>::type>::type; | ||||
|  | ||||
|   static constexpr int MAX_BITS = MaxBits; | ||||
|  | ||||
|   static constexpr unsigned to_bit(ValueType value) { return static_cast<unsigned>(value); } | ||||
|  | ||||
|   static constexpr ValueType from_bit(unsigned bit) { return static_cast<ValueType>(bit); } | ||||
| }; | ||||
|  | ||||
| /// Generic bitmask for storing a finite set of discrete values efficiently. | ||||
| /// Replaces std::set<ValueType> to eliminate red-black tree overhead (~586 bytes per instantiation). | ||||
| /// | ||||
| /// Template parameters: | ||||
| ///   ValueType: The type to store (typically enum, but can be any discrete bounded type) | ||||
| ///   BitPolicy: Policy class defining bit mapping and mask type (defaults to DefaultBitPolicy) | ||||
| /// | ||||
| /// BitPolicy requirements: | ||||
| ///   - using mask_t = <uint8_t|uint16_t|uint32_t>  // Bitmask storage type | ||||
| ///   - static constexpr int MAX_BITS               // Maximum number of bits | ||||
| ///   - static constexpr unsigned to_bit(ValueType) // Convert value to bit position | ||||
| ///   - static constexpr ValueType from_bit(unsigned) // Convert bit position to value | ||||
| /// | ||||
| /// Example usage (1:1 mapping - climate enums): | ||||
| ///   // For contiguous enums starting at 0, use DefaultBitPolicy | ||||
| ///   using ClimateModeMask = FiniteSetMask<ClimateMode, DefaultBitPolicy<ClimateMode, CLIMATE_MODE_AUTO + 1>>; | ||||
| ///   ClimateModeMask modes({CLIMATE_MODE_HEAT, CLIMATE_MODE_COOL}); | ||||
| ///   if (modes.count(CLIMATE_MODE_HEAT)) { ... } | ||||
| ///   for (auto mode : modes) { ... } | ||||
| /// | ||||
| /// Example usage (custom mapping - ColorMode): | ||||
| ///   // For custom mappings, define a custom BitPolicy | ||||
| ///   // See esphome/components/light/color_mode.h for complete example | ||||
| /// | ||||
| /// Design notes: | ||||
| ///   - Policy-based design allows custom bit mappings without template specialization | ||||
| ///   - Iterator converts bit positions to actual values during traversal | ||||
| ///   - All operations are constexpr-compatible for compile-time initialization | ||||
| ///   - Drop-in replacement for std::set<ValueType> with simpler API | ||||
| /// | ||||
| template<typename ValueType, typename BitPolicy = DefaultBitPolicy<ValueType, 16>> class FiniteSetMask { | ||||
|  public: | ||||
|   using bitmask_t = typename BitPolicy::mask_t; | ||||
|  | ||||
|   constexpr FiniteSetMask() = default; | ||||
|  | ||||
|   /// Construct from initializer list: {VALUE1, VALUE2, ...} | ||||
|   constexpr FiniteSetMask(std::initializer_list<ValueType> values) { | ||||
|     for (auto value : values) { | ||||
|       this->insert(value); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /// Add a single value to the set (std::set compatibility) | ||||
|   constexpr void insert(ValueType value) { this->mask_ |= (static_cast<bitmask_t>(1) << BitPolicy::to_bit(value)); } | ||||
|  | ||||
|   /// Add multiple values from initializer list | ||||
|   constexpr void insert(std::initializer_list<ValueType> values) { | ||||
|     for (auto value : values) { | ||||
|       this->insert(value); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /// Remove a value from the set (std::set compatibility) | ||||
|   constexpr void erase(ValueType value) { this->mask_ &= ~(static_cast<bitmask_t>(1) << BitPolicy::to_bit(value)); } | ||||
|  | ||||
|   /// Clear all values from the set | ||||
|   constexpr void clear() { this->mask_ = 0; } | ||||
|  | ||||
|   /// Check if the set contains a specific value (std::set compatibility) | ||||
|   /// Returns 1 if present, 0 if not (same as std::set for unique elements) | ||||
|   constexpr size_t count(ValueType value) const { | ||||
|     return (this->mask_ & (static_cast<bitmask_t>(1) << BitPolicy::to_bit(value))) != 0 ? 1 : 0; | ||||
|   } | ||||
|  | ||||
|   /// Count the number of 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 values | ||||
|   /// Optimization: removes bits from mask as we iterate | ||||
|   class Iterator { | ||||
|    public: | ||||
|     using iterator_category = std::forward_iterator_tag; | ||||
|     using value_type = ValueType; | ||||
|     using difference_type = std::ptrdiff_t; | ||||
|     using pointer = const ValueType *; | ||||
|     using reference = ValueType; | ||||
|  | ||||
|     constexpr explicit Iterator(bitmask_t mask) : mask_(mask) {} | ||||
|  | ||||
|     constexpr ValueType operator*() const { | ||||
|       // Return value for the first set bit | ||||
|       return BitPolicy::from_bit(find_next_set_bit(mask_, 0)); | ||||
|     } | ||||
|  | ||||
|     constexpr Iterator &operator++() { | ||||
|       // Clear the lowest set bit (Brian Kernighan's algorithm) | ||||
|       mask_ &= mask_ - 1; | ||||
|       return *this; | ||||
|     } | ||||
|  | ||||
|     constexpr bool operator==(const Iterator &other) const { return mask_ == other.mask_; } | ||||
|  | ||||
|     constexpr bool operator!=(const Iterator &other) const { return !(*this == other); } | ||||
|  | ||||
|    private: | ||||
|     bitmask_t mask_; | ||||
|   }; | ||||
|  | ||||
|   constexpr Iterator begin() const { return Iterator(mask_); } | ||||
|   constexpr Iterator end() const { return Iterator(0); } | ||||
|  | ||||
|   /// Get the raw bitmask value for optimized operations | ||||
|   constexpr bitmask_t get_mask() const { return this->mask_; } | ||||
|  | ||||
|   /// Check if a specific value is present in a raw bitmask | ||||
|   /// Useful for checking intersection results without creating temporary objects | ||||
|   static constexpr bool mask_contains(bitmask_t mask, ValueType value) { | ||||
|     return (mask & (static_cast<bitmask_t>(1) << BitPolicy::to_bit(value))) != 0; | ||||
|   } | ||||
|  | ||||
|   /// Get the first value from a raw bitmask | ||||
|   /// Used for optimizing intersection logic (e.g., "pick first suitable mode") | ||||
|   static constexpr ValueType first_value_from_mask(bitmask_t mask) { | ||||
|     return BitPolicy::from_bit(find_next_set_bit(mask, 0)); | ||||
|   } | ||||
|  | ||||
|   /// Find the next set bit in a bitmask starting from a given position | ||||
|   /// Returns the bit position, or MAX_BITS 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 < BitPolicy::MAX_BITS && !(mask & (static_cast<bitmask_t>(1) << bit))) { | ||||
|       ++bit; | ||||
|     } | ||||
|     return bit; | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   bitmask_t mask_{0}; | ||||
| }; | ||||
|  | ||||
| }  // namespace esphome | ||||
		Reference in New Issue
	
	Block a user