This commit is contained in:
J. Nick Koston 2025-07-10 11:06:08 -10:00
parent 9a9aebe8b4
commit b3bb059e42
No known key found for this signature in database
5 changed files with 3039 additions and 4388 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -305,24 +305,7 @@ struct FieldMetaV3 {
uint8_t get_message_type_id() const { return message_type_id; }
};
// Keep V2 for now during transition
struct FieldMetaV2 {
uint8_t field_num; // Protobuf field number (1-255)
uint16_t offset; // offset of field in class
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
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;
};
// V2 structures removed - we only use V3 now
class ProtoWriteBuffer {
public:
@ -508,35 +491,13 @@ struct RepeatedFieldMetaV3 {
uint8_t get_message_type_id() const { return message_type_id; }
};
// Keep V2 for now during transition
struct RepeatedFieldMetaV2 {
uint8_t field_num;
uint16_t offset;
ProtoFieldType type; // Element type
uint8_t precalced_field_id_size; // Pre-calculated size of field tag in bytes
union {
// For TYPE_MESSAGE: function pointers
struct {
RepeatedEncodeFunc encode;
RepeatedSizeFunc size;
RepeatedDecodeLengthFunc decode;
} message;
// For TYPE_ENUM: enum ID
uint8_t enum_id;
} handler;
};
// V2 structures removed - we only use V3 now
class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
// V2 metadata getters - must be implemented by derived classes
virtual const FieldMetaV2 *get_field_metadata_v2() const { return nullptr; }
virtual size_t get_field_count_v2() const { return 0; }
virtual const RepeatedFieldMetaV2 *get_repeated_field_metadata_v2() const { return nullptr; }
virtual size_t get_repeated_field_count_v2() const { return 0; }
// V3 metadata getters - for optimized implementation
// V3 metadata getters - must be implemented by derived classes
virtual const FieldMetaV3 *get_field_metadata_v3() const { return nullptr; }
virtual size_t get_field_count_v3() const { return 0; }
virtual const RepeatedFieldMetaV3 *get_repeated_field_metadata_v3() const { return nullptr; }
@ -617,117 +578,23 @@ class ProtoService {
}
};
// Type-specific encoding functions
void encode_string_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_fixed32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_bool_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_float_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_int32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_uint32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_int64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_uint64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_sint32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_sint64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_fixed64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_double_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_bytes_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
// Type-specific functions removed - V3 inlines all operations directly
// Type-specific decode functions
bool decode_string_field(void *field_ptr, ProtoLengthDelimited value);
bool decode_fixed32_field(void *field_ptr, Proto32Bit value);
bool decode_bool_field(void *field_ptr, ProtoVarInt value);
bool decode_float_field(void *field_ptr, Proto32Bit value);
bool decode_int32_field(void *field_ptr, ProtoVarInt value);
bool decode_uint32_field(void *field_ptr, ProtoVarInt value);
bool decode_int64_field(void *field_ptr, ProtoVarInt value);
bool decode_uint64_field(void *field_ptr, ProtoVarInt value);
bool decode_sint32_field(void *field_ptr, ProtoVarInt value);
bool decode_sint64_field(void *field_ptr, ProtoVarInt value);
bool decode_fixed64_field(void *field_ptr, Proto64Bit value);
bool decode_double_field(void *field_ptr, Proto64Bit value);
bool decode_bytes_field(void *field_ptr, ProtoLengthDelimited value);
// Template enum field functions removed - V3 inlines all operations directly
// Template enum decode function
template<typename EnumType> bool decode_enum_field(void *field_ptr, ProtoVarInt value);
// Type-specific size calculation functions
void size_string_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_fixed32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_bool_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_float_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_int32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_uint32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_int64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_uint64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_sint32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_sint64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_fixed64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_double_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
void size_bytes_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
// Template enum field functions
template<typename EnumType> void encode_enum_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
template<typename EnumType>
void size_enum_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
// Repeated field handling functions
void encode_repeated_string_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_bool_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_uint32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_int32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_uint64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_int64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_sint32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_sint64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_fixed32_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_fixed64_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_float_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
void encode_repeated_double_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
template<typename EnumType>
void encode_repeated_enum_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
// Repeated field handling functions removed - V3 inlines encoding directly
// Note: The template encode_repeated_message_field is still used by the message handler registry
template<typename MessageType>
void encode_repeated_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);
// Size calculation for repeated fields
void size_repeated_string_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_bool_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_uint32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_int32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_uint64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_int64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_sint32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_sint64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_fixed32_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_fixed64_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_float_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
void size_repeated_double_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
template<typename EnumType>
void size_repeated_enum_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
// Size calculation for repeated fields removed - V3 inlines size calculation directly
// Note: The template size_repeated_message_field is still used by the message handler registry
template<typename MessageType>
void size_repeated_message_field(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size);
// Repeated field decode functions
bool decode_repeated_string_field(void *field_ptr, ProtoLengthDelimited value);
bool decode_repeated_bool_field(void *field_ptr, ProtoVarInt value);
bool decode_repeated_uint32_field(void *field_ptr, ProtoVarInt value);
bool decode_repeated_int32_field(void *field_ptr, ProtoVarInt value);
bool decode_repeated_uint64_field(void *field_ptr, ProtoVarInt value);
bool decode_repeated_int64_field(void *field_ptr, ProtoVarInt value);
bool decode_repeated_sint32_field(void *field_ptr, ProtoVarInt value);
bool decode_repeated_sint64_field(void *field_ptr, ProtoVarInt value);
bool decode_repeated_fixed32_field(void *field_ptr, Proto32Bit value);
bool decode_repeated_fixed64_field(void *field_ptr, Proto64Bit value);
bool decode_repeated_float_field(void *field_ptr, Proto32Bit value);
bool decode_repeated_double_field(void *field_ptr, Proto64Bit value);
template<typename EnumType> bool decode_repeated_enum_field(void *field_ptr, ProtoVarInt value);
template<typename MessageType> bool decode_repeated_message_field(void *field_ptr, ProtoLengthDelimited value);
// Repeated field decode functions removed - V3 inlines decoding directly
// Forward declarations for message field template functions
template<typename MessageType>
@ -736,7 +603,7 @@ void encode_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8
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 decode functions removed - V3 inlines decoding directly
template<typename MessageType>
void encode_repeated_message_field(ProtoWriteBuffer &buffer, const void *field_ptr, uint8_t field_num);

