From fb3befe377250ad405f9ce4430a312b39fbdef7b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 7 Sep 2026 13:50:29 +0200 Subject: [PATCH] [api] Second cleanup pass over the decode generator Repeated fields encode their elements through one encode_element() hook instead of two isinstance ladders, the fixed32 precomputed tag path owns its own guard, the generated switches drop the dead default case, StringRef takes the byte pointer directly, the three hand written tag expressions in proto.h go through proto_tag(), and stale comments about the previous decode design go. The compiled functions are unchanged. --- esphome/components/api/api_pb2.cpp | 174 +++--------------- esphome/components/api/proto.h | 7 +- script/api_protobuf/api_protobuf.py | 69 +++---- .../components/api/bench_proto_proxy.cpp | 2 +- .../api/test_api_protobuf_generator.py | 1 - 5 files changed, 58 insertions(+), 195 deletions(-) diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index c08ffd202a..53bafcde37 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -11,7 +11,7 @@ void HelloRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_ const ProtoFieldValue value(data, scalar); switch (tag) { case proto_tag(1, WIRE_TYPE_LENGTH_DELIMITED): - this->client_info = StringRef(reinterpret_cast(value.data()), value.size()); + this->client_info = StringRef(value.data(), value.size()); break; case proto_tag(2, WIRE_TYPE_VARINT): this->api_version_major = value.as_varint(); @@ -19,8 +19,6 @@ void HelloRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_ case proto_tag(3, WIRE_TYPE_VARINT): this->api_version_minor = value.as_varint(); break; - default: - break; } } uint8_t *HelloResponse::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -47,8 +45,6 @@ void DisconnectRequest::decode_field(uint32_t tag, const uint8_t *data, proto_va case proto_tag(1, WIRE_TYPE_VARINT): this->reason = static_cast(value.as_varint()); break; - default: - break; } } uint8_t *DisconnectRequest::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -489,8 +485,6 @@ void CoverCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_ this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -605,15 +599,13 @@ void FanCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_va this->has_preset_mode = value.as_bool(); break; case proto_tag(13, WIRE_TYPE_LENGTH_DELIMITED): - this->preset_mode = StringRef(reinterpret_cast(value.data()), value.size()); + this->preset_mode = StringRef(value.data(), value.size()); break; #ifdef USE_DEVICES case proto_tag(14, WIRE_TYPE_VARINT): this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -814,15 +806,13 @@ void LightCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_ this->has_effect = value.as_bool(); break; case proto_tag(19, WIRE_TYPE_LENGTH_DELIMITED): - this->effect = StringRef(reinterpret_cast(value.data()), value.size()); + this->effect = StringRef(value.data(), value.size()); break; #ifdef USE_DEVICES case proto_tag(28, WIRE_TYPE_VARINT): this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -969,8 +959,6 @@ void SwitchCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -1042,8 +1030,6 @@ void SubscribeLogsRequest::decode_field(uint32_t tag, const uint8_t *data, proto case proto_tag(2, WIRE_TYPE_VARINT): this->dump_config = value.as_bool(); break; - default: - break; } } __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes) @@ -1074,8 +1060,6 @@ void NoiseEncryptionSetKeyRequest::decode_field(uint32_t tag, const uint8_t *dat this->key = value.data(); this->key_len = value.size(); break; - default: - break; } } uint8_t *NoiseEncryptionSetKeyResponse::encode_msg(const void *self, @@ -1175,7 +1159,7 @@ void HomeassistantActionResponse::decode_field(uint32_t tag, const uint8_t *data this->success = value.as_bool(); break; case proto_tag(3, WIRE_TYPE_LENGTH_DELIMITED): - this->error_message = StringRef(reinterpret_cast(value.data()), value.size()); + this->error_message = StringRef(value.data(), value.size()); break; #ifdef USE_API_HOMEASSISTANT_ACTION_RESPONSES_JSON case proto_tag(4, WIRE_TYPE_LENGTH_DELIMITED): @@ -1183,8 +1167,6 @@ void HomeassistantActionResponse::decode_field(uint32_t tag, const uint8_t *data this->response_data_len = value.size(); break; #endif - default: - break; } } #endif @@ -1210,15 +1192,13 @@ void HomeAssistantStateResponse::decode_field(uint32_t tag, const uint8_t *data, const ProtoFieldValue value(data, scalar); switch (tag) { case proto_tag(1, WIRE_TYPE_LENGTH_DELIMITED): - this->entity_id = StringRef(reinterpret_cast(value.data()), value.size()); + this->entity_id = StringRef(value.data(), value.size()); break; case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED): - this->state = StringRef(reinterpret_cast(value.data()), value.size()); + this->state = StringRef(value.data(), value.size()); break; case proto_tag(3, WIRE_TYPE_LENGTH_DELIMITED): - this->attribute = StringRef(reinterpret_cast(value.data()), value.size()); - break; - default: + this->attribute = StringRef(value.data(), value.size()); break; } } @@ -1244,8 +1224,6 @@ void DSTRule::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value case proto_tag(6, WIRE_TYPE_VARINT): this->day_of_week = value.as_varint(); break; - default: - break; } } void ParsedTimezone::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -1263,8 +1241,6 @@ void ParsedTimezone::decode_field(uint32_t tag, const uint8_t *data, proto_varin case proto_tag(4, WIRE_TYPE_LENGTH_DELIMITED): value.decode_to_message(this->dst_end); break; - default: - break; } } void GetTimeResponse::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -1277,8 +1253,6 @@ void GetTimeResponse::decode_field(uint32_t tag, const uint8_t *data, proto_vari value.decode_to_message(this->parsed_timezone); this->has_parsed_timezone = true; break; - default: - break; } } #ifdef USE_API_USER_DEFINED_ACTIONS @@ -1351,7 +1325,7 @@ void ExecuteServiceArgument::decode_field(uint32_t tag, const uint8_t *data, pro this->float_ = value.as_float(); break; case proto_tag(4, WIRE_TYPE_LENGTH_DELIMITED): - this->string_ = StringRef(reinterpret_cast(value.data()), value.size()); + this->string_ = StringRef(value.data(), value.size()); break; case proto_tag(5, WIRE_TYPE_VARINT): this->int_ = decode_zigzag32(static_cast(value.as_varint())); @@ -1368,8 +1342,6 @@ void ExecuteServiceArgument::decode_field(uint32_t tag, const uint8_t *data, pro case proto_tag(9, WIRE_TYPE_LENGTH_DELIMITED): this->string_array.push_back(value.as_string()); break; - default: - break; } } void ExecuteServiceArgument::decode(const uint8_t *buffer, size_t length) { @@ -1403,8 +1375,6 @@ void ExecuteServiceRequest::decode_field(uint32_t tag, const uint8_t *data, prot this->return_response = value.as_bool(); break; #endif - default: - break; } } void ExecuteServiceRequest::decode(const uint8_t *buffer, size_t length) { @@ -1501,8 +1471,6 @@ void CameraImageRequest::decode_field(uint32_t tag, const uint8_t *data, proto_v case proto_tag(2, WIRE_TYPE_VARINT): this->stream = value.as_bool(); break; - default: - break; } } #endif @@ -1713,7 +1681,7 @@ void ClimateCommandRequest::decode_field(uint32_t tag, const uint8_t *data, prot this->has_custom_fan_mode = value.as_bool(); break; case proto_tag(17, WIRE_TYPE_LENGTH_DELIMITED): - this->custom_fan_mode = StringRef(reinterpret_cast(value.data()), value.size()); + this->custom_fan_mode = StringRef(value.data(), value.size()); break; case proto_tag(18, WIRE_TYPE_VARINT): this->has_preset = value.as_bool(); @@ -1725,7 +1693,7 @@ void ClimateCommandRequest::decode_field(uint32_t tag, const uint8_t *data, prot this->has_custom_preset = value.as_bool(); break; case proto_tag(21, WIRE_TYPE_LENGTH_DELIMITED): - this->custom_preset = StringRef(reinterpret_cast(value.data()), value.size()); + this->custom_preset = StringRef(value.data(), value.size()); break; case proto_tag(22, WIRE_TYPE_VARINT): this->has_target_humidity = value.as_bool(); @@ -1738,8 +1706,6 @@ void ClimateCommandRequest::decode_field(uint32_t tag, const uint8_t *data, prot this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -1866,8 +1832,6 @@ void WaterHeaterCommandRequest::decode_field(uint32_t tag, const uint8_t *data, case proto_tag(8, WIRE_TYPE_FIXED32): this->target_temperature_high = value.as_float(); break; - default: - break; } } #endif @@ -1960,8 +1924,6 @@ void NumberCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -2035,15 +1997,13 @@ void SelectCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto this->key = value.as_fixed32(); break; case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED): - this->state = StringRef(reinterpret_cast(value.data()), value.size()); + this->state = StringRef(value.data(), value.size()); break; #ifdef USE_DEVICES case proto_tag(3, WIRE_TYPE_VARINT): this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -2128,7 +2088,7 @@ void SirenCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_ this->has_tone = value.as_bool(); break; case proto_tag(5, WIRE_TYPE_LENGTH_DELIMITED): - this->tone = StringRef(reinterpret_cast(value.data()), value.size()); + this->tone = StringRef(value.data(), value.size()); break; case proto_tag(6, WIRE_TYPE_VARINT): this->has_duration = value.as_bool(); @@ -2147,8 +2107,6 @@ void SirenCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_ this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -2226,15 +2184,13 @@ void LockCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_v this->has_code = value.as_bool(); break; case proto_tag(4, WIRE_TYPE_LENGTH_DELIMITED): - this->code = StringRef(reinterpret_cast(value.data()), value.size()); + this->code = StringRef(value.data(), value.size()); break; #ifdef USE_DEVICES case proto_tag(5, WIRE_TYPE_VARINT): this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -2284,8 +2240,6 @@ void ButtonCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -2401,7 +2355,7 @@ void MediaPlayerCommandRequest::decode_field(uint32_t tag, const uint8_t *data, this->has_media_url = value.as_bool(); break; case proto_tag(7, WIRE_TYPE_LENGTH_DELIMITED): - this->media_url = StringRef(reinterpret_cast(value.data()), value.size()); + this->media_url = StringRef(value.data(), value.size()); break; case proto_tag(8, WIRE_TYPE_VARINT): this->has_announcement = value.as_bool(); @@ -2414,8 +2368,6 @@ void MediaPlayerCommandRequest::decode_field(uint32_t tag, const uint8_t *data, this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -2427,8 +2379,6 @@ void SubscribeBluetoothLEAdvertisementsRequest::decode_field(uint32_t tag, const case proto_tag(1, WIRE_TYPE_VARINT): this->flags = value.as_varint(); break; - default: - break; } } __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes) @@ -2488,8 +2438,6 @@ void BluetoothDeviceRequest::decode_field(uint32_t tag, const uint8_t *data, pro case proto_tag(4, WIRE_TYPE_VARINT): this->address_type = value.as_varint(); break; - default: - break; } } uint8_t *BluetoothDeviceConnectionResponse::encode_msg(const void *self, @@ -2517,8 +2465,6 @@ void BluetoothGATTGetServicesRequest::decode_field(uint32_t tag, const uint8_t * case proto_tag(1, WIRE_TYPE_VARINT): this->address = value.as_varint(); break; - default: - break; } } uint8_t *BluetoothGATTDescriptor::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -2648,8 +2594,6 @@ void BluetoothGATTReadRequest::decode_field(uint32_t tag, const uint8_t *data, p case proto_tag(2, WIRE_TYPE_VARINT): this->handle = value.as_varint(); break; - default: - break; } } uint8_t *BluetoothGATTReadResponse::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -2684,8 +2628,6 @@ void BluetoothGATTWriteRequest::decode_field(uint32_t tag, const uint8_t *data, this->data = value.data(); this->data_len = value.size(); break; - default: - break; } } void BluetoothGATTReadDescriptorRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -2697,8 +2639,6 @@ void BluetoothGATTReadDescriptorRequest::decode_field(uint32_t tag, const uint8_ case proto_tag(2, WIRE_TYPE_VARINT): this->handle = value.as_varint(); break; - default: - break; } } void BluetoothGATTWriteDescriptorRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -2714,8 +2654,6 @@ void BluetoothGATTWriteDescriptorRequest::decode_field(uint32_t tag, const uint8 this->data = value.data(); this->data_len = value.size(); break; - default: - break; } } void BluetoothGATTNotifyRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -2730,8 +2668,6 @@ void BluetoothGATTNotifyRequest::decode_field(uint32_t tag, const uint8_t *data, case proto_tag(3, WIRE_TYPE_VARINT): this->enable = value.as_bool(); break; - default: - break; } } uint8_t *BluetoothGATTNotifyDataResponse::encode_msg(const void *self, @@ -2896,8 +2832,6 @@ void BluetoothScannerSetModeRequest::decode_field(uint32_t tag, const uint8_t *d case proto_tag(1, WIRE_TYPE_VARINT): this->mode = static_cast(value.as_varint()); break; - default: - break; } } #endif @@ -2911,8 +2845,6 @@ void SubscribeVoiceAssistantRequest::decode_field(uint32_t tag, const uint8_t *d case proto_tag(2, WIRE_TYPE_VARINT): this->flags = value.as_varint(); break; - default: - break; } } uint8_t *VoiceAssistantAudioSettings::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -2962,20 +2894,16 @@ void VoiceAssistantResponse::decode_field(uint32_t tag, const uint8_t *data, pro case proto_tag(2, WIRE_TYPE_VARINT): this->error = value.as_bool(); break; - default: - break; } } void VoiceAssistantEventData::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { const ProtoFieldValue value(data, scalar); switch (tag) { case proto_tag(1, WIRE_TYPE_LENGTH_DELIMITED): - this->name = StringRef(reinterpret_cast(value.data()), value.size()); + this->name = StringRef(value.data(), value.size()); break; case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED): - this->value = StringRef(reinterpret_cast(value.data()), value.size()); - break; - default: + this->value = StringRef(value.data(), value.size()); break; } } @@ -2989,8 +2917,6 @@ void VoiceAssistantEventResponse::decode_field(uint32_t tag, const uint8_t *data this->data.emplace_back(); value.decode_to_message(this->data.back()); break; - default: - break; } } void VoiceAssistantAudio::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -3007,8 +2933,6 @@ void VoiceAssistantAudio::decode_field(uint32_t tag, const uint8_t *data, proto_ this->data2 = value.data(); this->data2_len = value.size(); break; - default: - break; } } uint8_t *VoiceAssistantAudio::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -3034,10 +2958,10 @@ void VoiceAssistantTimerEventResponse::decode_field(uint32_t tag, const uint8_t this->event_type = static_cast(value.as_varint()); break; case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED): - this->timer_id = StringRef(reinterpret_cast(value.data()), value.size()); + this->timer_id = StringRef(value.data(), value.size()); break; case proto_tag(3, WIRE_TYPE_LENGTH_DELIMITED): - this->name = StringRef(reinterpret_cast(value.data()), value.size()); + this->name = StringRef(value.data(), value.size()); break; case proto_tag(4, WIRE_TYPE_VARINT): this->total_seconds = value.as_varint(); @@ -3048,27 +2972,23 @@ void VoiceAssistantTimerEventResponse::decode_field(uint32_t tag, const uint8_t case proto_tag(6, WIRE_TYPE_VARINT): this->is_active = value.as_bool(); break; - default: - break; } } void VoiceAssistantAnnounceRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { const ProtoFieldValue value(data, scalar); switch (tag) { case proto_tag(1, WIRE_TYPE_LENGTH_DELIMITED): - this->media_id = StringRef(reinterpret_cast(value.data()), value.size()); + this->media_id = StringRef(value.data(), value.size()); break; case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED): - this->text = StringRef(reinterpret_cast(value.data()), value.size()); + this->text = StringRef(value.data(), value.size()); break; case proto_tag(3, WIRE_TYPE_LENGTH_DELIMITED): - this->preannounce_media_id = StringRef(reinterpret_cast(value.data()), value.size()); + this->preannounce_media_id = StringRef(value.data(), value.size()); break; case proto_tag(4, WIRE_TYPE_VARINT): this->start_conversation = value.as_bool(); break; - default: - break; } } uint8_t *VoiceAssistantAnnounceFinished::encode_msg(const void *self, @@ -3110,27 +3030,25 @@ void VoiceAssistantExternalWakeWord::decode_field(uint32_t tag, const uint8_t *d const ProtoFieldValue value(data, scalar); switch (tag) { case proto_tag(1, WIRE_TYPE_LENGTH_DELIMITED): - this->id = StringRef(reinterpret_cast(value.data()), value.size()); + this->id = StringRef(value.data(), value.size()); break; case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED): - this->wake_word = StringRef(reinterpret_cast(value.data()), value.size()); + this->wake_word = StringRef(value.data(), value.size()); break; case proto_tag(3, WIRE_TYPE_LENGTH_DELIMITED): this->trained_languages.push_back(value.as_string()); break; case proto_tag(4, WIRE_TYPE_LENGTH_DELIMITED): - this->model_type = StringRef(reinterpret_cast(value.data()), value.size()); + this->model_type = StringRef(value.data(), value.size()); break; case proto_tag(5, WIRE_TYPE_VARINT): this->model_size = value.as_varint(); break; case proto_tag(6, WIRE_TYPE_LENGTH_DELIMITED): - this->model_hash = StringRef(reinterpret_cast(value.data()), value.size()); + this->model_hash = StringRef(value.data(), value.size()); break; case proto_tag(7, WIRE_TYPE_LENGTH_DELIMITED): - this->url = StringRef(reinterpret_cast(value.data()), value.size()); - break; - default: + this->url = StringRef(value.data(), value.size()); break; } } @@ -3141,8 +3059,6 @@ void VoiceAssistantConfigurationRequest::decode_field(uint32_t tag, const uint8_ this->external_wake_words.emplace_back(); value.decode_to_message(this->external_wake_words.back()); break; - default: - break; } } uint8_t *VoiceAssistantConfigurationResponse::encode_msg(const void *self, @@ -3180,8 +3096,6 @@ void VoiceAssistantSetConfiguration::decode_field(uint32_t tag, const uint8_t *d case proto_tag(1, WIRE_TYPE_LENGTH_DELIMITED): this->active_wake_words.push_back(value.as_string()); break; - default: - break; } } #endif @@ -3256,15 +3170,13 @@ void AlarmControlPanelCommandRequest::decode_field(uint32_t tag, const uint8_t * this->command = static_cast(value.as_varint()); break; case proto_tag(3, WIRE_TYPE_LENGTH_DELIMITED): - this->code = StringRef(reinterpret_cast(value.data()), value.size()); + this->code = StringRef(value.data(), value.size()); break; #ifdef USE_DEVICES case proto_tag(4, WIRE_TYPE_VARINT): this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -3338,15 +3250,13 @@ void TextCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_v this->key = value.as_fixed32(); break; case proto_tag(2, WIRE_TYPE_LENGTH_DELIMITED): - this->state = StringRef(reinterpret_cast(value.data()), value.size()); + this->state = StringRef(value.data(), value.size()); break; #ifdef USE_DEVICES case proto_tag(3, WIRE_TYPE_VARINT): this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -3429,8 +3339,6 @@ void DateCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_v this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -3513,8 +3421,6 @@ void TimeCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_v this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -3667,8 +3573,6 @@ void ValveCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto_ this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -3743,8 +3647,6 @@ void DateTimeCommandRequest::decode_field(uint32_t tag, const uint8_t *data, pro this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -3835,8 +3737,6 @@ void UpdateCommandRequest::decode_field(uint32_t tag, const uint8_t *data, proto this->device_id = value.as_varint(); break; #endif - default: - break; } } #endif @@ -3848,8 +3748,6 @@ void ZWaveProxyFrame::decode_field(uint32_t tag, const uint8_t *data, proto_vari this->data = value.data(); this->data_len = value.size(); break; - default: - break; } } __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes) @@ -3878,8 +3776,6 @@ void ZWaveProxyRequest::decode_field(uint32_t tag, const uint8_t *data, proto_va this->data = value.data(); this->data_len = value.size(); break; - default: - break; } } uint8_t *ZWaveProxyRequest::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -3975,8 +3871,6 @@ void InfraredRFTransmitRawTimingsRequest::decode_field(uint32_t tag, const uint8 case proto_tag(6, WIRE_TYPE_VARINT): this->modulation = value.as_varint(); break; - default: - break; } } __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes) @@ -4075,8 +3969,6 @@ void SerialProxyConfigureRequest::decode_field(uint32_t tag, const uint8_t *data case proto_tag(6, WIRE_TYPE_VARINT): this->data_size = value.as_varint(); break; - default: - break; } } __attribute__((optimize("O2"))) // NOLINT(clang-diagnostic-unknown-attributes) @@ -4107,8 +3999,6 @@ void SerialProxyWriteRequest::decode_field(uint32_t tag, const uint8_t *data, pr this->data = value.data(); this->data_len = value.size(); break; - default: - break; } } void SerialProxySetModemPinsRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -4120,8 +4010,6 @@ void SerialProxySetModemPinsRequest::decode_field(uint32_t tag, const uint8_t *d case proto_tag(2, WIRE_TYPE_VARINT): this->line_states = value.as_varint(); break; - default: - break; } } void SerialProxyGetModemPinsRequest::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) { @@ -4130,8 +4018,6 @@ void SerialProxyGetModemPinsRequest::decode_field(uint32_t tag, const uint8_t *d case proto_tag(1, WIRE_TYPE_VARINT): this->instance = value.as_varint(); break; - default: - break; } } uint8_t *SerialProxyGetModemPinsResponse::encode_msg(const void *self, @@ -4160,8 +4046,6 @@ void SerialProxyRequest::decode_field(uint32_t tag, const uint8_t *data, proto_v case proto_tag(2, WIRE_TYPE_VARINT): this->type = static_cast(value.as_varint()); break; - default: - break; } } uint8_t *SerialProxyRequestResponse::encode_msg(const void *self, ProtoWriteBuffer &buffer PROTO_ENCODE_DEBUG_PARAM) { @@ -4202,8 +4086,6 @@ void BluetoothSetConnectionParamsRequest::decode_field(uint32_t tag, const uint8 case proto_tag(5, WIRE_TYPE_VARINT): this->timeout = value.as_varint(); break; - default: - break; } } uint8_t *BluetoothSetConnectionParamsResponse::encode_msg(const void *self, diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index e9f855785e..081affbaae 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -255,7 +255,7 @@ class ProtoWriteBuffer { * * Following https://protobuf.dev/programming-guides/encoding/#structure */ - void encode_field_raw(uint32_t field_id, uint32_t type) { this->encode_varint_raw((field_id << 3) | type); } + void encode_field_raw(uint32_t field_id, uint32_t type) { this->encode_varint_raw(proto_tag(field_id, type)); } /// Single-pass encode for repeated submessage elements. /// Thin template wrapper; all buffer work is in the non-template core. template void encode_sub_message(uint32_t field_id, const T &value); @@ -385,7 +385,7 @@ class ProtoEncode { } [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE encode_field_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t type) { - return encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, (field_id << 3) | type); + return encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, proto_tag(field_id, type)); } /// Write a single precomputed tag byte. Tag must be < 128. [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE @@ -738,7 +738,6 @@ class ProtoDecodableMessage : public ProtoMessage { /// Store one decoded field; \p scalar is the varint or fixed32 value, or the length of the /// length-delimited payload at \p data. An unknown field or wrong wire type matches no case and is skipped. /// Three register arguments keep the decode loop free of spills. - // NOTE: wire type 1 (64-bit fixed) is not supported virtual void decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {} }; @@ -865,7 +864,7 @@ class ProtoSize { * @return The number of bytes needed to encode the field ID and wire type */ static constexpr uint32_t field(uint32_t field_id, uint32_t type) { - uint32_t tag = (field_id << 3) | (type & WIRE_TYPE_MASK); + uint32_t tag = proto_tag(field_id, type & WIRE_TYPE_MASK); return varint(tag); } diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index d6dc81d809..e63e28096e 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -321,11 +321,12 @@ class TypeInfo(ABC): ) ) - def _encode_fixed32_with_precomputed_tag(self, value_expr: str) -> str | None: - """Single-byte tag fixed32 write, or None for multi-byte tags.""" + def _encode_fixed32_with_precomputed_tag(self, value: str) -> str | None: + """Single-byte tag fixed32 write, or None for other types and multi-byte tags.""" tag = self.calculate_tag() - if tag >= 128: + if self.fixed32_value_template is None or tag >= 128: return None + value_expr = self.fixed32_value_template.format(value=value) if self.force: return _encode_call("write_tag_and_fixed32", str(tag), value_expr) return ( @@ -339,14 +340,14 @@ class TypeInfo(ABC): value = f"this->{self.field_name}" if result := self._encode_with_precomputed_tag(value): return result - if self.fixed32_value_template is not None and ( - result := self._encode_fixed32_with_precomputed_tag( - self.fixed32_value_template.format(value=value) - ) - ): + if result := self._encode_fixed32_with_precomputed_tag(value): return result return _encode_call(self.encode_func, str(self.number), value, force=self.force) + def encode_element(self, number: int, element: str) -> str: + """Encode one element of a repeated field; elements are always written.""" + return _encode_call(self.encode_func, str(number), element, force=True) + encode_func = None @classmethod @@ -939,6 +940,9 @@ class MessageType(TypeInfo): def can_use_dump_field(cls) -> bool: return False + def encode_element(self, number: int, element: str) -> str: + return _encode_call("encode_sub_message", "buffer", str(number), element) + @property def cpp_type(self) -> str: return self._field.type_name[1:] @@ -981,7 +985,6 @@ class MessageType(TypeInfo): @property def decode_content(self) -> str: - # Custom decode that doesn't use templates body = f"value.decode_to_message(this->{self.field_name});" if self._track_presence: # decode_to_message() cannot report failure, so setting the flag @@ -1244,7 +1247,7 @@ class PointerToStringBufferType(PointerToBufferTypeBase): @property def decode_content(self) -> str: return self.decode_case( - f"this->{self.field_name} = StringRef(reinterpret_cast(value.data()), value.size());", + f"this->{self.field_name} = StringRef(value.data(), value.size());", ) def dump(self, name: str) -> str: @@ -1495,6 +1498,14 @@ class UInt32Type(VarintTypeMixin, TypeInfo): class EnumType(VarintTypeMixin, TypeInfo): _varint_max_bits = 32 + def encode_element(self, number: int, element: str) -> str: + return _encode_call( + self.encode_func, + str(number), + f"static_cast({element})", + force=True, + ) + @property def cpp_type(self) -> str: return f"enums::{self._field.type_name[1:]}" @@ -1774,23 +1785,11 @@ class FixedArrayRepeatedType(TypeInfo): def _encode_element(self, element: str) -> str: """Helper to generate encode statement for a single element.""" - if isinstance(self._ti, EnumType): - return _encode_call( - self._ti.encode_func, - str(self.number), - f"static_cast({element})", - force=True, + if isinstance(self._ti, MessageType) and _is_inline_encode(self._ti.cpp_type): + return _generate_inline_encode_block( + self.number, self._ti.cpp_type, element ) - # Repeated message elements use encode_sub_message (force=true is default) - if isinstance(self._ti, MessageType): - if _is_inline_encode(self._ti.cpp_type): - return _generate_inline_encode_block( - self.number, self._ti.cpp_type, element - ) - return _encode_call( - "encode_sub_message", "buffer", str(self.number), element - ) - return _encode_call(self._ti.encode_func, str(self.number), element, force=True) + return self._ti.encode_element(self.number, element) @property def cpp_type(self) -> str: @@ -2096,7 +2095,6 @@ class RepeatedTypeInfo(TypeInfo): if self._use_pointer: return None if isinstance(self._ti, MessageType): - # Special handling for non-template message decoding return self.decode_case( f"this->{self.field_name}.emplace_back();\n" f"value.decode_to_message(this->{self.field_name}.back());" @@ -2109,20 +2107,7 @@ class RepeatedTypeInfo(TypeInfo): return isinstance(self._ti, BoolType) def _encode_element_call(self, element: str) -> str: - """Helper to generate encode call for a single element.""" - if isinstance(self._ti, EnumType): - return _encode_call( - self._ti.encode_func, - str(self.number), - f"static_cast({element})", - force=True, - ) - # Repeated message elements use encode_sub_message (force=true is default) - if isinstance(self._ti, MessageType): - return _encode_call( - "encode_sub_message", "buffer", str(self.number), element - ) - return _encode_call(self._ti.encode_func, str(self.number), element, force=True) + return self._ti.encode_element(self.number, element) @property def encode_content(self) -> str: @@ -2662,8 +2647,6 @@ def build_message_type( o += " const ProtoFieldValue value(data, scalar);\n" o += " switch (tag) {\n" o += indent("\n".join(decode), " ") + "\n" - o += " default:\n" - o += " break;\n" o += " }\n" o += "}\n" cpp += o diff --git a/tests/benchmarks/components/api/bench_proto_proxy.cpp b/tests/benchmarks/components/api/bench_proto_proxy.cpp index 05bbcc73dd..e45966999b 100644 --- a/tests/benchmarks/components/api/bench_proto_proxy.cpp +++ b/tests/benchmarks/components/api/bench_proto_proxy.cpp @@ -249,7 +249,7 @@ static APIBuffer build_infrared_rf_transmit_wire() { std::memcpy(bytes + len, packed, packed_len); len += packed_len; // field 6: modulation = 1 (non-zero so it's actually emitted and exercises - // decode_varint for this field, matching the documented layout above). + // decode_field for this field, matching the documented layout above). put_byte(0x30); put_varint(1); diff --git a/tests/unit_tests/components/api/test_api_protobuf_generator.py b/tests/unit_tests/components/api/test_api_protobuf_generator.py index 48495c8bd6..72ff18d940 100644 --- a/tests/unit_tests/components/api/test_api_protobuf_generator.py +++ b/tests/unit_tests/components/api/test_api_protobuf_generator.py @@ -225,7 +225,6 @@ def test_message_gets_a_single_decode_field_override() -> None: header, cpp, _ = build_message_type(desc, {}, {"Mixed": SOURCE_CLIENT}) decl = "void 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( "void Mixed::decode_field(uint32_t tag, const uint8_t *data, proto_varint_value_t scalar) {"