This commit is contained in:
J. Nick Koston 2025-07-10 07:48:08 -10:00
parent e24d4450ac
commit 0c8693b8c6
No known key found for this signature in database
5 changed files with 1124 additions and 7070 deletions

File diff suppressed because it is too large Load Diff

View File

@ -889,9 +889,10 @@ void ProtoMessage::decode_v2(const uint8_t *buffer, size_t length) {
break;
}
case ProtoFieldType::TYPE_MESSAGE: {
// Use message type ID to call the appropriate handler
decode_message_field_by_type(field_addr, value, fields[j].message_type_id);
decoded = true;
// Use function pointer from metadata
if (fields[j].handler.message.decode) {
decoded = fields[j].handler.message.decode(field_addr, value);
}
break;
}
default:
@ -921,9 +922,10 @@ void ProtoMessage::decode_v2(const uint8_t *buffer, size_t length) {
break;
}
case ProtoFieldType::TYPE_MESSAGE: {
// Use message type ID to call the appropriate handler
decode_repeated_message_field_by_type(field_addr, value, repeated_fields[j].message_type_id);
decoded = true;
// Use function pointer from metadata
if (repeated_fields[j].handler.message.decode) {
decoded = repeated_fields[j].handler.message.decode(field_addr, value);
}
break;
}
default:
@ -1202,8 +1204,10 @@ void ProtoMessage::encode_v2(ProtoWriteBuffer buffer) const {
break;
}
case ProtoFieldType::TYPE_MESSAGE: {
// Use message type ID to call the appropriate handler
encode_message_field_by_type(buffer, fields[i].field_num, field_addr, fields[i].message_type_id);
// Use function pointer from metadata
if (fields[i].handler.message.encode) {
fields[i].handler.message.encode(buffer, field_addr, fields[i].field_num);
}
break;
}
}
@ -1337,9 +1341,10 @@ void ProtoMessage::encode_v2(ProtoWriteBuffer buffer) const {
break;
}
case ProtoFieldType::TYPE_MESSAGE: {
// Use message type ID to call the appropriate handler
encode_repeated_message_field_by_type(buffer, repeated_fields[i].field_num, field_addr,
repeated_fields[i].message_type_id);
// Use function pointer from metadata
if (repeated_fields[i].handler.message.encode) {
repeated_fields[i].handler.message.encode(buffer, field_addr, repeated_fields[i].field_num);
}
break;
}
}
@ -1447,9 +1452,11 @@ void ProtoMessage::calculate_size_v2(uint32_t &total_size) const {
break;
}
case ProtoFieldType::TYPE_MESSAGE: {
// Use message type ID to call the appropriate handler
size_message_field_by_type(total_size, fields[i].precalced_field_id_size, field_addr, fields[i].message_type_id,
fields[i].force_encode);
// Use function pointer from metadata
if (fields[i].handler.message.size) {
fields[i].handler.message.size(total_size, field_addr, fields[i].precalced_field_id_size,
fields[i].force_encode);
}
break;
}
}
@ -1577,9 +1584,10 @@ void ProtoMessage::calculate_size_v2(uint32_t &total_size) const {
break;
}
case ProtoFieldType::TYPE_MESSAGE: {
// Use message type ID to call the appropriate handler
size_repeated_message_field_by_type(total_size, repeated_fields[i].precalced_field_id_size, field_addr,
repeated_fields[i].message_type_id);
// Use function pointer from metadata
if (repeated_fields[i].handler.message.size) {
repeated_fields[i].handler.message.size(total_size, field_addr, repeated_fields[i].precalced_field_id_size);
}
break;
}
}

View File