View File

@ -32,7 +32,11 @@ class WireType(IntEnum):
# Message type registry - maps message names to type IDs
MESSAGE_TYPE_REGISTRY = {}
NEXT_MESSAGE_TYPE_ID = 1
NEXT_MESSAGE_TYPE_ID = 0 # Start at 0 for array indexing
# Repeated message type registry - separate from regular messages
REPEATED_MESSAGE_TYPE_REGISTRY = {}
NEXT_REPEATED_MESSAGE_TYPE_ID = 0 # Start at 0 for array indexing
def get_message_type_id(message_name):
@ -44,6 +48,15 @@ def get_message_type_id(message_name):
return MESSAGE_TYPE_REGISTRY[message_name]
def get_repeated_message_type_id(message_name):
"""Get or assign a type ID for a repeated message type."""
global NEXT_REPEATED_MESSAGE_TYPE_ID
if message_name not in REPEATED_MESSAGE_TYPE_REGISTRY:
REPEATED_MESSAGE_TYPE_REGISTRY[message_name] = NEXT_REPEATED_MESSAGE_TYPE_ID
NEXT_REPEATED_MESSAGE_TYPE_ID += 1
return REPEATED_MESSAGE_TYPE_REGISTRY[message_name]
# Mapping from protobuf types to our ProtoFieldType enum
PROTO_TYPE_MAP = {
descriptor.FieldDescriptorProto.TYPE_BOOL: "ProtoFieldType::TYPE_BOOL",
@ -65,6 +78,27 @@ PROTO_TYPE_MAP = {
descriptor.FieldDescriptorProto.TYPE_SFIXED64: "ProtoFieldType::TYPE_SFIXED64",
}
# Mapping from protobuf types to numeric values (must match proto.h enum)
PROTO_TYPE_NUM_MAP = {
descriptor.FieldDescriptorProto.TYPE_BOOL: 0,
descriptor.FieldDescriptorProto.TYPE_INT32: 1,
descriptor.FieldDescriptorProto.TYPE_UINT32: 2,
descriptor.FieldDescriptorProto.TYPE_INT64: 3,
descriptor.FieldDescriptorProto.TYPE_UINT64: 4,
descriptor.FieldDescriptorProto.TYPE_SINT32: 5,
descriptor.FieldDescriptorProto.TYPE_SINT64: 6,
descriptor.FieldDescriptorProto.TYPE_ENUM: 7,
descriptor.FieldDescriptorProto.TYPE_STRING: 8,
descriptor.FieldDescriptorProto.TYPE_BYTES: 9,
descriptor.FieldDescriptorProto.TYPE_MESSAGE: 10,
descriptor.FieldDescriptorProto.TYPE_FLOAT: 11,
descriptor.FieldDescriptorProto.TYPE_FIXED32: 12,
descriptor.FieldDescriptorProto.TYPE_SFIXED32: 13,
descriptor.FieldDescriptorProto.TYPE_DOUBLE: 14,
descriptor.FieldDescriptorProto.TYPE_FIXED64: 15,
descriptor.FieldDescriptorProto.TYPE_SFIXED64: 16,
}
# Generate with
# protoc --python_out=script/api_protobuf -I esphome/components/api/ api_options.proto
@ -1290,90 +1324,83 @@ def build_message_type(
# Generate metadata arrays for all classes using metadata approach
regular_fields = []
repeated_fields = []
regular_fields_v3 = []
repeated_fields_v3 = []
metadata_info = None
if use_metadata:
# Generate V2 metadata
# V2 metadata generation removed - we only use V3 now
# Skip the old V2 generation code from lines 1322-1397
# Generate V3 metadata
for field in desc.field:
if field.label == 3: # Repeated field
ti = RepeatedTypeInfo(field)
field_type = PROTO_TYPE_MAP.get(field.type, None)
if field_type:
field_tag_size = ti.calculate_field_id_size()
# Pack type and size into type_and_size byte
type_num = PROTO_TYPE_NUM_MAP.get(field.type, 0)
type_and_size = (type_num & 0x1F) | ((field_tag_size - 1) << 5)
if field.type == descriptor.FieldDescriptorProto.TYPE_MESSAGE:
# For messages, generate function pointers
repeated_fields.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}>}}}}}}"
# For messages, use offset_low and message_type_id
message_type_id = get_repeated_message_type_id(ti._ti.type_name)
offset = f"PROTO_FIELD_OFFSET({desc.name}, {ti.field_name})"
repeated_fields_v3.append(
f"{{{field.number}, {type_and_size}, {{.offset_low = static_cast<uint8_t>({offset}), .message_type_id = {message_type_id}}}}}"
)
else:
# Non-message types don't need handler info
repeated_fields.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {field_tag_size}, {{}}}}"
# Non-message types use full offset
repeated_fields_v3.append(
f"{{{field.number}, {type_and_size}, {{.offset = PROTO_FIELD_OFFSET({desc.name}, {ti.field_name})}}}}"
)
elif isinstance(ti._ti, EnumType):
enum_type_id = get_message_type_id(ti._ti.type_name)
repeated_fields.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"ProtoFieldType::TYPE_ENUM, {ti.calculate_field_id_size()}, {{.enum_id = {enum_type_id}}}}}"
)
elif isinstance(ti._ti, MessageType):
repeated_fields.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
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)
field_type = PROTO_TYPE_MAP.get(field.type, None)
force = "true" if field.label == 2 else "false" # Required fields
if field_type:
field_tag_size = ti.calculate_field_id_size()
# Pack type and size into type_and_size byte
type_num = PROTO_TYPE_NUM_MAP.get(field.type, 0)
type_and_size = (type_num & 0x1F) | ((field_tag_size - 1) << 5)
if field.type == descriptor.FieldDescriptorProto.TYPE_MESSAGE:
# For messages, generate function pointers
regular_fields.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}>}}}}}}"
# For messages, use offset_low and message_type_id
message_type_id = get_message_type_id(ti.type_name)
offset = f"PROTO_FIELD_OFFSET({desc.name}, {ti.field_name})"
# Check if offset fits in 8 bits
regular_fields_v3.append(
f"{{{field.number}, {type_and_size}, {{.offset_low = static_cast<uint8_t>({offset}), .message_type_id = {message_type_id}}}}}"
)
else:
# Non-message types don't need handler info
regular_fields.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"{field_type}, {force}, {field_tag_size}, {{}}}}"
# Non-message types use full offset
regular_fields_v3.append(
f"{{{field.number}, {type_and_size}, {{.offset = PROTO_FIELD_OFFSET({desc.name}, {ti.field_name})}}}}"
)
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.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
f"ProtoFieldType::TYPE_ENUM, {force}, {field_tag_size}, {{.enum_id = {enum_type_id}}}}}"
# Enums are TYPE_ENUM (7)
type_and_size = (7 & 0x1F) | ((field_tag_size - 1) << 5)
regular_fields_v3.append(
f"{{{field.number}, {type_and_size}, {{.offset = PROTO_FIELD_OFFSET({desc.name}, {ti.field_name})}}}}"
)
elif isinstance(ti, MessageType):
field_tag_size = ti.calculate_field_id_size()
# For messages, generate function pointers
regular_fields.append(
f"{{{field.number}, PROTO_FIELD_OFFSET({desc.name}, {ti.field_name}), "
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}>}}}}}}"
# Messages are TYPE_MESSAGE (10)
type_and_size = (10 & 0x1F) | ((field_tag_size - 1) << 5)
message_type_id = get_message_type_id(ti.type_name)
offset = f"PROTO_FIELD_OFFSET({desc.name}, {ti.field_name})"
regular_fields_v3.append(
f"{{{field.number}, {type_and_size}, {{.offset_low = static_cast<uint8_t>({offset}), .message_type_id = {message_type_id}}}}}"
)
# Store metadata info for later generation outside the class
metadata_info = {
"regular_fields": regular_fields,
"repeated_fields": repeated_fields,
"regular_fields_v3": regular_fields_v3,
"repeated_fields_v3": repeated_fields_v3,
"class_name": desc.name,
}
@ -1426,51 +1453,53 @@ def build_message_type(
# Metadata arrays for classes using metadata are already generated above
if use_metadata:
# Add static declarations inside the class (definitions will be in cpp file)
if regular_fields:
public_content.append(
f"static const FieldMetaV2 FIELDS_V2[{len(regular_fields)}];"
)
public_content.append(
f"static constexpr size_t FIELD_COUNT_V2 = {len(regular_fields)};"
)
else:
public_content.append("static constexpr size_t FIELD_COUNT_V2 = 0;")
# V2 metadata removed - we only use V3 now
if repeated_fields:
# Add V3 metadata declarations
if regular_fields_v3:
public_content.append(
f"static const RepeatedFieldMetaV2 REPEATED_FIELDS_V2[{len(repeated_fields)}];"
f"static const FieldMetaV3 FIELDS_V3[{len(regular_fields_v3)}];"
)
public_content.append(
f"static constexpr size_t REPEATED_COUNT_V2 = {len(repeated_fields)};"
f"static constexpr size_t FIELD_COUNT_V3 = {len(regular_fields_v3)};"
)
else:
public_content.append("static constexpr size_t REPEATED_COUNT_V2 = 0;")
public_content.append("static constexpr size_t FIELD_COUNT_V3 = 0;")
# Add virtual getter methods that return the metadata
public_content.append("// Virtual metadata getters")
if regular_fields:
if repeated_fields_v3:
public_content.append(
"const FieldMetaV2 *get_field_metadata_v2() const override { return FIELDS_V2; }"
f"static const RepeatedFieldMetaV3 REPEATED_FIELDS_V3[{len(repeated_fields_v3)}];"
)
public_content.append(
f"static constexpr size_t REPEATED_COUNT_V3 = {len(repeated_fields_v3)};"
)
else:
public_content.append("static constexpr size_t REPEATED_COUNT_V3 = 0;")
# Add V3 virtual getter methods
public_content.append("// V3 metadata getters")
if regular_fields_v3:
public_content.append(
"const FieldMetaV3 *get_field_metadata_v3() const override { return FIELDS_V3; }"
)
else:
public_content.append(
"const FieldMetaV2 *get_field_metadata_v2() const override { return nullptr; }"
"const FieldMetaV3 *get_field_metadata_v3() const override { return nullptr; }"
)
public_content.append(
"size_t get_field_count_v2() const override { return FIELD_COUNT_V2; }"
"size_t get_field_count_v3() const override { return FIELD_COUNT_V3; }"
)
if repeated_fields:
if repeated_fields_v3:
public_content.append(
"const RepeatedFieldMetaV2 *get_repeated_field_metadata_v2() const override { return REPEATED_FIELDS_V2; }"
"const RepeatedFieldMetaV3 *get_repeated_field_metadata_v3() const override { return REPEATED_FIELDS_V3; }"
)
else:
public_content.append(
"const RepeatedFieldMetaV2 *get_repeated_field_metadata_v2() const override { return nullptr; }"
"const RepeatedFieldMetaV3 *get_repeated_field_metadata_v3() const override { return nullptr; }"
)
public_content.append(
"size_t get_repeated_field_count_v2() const override { return REPEATED_COUNT_V2; }"
"size_t get_repeated_field_count_v3() const override { return REPEATED_COUNT_V3; }"
)
# Only generate encode method if there are fields to encode
@ -1921,6 +1950,8 @@ namespace api {
class_name = meta["class_name"]
regular_fields = meta["regular_fields"]
repeated_fields = meta["repeated_fields"]
regular_fields_v3 = meta.get("regular_fields_v3", [])
repeated_fields_v3 = meta.get("repeated_fields_v3", [])
msg_ifdef = meta["ifdef"]
# Handle ifdef changes
@ -1931,20 +1962,22 @@ namespace api {
cpp += f"#ifdef {msg_ifdef}\n"
current_ifdef = msg_ifdef
# Generate V2 metadata arrays
if regular_fields:
cpp += f"const FieldMetaV2 {class_name}::FIELDS_V2[{len(regular_fields)}] = {{\n"
for i, field in enumerate(regular_fields):
if i < len(regular_fields) - 1:
# V2 metadata arrays removed - we only use V3 now
# Generate V3 metadata arrays
if regular_fields_v3:
cpp += f"const FieldMetaV3 {class_name}::FIELDS_V3[{len(regular_fields_v3)}] = {{\n"
for i, field in enumerate(regular_fields_v3):
if i < len(regular_fields_v3) - 1:
cpp += f" {field},\n"
else:
cpp += f" {field}\n"
cpp += "};\n"
if repeated_fields:
cpp += f"const RepeatedFieldMetaV2 {class_name}::REPEATED_FIELDS_V2[{len(repeated_fields)}] = {{\n"
for i, field in enumerate(repeated_fields):
if i < len(repeated_fields) - 1:
if repeated_fields_v3:
cpp += f"const RepeatedFieldMetaV3 {class_name}::REPEATED_FIELDS_V3[{len(repeated_fields_v3)}] = {{\n"
for i, field in enumerate(repeated_fields_v3):
if i < len(repeated_fields_v3) - 1:
cpp += f" {field},\n"
else:
cpp += f" {field}\n"
@ -1954,6 +1987,136 @@ namespace api {
if current_ifdef is not None:
cpp += "#endif\n"
# Generate message handler registries for V3
# We need to track which message types are used and respect their ifdefs
cpp += "\n// Message handler registries for V3\n"
# Collect message types used in regular and repeated fields
message_types_with_ifdefs = {}
repeated_message_types_with_ifdefs = {}
# Scan all message descriptors to find which message types are used
for desc in mt:
msg_ifdef = ifdefs.get(desc.name, None) or message_ifdef_map.get(
desc.name, None
)
# Check all fields in this message
for field in desc.field:
if field.type == descriptor.FieldDescriptorProto.TYPE_MESSAGE:
# Get the message type name (strip leading dot if present)
msg_type_name = field.type_name.lstrip(".")
# Get the ifdef for the message type itself
msg_type_ifdef = ifdefs.get(
msg_type_name, None
) or message_ifdef_map.get(msg_type_name, None)
# Use the most specific ifdef available
final_ifdef = msg_type_ifdef or msg_ifdef
if field.label == descriptor.FieldDescriptorProto.LABEL_REPEATED:
# This is a repeated message field
if msg_type_name not in repeated_message_types_with_ifdefs:
repeated_message_types_with_ifdefs[msg_type_name] = final_ifdef
elif (
final_ifdef
and not repeated_message_types_with_ifdefs[msg_type_name]
):
# Update if we found a more specific ifdef
repeated_message_types_with_ifdefs[msg_type_name] = final_ifdef
# This is a regular message field
elif msg_type_name not in message_types_with_ifdefs:
message_types_with_ifdefs[msg_type_name] = final_ifdef
elif final_ifdef and not message_types_with_ifdefs[msg_type_name]:
# Update if we found a more specific ifdef
message_types_with_ifdefs[msg_type_name] = final_ifdef
# Sort message types for consistent ordering
sorted_message_types = sorted(message_types_with_ifdefs.keys())
sorted_repeated_message_types = sorted(repeated_message_types_with_ifdefs.keys())
# Assign consistent IDs to message types
message_type_to_id = {}
for i, msg_type in enumerate(sorted_message_types):
message_type_to_id[msg_type] = i
# Assign consistent IDs to repeated message types
repeated_message_type_to_id = {}
for i, msg_type in enumerate(sorted_repeated_message_types):
repeated_message_type_to_id[msg_type] = i
# Sort message types by their assigned IDs to match the metadata
sorted_message_types_by_id = sorted(
MESSAGE_TYPE_REGISTRY.keys(), key=lambda x: MESSAGE_TYPE_REGISTRY[x]
)
sorted_repeated_message_types_by_id = sorted(
REPEATED_MESSAGE_TYPE_REGISTRY.keys(),
key=lambda x: REPEATED_MESSAGE_TYPE_REGISTRY[x],
)
# Generate MESSAGE_HANDLERS array with proper ifdefs
cpp += f"const MessageHandler MESSAGE_HANDLERS[{len(MESSAGE_TYPE_REGISTRY) or 1}] = {{\n"
# Generate entries in ID order
for msg_type in sorted_message_types_by_id:
if msg_type not in message_types_with_ifdefs:
# This message type is not used in regular fields, add null entry
cpp += " {nullptr, nullptr, nullptr},\n"
continue
msg_ifdef = message_types_with_ifdefs[msg_type]
# Each array element is independently ifdef'd
if msg_ifdef is not None:
cpp += f"#ifdef {msg_ifdef}\n"
cpp += f" {{&encode_message_field<{msg_type}>, &size_message_field<{msg_type}>, &decode_message_field<{msg_type}>}},"
# Add else clause to fill array slot when ifdef is not defined
if msg_ifdef is not None:
cpp += "\n#else\n {nullptr, nullptr, nullptr},"
cpp += "\n#endif"
cpp += "\n"
# Remove trailing comma and close array
if cpp.endswith(",\n"):
cpp = cpp[:-2] + "\n"
cpp += "};\n"
cpp += f"const size_t MESSAGE_HANDLER_COUNT = {len(MESSAGE_TYPE_REGISTRY)};\n"
# Generate REPEATED_MESSAGE_HANDLERS array with same approach
cpp += f"\nconst RepeatedMessageHandler REPEATED_MESSAGE_HANDLERS[{len(REPEATED_MESSAGE_TYPE_REGISTRY) or 1}] = {{\n"
# Generate entries in ID order
for msg_type in sorted_repeated_message_types_by_id:
if msg_type not in repeated_message_types_with_ifdefs:
# This message type is not used in repeated fields, add null entry
cpp += " {nullptr, nullptr, nullptr},\n"
continue
msg_ifdef = repeated_message_types_with_ifdefs[msg_type]
# Each array element is independently ifdef'd
if msg_ifdef is not None:
cpp += f"#ifdef {msg_ifdef}\n"
cpp += f" {{&encode_repeated_message_field<{msg_type}>, &size_repeated_message_field<{msg_type}>, &decode_repeated_message_field<{msg_type}>}},"
# Add else clause to fill array slot when ifdef is not defined
if msg_ifdef is not None:
cpp += "\n#else\n {nullptr, nullptr, nullptr},"
cpp += "\n#endif"
cpp += "\n"
# Remove trailing comma and close array
if cpp.endswith(",\n"):
cpp = cpp[:-2] + "\n"
cpp += "};\n"
cpp += f"const size_t REPEATED_MESSAGE_HANDLER_COUNT = {len(REPEATED_MESSAGE_TYPE_REGISTRY)};\n"
cpp += """\
} // namespace api