[api] Inline ProtoVarInt::parse fast path and return consumed in struct (#14638)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
J. Nick Koston
2026-03-10 09:10:55 -10:00
committed by GitHub
parent 2c7ef4f758
commit 6e468936ec
6 changed files with 376 additions and 393 deletions
@@ -128,37 +128,37 @@ APIError APIPlaintextFrameHelper::try_read_frame_() {
// Skip indicator byte at position 0
uint8_t varint_pos = 1;
uint32_t consumed = 0;
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
// rx_header_buf_pos_ >= 3 and varint_pos == 1, so len >= 2
auto msg_size_varint = ProtoVarInt::parse_non_empty(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos);
if (!msg_size_varint.has_value()) {
// not enough data there yet
continue;
}
if (msg_size_varint->as_uint32() > MAX_MESSAGE_SIZE) {
if (msg_size_varint.value > MAX_MESSAGE_SIZE) {
state_ = State::FAILED;
HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u", msg_size_varint->as_uint32(),
MAX_MESSAGE_SIZE);
HELPER_LOG("Bad packet: message size %" PRIu32 " exceeds maximum %u",
static_cast<uint32_t>(msg_size_varint.value), MAX_MESSAGE_SIZE);
return APIError::BAD_DATA_PACKET;
}
rx_header_parsed_len_ = msg_size_varint->as_uint16();
rx_header_parsed_len_ = static_cast<uint16_t>(msg_size_varint.value);
// Move to next varint position
varint_pos += consumed;
varint_pos += msg_size_varint.consumed;
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos, &consumed);
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos);
if (!msg_type_varint.has_value()) {
// not enough data there yet
continue;
}
if (msg_type_varint->as_uint32() > std::numeric_limits<uint16_t>::max()) {
if (msg_type_varint.value > std::numeric_limits<uint16_t>::max()) {
state_ = State::FAILED;
HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u", msg_type_varint->as_uint32(),
std::numeric_limits<uint16_t>::max());
HELPER_LOG("Bad packet: message type %" PRIu32 " exceeds maximum %u",
static_cast<uint32_t>(msg_type_varint.value), std::numeric_limits<uint16_t>::max());
return APIError::BAD_DATA_PACKET;
}
rx_header_parsed_type_ = msg_type_varint->as_uint16();
rx_header_parsed_type_ = static_cast<uint16_t>(msg_type_varint.value);
rx_header_parsed_ = true;
}
// header reading done
File diff suppressed because it is too large Load Diff
+51 -51
View File
@@ -399,7 +399,7 @@ class HelloRequest final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class HelloResponse final : public ProtoMessage {
public:
@@ -688,7 +688,7 @@ class CoverCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_FAN
@@ -756,7 +756,7 @@ class FanCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_LIGHT
@@ -846,7 +846,7 @@ class LightCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_SENSOR
@@ -936,7 +936,7 @@ class SwitchCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_TEXT_SENSOR
@@ -988,7 +988,7 @@ class SubscribeLogsRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class SubscribeLogsResponse final : public ProtoMessage {
public:
@@ -1110,7 +1110,7 @@ class HomeassistantActionResponse final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_API_HOMEASSISTANT_STATES
@@ -1176,7 +1176,7 @@ class DSTRule final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class ParsedTimezone final : public ProtoDecodableMessage {
public:
@@ -1190,7 +1190,7 @@ class ParsedTimezone final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class GetTimeResponse final : public ProtoDecodableMessage {
public:
@@ -1261,7 +1261,7 @@ class ExecuteServiceArgument final : public ProtoDecodableMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class ExecuteServiceRequest final : public ProtoDecodableMessage {
public:
@@ -1286,7 +1286,7 @@ class ExecuteServiceRequest final : public ProtoDecodableMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
@@ -1365,7 +1365,7 @@ class CameraImageRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_CLIMATE
@@ -1464,7 +1464,7 @@ class ClimateCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_WATER_HEATER
@@ -1528,7 +1528,7 @@ class WaterHeaterCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_NUMBER
@@ -1584,7 +1584,7 @@ class NumberCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_SELECT
@@ -1636,7 +1636,7 @@ class SelectCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_SIREN
@@ -1696,7 +1696,7 @@ class SirenCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_LOCK
@@ -1752,7 +1752,7 @@ class LockCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_BUTTON
@@ -1785,7 +1785,7 @@ class ButtonCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_MEDIA_PLAYER
@@ -1862,7 +1862,7 @@ class MediaPlayerCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_BLUETOOTH_PROXY
@@ -1879,7 +1879,7 @@ class SubscribeBluetoothLEAdvertisementsRequest final : public ProtoDecodableMes
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothLERawAdvertisement final : public ProtoMessage {
public:
@@ -1929,7 +1929,7 @@ class BluetoothDeviceRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothDeviceConnectionResponse final : public ProtoMessage {
public:
@@ -1963,7 +1963,7 @@ class BluetoothGATTGetServicesRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothGATTDescriptor final : public ProtoMessage {
public:
@@ -2054,7 +2054,7 @@ class BluetoothGATTReadRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothGATTReadResponse final : public ProtoMessage {
public:
@@ -2097,7 +2097,7 @@ class BluetoothGATTWriteRequest final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothGATTReadDescriptorRequest final : public ProtoDecodableMessage {
public:
@@ -2113,7 +2113,7 @@ class BluetoothGATTReadDescriptorRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothGATTWriteDescriptorRequest final : public ProtoDecodableMessage {
public:
@@ -2132,7 +2132,7 @@ class BluetoothGATTWriteDescriptorRequest final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothGATTNotifyRequest final : public ProtoDecodableMessage {
public:
@@ -2149,7 +2149,7 @@ class BluetoothGATTNotifyRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothGATTNotifyDataResponse final : public ProtoMessage {
public:
@@ -2329,7 +2329,7 @@ class BluetoothScannerSetModeRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_VOICE_ASSISTANT
@@ -2347,7 +2347,7 @@ class SubscribeVoiceAssistantRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class VoiceAssistantAudioSettings final : public ProtoMessage {
public:
@@ -2396,7 +2396,7 @@ class VoiceAssistantResponse final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class VoiceAssistantEventData final : public ProtoDecodableMessage {
public:
@@ -2424,7 +2424,7 @@ class VoiceAssistantEventResponse final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class VoiceAssistantAudio final : public ProtoDecodableMessage {
public:
@@ -2444,7 +2444,7 @@ class VoiceAssistantAudio final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class VoiceAssistantTimerEventResponse final : public ProtoDecodableMessage {
public:
@@ -2465,7 +2465,7 @@ class VoiceAssistantTimerEventResponse final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class VoiceAssistantAnnounceRequest final : public ProtoDecodableMessage {
public:
@@ -2484,7 +2484,7 @@ class VoiceAssistantAnnounceRequest final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class VoiceAssistantAnnounceFinished final : public ProtoMessage {
public:
@@ -2530,7 +2530,7 @@ class VoiceAssistantExternalWakeWord final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class VoiceAssistantConfigurationRequest final : public ProtoDecodableMessage {
public:
@@ -2632,7 +2632,7 @@ class AlarmControlPanelCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_TEXT
@@ -2687,7 +2687,7 @@ class TextCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_DATETIME_DATE
@@ -2741,7 +2741,7 @@ class DateCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_DATETIME_TIME
@@ -2795,7 +2795,7 @@ class TimeCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_EVENT
@@ -2886,7 +2886,7 @@ class ValveCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_DATETIME_DATETIME
@@ -2936,7 +2936,7 @@ class DateTimeCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_UPDATE
@@ -2994,7 +2994,7 @@ class UpdateCommandRequest final : public CommandProtoMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_ZWAVE_PROXY
@@ -3034,7 +3034,7 @@ class ZWaveProxyRequest final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
#endif
#ifdef USE_INFRARED
@@ -3079,7 +3079,7 @@ class InfraredRFTransmitRawTimingsRequest final : public ProtoDecodableMessage {
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class InfraredRFReceiveEvent final : public ProtoMessage {
public:
@@ -3121,7 +3121,7 @@ class SerialProxyConfigureRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class SerialProxyDataReceived final : public ProtoMessage {
public:
@@ -3161,7 +3161,7 @@ class SerialProxyWriteRequest final : public ProtoDecodableMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class SerialProxySetModemPinsRequest final : public ProtoDecodableMessage {
public:
@@ -3177,7 +3177,7 @@ class SerialProxySetModemPinsRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class SerialProxyGetModemPinsRequest final : public ProtoDecodableMessage {
public:
@@ -3192,7 +3192,7 @@ class SerialProxyGetModemPinsRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class SerialProxyGetModemPinsResponse final : public ProtoMessage {
public:
@@ -3225,7 +3225,7 @@ class SerialProxyRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class SerialProxyRequestResponse final : public ProtoMessage {
public:
@@ -3265,7 +3265,7 @@ class BluetoothSetConnectionParamsRequest final : public ProtoDecodableMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
bool decode_varint(uint32_t field_id, proto_varint_value_t value) override;
};
class BluetoothSetConnectionParamsResponse final : public ProtoMessage {
public:
+46 -29
View File
@@ -20,20 +20,40 @@ void ProtoWriteBuffer::encode_varint_raw_slow_(uint32_t value) {
*this->pos_++ = static_cast<uint8_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
optional<ProtoVarInt> ProtoVarInt::parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed,
uint32_t result32) {
uint32_t limit = std::min(len, uint32_t(4));
#else
uint32_t limit = std::min(len, uint32_t(5));
#endif
for (uint32_t i = 1; i < limit; i++) {
uint8_t val = buffer[i];
result32 |= uint32_t(val & 0x7F) << (i * 7);
if ((val & 0x80) == 0) {
return {result32, i + 1};
}
}
#ifdef USE_API_VARINT64
return parse_wide(buffer, len, result32);
#else
return {0, PROTO_VARINT_PARSE_FAILED};
#endif
}
#ifdef USE_API_VARINT64
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) {
*consumed = i + 1;
return ProtoVarInt(result64);
return {result64, i + 1};
}
}
return {};
return {0, PROTO_VARINT_PARSE_FAILED};
}
#endif
@@ -43,18 +63,16 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
const uint8_t *end = buffer + length;
while (ptr < end) {
uint32_t consumed;
// Parse field header (tag)
auto res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
// Parse field header (tag) - ptr < end guarantees len >= 1
auto res = ProtoVarInt::parse_non_empty(ptr, end - ptr);
if (!res.has_value()) {
break; // Invalid data, stop counting
}
uint32_t tag = res->as_uint32();
uint32_t tag = static_cast<uint32_t>(res.value);
uint32_t field_type = tag & WIRE_TYPE_MASK;
uint32_t field_id = tag >> 3;
ptr += consumed;
ptr += res.consumed;
// Count if this is the target field
if (field_id == target_field_id) {
@@ -64,20 +82,20 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
// Skip field data based on wire type
switch (field_type) {
case WIRE_TYPE_VARINT: { // VarInt - parse and skip
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
res = ProtoVarInt::parse(ptr, end - ptr);
if (!res.has_value()) {
return count; // Invalid data, return what we have
}
ptr += consumed;
ptr += res.consumed;
break;
}
case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited - parse length and skip data
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
res = ProtoVarInt::parse(ptr, end - ptr);
if (!res.has_value()) {
return count;
}
uint32_t field_length = res->as_uint32();
ptr += consumed;
uint32_t field_length = static_cast<uint32_t>(res.value);
ptr += res.consumed;
if (field_length > static_cast<size_t>(end - ptr)) {
return count; // Out of bounds
}
@@ -190,41 +208,40 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
const uint8_t *end = buffer + length;
while (ptr < end) {
uint32_t consumed;
// Parse field header
auto res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
// Parse field header - ptr < end guarantees len >= 1
auto res = ProtoVarInt::parse_non_empty(ptr, end - ptr);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid field start at offset %ld", (long) (ptr - buffer));
return;
}
uint32_t tag = res->as_uint32();
uint32_t tag = static_cast<uint32_t>(res.value);
uint32_t field_type = tag & WIRE_TYPE_MASK;
uint32_t field_id = tag >> 3;
ptr += consumed;
ptr += res.consumed;
switch (field_type) {
case WIRE_TYPE_VARINT: { // VarInt
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
res = ProtoVarInt::parse(ptr, end - ptr);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer));
return;
}
if (!this->decode_varint(field_id, *res)) {
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
if (!this->decode_varint(field_id, res.value)) {
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu64 "!", field_id,
static_cast<uint64_t>(res.value));
}
ptr += consumed;
ptr += res.consumed;
break;
}
case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited
res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
res = ProtoVarInt::parse(ptr, end - ptr);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer));
return;
}
uint32_t field_length = res->as_uint32();
ptr += consumed;
uint32_t field_length = static_cast<uint32_t>(res.value);
ptr += res.consumed;
if (field_length > static_cast<size_t>(end - ptr)) {
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
return;
+42 -76
View File
@@ -98,90 +98,56 @@ inline void encode_varint_to_buffer(uint32_t val, uint8_t *buffer) {
* within the same function scope where temporaries are created.
*/
/// Representation of a VarInt - in ProtoBuf should be 64bit but we only use 32bit
/// Type used for decoded varint values - uint64_t when BLE needs 64-bit addresses, uint32_t otherwise
#ifdef USE_API_VARINT64
using proto_varint_value_t = uint64_t;
#else
using proto_varint_value_t = uint32_t;
#endif
/// Sentinel value for consumed field indicating parse failure
inline constexpr uint32_t PROTO_VARINT_PARSE_FAILED = 0;
/// Result of parsing a varint: value + number of bytes consumed.
/// consumed == PROTO_VARINT_PARSE_FAILED indicates parse failure (not enough data or invalid).
struct ProtoVarIntResult {
proto_varint_value_t value;
uint32_t consumed; // PROTO_VARINT_PARSE_FAILED = parse failed
constexpr bool has_value() const { return this->consumed != PROTO_VARINT_PARSE_FAILED; }
};
/// Static varint parsing methods for the protobuf wire format.
class ProtoVarInt {
public:
ProtoVarInt() : value_(0) {}
explicit ProtoVarInt(uint64_t value) : value_(value) {}
/// Parse a varint from buffer. consumed must be a valid pointer (not null).
static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
/// Parse a varint from buffer. Caller must ensure len >= 1.
/// Returns result with consumed=0 on failure (truncated multi-byte varint).
static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse_non_empty(const uint8_t *buffer, uint32_t len) {
#ifdef ESPHOME_DEBUG_API
assert(consumed != nullptr);
assert(len > 0);
#endif
if (len == 0)
return {};
// Fast path: single-byte varints (0-127) are the most common case
// (booleans, small enums, field tags). Avoid loop overhead entirely.
if ((buffer[0] & 0x80) == 0) {
*consumed = 1;
return ProtoVarInt(buffer[0]);
}
// 32-bit phase: process remaining bytes with native 32-bit shifts.
// Without USE_API_VARINT64: cover bytes 1-4 (shifts 7, 14, 21, 28) — the uint32_t
// shift at byte 4 (shift by 28) may lose bits 32-34, but those are always zero for valid uint32 values.
// With USE_API_VARINT64: cover bytes 1-3 (shifts 7, 14, 21) so parse_wide handles
// byte 4+ with full 64-bit arithmetic (avoids truncating values > UINT32_MAX).
uint32_t result32 = buffer[0] & 0x7F;
#ifdef USE_API_VARINT64
uint32_t limit = std::min(len, uint32_t(4));
#else
uint32_t limit = std::min(len, uint32_t(5));
#endif
for (uint32_t i = 1; i < limit; i++) {
uint8_t val = buffer[i];
result32 |= uint32_t(val & 0x7F) << (i * 7);
if ((val & 0x80) == 0) {
*consumed = i + 1;
return ProtoVarInt(result32);
}
}
// 64-bit phase for remaining bytes (BLE addresses etc.)
#ifdef USE_API_VARINT64
return parse_wide(buffer, len, consumed, result32);
#else
return {};
#endif
// (booleans, small enums, field tags, small message sizes/types).
if ((buffer[0] & 0x80) == 0) [[likely]]
return {buffer[0], 1};
return parse_slow(buffer, len);
}
/// Parse a varint from buffer (safe for empty buffers).
/// Returns result with consumed=0 on failure (empty buffer or truncated varint).
static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse(const uint8_t *buffer, uint32_t len) {
if (len == 0)
return {0, PROTO_VARINT_PARSE_FAILED};
return parse_non_empty(buffer, len);
}
#ifdef USE_API_VARINT64
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.
/// Separated to keep 64-bit shift code (__ashldi3 on 32-bit platforms) out of the common path.
static optional<ProtoVarInt> parse_wide(const uint8_t *buffer, uint32_t len, uint32_t *consumed, uint32_t result32)
__attribute__((noinline));
public:
#endif
constexpr uint16_t as_uint16() const { return this->value_; }
constexpr uint32_t as_uint32() const { return this->value_; }
constexpr bool as_bool() const { return this->value_; }
constexpr int32_t as_int32() const {
// Not ZigZag encoded
return static_cast<int32_t>(this->value_);
}
constexpr int32_t as_sint32() const {
// with ZigZag encoding
return decode_zigzag32(static_cast<uint32_t>(this->value_));
}
#ifdef USE_API_VARINT64
constexpr uint64_t as_uint64() const { return this->value_; }
constexpr int64_t as_int64() const {
// Not ZigZag encoded
return static_cast<int64_t>(this->value_);
}
constexpr int64_t as_sint64() const {
// with ZigZag encoding
return decode_zigzag64(this->value_);
}
#endif
protected:
#ifdef USE_API_VARINT64
uint64_t value_;
#else
uint32_t value_;
static ProtoVarIntResult parse_wide(const uint8_t *buffer, uint32_t len, uint32_t result32) __attribute__((noinline));
#endif
};
@@ -499,7 +465,7 @@ class ProtoDecodableMessage : public ProtoMessage {
protected:
~ProtoDecodableMessage() = default;
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
virtual bool decode_varint(uint32_t field_id, proto_varint_value_t value) { return false; }
virtual bool decode_length(uint32_t field_id, ProtoLengthDelimited value) { return false; }
virtual bool decode_32bit(uint32_t field_id, Proto32Bit value) { return false; }
// NOTE: decode_64bit removed - wire type 1 not supported
+10 -10
View File
@@ -461,7 +461,7 @@ class FloatType(TypeInfo):
class Int64Type(TypeInfo):
cpp_type = "int64_t"
default_value = "0"
decode_varint = "value.as_int64()"
decode_varint = "static_cast<int64_t>(value)"
encode_func = "encode_int64"
wire_type = WireType.VARINT # Uses wire type 0
@@ -481,7 +481,7 @@ class Int64Type(TypeInfo):
class UInt64Type(TypeInfo):
cpp_type = "uint64_t"
default_value = "0"
decode_varint = "value.as_uint64()"
decode_varint = "value"
encode_func = "encode_uint64"
wire_type = WireType.VARINT # Uses wire type 0
@@ -501,7 +501,7 @@ class UInt64Type(TypeInfo):
class Int32Type(TypeInfo):
cpp_type = "int32_t"
default_value = "0"
decode_varint = "value.as_int32()"
decode_varint = "static_cast<int32_t>(value)"
encode_func = "encode_int32"
wire_type = WireType.VARINT # Uses wire type 0
@@ -573,7 +573,7 @@ class Fixed32Type(TypeInfo):
class BoolType(TypeInfo):
cpp_type = "bool"
default_value = "false"
decode_varint = "value.as_bool()"
decode_varint = "value != 0"
encode_func = "encode_bool"
wire_type = WireType.VARINT # Uses wire type 0
@@ -1151,7 +1151,7 @@ class FixedArrayBytesType(TypeInfo):
class UInt32Type(TypeInfo):
cpp_type = "uint32_t"
default_value = "0"
decode_varint = "value.as_uint32()"
decode_varint = "value"
encode_func = "encode_uint32"
wire_type = WireType.VARINT # Uses wire type 0
@@ -1175,7 +1175,7 @@ class EnumType(TypeInfo):
@property
def decode_varint(self) -> str:
return f"static_cast<{self.cpp_type}>(value.as_uint32())"
return f"static_cast<{self.cpp_type}>(value)"
default_value = ""
wire_type = WireType.VARINT # Uses wire type 0
@@ -1262,7 +1262,7 @@ class SFixed64Type(TypeInfo):
class SInt32Type(TypeInfo):
cpp_type = "int32_t"
default_value = "0"
decode_varint = "value.as_sint32()"
decode_varint = "decode_zigzag32(static_cast<uint32_t>(value))"
encode_func = "encode_sint32"
wire_type = WireType.VARINT # Uses wire type 0
@@ -1282,7 +1282,7 @@ class SInt32Type(TypeInfo):
class SInt64Type(TypeInfo):
cpp_type = "int64_t"
default_value = "0"
decode_varint = "value.as_sint64()"
decode_varint = "decode_zigzag64(value)"
encode_func = "encode_sint64"
wire_type = WireType.VARINT # Uses wire type 0
@@ -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, proto_varint_value_t value) {{\n"
o += " switch (field_id) {\n"
o += indent("\n".join(decode_varint), " ") + "\n"
o += " default: return false;\n"
@@ -2213,7 +2213,7 @@ 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, proto_varint_value_t value) override;"
protected_content.insert(0, prot)
if decode_length:
o = f"bool {desc.name}::decode_length(uint32_t field_id, ProtoLengthDelimited value) {{\n"