diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index 440655a32d5..3d1dc3fde07 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -16,6 +16,19 @@ void ProtoByteBuffer::grow_(size_t n) { this->capacity_ = n; } +uint32_t ProtoSize::varint_slow_(uint32_t value) { + // value is guaranteed >= 128 here (fast path handled inline) + if (value < 16384) { + return 2; // 14 bits + } else if (value < 2097152) { + return 3; // 21 bits + } else if (value < 268435456) { + return 4; // 28 bits + } else { + return 5; // 32 bits (maximum for uint32_t) + } +} + #ifdef USE_API_VARINT64 optional ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed, uint32_t result32) { diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index c24006ec291..bd73243ab52 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -553,23 +553,23 @@ class ProtoSize { * @param value The uint32_t value to calculate size for * @return The number of bytes needed to encode the value */ - static constexpr uint32_t varint(uint32_t value) { - // Optimized varint size calculation using leading zeros - // Each 7 bits requires one byte in the varint encoding + static constexpr inline uint32_t ESPHOME_ALWAYS_INLINE varint(uint32_t value) { if (value < 128) - return 1; // 7 bits, common case for small values - - // For larger values, count bytes needed based on the position of the highest bit set - if (value < 16384) { - return 2; // 14 bits - } else if (value < 2097152) { - return 3; // 21 bits - } else if (value < 268435456) { - return 4; // 28 bits - } else { - return 5; // 32 bits (maximum for uint32_t) + return 1; // Fast path: 7 bits, most common case + if (__builtin_is_constant_evaluated()) { + // Compile-time: full cascade for constexpr callers + if (value < 16384) + return 2; + if (value < 2097152) + return 3; + if (value < 268435456) + return 4; + return 5; } + return varint_slow_(value); } + // Slow path for varint >= 128, outlined to keep fast path small + static uint32_t varint_slow_(uint32_t value) __attribute__((noinline)); /** * @brief Calculates the size in bytes needed to encode a uint64_t value as a varint