diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index cb3813ef87..7a638680be 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -20,7 +20,7 @@ void ProtoWriteBuffer::encode_varint_raw_slow_(uint32_t value) { *this->pos_++ = static_cast(value); } -uint32_t ProtoVarInt::parse_slow(const uint8_t *buffer, uint32_t len, proto_varint_value_t &value) { +ProtoVarIntResult ProtoVarInt::parse_slow(const uint8_t *buffer, uint32_t len) { // Multi-byte varint: first byte already checked to have high bit set uint32_t result32 = buffer[0] & 0x7F; #ifdef USE_API_VARINT64 @@ -32,30 +32,28 @@ uint32_t ProtoVarInt::parse_slow(const uint8_t *buffer, uint32_t len, proto_vari uint8_t val = buffer[i]; result32 |= uint32_t(val & 0x7F) << (i * 7); if ((val & 0x80) == 0) { - value = result32; - return i + 1; + return {result32, i + 1}; } } #ifdef USE_API_VARINT64 - return parse_wide(buffer, len, result32, value); + return parse_wide(buffer, len, result32); #else - return PROTO_VARINT_PARSE_FAILED; + return {0, PROTO_VARINT_PARSE_FAILED}; #endif } #ifdef USE_API_VARINT64 -uint32_t ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32, uint64_t &value) { +ProtoVarIntResult ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) { uint64_t result64 = result32; uint32_t limit = std::min(len, uint32_t(10)); for (uint32_t i = 4; i < limit; i++) { uint8_t val = buffer[i]; result64 |= uint64_t(val & 0x7F) << (i * 7); if ((val & 0x80) == 0) { - value = result64; - return i + 1; + return {result64, i + 1}; } } - return PROTO_VARINT_PARSE_FAILED; + return {0, PROTO_VARINT_PARSE_FAILED}; } #endif @@ -224,13 +222,11 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) { value = *ptr++; return true; } - // Separate out variable so the fast path above keeps `value` in a register - proto_varint_value_t wide; - uint32_t consumed = ProtoVarInt::parse_slow(ptr, end - ptr, wide); - if (consumed == PROTO_VARINT_PARSE_FAILED) + auto res = ProtoVarInt::parse_non_empty(ptr, end - ptr); + if (!res.has_value()) return false; - value = wide; - ptr += consumed; + value = res.value; + ptr += res.consumed; return true; }; diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 1d88088536..e9f855785e 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -145,9 +145,7 @@ class ProtoVarInt { // (booleans, small enums, field tags, small message sizes/types). if ((buffer[0] & 0x80) == 0) [[likely]] return {buffer[0], 1}; - ProtoVarIntResult res; - res.consumed = parse_slow(buffer, len, res.value); - return res; + return parse_slow(buffer, len); } /// Parse a varint from buffer (safe for empty buffers). @@ -158,16 +156,13 @@ class ProtoVarInt { return parse_non_empty(buffer, len); } - /// Multi-byte varint (first byte has the high bit set), outlined to keep the fast path small. - /// Writes the value and returns the bytes consumed, PROTO_VARINT_PARSE_FAILED when truncated. - static uint32_t parse_slow(const uint8_t *buffer, uint32_t len, proto_varint_value_t &value) - __attribute__((noinline)); - protected: + // Slow path for multi-byte varints (>= 128), outlined to keep fast path small + static ProtoVarIntResult parse_slow(const uint8_t *buffer, uint32_t len) __attribute__((noinline)); + #ifdef USE_API_VARINT64 /// Continue parsing varint bytes 4-9 with 64-bit arithmetic. - static uint32_t parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32, uint64_t &value) - __attribute__((noinline)); + static ProtoVarIntResult parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) __attribute__((noinline)); #endif };