[api] Inline ProtoVarInt::parse fast path and return consumed in struct

Replace optional<ProtoVarInt> + consumed pointer with ProtoVarIntResult
struct that returns value + consumed count directly. This eliminates
memory stores through a pointer on the fast path (single-byte varints
< 128), keeping everything in registers.

The parse() method is now ESPHOME_ALWAYS_INLINE with the multi-byte
slow path outlined to parse_slow_(). The common case for protobuf
field tags, small enums, booleans, and typical message sizes/types
is a simple high-bit check + register return with no function call.

Measured on ESP32 (Xtensa): try_read_frame_ grows only +12 bytes
(320 → 332) for two inlined parse sites, while eliminating two
function calls per message on the hot path.
This commit is contained in:
J. Nick Koston
2026-03-08 17:13:03 -10:00
parent 5b9cab02be
commit e285fd6815
6 changed files with 196 additions and 184 deletions
+4 -2
View File
@@ -2205,7 +2205,7 @@ def build_message_type(
cpp = ""
if decode_varint:
o = f"bool {desc.name}::decode_varint(uint32_t field_id, ProtoVarInt value) {{\n"
o = f"bool {desc.name}::decode_varint(uint32_t field_id, ProtoVarIntResult value) {{\n"
o += " switch (field_id) {\n"
o += indent("\n".join(decode_varint), " ") + "\n"
o += " default: return false;\n"
@@ -2213,7 +2213,9 @@ def build_message_type(
o += " return true;\n"
o += "}\n"
cpp += o
prot = "bool decode_varint(uint32_t field_id, ProtoVarInt value) override;"
prot = (
"bool decode_varint(uint32_t field_id, ProtoVarIntResult value) override;"
)
protected_content.insert(0, prot)
if decode_length:
o = f"bool {desc.name}::decode_length(uint32_t field_id, ProtoLengthDelimited value) {{\n"