@ -292,7 +292,16 @@ struct FieldMetaV2 {
ProtoFieldType type; // Field type enum
bool force_encode; // If true, encode even if value is default/empty
uint8_t precalced_field_id_size; // Pre-calculated size of field tag in bytes
uint8_t message_type_id; // For TYPE_MESSAGE/TYPE_ENUM: identifies which type (0 for non-message/enum)
union {
// For TYPE_MESSAGE: function pointers (12 bytes on ESP32)
struct {
EncodeFunc encode;
SizeFunc size;
DecodeLengthFunc decode;
} message;
// For TYPE_ENUM: enum ID (1 byte)
uint8_t enum_id;
} handler;
};
class ProtoWriteBuffer {
@ -483,7 +492,16 @@ struct RepeatedFieldMetaV2 {
uint16_t offset;
ProtoFieldType type; // Element type
uint8_t precalced_field_id_size; // Pre-calculated size of field tag in bytes
uint8_t message_type_id; // For TYPE_MESSAGE/TYPE_ENUM: identifies which type (0 for non-message/enum)
union {
// For TYPE_MESSAGE: function pointers
struct {
RepeatedEncodeFunc encode;
RepeatedSizeFunc size;
RepeatedDecodeLengthFunc decode;
} message;
// For TYPE_ENUM: enum ID
uint8_t enum_id;
} handler;
};
class ProtoMessage {
@ -688,6 +706,21 @@ template<typename EnumType> bool decode_repeated_enum_field(void *field_ptr, Pro
template<typename MessageType> bool decode_repeated_message_field(void *field_ptr, ProtoLengthDelimited value);
// Forward declarations for message field template functions
template<typename MessageType>
void encode_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
template<typename MessageType>
void size_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
template<typename MessageType> bool decode_message_field(void *field_ptr, ProtoLengthDelimited value);
template<typename MessageType>
void encode_repeated_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
template<typename MessageType>
void size_repeated_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
// Core shared functions
void encode_from_metadata(ProtoWriteBuffer buffer, const void *obj, const FieldMeta *fields, size_t field_count,
const RepeatedFieldMeta *repeated_fields = nullptr, size_t repeated_count = 0);
@ -695,18 +728,5 @@ void encode_from_metadata(ProtoWriteBuffer buffer, const void *obj, const FieldM
void calculate_size_from_metadata(uint32_t &total_size, const void *obj, const FieldMeta *fields, size_t field_count,
const RepeatedFieldMeta *repeated_fields = nullptr, size_t repeated_count = 0);
// Message type handlers for V2 system
void encode_message_field_by_type(ProtoWriteBuffer &buffer, uint8_t field_num, const void *field_ptr,
uint8_t message_type_id);
void decode_message_field_by_type(void *field_ptr, ProtoLengthDelimited value, uint8_t message_type_id);
void size_message_field_by_type(uint32_t &total_size, uint8_t precalced_field_id_size, const void *field_ptr,
uint8_t message_type_id, bool force);
void encode_repeated_message_field_by_type(ProtoWriteBuffer &buffer, uint8_t field_num, const void *field_ptr,
uint8_t message_type_id);
void decode_repeated_message_field_by_type(void *field_ptr, ProtoLengthDelimited value, uint8_t message_type_id);
void size_repeated_message_field_by_type(uint32_t &total_size, uint8_t precalced_field_id_size, const void *field_ptr,
uint8_t message_type_id);
} // namespace api
} // namespace esphome

View File

@ -25,6 +25,26 @@ template<typename EnumType> inline bool decode_enum_field(void *field_ptr, Proto
return true;
}
// Template functions for message field handling (regular fields)
template<typename MessageType>
inline void encode_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num) {
const MessageType *msg = static_cast<const MessageType *>(field_ptr);
buffer.encode_message<MessageType>(field_num, *msg);
}
template<typename MessageType>
inline void size_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size,
bool force) {
const MessageType *msg = static_cast<const MessageType *>(field_ptr);
ProtoSize::add_message_object(total_size, precalced_field_id_size, *msg, force);
}
template<typename MessageType> inline bool decode_message_field(void *field_ptr, ProtoLengthDelimited value) {
MessageType *msg = static_cast<MessageType *>(field_ptr);
*msg = value.as_message<MessageType>();
return true;
}
// Template repeated field functions (must be in header for instantiation)
template<typename EnumType>
inline void encode_repeated_enum_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num) {

View File

@ -1385,25 +1385,33 @@ def build_message_type(
if field_type:
field_tag_size = ti.calculate_field_id_size()
if field.type == descriptor.FieldDescriptorProto.TYPE_MESSAGE:
# For messages, get the type ID
message_type_id = get_message_type_id(ti._ti.type_name)
# For messages, generate function pointers
repeated_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {field_tag_size}, "
f"{{.message = {{&encode_repeated_message_field<{ti._ti.type_name}>, "
f"&size_repeated_message_field<{ti._ti.type_name}>, "
f"&decode_repeated_message_field<{ti._ti.type_name}>}}}}}}"
)
else:
message_type_id = 0 # Non-message types have ID 0
repeated_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {field_tag_size}, {message_type_id}}}"
)
# Non-message types don't need handler info
repeated_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {field_tag_size}, {{}}}}"
)
elif isinstance(ti._ti, EnumType):
enum_type_id = get_message_type_id(ti._ti.type_name)
repeated_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"ProtoFieldType::TYPE_ENUM, {ti.calculate_field_id_size()}, {enum_type_id}}}"
f"ProtoFieldType::TYPE_ENUM, {ti.calculate_field_id_size()}, {{.enum_id = {enum_type_id}}}}}"
)
elif isinstance(ti._ti, MessageType):
message_type_id = get_message_type_id(ti._ti.type_name)
repeated_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"ProtoFieldType::TYPE_MESSAGE, {ti.calculate_field_id_size()}, {message_type_id}}}"
f"ProtoFieldType::TYPE_MESSAGE, {ti.calculate_field_id_size()}, "
f"{{.message = {{&encode_repeated_message_field<{ti._ti.type_name}>, "
f"&size_repeated_message_field<{ti._ti.type_name}>, "
f"&decode_repeated_message_field<{ti._ti.type_name}>}}}}}}"
)
else:
ti = TYPE_INFO[field.type](field)
@ -1413,29 +1421,37 @@ def build_message_type(
if field_type:
field_tag_size = ti.calculate_field_id_size()
if field.type == descriptor.FieldDescriptorProto.TYPE_MESSAGE:
# For messages, get the type ID
message_type_id = get_message_type_id(ti.type_name)
# For messages, generate function pointers
regular_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {force}, {field_tag_size}, "
f"{{.message = {{&encode_message_field<{ti.type_name}>, "
f"&size_message_field<{ti.type_name}>, "
f"&decode_message_field<{ti.type_name}>}}}}}}"
)
else:
message_type_id = 0 # Non-message types have ID 0
regular_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {force}, {field_tag_size}, {message_type_id}}}"
)
# Non-message types don't need handler info
regular_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {force}, {field_tag_size}, {{}}}}"
)
elif isinstance(ti, EnumType):
field_tag_size = ti.calculate_field_id_size()
# For enums, use the enum name as type ID
enum_type_id = get_message_type_id(ti.type_name)
regular_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"ProtoFieldType::TYPE_ENUM, {force}, {field_tag_size}, {enum_type_id}}}"
f"ProtoFieldType::TYPE_ENUM, {force}, {field_tag_size}, {{.enum_id = {enum_type_id}}}}}"
)
elif isinstance(ti, MessageType):
field_tag_size = ti.calculate_field_id_size()
# For messages, use the message type name as type ID
message_type_id = get_message_type_id(ti.type_name)
# For messages, generate function pointers
regular_fields_v2.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"ProtoFieldType::TYPE_MESSAGE, {force}, {field_tag_size}, {message_type_id}}}"
f"ProtoFieldType::TYPE_MESSAGE, {force}, {field_tag_size}, "
f"{{.message = {{&encode_message_field<{ti.type_name}>, "
f"&size_message_field<{ti.type_name}>, "
f"&decode_message_field<{ti.type_name}>}}}}}}"
)
metadata_info["regular_fields_v2"] = regular_fields_v2
@ -2085,130 +2101,6 @@ namespace api {
if current_ifdef is not None:
cpp += "#endif\n"
# Generate message type handler functions
cpp += "\n// Message type handler implementations\n"
# Sort by type ID for consistent output
sorted_types = sorted(MESSAGE_TYPE_REGISTRY.items(), key=lambda x: x[1])
# Generate encode handler
cpp += "void encode_message_field_by_type(ProtoWriteBuffer &buffer, uint8_t field_num, const void *field_ptr, uint8_t message_type_id) {\n"
cpp += " switch (message_type_id) {\n"
for message_name, type_id in sorted_types:
ifdef = message_ifdef_map.get(message_name)
if ifdef:
cpp += f"#ifdef {ifdef}\n"
cpp += f" case {type_id}: {{\n"
cpp += f" const {message_name} *msg = static_cast<const {message_name} *>(field_ptr);\n"
cpp += f" buffer.encode_message<{message_name}>(field_num, *msg);\n"
cpp += " break;\n"
cpp += " }\n"
if ifdef:
cpp += "#endif\n"
cpp += " default:\n"
cpp += " break;\n"
cpp += " }\n"
cpp += "}\n\n"
# Generate decode handler
cpp += "void decode_message_field_by_type(void *field_ptr, ProtoLengthDelimited value, uint8_t message_type_id) {\n"
cpp += " switch (message_type_id) {\n"
for message_name, type_id in sorted_types:
ifdef = message_ifdef_map.get(message_name)
if ifdef:
cpp += f"#ifdef {ifdef}\n"
cpp += f" case {type_id}: {{\n"
cpp += (
f" {message_name} *msg = static_cast<{message_name} *>(field_ptr);\n"
)
cpp += f" *msg = value.as_message<{message_name}>();\n"
cpp += " break;\n"
cpp += " }\n"
if ifdef:
cpp += "#endif\n"
cpp += " default:\n"
cpp += " break;\n"
cpp += " }\n"
cpp += "}\n\n"
# Generate size handler
cpp += "void size_message_field_by_type(uint32_t &total_size, uint8_t precalced_field_id_size, const void *field_ptr, uint8_t message_type_id, bool force) {\n"
cpp += " switch (message_type_id) {\n"
for message_name, type_id in sorted_types:
ifdef = message_ifdef_map.get(message_name)
if ifdef:
cpp += f"#ifdef {ifdef}\n"
cpp += f" case {type_id}: {{\n"
cpp += f" const {message_name} *msg = static_cast<const {message_name} *>(field_ptr);\n"
cpp += " ProtoSize::add_message_object(total_size, precalced_field_id_size, *msg, force);\n"
cpp += " break;\n"
cpp += " }\n"
if ifdef:
cpp += "#endif\n"
cpp += " default:\n"
cpp += " break;\n"
cpp += " }\n"
cpp += "}\n\n"
# Generate repeated encode handler
cpp += "void encode_repeated_message_field_by_type(ProtoWriteBuffer &buffer, uint8_t field_num, const void *field_ptr, uint8_t message_type_id) {\n"
cpp += " switch (message_type_id) {\n"
for message_name, type_id in sorted_types:
ifdef = message_ifdef_map.get(message_name)
if ifdef:
cpp += f"#ifdef {ifdef}\n"
cpp += f" case {type_id}: {{\n"
cpp += f" const std::vector<{message_name}> *vec = static_cast<const std::vector<{message_name}> *>(field_ptr);\n"
cpp += " for (const auto &msg : *vec) {\n"
cpp += f" buffer.encode_message<{message_name}>(field_num, msg, true);\n"
cpp += " }\n"
cpp += " break;\n"
cpp += " }\n"
if ifdef:
cpp += "#endif\n"
cpp += " default:\n"
cpp += " break;\n"
cpp += " }\n"
cpp += "}\n\n"
# Generate repeated decode handler
cpp += "void decode_repeated_message_field_by_type(void *field_ptr, ProtoLengthDelimited value, uint8_t message_type_id) {\n"
cpp += " switch (message_type_id) {\n"
for message_name, type_id in sorted_types:
ifdef = message_ifdef_map.get(message_name)
if ifdef:
cpp += f"#ifdef {ifdef}\n"
cpp += f" case {type_id}: {{\n"
cpp += f" std::vector<{message_name}> *vec = static_cast<std::vector<{message_name}> *>(field_ptr);\n"
cpp += f" vec->push_back(value.as_message<{message_name}>());\n"
cpp += " break;\n"
cpp += " }\n"
if ifdef:
cpp += "#endif\n"
cpp += " default:\n"
cpp += " break;\n"
cpp += " }\n"
cpp += "}\n\n"
# Generate repeated size handler
cpp += "void size_repeated_message_field_by_type(uint32_t &total_size, uint8_t precalced_field_id_size, const void *field_ptr, uint8_t message_type_id) {\n"
cpp += " switch (message_type_id) {\n"
for message_name, type_id in sorted_types:
ifdef = message_ifdef_map.get(message_name)
if ifdef:
cpp += f"#ifdef {ifdef}\n"
cpp += f" case {type_id}: {{\n"
cpp += f" const std::vector<{message_name}> *vec = static_cast<const std::vector<{message_name}> *>(field_ptr);\n"
cpp += f" ProtoSize::add_repeated_message<{message_name}>(total_size, precalced_field_id_size, *vec);\n"
cpp += " break;\n"
cpp += " }\n"
if ifdef:
cpp += "#endif\n"
cpp += " default:\n"
cpp += " break;\n"
cpp += " }\n"
cpp += "}\n"
cpp += """\
} // namespace api