diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 30efdfbb61..3ff65029e1 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -344,13 +344,17 @@ class ProtoEncode { } /// Encode a 48-bit MAC address (stored in a uint64) as varint. /// Real MAC addresses occupy the full 48 bits (OUI in upper 24), so the - /// fast path -- any non-zero bit in bits [42..47] with value fitting in - /// 48 bits -- emits exactly 7 bytes with no per-byte branch. Values outside - /// [1<<42, 1<<48) fall back to the general encoder to preserve correctness - /// if this helper is ever misapplied to a non-MAC uint64. + /// fast path -- any non-zero bit in the top 6 of 48 -- emits exactly 7 bytes + /// with no per-byte branch. Falls back to the general loop otherwise. + /// Caller must guarantee value fits in 48 bits (checked in debug builds). static inline void ESPHOME_ALWAYS_INLINE encode_varint_raw_48bit(uint8_t *__restrict__ &pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value) { - if (value >= (1ULL << 42) && value < (1ULL << 48)) [[likely]] { +#ifdef ESPHOME_DEBUG_API + assert(value < (1ULL << (MAC_ADDRESS_SIZE * 8)) && "encode_varint_raw_48bit: value exceeds 48 bits"); +#endif + // 7-byte varint holds 49 bits (7 * 7), so a 48-bit value needs all 7 bytes + // whenever bit 42 or higher is set (i.e. value >= 1 << (48 - 6)). + if (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6))) [[likely]] { PROTO_ENCODE_CHECK_BOUNDS(pos, 7); pos[0] = static_cast(value | 0x80); pos[1] = static_cast((value >> 7) | 0x80); @@ -840,12 +844,12 @@ class ProtoSize { return field_id_size + varint(value); } /// 48-bit MAC address variant: matches encode_varint_raw_48bit's fast path. - /// When value is in [1<<42, 1<<48) the encoded varint is 7 bytes; values - /// outside that range fall back to the general size calculation so the - /// result stays correct if this helper is ever misapplied. + /// When any of the top 6 of 48 bits is set the encoded varint is 7 bytes; + /// otherwise fall back to the general size calculation. + /// Caller must guarantee value fits in 48 bits (encoder asserts in debug). static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE calc_uint64_48bit_force(uint32_t field_id_size, uint64_t value) { - return field_id_size + ((value >= (1ULL << 42) && value < (1ULL << 48)) ? 7 : varint(value)); + return field_id_size + (value >= (1ULL << (MAC_ADDRESS_SIZE * 8 - 6)) ? 7 : varint(value)); } static constexpr uint32_t calc_length(uint32_t field_id_size, size_t len) { return len ? field_id_size + varint(static_cast(len)) + static_cast(len) : 0;