mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 15:27:33 +00:00
[api] Keep the decode loop register resident and inline the varint fast path
CodSpeed showed the single virtual costing 7 to 18 percent on the decode benchmarks. The x86-64 disassembly pointed at the call, not the switch: passing the field number and wire type alongside the tag plus a 16 byte union payload kept five values live across the call, so the compiler spilled this, the end pointer and half of the payload to the stack and reloaded them for every field. decode_field() now takes only the tag, the payload pointer (already the loop cursor) and one scalar that holds the varint or fixed32 value or the payload length. The generated override wraps them in a ProtoFieldValue that never exists in memory. On the host the switch key is the field number derived with one shift and the guard compares the whole tag against the constant the case declares, which is the same two instructions the old per wire type dispatch cost. The loop also handles single byte varints inline instead of going through the parse result struct, which drops the materialized consumed count and its add on every tag and small value.
This commit is contained in:
+438
-404
File diff suppressed because it is too large
Load Diff
@@ -424,7 +424,7 @@ class HelloRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class HelloResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -468,7 +468,7 @@ class DisconnectRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class DisconnectResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -844,7 +844,7 @@ class CoverCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
@@ -918,7 +918,7 @@ class FanCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
@@ -1014,7 +1014,7 @@ class LightCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
@@ -1119,7 +1119,7 @@ class SwitchCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
@@ -1179,7 +1179,7 @@ class SubscribeLogsRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class SubscribeLogsResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -1222,7 +1222,7 @@ class NoiseEncryptionSetKeyRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class NoiseEncryptionSetKeyResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -1316,7 +1316,7 @@ class HomeassistantActionResponse final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_API_HOMEASSISTANT_STATES
|
||||
@@ -1357,7 +1357,7 @@ class HomeAssistantStateResponse final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
class GetTimeRequest final : public ProtoMessage {
|
||||
@@ -1386,7 +1386,7 @@ class DSTRule final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class ParsedTimezone final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -1399,7 +1399,7 @@ class ParsedTimezone final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class GetTimeResponse final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -1416,7 +1416,7 @@ class GetTimeResponse final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#ifdef USE_API_USER_DEFINED_ACTIONS
|
||||
class ListEntitiesServicesArgument final : public ProtoMessage {
|
||||
@@ -1484,7 +1484,7 @@ class ExecuteServiceArgument final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class ExecuteServiceRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -1507,7 +1507,7 @@ class ExecuteServiceRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_API_USER_DEFINED_ACTION_RESPONSES
|
||||
@@ -1598,7 +1598,7 @@ class CameraImageRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
@@ -1704,7 +1704,7 @@ class ClimateCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_WATER_HEATER
|
||||
@@ -1776,7 +1776,7 @@ class WaterHeaterCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
@@ -1839,7 +1839,7 @@ class NumberCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
@@ -1897,7 +1897,7 @@ class SelectCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_SIREN
|
||||
@@ -1963,7 +1963,7 @@ class SirenCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
@@ -2025,7 +2025,7 @@ class LockCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_BUTTON
|
||||
@@ -2061,7 +2061,7 @@ class ButtonCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
@@ -2147,7 +2147,7 @@ class MediaPlayerCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
@@ -2164,7 +2164,7 @@ class SubscribeBluetoothLEAdvertisementsRequest final : public ProtoDecodableMes
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothLERawAdvertisement final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2218,7 +2218,7 @@ class BluetoothDeviceRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothDeviceConnectionResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2256,7 +2256,7 @@ class BluetoothGATTGetServicesRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothGATTDescriptor final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2367,7 +2367,7 @@ class BluetoothGATTReadRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothGATTReadResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2413,7 +2413,7 @@ class BluetoothGATTWriteRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothGATTReadDescriptorRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2429,7 +2429,7 @@ class BluetoothGATTReadDescriptorRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothGATTWriteDescriptorRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2447,7 +2447,7 @@ class BluetoothGATTWriteDescriptorRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothGATTNotifyRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2464,7 +2464,7 @@ class BluetoothGATTNotifyRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothGATTNotifyDataResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2682,7 +2682,7 @@ class BluetoothScannerSetModeRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_VOICE_ASSISTANT
|
||||
@@ -2700,7 +2700,7 @@ class SubscribeVoiceAssistantRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantAudioSettings final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2757,7 +2757,7 @@ class VoiceAssistantResponse final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantEventData final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2768,7 +2768,7 @@ class VoiceAssistantEventData final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantEventResponse final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2784,7 +2784,7 @@ class VoiceAssistantEventResponse final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantAudio final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2809,7 +2809,7 @@ class VoiceAssistantAudio final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantTimerEventResponse final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2829,7 +2829,7 @@ class VoiceAssistantTimerEventResponse final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantAnnounceRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2847,7 +2847,7 @@ class VoiceAssistantAnnounceRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantAnnounceFinished final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2900,7 +2900,7 @@ class VoiceAssistantExternalWakeWord final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantConfigurationRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -2915,7 +2915,7 @@ class VoiceAssistantConfigurationRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class VoiceAssistantConfigurationResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -2952,7 +2952,7 @@ class VoiceAssistantSetConfiguration final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_ALARM_CONTROL_PANEL
|
||||
@@ -3012,7 +3012,7 @@ class AlarmControlPanelCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
@@ -3073,7 +3073,7 @@ class TextCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
@@ -3134,7 +3134,7 @@ class DateCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_DATETIME_TIME
|
||||
@@ -3195,7 +3195,7 @@ class TimeCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
@@ -3301,7 +3301,7 @@ class ValveCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
@@ -3358,7 +3358,7 @@ class DateTimeCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
@@ -3423,7 +3423,7 @@ class UpdateCommandRequest final : public CommandProtoMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
#endif
|
||||
#ifdef USE_ZWAVE_PROXY
|
||||
@@ -3447,7 +3447,7 @@ class ZWaveProxyFrame final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class ZWaveProxyRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -3470,7 +3470,7 @@ class ZWaveProxyRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class ZWaveProxyRequestResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -3540,7 +3540,7 @@ class InfraredRFTransmitRawTimingsRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class InfraredRFReceiveEvent final : public ProtoMessage {
|
||||
public:
|
||||
@@ -3611,7 +3611,7 @@ class SerialProxyConfigureRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class SerialProxyDataReceived final : public ProtoMessage {
|
||||
public:
|
||||
@@ -3654,7 +3654,7 @@ class SerialProxyWriteRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class SerialProxySetModemPinsRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -3670,7 +3670,7 @@ class SerialProxySetModemPinsRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class SerialProxyGetModemPinsRequest final : public ProtoDecodableMessage {
|
||||
public:
|
||||
@@ -3685,7 +3685,7 @@ class SerialProxyGetModemPinsRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class SerialProxyGetModemPinsResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -3723,7 +3723,7 @@ class SerialProxyRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class SerialProxyRequestResponse final : public ProtoMessage {
|
||||
public:
|
||||
@@ -3767,7 +3767,7 @@ class BluetoothSetConnectionParamsRequest final : public ProtoDecodableMessage {
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;
|
||||
bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;
|
||||
};
|
||||
class BluetoothSetConnectionParamsResponse final : public ProtoMessage {
|
||||
public:
|
||||
|
||||
@@ -214,45 +214,57 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
const uint8_t *ptr = buffer;
|
||||
const uint8_t *end = buffer + length;
|
||||
|
||||
while (ptr < end) {
|
||||
// Parse field header - ptr < end guarantees len >= 1
|
||||
// Single-byte varints dominate real messages (tags, small lengths, bools, enums), so that case
|
||||
// advances the cursor inline; a merged path would materialize the consumed count and add it.
|
||||
auto read_varint = [&](proto_varint_value_t &value) ESPHOME_ALWAYS_INLINE {
|
||||
if (ptr == end)
|
||||
return false;
|
||||
if ((*ptr & 0x80) == 0) [[likely]] {
|
||||
value = *ptr++;
|
||||
return true;
|
||||
}
|
||||
auto res = ProtoVarInt::parse_non_empty(ptr, end - ptr);
|
||||
if (!res.has_value()) {
|
||||
if (!res.has_value())
|
||||
return false;
|
||||
value = res.value;
|
||||
ptr += res.consumed;
|
||||
return true;
|
||||
};
|
||||
|
||||
while (ptr < end) {
|
||||
proto_varint_value_t tag_value;
|
||||
if (!read_varint(tag_value)) {
|
||||
ESP_LOGV(TAG, "Invalid field start at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t tag = static_cast<uint32_t>(res.value);
|
||||
uint32_t tag = static_cast<uint32_t>(tag_value);
|
||||
uint32_t field_type = tag & WIRE_TYPE_MASK;
|
||||
uint32_t field_id = tag >> 3;
|
||||
ptr += res.consumed;
|
||||
ProtoFieldValue value;
|
||||
// Payload start for scalar wire types; length-delimited fields advance it past the length.
|
||||
const uint8_t *data = ptr;
|
||||
proto_varint_value_t scalar;
|
||||
|
||||
switch (field_type) {
|
||||
case WIRE_TYPE_VARINT: { // VarInt
|
||||
res = ProtoVarInt::parse(ptr, end - ptr);
|
||||
if (!res.has_value()) {
|
||||
if (!read_varint(scalar)) {
|
||||
ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
value.varint_ = res.value;
|
||||
ptr += res.consumed;
|
||||
break;
|
||||
}
|
||||
case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited
|
||||
res = ProtoVarInt::parse(ptr, end - ptr);
|
||||
if (!res.has_value()) {
|
||||
proto_varint_value_t length_value;
|
||||
if (!read_varint(length_value)) {
|
||||
ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
uint32_t field_length = static_cast<uint32_t>(res.value);
|
||||
ptr += res.consumed;
|
||||
uint32_t field_length = static_cast<uint32_t>(length_value);
|
||||
if (field_length > static_cast<size_t>(end - ptr)) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
value.ld_.data = ptr;
|
||||
value.ld_.len = field_length;
|
||||
data = ptr;
|
||||
scalar = field_length;
|
||||
ptr += field_length;
|
||||
break;
|
||||
}
|
||||
@@ -261,12 +273,14 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
uint32_t val;
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
// Protobuf fixed32 is little-endian — direct load on LE platforms
|
||||
memcpy(&value.fixed32_, ptr, 4);
|
||||
memcpy(&val, ptr, 4);
|
||||
#else
|
||||
value.fixed32_ = encode_uint32(ptr[3], ptr[2], ptr[1], ptr[0]);
|
||||
val = encode_uint32(ptr[3], ptr[2], ptr[1], ptr[0]);
|
||||
#endif
|
||||
scalar = val;
|
||||
ptr += 4;
|
||||
break;
|
||||
}
|
||||
@@ -274,8 +288,8 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
ESP_LOGV(TAG, "Invalid field type %" PRIu32 " at offset %ld", field_type, (long) (ptr - buffer));
|
||||
return;
|
||||
}
|
||||
if (!this->decode_field(tag, field_id, field_type, value)) {
|
||||
ESP_LOGV(TAG, "Cannot decode field %" PRIu32 " with wire type %" PRIu32 "!", field_id, field_type);
|
||||
if (!this->decode_field(tag, data, scalar)) {
|
||||
ESP_LOGV(TAG, "Cannot decode field %" PRIu32 " with wire type %" PRIu32 "!", tag >> 3, field_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,55 +174,54 @@ class ProtoSize;
|
||||
// PROTO_DECODE_CASE. Embedded targets compile switches to compare chains (ESP-IDF passes
|
||||
// -fno-jump-tables), so keying on the full wire tag costs one compare per field and needs no
|
||||
// separate wire type check. The host compiler turns the dense field number switch into a jump
|
||||
// table, so there the key is the field number and PROTO_DECODE_GUARD rejects the wrong wire type
|
||||
// before the field is read. Both forms drop a field that arrives with a wire type it does not
|
||||
// declare, which is what the per wire type virtuals did before.
|
||||
// table, so there the key is the field number and PROTO_DECODE_GUARD compares the tag against the
|
||||
// one the case declares, which rejects the wrong wire type in a single compare. Both forms drop a
|
||||
// field that arrives with a wire type it does not declare, as the per wire type virtuals did.
|
||||
#ifdef USE_HOST
|
||||
#define PROTO_DECODE_KEY(tag, field_id) (field_id)
|
||||
#define PROTO_DECODE_KEY(tag) ((tag) >> 3)
|
||||
#define PROTO_DECODE_CASE(field_id, wire_type) (field_id)
|
||||
#define PROTO_DECODE_GUARD(wire_type, expected) \
|
||||
if ((wire_type) != (expected)) \
|
||||
#define PROTO_DECODE_GUARD(tag, field_id, wire_type) \
|
||||
if ((tag) != (((field_id) << 3) | (wire_type))) \
|
||||
return false
|
||||
#else
|
||||
#define PROTO_DECODE_KEY(tag, field_id) (tag)
|
||||
#define PROTO_DECODE_KEY(tag) (tag)
|
||||
#define PROTO_DECODE_CASE(field_id, wire_type) (((field_id) << 3) | (wire_type))
|
||||
#define PROTO_DECODE_GUARD(wire_type, expected) (void) 0
|
||||
#define PROTO_DECODE_GUARD(tag, field_id, wire_type) (void) 0
|
||||
#endif
|
||||
|
||||
/// Payload of one decoded field, handed to ProtoDecodableMessage::decode_field() together with
|
||||
/// the field number and wire type. The wire type says which member is live; the accessors do not check.
|
||||
/// Eight bytes with or without USE_API_VARINT64, so it travels in two registers on every target.
|
||||
struct ProtoFieldValue {
|
||||
union {
|
||||
proto_varint_value_t varint_;
|
||||
struct {
|
||||
const uint8_t *data;
|
||||
uint32_t len;
|
||||
} ld_;
|
||||
uint32_t fixed32_;
|
||||
};
|
||||
/// Payload of one decoded field as decode_field() receives it: the payload pointer and one scalar
|
||||
/// that holds the varint or fixed32 value, or the byte length of a length-delimited field. The
|
||||
/// wire type in the tag says which reading applies; the accessors do not check. Built by the
|
||||
/// generated decode_field() from its two register arguments, so it never exists in memory.
|
||||
class ProtoFieldValue {
|
||||
public:
|
||||
ProtoFieldValue(const uint8_t *data, proto_varint_value_t scalar) : data_(data), scalar_(scalar) {}
|
||||
|
||||
proto_varint_value_t as_varint() const { return this->varint_; }
|
||||
proto_varint_value_t as_varint() const { return this->scalar_; }
|
||||
|
||||
// Length-delimited accessors
|
||||
const uint8_t *data() const { return this->ld_.data; }
|
||||
size_t size() const { return this->ld_.len; }
|
||||
std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->ld_.data), this->ld_.len); }
|
||||
const uint8_t *data() const { return this->data_; }
|
||||
size_t size() const { return static_cast<size_t>(this->scalar_); }
|
||||
std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->data_), this->size()); }
|
||||
/// Decode the length-delimited payload into a message instance.
|
||||
/// Template preserves concrete type so decode() resolves statically.
|
||||
template<typename T> void decode_to_message(T &msg) const { msg.decode(this->ld_.data, this->ld_.len); }
|
||||
template<typename T> void decode_to_message(T &msg) const { msg.decode(this->data_, this->size()); }
|
||||
|
||||
// Fixed32 accessors
|
||||
uint32_t as_fixed32() const { return this->fixed32_; }
|
||||
int32_t as_sfixed32() const { return static_cast<int32_t>(this->fixed32_); }
|
||||
uint32_t as_fixed32() const { return static_cast<uint32_t>(this->scalar_); }
|
||||
int32_t as_sfixed32() const { return static_cast<int32_t>(this->as_fixed32()); }
|
||||
float as_float() const {
|
||||
union {
|
||||
uint32_t raw;
|
||||
float value;
|
||||
} s{};
|
||||
s.raw = this->fixed32_;
|
||||
s.raw = this->as_fixed32();
|
||||
return s.value;
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t *data_;
|
||||
proto_varint_value_t scalar_;
|
||||
};
|
||||
|
||||
// NOTE: Proto64Bit class removed - wire type 1 (64-bit fixed) not supported
|
||||
@@ -751,16 +750,15 @@ class ProtoDecodableMessage : public ProtoMessage {
|
||||
|
||||
protected:
|
||||
~ProtoDecodableMessage() = default;
|
||||
/// Store one decoded field. \p field_id and \p wire_type are \p tag split in two; the loop has all
|
||||
/// three at hand, so passing them costs nothing and the generated switch keys on whichever form is
|
||||
/// cheapest for the target (see PROTO_DECODE_KEY). \p wire_type selects the live ProtoFieldValue
|
||||
/// member; overrides reject a field that arrived with a wire type other than the one it declares.
|
||||
/// Return false for unknown or mismatched fields.
|
||||
/// Store one decoded field. \p tag is the wire tag (field number and wire type), \p data points at
|
||||
/// the field payload and \p scalar is the varint or fixed32 value, or the payload length for a
|
||||
/// length-delimited field. Three register arguments keep the shared loop free of spills; the
|
||||
/// generated override wraps them in a ProtoFieldValue and keys its switch through
|
||||
/// PROTO_DECODE_KEY. Overrides reject a field that arrived with a wire type other than the one it
|
||||
/// declares. Return false for unknown or mismatched fields.
|
||||
/// One virtual instead of one per wire type keeps each message's vtable at a single slot.
|
||||
// NOTE: wire type 1 (64-bit fixed) is not supported
|
||||
virtual bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) {
|
||||
return false;
|
||||
}
|
||||
virtual bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { return false; }
|
||||
};
|
||||
|
||||
class ProtoSize {
|
||||
|
||||
@@ -237,7 +237,7 @@ class TypeInfo(ABC):
|
||||
"""Emit one decode_field() case for a field and the wire type it expects."""
|
||||
return (
|
||||
f"case PROTO_DECODE_CASE({self.number}, {int(wire_type)}):\n"
|
||||
f" PROTO_DECODE_GUARD(wire_type, {int(wire_type)});\n"
|
||||
f" PROTO_DECODE_GUARD(tag, {self.number}, {int(wire_type)});\n"
|
||||
f" {body}\n"
|
||||
f" break;"
|
||||
)
|
||||
@@ -2741,15 +2741,16 @@ def build_message_type(
|
||||
if decode:
|
||||
# One virtual per message: the shared decode loop parses the payload for the wire
|
||||
# type and hands it over with the tag, so a single switch covers every field.
|
||||
o = f"bool {desc.name}::decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) {{\n"
|
||||
o += " switch (PROTO_DECODE_KEY(tag, field_id)) {\n"
|
||||
o = f"bool {desc.name}::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {{\n"
|
||||
o += " const ProtoFieldValue value(data, scalar);\n"
|
||||
o += " switch (PROTO_DECODE_KEY(tag)) {\n"
|
||||
o += indent("\n".join(decode), " ") + "\n"
|
||||
o += " default: return false;\n"
|
||||
o += " }\n"
|
||||
o += " return true;\n"
|
||||
o += "}\n"
|
||||
cpp += o
|
||||
prot = "bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;"
|
||||
prot = "bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;"
|
||||
protected_content.insert(0, prot)
|
||||
|
||||
# Generate custom decode() override for messages with FixedVector fields
|
||||
|
||||
@@ -226,7 +226,9 @@ def test_decode_cases_carry_field_number_and_wire_type(
|
||||
assert len(cases) == 1, cases
|
||||
lines = cases[0].splitlines()
|
||||
assert lines[0] == f"case PROTO_DECODE_CASE({number}, {wire_type}):", cases[0]
|
||||
assert lines[1].strip() == f"PROTO_DECODE_GUARD(wire_type, {wire_type});", cases[0]
|
||||
assert lines[1].strip() == f"PROTO_DECODE_GUARD(tag, {number}, {wire_type});", (
|
||||
cases[0]
|
||||
)
|
||||
assert accessor in cases[0], cases[0]
|
||||
|
||||
|
||||
@@ -237,15 +239,16 @@ def test_message_gets_a_single_decode_field_override() -> None:
|
||||
desc.field.add(name="count", number=2, type=UINT32_T)
|
||||
desc.field.add(name="level", number=3, type=FLOAT)
|
||||
header, cpp, _ = build_message_type(desc, {}, {"Mixed": SOURCE_CLIENT})
|
||||
decl = "bool decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) override;"
|
||||
decl = "bool decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) override;"
|
||||
assert header.count(decl) == 1
|
||||
assert "decode_varint" not in header and "decode_length" not in header
|
||||
assert (
|
||||
cpp.count(
|
||||
"bool Mixed::decode_field(uint32_t tag, uint32_t field_id, uint32_t wire_type, ProtoFieldValue value) {"
|
||||
"bool Mixed::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {"
|
||||
)
|
||||
== 1
|
||||
)
|
||||
assert "switch (PROTO_DECODE_KEY(tag, field_id)) {" in cpp
|
||||
assert "switch (PROTO_DECODE_KEY(tag)) {" in cpp
|
||||
assert "const ProtoFieldValue value(data, scalar);" in cpp
|
||||
for number, wire_type in ((1, 2), (2, 0), (3, 5)):
|
||||
assert f"case PROTO_DECODE_CASE({number}, {wire_type}):" in cpp, cpp
|
||||
|
||||
Reference in New Issue
Block a user