Store entity string indices as 3 uint8_t fields in struct padding

Replace uint32_t entity_string_packed_ with 3 separate uint8_t
fields (device_class_idx_, uom_idx_, icon_idx_) placed after the
1-byte EntityFlags struct. These fill the 3 padding bytes that
the compiler would insert anyway for alignment, saving 4 bytes
per entity with zero layout overhead.

The set_entity_strings(uint32_t) API is preserved — it unpacks
into the individual fields.
This commit is contained in:
J. Nick Koston
2026-02-21 00:28:16 -06:00
parent 2e93a7db78
commit 95ee73e07f
3 changed files with 24 additions and 34 deletions
+6 -9
View File
@@ -50,25 +50,22 @@ __attribute__((weak)) const char *entity_device_class_lookup(uint16_t) { return
__attribute__((weak)) const char *entity_uom_lookup(uint16_t) { return ""; }
__attribute__((weak)) const char *entity_icon_lookup(uint16_t) { return ""; }
// Entity device class (from packed index)
// Entity device class (from index)
StringRef EntityBase::get_device_class_ref() const {
return StringRef(
entity_device_class_lookup((this->entity_string_packed_ >> ENTITY_STR_DC_SHIFT) & ENTITY_STR_DC_MASK));
return StringRef(entity_device_class_lookup(this->device_class_idx_));
}
std::string EntityBase::get_device_class() const { return std::string(this->get_device_class_ref().c_str()); }
// Entity unit of measurement (from packed index)
StringRef EntityBase::get_unit_of_measurement_ref() const {
return StringRef(entity_uom_lookup((this->entity_string_packed_ >> ENTITY_STR_UOM_SHIFT) & ENTITY_STR_UOM_MASK));
}
// Entity unit of measurement (from index)
StringRef EntityBase::get_unit_of_measurement_ref() const { return StringRef(entity_uom_lookup(this->uom_idx_)); }
std::string EntityBase::get_unit_of_measurement() const {
return std::string(this->get_unit_of_measurement_ref().c_str());
}
// Entity icon (from packed index)
// Entity icon (from index)
StringRef EntityBase::get_icon_ref() const {
#ifdef USE_ENTITY_ICON
return StringRef(entity_icon_lookup((this->entity_string_packed_ >> ENTITY_STR_ICON_SHIFT) & ENTITY_STR_ICON_MASK));
return StringRef(entity_icon_lookup(this->icon_idx_));
#else
return StringRef(entity_icon_lookup(0));
#endif
+12 -14
View File
@@ -14,21 +14,12 @@
namespace esphome {
// Extern lookup functions for packed entity string tables.
// Extern lookup functions for entity string tables.
// Generated code provides strong definitions; weak defaults return "".
extern const char *entity_device_class_lookup(uint16_t index);
extern const char *entity_uom_lookup(uint16_t index);
extern const char *entity_icon_lookup(uint16_t index);
// Bit layout for entity_string_packed_:
// [31..24] reserved | [23..16] icon (8 bits) | [15..8] UoM (8 bits) | [7..0] device_class (8 bits)
static constexpr uint8_t ENTITY_STR_DC_SHIFT = 0;
static constexpr uint8_t ENTITY_STR_UOM_SHIFT = 8;
static constexpr uint8_t ENTITY_STR_ICON_SHIFT = 16;
static constexpr uint8_t ENTITY_STR_DC_MASK = 0xFF;
static constexpr uint8_t ENTITY_STR_UOM_MASK = 0xFF;
static constexpr uint8_t ENTITY_STR_ICON_MASK = 0xFF;
// Maximum device name length - keep in sync with validate_hostname() in esphome/core/config.py
static constexpr size_t ESPHOME_DEVICE_NAME_MAX_LEN = 31;
@@ -104,9 +95,13 @@ class EntityBase {
this->flags_.entity_category = static_cast<uint8_t>(entity_category);
}
// Set packed entity string indices — one call per entity from codegen.
// Bit layout: [23..16] icon (8 bits) | [15..8] UoM (8 bits) | [7..0] device_class (8 bits)
void set_entity_strings(uint32_t packed) { this->entity_string_packed_ = packed; }
// Set entity string table indices — one call per entity from codegen.
// Packed: [23..16] icon | [15..8] UoM | [7..0] device_class (each 8 bits)
void set_entity_strings(uint32_t packed) {
this->device_class_idx_ = packed & 0xFF;
this->uom_idx_ = (packed >> 8) & 0xFF;
this->icon_idx_ = (packed >> 16) & 0xFF;
}
// Get device class as StringRef (from packed index)
StringRef get_device_class_ref() const;
@@ -199,7 +194,6 @@ class EntityBase {
void calc_object_id_();
StringRef name_;
uint32_t entity_string_packed_{0}; // bits 0-7: device_class, 8-15: uom, 16-23: icon, 24-31: reserved
uint32_t object_id_hash_{};
#ifdef USE_DEVICES
Device *device_{};
@@ -214,6 +208,10 @@ class EntityBase {
uint8_t entity_category : 2; // Supports up to 4 categories
uint8_t reserved : 2; // Reserved for future use
} flags_{};
// String table indices — packed into the 3 padding bytes after flags_
uint8_t device_class_idx_{};
uint8_t uom_idx_{};
uint8_t icon_idx_{};
};
/// Log entity icon if set (for use in dump_config)
+6 -11
View File
@@ -31,21 +31,16 @@ _KEY_DC_IDX = "_entity_dc_idx"
_KEY_UOM_IDX = "_entity_uom_idx"
_KEY_ICON_IDX = "_entity_icon_idx"
# Bit layout for entity_string_packed_ (must match C++ constants in entity_base.h):
#
# uint32_t entity_string_packed_:
# [31 ...... 24][23 ...... 16][15 ....... 8][7 ........ 0]
# reserved (8) icon (8) UoM (8) device_class (8)
# max 255 max 255 max 255
#
# Bit layout for set_entity_strings(packed) — must match C++ setter in entity_base.h:
# [23..16] icon (8 bits) | [15..8] UoM (8 bits) | [7..0] device_class (8 bits)
_DC_SHIFT = 0
_UOM_SHIFT = 8
_ICON_SHIFT = 16
# Maximum unique strings per category (must match bit widths above)
_MAX_DEVICE_CLASSES = 0xFF # 8 bits → 255
_MAX_UNITS = 0xFF # 8 bits → 255
_MAX_ICONS = 0xFF # 8 bits → 255
# Maximum unique strings per category (8-bit index, 0 = not set)
_MAX_DEVICE_CLASSES = 0xFF # 255
_MAX_UNITS = 0xFF # 255
_MAX_ICONS = 0xFF # 255
@dataclass