[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:
J. Nick Koston
2026-09-07 15:57:13 +02:00
parent b0ce7f58f3
commit bcf812d62b
6 changed files with 578 additions and 528 deletions
File diff suppressed because it is too large Load Diff
+59 -59
View File
@@ -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:
+35 -21
View File
@@ -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);
}
}
}
+34 -36
View File
@@ -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 {
+5 -4
View File
@@ -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