Reduce API proto vtable overhead by splitting decode functionality (#9541)

This commit is contained in:
J. Nick Koston 2025-07-15 22:46:04 -10:00 committed by GitHub
parent 2c478efcba
commit 15768ec00d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 115 additions and 392 deletions

View File

@ -56,26 +56,6 @@ void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool
void ConnectResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->invalid_password);
}
bool AreaInfo::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->area_id = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool AreaInfo::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2:
this->name = value.as_string();
break;
default:
return false;
}
return true;
}
void AreaInfo::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->area_id);
buffer.encode_string(2, this->name);
@ -84,29 +64,6 @@ void AreaInfo::calculate_size(uint32_t &total_size) const {
ProtoSize::add_uint32_field(total_size, 1, this->area_id);
ProtoSize::add_string_field(total_size, 1, this->name);
}
bool DeviceInfo::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->device_id = value.as_uint32();
break;
case 3:
this->area_id = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool DeviceInfo::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2:
this->name = value.as_string();
break;
default:
return false;
}
return true;
}
void DeviceInfo::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->device_id);
buffer.encode_string(2, this->name);
@ -918,19 +875,6 @@ void NoiseEncryptionSetKeyResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->success);
}
#endif
bool HomeassistantServiceMap::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1:
this->key = value.as_string();
break;
case 2:
this->value = value.as_string();
break;
default:
return false;
}
return true;
}
void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->key);
buffer.encode_string(2, this->value);
@ -1000,26 +944,6 @@ void GetTimeResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_fixed32_field(total_size, 1, this->epoch_seconds);
}
#ifdef USE_API_SERVICES
bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2:
this->type = static_cast<enums::ServiceArgType>(value.as_uint32());
break;
default:
return false;
}
return true;
}
bool ListEntitiesServicesArgument::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1:
this->name = value.as_string();
break;
default:
return false;
}
return true;
}
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_uint32(2, static_cast<uint32_t>(this->type));
@ -1088,50 +1012,6 @@ bool ExecuteServiceArgument::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
return true;
}
void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(1, this->bool_);
buffer.encode_int32(2, this->legacy_int);
buffer.encode_float(3, this->float_);
buffer.encode_string(4, this->string_);
buffer.encode_sint32(5, this->int_);
for (auto it : this->bool_array) {
buffer.encode_bool(6, it, true);
}
for (auto &it : this->int_array) {
buffer.encode_sint32(7, it, true);
}
for (auto &it : this->float_array) {
buffer.encode_float(8, it, true);
}
for (auto &it : this->string_array) {
buffer.encode_string(9, it, true);
}
}
void ExecuteServiceArgument::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->bool_);
ProtoSize::add_int32_field(total_size, 1, this->legacy_int);
ProtoSize::add_float_field(total_size, 1, this->float_);
ProtoSize::add_string_field(total_size, 1, this->string_);
ProtoSize::add_sint32_field(total_size, 1, this->int_);
if (!this->bool_array.empty()) {
for (const auto it : this->bool_array) {
ProtoSize::add_bool_field_repeated(total_size, 1, it);
}
}
if (!this->int_array.empty()) {
for (const auto &it : this->int_array) {
ProtoSize::add_sint32_field_repeated(total_size, 1, it);
}
}
if (!this->float_array.empty()) {
total_size += this->float_array.size() * 5;
}
if (!this->string_array.empty()) {
for (const auto &it : this->string_array) {
ProtoSize::add_string_field_repeated(total_size, 1, it);
}
}
}
bool ExecuteServiceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 2:
@ -1859,35 +1739,6 @@ bool ButtonCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
}
#endif
#ifdef USE_MEDIA_PLAYER
bool MediaPlayerSupportedFormat::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2:
this->sample_rate = value.as_uint32();
break;
case 3:
this->num_channels = value.as_uint32();
break;
case 4:
this->purpose = static_cast<enums::MediaPlayerFormatPurpose>(value.as_uint32());
break;
case 5:
this->sample_bytes = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool MediaPlayerSupportedFormat::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1:
this->format = value.as_string();
break;
default:
return false;
}
return true;
}
void MediaPlayerSupportedFormat::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->format);
buffer.encode_uint32(2, this->sample_rate);
@ -2017,29 +1868,6 @@ bool SubscribeBluetoothLEAdvertisementsRequest::decode_varint(uint32_t field_id,
}
return true;
}
bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 2:
this->legacy_data.push_back(value.as_uint32());
break;
default:
return false;
}
return true;
}
bool BluetoothServiceData::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1:
this->uuid = value.as_string();
break;
case 3:
this->data = value.as_string();
break;
default:
return false;
}
return true;
}
void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->uuid);
for (auto &it : this->legacy_data) {
@ -2084,32 +1912,6 @@ void BluetoothLEAdvertisementResponse::calculate_size(uint32_t &total_size) cons
ProtoSize::add_repeated_message(total_size, 1, this->manufacturer_data);
ProtoSize::add_uint32_field(total_size, 1, this->address_type);
}
bool BluetoothLERawAdvertisement::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->address = value.as_uint64();
break;
case 2:
this->rssi = value.as_sint32();
break;
case 3:
this->address_type = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool BluetoothLERawAdvertisement::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 4:
this->data = value.as_string();
break;
default:
return false;
}
return true;
}
void BluetoothLERawAdvertisement::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_sint32(2, this->rssi);
@ -2171,19 +1973,6 @@ bool BluetoothGATTGetServicesRequest::decode_varint(uint32_t field_id, ProtoVarI
}
return true;
}
bool BluetoothGATTDescriptor::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->uuid.push_back(value.as_uint64());
break;
case 2:
this->handle = value.as_uint32();
break;
default:
return false;
}
return true;
}
void BluetoothGATTDescriptor::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->uuid) {
buffer.encode_uint64(1, it, true);
@ -2198,33 +1987,6 @@ void BluetoothGATTDescriptor::calculate_size(uint32_t &total_size) const {
}
ProtoSize::add_uint32_field(total_size, 1, this->handle);
}
bool BluetoothGATTCharacteristic::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->uuid.push_back(value.as_uint64());
break;
case 2:
this->handle = value.as_uint32();
break;
case 3:
this->properties = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool BluetoothGATTCharacteristic::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 4:
this->descriptors.emplace_back();
value.decode_to_message(this->descriptors.back());
break;
default:
return false;
}
return true;
}
void BluetoothGATTCharacteristic::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->uuid) {
buffer.encode_uint64(1, it, true);
@ -2245,30 +2007,6 @@ void BluetoothGATTCharacteristic::calculate_size(uint32_t &total_size) const {
ProtoSize::add_uint32_field(total_size, 1, this->properties);
ProtoSize::add_repeated_message(total_size, 1, this->descriptors);
}
bool BluetoothGATTService::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->uuid.push_back(value.as_uint64());
break;
case 2:
this->handle = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool BluetoothGATTService::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 3:
this->characteristics.emplace_back();
value.decode_to_message(this->characteristics.back());
break;
default:
return false;
}
return true;
}
void BluetoothGATTService::encode(ProtoWriteBuffer buffer) const {
for (auto &it : this->uuid) {
buffer.encode_uint64(1, it, true);
@ -2519,29 +2257,6 @@ bool SubscribeVoiceAssistantRequest::decode_varint(uint32_t field_id, ProtoVarIn
}
return true;
}
bool VoiceAssistantAudioSettings::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
this->noise_suppression_level = value.as_uint32();
break;
case 2:
this->auto_gain = value.as_uint32();
break;
default:
return false;
}
return true;
}
bool VoiceAssistantAudioSettings::decode_32bit(uint32_t field_id, Proto32Bit value) {
switch (field_id) {
case 3:
this->volume_multiplier = value.as_float();
break;
default:
return false;
}
return true;
}
void VoiceAssistantAudioSettings::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->noise_suppression_level);
buffer.encode_uint32(2, this->auto_gain);
@ -2592,14 +2307,6 @@ bool VoiceAssistantEventData::decode_length(uint32_t field_id, ProtoLengthDelimi
}
return true;
}
void VoiceAssistantEventData::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->name);
buffer.encode_string(2, this->value);
}
void VoiceAssistantEventData::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->name);
ProtoSize::add_string_field(total_size, 1, this->value);
}
bool VoiceAssistantEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
case 1:
@ -2711,22 +2418,6 @@ void VoiceAssistantAnnounceFinished::encode(ProtoWriteBuffer buffer) const { buf
void VoiceAssistantAnnounceFinished::calculate_size(uint32_t &total_size) const {
ProtoSize::add_bool_field(total_size, 1, this->success);
}
bool VoiceAssistantWakeWord::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
switch (field_id) {
case 1:
this->id = value.as_string();
break;
case 2:
this->wake_word = value.as_string();
break;
case 3:
this->trained_languages.push_back(value.as_string());
break;
default:
return false;
}
return true;
}
void VoiceAssistantWakeWord::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(1, this->id);
buffer.encode_string(2, this->wake_word);

View File

@ -308,7 +308,7 @@ class StateResponseProtoMessage : public ProtoMessage {
protected:
};
class CommandProtoMessage : public ProtoMessage {
class CommandProtoMessage : public ProtoDecodableMessage {
public:
~CommandProtoMessage() override = default;
uint32_t key{0};
@ -316,7 +316,7 @@ class CommandProtoMessage : public ProtoMessage {
protected:
};
class HelloRequest : public ProtoMessage {
class HelloRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 1;
static constexpr uint8_t ESTIMATED_SIZE = 17;
@ -353,7 +353,7 @@ class HelloResponse : public ProtoMessage {
protected:
};
class ConnectRequest : public ProtoMessage {
class ConnectRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 3;
static constexpr uint8_t ESTIMATED_SIZE = 9;
@ -384,7 +384,7 @@ class ConnectResponse : public ProtoMessage {
protected:
};
class DisconnectRequest : public ProtoMessage {
class DisconnectRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 5;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -397,7 +397,7 @@ class DisconnectRequest : public ProtoMessage {
protected:
};
class DisconnectResponse : public ProtoMessage {
class DisconnectResponse : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 6;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -410,7 +410,7 @@ class DisconnectResponse : public ProtoMessage {
protected:
};
class PingRequest : public ProtoMessage {
class PingRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 7;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -423,7 +423,7 @@ class PingRequest : public ProtoMessage {
protected:
};
class PingResponse : public ProtoMessage {
class PingResponse : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 8;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -436,7 +436,7 @@ class PingResponse : public ProtoMessage {
protected:
};
class DeviceInfoRequest : public ProtoMessage {
class DeviceInfoRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 9;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -460,8 +460,6 @@ class AreaInfo : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class DeviceInfo : public ProtoMessage {
public:
@ -475,8 +473,6 @@ class DeviceInfo : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class DeviceInfoResponse : public ProtoMessage {
public:
@ -543,7 +539,7 @@ class DeviceInfoResponse : public ProtoMessage {
protected:
};
class ListEntitiesRequest : public ProtoMessage {
class ListEntitiesRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 11;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -569,7 +565,7 @@ class ListEntitiesDoneResponse : public ProtoMessage {
protected:
};
class SubscribeStatesRequest : public ProtoMessage {
class SubscribeStatesRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 20;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -972,7 +968,7 @@ class TextSensorStateResponse : public StateResponseProtoMessage {
protected:
};
#endif
class SubscribeLogsRequest : public ProtoMessage {
class SubscribeLogsRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 28;
static constexpr uint8_t ESTIMATED_SIZE = 4;
@ -1007,7 +1003,7 @@ class SubscribeLogsResponse : public ProtoMessage {
protected:
};
#ifdef USE_API_NOISE
class NoiseEncryptionSetKeyRequest : public ProtoMessage {
class NoiseEncryptionSetKeyRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 124;
static constexpr uint8_t ESTIMATED_SIZE = 9;
@ -1039,7 +1035,7 @@ class NoiseEncryptionSetKeyResponse : public ProtoMessage {
protected:
};
#endif
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
class SubscribeHomeassistantServicesRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 34;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -1063,7 +1059,6 @@ class HomeassistantServiceMap : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class HomeassistantServiceResponse : public ProtoMessage {
public:
@ -1085,7 +1080,7 @@ class HomeassistantServiceResponse : public ProtoMessage {
protected:
};
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
class SubscribeHomeAssistantStatesRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 38;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -1116,7 +1111,7 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
protected:
};
class HomeAssistantStateResponse : public ProtoMessage {
class HomeAssistantStateResponse : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 40;
static constexpr uint8_t ESTIMATED_SIZE = 27;
@ -1133,7 +1128,7 @@ class HomeAssistantStateResponse : public ProtoMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class GetTimeRequest : public ProtoMessage {
class GetTimeRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 36;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -1146,7 +1141,7 @@ class GetTimeRequest : public ProtoMessage {
protected:
};
class GetTimeResponse : public ProtoMessage {
class GetTimeResponse : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 37;
static constexpr uint8_t ESTIMATED_SIZE = 5;
@ -1175,8 +1170,6 @@ class ListEntitiesServicesArgument : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesServicesResponse : public ProtoMessage {
public:
@ -1196,7 +1189,7 @@ class ListEntitiesServicesResponse : public ProtoMessage {
protected:
};
class ExecuteServiceArgument : public ProtoMessage {
class ExecuteServiceArgument : public ProtoDecodableMessage {
public:
bool bool_{false};
int32_t legacy_int{0};
@ -1207,8 +1200,6 @@ class ExecuteServiceArgument : public ProtoMessage {
std::vector<int32_t> int_array{};
std::vector<float> float_array{};
std::vector<std::string> string_array{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@ -1218,7 +1209,7 @@ class ExecuteServiceArgument : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ExecuteServiceRequest : public ProtoMessage {
class ExecuteServiceRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 42;
static constexpr uint8_t ESTIMATED_SIZE = 39;
@ -1269,7 +1260,7 @@ class CameraImageResponse : public StateResponseProtoMessage {
protected:
};
class CameraImageRequest : public ProtoMessage {
class CameraImageRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 45;
static constexpr uint8_t ESTIMATED_SIZE = 4;
@ -1660,8 +1651,6 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage {
public:
@ -1724,7 +1713,7 @@ class MediaPlayerCommandRequest : public CommandProtoMessage {
};
#endif
#ifdef USE_BLUETOOTH_PROXY
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 66;
static constexpr uint8_t ESTIMATED_SIZE = 4;
@ -1751,8 +1740,6 @@ class BluetoothServiceData : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothLEAdvertisementResponse : public ProtoMessage {
public:
@ -1789,8 +1776,6 @@ class BluetoothLERawAdvertisement : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
public:
@ -1808,7 +1793,7 @@ class BluetoothLERawAdvertisementsResponse : public ProtoMessage {
protected:
};
class BluetoothDeviceRequest : public ProtoMessage {
class BluetoothDeviceRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 68;
static constexpr uint8_t ESTIMATED_SIZE = 12;
@ -1845,7 +1830,7 @@ class BluetoothDeviceConnectionResponse : public ProtoMessage {
protected:
};
class BluetoothGATTGetServicesRequest : public ProtoMessage {
class BluetoothGATTGetServicesRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 70;
static constexpr uint8_t ESTIMATED_SIZE = 4;
@ -1871,7 +1856,6 @@ class BluetoothGATTDescriptor : public ProtoMessage {
#endif
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTCharacteristic : public ProtoMessage {
public:
@ -1886,8 +1870,6 @@ class BluetoothGATTCharacteristic : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTService : public ProtoMessage {
public:
@ -1901,8 +1883,6 @@ class BluetoothGATTService : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTGetServicesResponse : public ProtoMessage {
public:
@ -1937,7 +1917,7 @@ class BluetoothGATTGetServicesDoneResponse : public ProtoMessage {
protected:
};
class BluetoothGATTReadRequest : public ProtoMessage {
class BluetoothGATTReadRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 73;
static constexpr uint8_t ESTIMATED_SIZE = 8;
@ -1971,7 +1951,7 @@ class BluetoothGATTReadResponse : public ProtoMessage {
protected:
};
class BluetoothGATTWriteRequest : public ProtoMessage {
class BluetoothGATTWriteRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 75;
static constexpr uint8_t ESTIMATED_SIZE = 19;
@ -1990,7 +1970,7 @@ class BluetoothGATTWriteRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
class BluetoothGATTReadDescriptorRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 76;
static constexpr uint8_t ESTIMATED_SIZE = 8;
@ -2006,7 +1986,7 @@ class BluetoothGATTReadDescriptorRequest : public ProtoMessage {
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
class BluetoothGATTWriteDescriptorRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 77;
static constexpr uint8_t ESTIMATED_SIZE = 17;
@ -2024,7 +2004,7 @@ class BluetoothGATTWriteDescriptorRequest : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class BluetoothGATTNotifyRequest : public ProtoMessage {
class BluetoothGATTNotifyRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 78;
static constexpr uint8_t ESTIMATED_SIZE = 10;
@ -2059,7 +2039,7 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage {
protected:
};
class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
class SubscribeBluetoothConnectionsFreeRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 80;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -2178,7 +2158,7 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage {
protected:
};
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 87;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -2226,7 +2206,7 @@ class BluetoothScannerStateResponse : public ProtoMessage {
protected:
};
class BluetoothScannerSetModeRequest : public ProtoMessage {
class BluetoothScannerSetModeRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 127;
static constexpr uint8_t ESTIMATED_SIZE = 2;
@ -2243,7 +2223,7 @@ class BluetoothScannerSetModeRequest : public ProtoMessage {
};
#endif
#ifdef USE_VOICE_ASSISTANT
class SubscribeVoiceAssistantRequest : public ProtoMessage {
class SubscribeVoiceAssistantRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 89;
static constexpr uint8_t ESTIMATED_SIZE = 6;
@ -2271,8 +2251,6 @@ class VoiceAssistantAudioSettings : public ProtoMessage {
#endif
protected:
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantRequest : public ProtoMessage {
public:
@ -2294,7 +2272,7 @@ class VoiceAssistantRequest : public ProtoMessage {
protected:
};
class VoiceAssistantResponse : public ProtoMessage {
class VoiceAssistantResponse : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 91;
static constexpr uint8_t ESTIMATED_SIZE = 6;
@ -2310,12 +2288,10 @@ class VoiceAssistantResponse : public ProtoMessage {
protected:
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantEventData : public ProtoMessage {
class VoiceAssistantEventData : public ProtoDecodableMessage {
public:
std::string name{};
std::string value{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
#endif
@ -2323,7 +2299,7 @@ class VoiceAssistantEventData : public ProtoMessage {
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class VoiceAssistantEventResponse : public ProtoMessage {
class VoiceAssistantEventResponse : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 92;
static constexpr uint8_t ESTIMATED_SIZE = 36;
@ -2340,7 +2316,7 @@ class VoiceAssistantEventResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantAudio : public ProtoMessage {
class VoiceAssistantAudio : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 106;
static constexpr uint8_t ESTIMATED_SIZE = 11;
@ -2359,7 +2335,7 @@ class VoiceAssistantAudio : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantTimerEventResponse : public ProtoMessage {
class VoiceAssistantTimerEventResponse : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 115;
static constexpr uint8_t ESTIMATED_SIZE = 30;
@ -2380,7 +2356,7 @@ class VoiceAssistantTimerEventResponse : public ProtoMessage {
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
};
class VoiceAssistantAnnounceRequest : public ProtoMessage {
class VoiceAssistantAnnounceRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 119;
static constexpr uint8_t ESTIMATED_SIZE = 29;
@ -2427,9 +2403,8 @@ class VoiceAssistantWakeWord : public ProtoMessage {
#endif
protected:
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
};
class VoiceAssistantConfigurationRequest : public ProtoMessage {
class VoiceAssistantConfigurationRequest : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 121;
static constexpr uint8_t ESTIMATED_SIZE = 0;
@ -2460,7 +2435,7 @@ class VoiceAssistantConfigurationResponse : public ProtoMessage {
protected:
};
class VoiceAssistantSetConfiguration : public ProtoMessage {
class VoiceAssistantSetConfiguration : public ProtoDecodableMessage {
public:
static constexpr uint8_t MESSAGE_TYPE = 123;
static constexpr uint8_t ESTIMATED_SIZE = 18;

View File

@ -8,7 +8,7 @@ namespace api {
static const char *const TAG = "api.proto";
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
uint32_t i = 0;
bool error = false;
while (i < length) {

View File

@ -135,6 +135,7 @@ class ProtoVarInt {
// Forward declaration for decode_to_message and encode_to_writer
class ProtoMessage;
class ProtoDecodableMessage;
class ProtoLengthDelimited {
public:
@ -142,15 +143,15 @@ class ProtoLengthDelimited {
std::string as_string() const { return std::string(reinterpret_cast<const char *>(this->value_), this->length_); }
/**
* Decode the length-delimited data into an existing ProtoMessage instance.
* Decode the length-delimited data into an existing ProtoDecodableMessage instance.
*
* This method allows decoding without templates, enabling use in contexts
* where the message type is not known at compile time. The ProtoMessage's
* where the message type is not known at compile time. The ProtoDecodableMessage's
* decode() method will be called with the raw data and length.
*
* @param msg The ProtoMessage instance to decode into
* @param msg The ProtoDecodableMessage instance to decode into
*/
void decode_to_message(ProtoMessage &msg) const;
void decode_to_message(ProtoDecodableMessage &msg) const;
protected:
const uint8_t *const value_;
@ -298,7 +299,6 @@ class ProtoMessage {
virtual ~ProtoMessage() = default;
// Default implementation for messages with no fields
virtual void encode(ProtoWriteBuffer buffer) const {}
void decode(const uint8_t *buffer, size_t length);
// Default implementation for messages with no fields
virtual void calculate_size(uint32_t &total_size) const {}
#ifdef HAS_PROTO_MESSAGE_DUMP
@ -306,6 +306,12 @@ class ProtoMessage {
virtual void dump_to(std::string &out) const = 0;
virtual const char *message_name() const { return "unknown"; }
#endif
};
// Base class for messages that support decoding
class ProtoDecodableMessage : public ProtoMessage {
public:
void decode(const uint8_t *buffer, size_t length);
protected:
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
@ -808,8 +814,8 @@ inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessa
assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
}
// Implementation of decode_to_message - must be after ProtoMessage is defined
inline void ProtoLengthDelimited::decode_to_message(ProtoMessage &msg) const {
// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
inline void ProtoLengthDelimited::decode_to_message(ProtoDecodableMessage &msg) const {
msg.decode(this->value_, this->length_);
}

View File

@ -877,14 +877,15 @@ class RepeatedTypeInfo(TypeInfo):
def build_type_usage_map(
file_desc: descriptor.FileDescriptorProto,
) -> tuple[dict[str, str | None], dict[str, str | None]]:
) -> tuple[dict[str, str | None], dict[str, str | None], dict[str, int]]:
"""Build mappings for both enums and messages to their ifdefs based on usage.
Returns:
tuple: (enum_ifdef_map, message_ifdef_map)
tuple: (enum_ifdef_map, message_ifdef_map, message_source_map)
"""
enum_ifdef_map: dict[str, str | None] = {}
message_ifdef_map: dict[str, str | None] = {}
message_source_map: dict[str, int] = {}
# Build maps of which types are used by which messages
enum_usage: dict[
@ -971,7 +972,44 @@ def build_type_usage_map(
message_ifdef_map[message.name] = parent_ifdefs.pop()
changed = True
return enum_ifdef_map, message_ifdef_map
# Build message source map
# First pass: Get explicit sources for messages with source option or id
for msg in file_desc.message_type:
if msg.options.HasExtension(pb.source):
# Explicit source option takes precedence
message_source_map[msg.name] = get_opt(msg, pb.source, SOURCE_BOTH)
elif msg.options.HasExtension(pb.id):
# Service messages (with id) default to SOURCE_BOTH
message_source_map[msg.name] = SOURCE_BOTH
# Second pass: Determine sources for embedded messages based on their usage
for msg in file_desc.message_type:
if msg.name in message_source_map:
continue # Already has explicit source
if msg.name in message_usage:
# Get sources from all parent messages that use this one
parent_sources = {
message_source_map[parent]
for parent in message_usage[msg.name]
if parent in message_source_map
}
# Combine parent sources
if not parent_sources:
# No parent has explicit source, default to encode-only
message_source_map[msg.name] = SOURCE_SERVER
elif len(parent_sources) > 1:
# Multiple different sources or SOURCE_BOTH present
message_source_map[msg.name] = SOURCE_BOTH
else:
# Inherit single parent source
message_source_map[msg.name] = parent_sources.pop()
else:
# Not used by any message and no explicit source - default to encode-only
message_source_map[msg.name] = SOURCE_SERVER
return enum_ifdef_map, message_ifdef_map, message_source_map
def build_enum_type(desc, enum_ifdef_map) -> tuple[str, str, str]:
@ -1023,7 +1061,8 @@ def calculate_message_estimated_size(desc: descriptor.DescriptorProto) -> int:
def build_message_type(
desc: descriptor.DescriptorProto,
base_class_fields: dict[str, list[descriptor.FieldDescriptorProto]] = None,
base_class_fields: dict[str, list[descriptor.FieldDescriptorProto]],
message_source_map: dict[str, int],
) -> tuple[str, str, str]:
public_content: list[str] = []
protected_content: list[str] = []
@ -1045,7 +1084,7 @@ def build_message_type(
message_id: int | None = get_opt(desc, pb.id)
# Get source direction to determine if we need decode/encode methods
source: int = get_opt(desc, pb.source, SOURCE_BOTH)
source = message_source_map[desc.name]
needs_decode = source in (SOURCE_BOTH, SOURCE_CLIENT)
needs_encode = source in (SOURCE_BOTH, SOURCE_SERVER)
@ -1250,7 +1289,9 @@ def build_message_type(
if base_class:
out = f"class {desc.name} : public {base_class} {{\n"
else:
out = f"class {desc.name} : public ProtoMessage {{\n"
# Determine inheritance based on whether the message needs decoding
base_class = "ProtoDecodableMessage" if needs_decode else "ProtoMessage"
out = f"class {desc.name} : public {base_class} {{\n"
out += " public:\n"
out += indent("\n".join(public_content)) + "\n"
out += "\n"
@ -1351,6 +1392,7 @@ def find_common_fields(
def build_base_class(
base_class_name: str,
common_fields: list[descriptor.FieldDescriptorProto],
messages: list[descriptor.DescriptorProto],
) -> tuple[str, str, str]:
"""Build the base class definition and implementation."""
public_content = []
@ -1365,8 +1407,15 @@ def build_base_class(
protected_content.extend(ti.protected_content)
public_content.extend(ti.public_content)
# Determine if any message using this base class needs decoding
needs_decode = any(
get_opt(msg, pb.source, SOURCE_BOTH) in (SOURCE_BOTH, SOURCE_CLIENT)
for msg in messages
)
# Build header
out = f"class {base_class_name} : public ProtoMessage {{\n"
parent_class = "ProtoDecodableMessage" if needs_decode else "ProtoMessage"
out = f"class {base_class_name} : public {parent_class} {{\n"
out += " public:\n"
# Add destructor with override
@ -1404,7 +1453,9 @@ def generate_base_classes(
if common_fields:
# Generate base class
header, cpp, dump_cpp = build_base_class(base_class_name, common_fields)
header, cpp, dump_cpp = build_base_class(
base_class_name, common_fields, messages
)
all_headers.append(header)
all_cpp.append(cpp)
all_dump_cpp.append(dump_cpp)
@ -1516,7 +1567,7 @@ namespace api {
content += "namespace enums {\n\n"
# Build dynamic ifdef mappings for both enums and messages
enum_ifdef_map, message_ifdef_map = build_type_usage_map(file)
enum_ifdef_map, message_ifdef_map, message_source_map = build_type_usage_map(file)
# Simple grouping of enums by ifdef
current_ifdef = None
@ -1570,7 +1621,7 @@ namespace api {
current_ifdef = None
for m in mt:
s, c, dc = build_message_type(m, base_class_fields)
s, c, dc = build_message_type(m, base_class_fields, message_source_map)
msg_ifdef = message_ifdef_map.get(m.name)
# Handle ifdef changes