working but horrid

This commit is contained in:
J. Nick Koston 2025-07-10 07:33:23 -10:00
parent 1818a56096
commit e24d4450ac
No known key found for this signature in database
5 changed files with 9366 additions and 4 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,13 @@ namespace api {
static const char *const TAG = "api.proto";
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
// Use V2 type-based implementation if available
if (get_field_metadata_v2() != nullptr || get_repeated_field_metadata_v2() != nullptr) {
decode_v2(buffer, length);
return;
}
// Fall back to old implementation
uint32_t i = 0;
bool error = false;
uint8_t *base = reinterpret_cast<uint8_t *>(this);
@ -678,14 +685,908 @@ void calculate_size_from_metadata(uint32_t &total_size, const void *obj, const F
// ProtoMessage implementations using metadata
void ProtoMessage::encode(ProtoWriteBuffer buffer) const {
encode_from_metadata(buffer, this, get_field_metadata(), get_field_count(), get_repeated_field_metadata(),
get_repeated_field_count());
if (get_field_metadata_v2() != nullptr || get_repeated_field_metadata_v2() != nullptr) {
encode_v2(buffer);
} else {
encode_from_metadata(buffer, this, get_field_metadata(), get_field_count(), get_repeated_field_metadata(),
get_repeated_field_count());
}
}
void ProtoMessage::calculate_size(uint32_t &total_size) const {
calculate_size_from_metadata(total_size, this, get_field_metadata(), get_field_count(), get_repeated_field_metadata(),
get_repeated_field_count());
if (get_field_metadata_v2() != nullptr || get_repeated_field_metadata_v2() != nullptr) {
calculate_size_v2(total_size);
} else {
calculate_size_from_metadata(total_size, this, get_field_metadata(), get_field_count(),
get_repeated_field_metadata(), get_repeated_field_count());
}
}
// Type-based decode implementation
void ProtoMessage::decode_v2(const uint8_t *buffer, size_t length) {
uint32_t i = 0;
bool error = false;
uint8_t *base = reinterpret_cast<uint8_t *>(this);
// Get V2 metadata once at the start
const FieldMetaV2 *fields = get_field_metadata_v2();
size_t field_count = get_field_count_v2();
const RepeatedFieldMetaV2 *repeated_fields = get_repeated_field_metadata_v2();
size_t repeated_count = get_repeated_field_count_v2();
while (i < length) {
uint32_t consumed;
auto res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid field start at %" PRIu32, i);
break;
}
uint32_t field_type = (res->as_uint32()) & 0b111;
uint32_t field_id = (res->as_uint32()) >> 3;
i += consumed;
switch (field_type) {
case 0: { // VarInt
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid VarInt at %" PRIu32, i);
error = true;
break;
}
ProtoVarInt value = *res;
bool decoded = false;
// Check regular fields
for (size_t j = 0; j < field_count; j++) {
if (fields[j].field_num == field_id && get_wire_type(fields[j].type) == 0) {
void *field_addr = base + fields[j].offset;
switch (fields[j].type) {
case ProtoFieldType::TYPE_BOOL:
*static_cast<bool *>(field_addr) = value.as_bool();
decoded = true;
break;
case ProtoFieldType::TYPE_INT32:
*static_cast<int32_t *>(field_addr) = value.as_int32();
decoded = true;
break;
case ProtoFieldType::TYPE_UINT32:
*static_cast<uint32_t *>(field_addr) = value.as_uint32();
decoded = true;
break;
case ProtoFieldType::TYPE_INT64:
*static_cast<int64_t *>(field_addr) = value.as_int64();
decoded = true;
break;
case ProtoFieldType::TYPE_UINT64:
*static_cast<uint64_t *>(field_addr) = value.as_uint64();
decoded = true;
break;
case ProtoFieldType::TYPE_SINT32:
*static_cast<int32_t *>(field_addr) = value.as_sint32();
decoded = true;
break;
case ProtoFieldType::TYPE_SINT64:
*static_cast<int64_t *>(field_addr) = value.as_sint64();
decoded = true;
break;
case ProtoFieldType::TYPE_ENUM:
// For enums, we need to use the old metadata for now
// This will be fixed in Phase 5
*static_cast<uint32_t *>(field_addr) = value.as_uint32();
decoded = true;
break;
default:
break;
}
break;
}
}
// Check repeated fields if not found
if (!decoded && repeated_fields) {
for (size_t j = 0; j < repeated_count; j++) {
if (repeated_fields[j].field_num == field_id && get_wire_type(repeated_fields[j].type) == 0) {
void *field_addr = base + repeated_fields[j].offset;
switch (repeated_fields[j].type) {
case ProtoFieldType::TYPE_BOOL: {
auto *vec = static_cast<std::vector<bool> *>(field_addr);
vec->push_back(value.as_bool());
decoded = true;
break;
}
case ProtoFieldType::TYPE_INT32: {
auto *vec = static_cast<std::vector<int32_t> *>(field_addr);
vec->push_back(value.as_int32());
decoded = true;
break;
}
case ProtoFieldType::TYPE_UINT32: {
auto *vec = static_cast<std::vector<uint32_t> *>(field_addr);
vec->push_back(value.as_uint32());
decoded = true;
break;
}
case ProtoFieldType::TYPE_INT64: {
auto *vec = static_cast<std::vector<int64_t> *>(field_addr);
vec->push_back(value.as_int64());
decoded = true;
break;
}
case ProtoFieldType::TYPE_UINT64: {
auto *vec = static_cast<std::vector<uint64_t> *>(field_addr);
vec->push_back(value.as_uint64());
decoded = true;
break;
}
case ProtoFieldType::TYPE_SINT32: {
auto *vec = static_cast<std::vector<int32_t> *>(field_addr);
vec->push_back(value.as_sint32());
decoded = true;
break;
}
case ProtoFieldType::TYPE_SINT64: {
auto *vec = static_cast<std::vector<int64_t> *>(field_addr);
vec->push_back(value.as_sint64());
decoded = true;
break;
}
case ProtoFieldType::TYPE_ENUM: {
// For repeated enums, use old metadata for now
auto *vec = static_cast<std::vector<uint32_t> *>(field_addr);
vec->push_back(value.as_uint32());
decoded = true;
break;
}
default:
break;
}
break;
}
}
}
if (!decoded) {
ESP_LOGV(TAG, "Cannot decode VarInt field %" PRIu32 " with value %" PRIu32 "!", field_id, res->as_uint32());
}
i += consumed;
break;
}
case 2: { // Length-delimited
res = ProtoVarInt::parse(&buffer[i], length - i, &consumed);
if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid Length Delimited at %" PRIu32, i);
error = true;
break;
}
uint32_t field_length = res->as_uint32();
i += consumed;
if (field_length > length - i) {
ESP_LOGV(TAG, "Out-of-bounds Length Delimited at %" PRIu32, i);
error = true;
break;
}
ProtoLengthDelimited value(&buffer[i], field_length);
bool decoded = false;
// Check regular fields
for (size_t j = 0; j < field_count; j++) {
if (fields[j].field_num == field_id && get_wire_type(fields[j].type) == 2) {
void *field_addr = base + fields[j].offset;
switch (fields[j].type) {
case ProtoFieldType::TYPE_STRING:
*static_cast<std::string *>(field_addr) = value.as_string();
decoded = true;
break;
case ProtoFieldType::TYPE_BYTES: {
// ProtoLengthDelimited has protected members, use buffer directly
static_cast<std::string *>(field_addr)
->assign(reinterpret_cast<const char *>(&buffer[i]), field_length);
decoded = true;
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;
break;
}
default:
break;
}
break;
}
}
// Check repeated fields if not found
if (!decoded && repeated_fields) {
for (size_t j = 0; j < repeated_count; j++) {
if (repeated_fields[j].field_num == field_id && get_wire_type(repeated_fields[j].type) == 2) {
void *field_addr = base + repeated_fields[j].offset;
switch (repeated_fields[j].type) {
case ProtoFieldType::TYPE_STRING: {
auto *vec = static_cast<std::vector<std::string> *>(field_addr);
vec->push_back(value.as_string());
decoded = true;
break;
}
case ProtoFieldType::TYPE_BYTES: {
auto *vec = static_cast<std::vector<std::string> *>(field_addr);
vec->emplace_back(reinterpret_cast<const char *>(&buffer[i]), field_length);
decoded = true;
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;
break;
}
default:
break;
}
break;
}
}
}
if (!decoded) {
ESP_LOGV(TAG, "Cannot decode Length field %" PRIu32, field_id);
}
i += field_length;
break;
}
case 5: { // 32-bit
if (length - i < 4) {
ESP_LOGV(TAG, "Invalid 32-bit at %" PRIu32, i);
error = true;
break;
}
uint32_t raw = (buffer[i]) | (buffer[i + 1] << 8) | (buffer[i + 2] << 16) | (buffer[i + 3] << 24);
Proto32Bit value(raw);
bool decoded = false;
// Check regular fields
for (size_t j = 0; j < field_count; j++) {
if (fields[j].field_num == field_id && get_wire_type(fields[j].type) == 5) {
void *field_addr = base + fields[j].offset;
switch (fields[j].type) {
case ProtoFieldType::TYPE_FLOAT: {
float *val = static_cast<float *>(field_addr);
*val = value.as_float();
decoded = true;
break;
}
case ProtoFieldType::TYPE_FIXED32:
*static_cast<uint32_t *>(field_addr) = value.as_fixed32();
decoded = true;
break;
case ProtoFieldType::TYPE_SFIXED32:
*static_cast<int32_t *>(field_addr) = value.as_sfixed32();
decoded = true;
break;
default:
break;
}
break;
}
}
// Check repeated fields if not found
if (!decoded && repeated_fields) {
for (size_t j = 0; j < repeated_count; j++) {
if (repeated_fields[j].field_num == field_id && get_wire_type(repeated_fields[j].type) == 5) {
void *field_addr = base + repeated_fields[j].offset;
switch (repeated_fields[j].type) {
case ProtoFieldType::TYPE_FLOAT: {
auto *vec = static_cast<std::vector<float> *>(field_addr);
vec->push_back(value.as_float());
decoded = true;
break;
}
case ProtoFieldType::TYPE_FIXED32: {
auto *vec = static_cast<std::vector<uint32_t> *>(field_addr);
vec->push_back(value.as_fixed32());
decoded = true;
break;
}
case ProtoFieldType::TYPE_SFIXED32: {
auto *vec = static_cast<std::vector<int32_t> *>(field_addr);
vec->push_back(value.as_sfixed32());
decoded = true;
break;
}
default:
break;
}
break;
}
}
}
if (!decoded) {
ESP_LOGV(TAG, "Cannot decode 32-bit field %" PRIu32, field_id);
}
i += 4;
break;
}
case 1: { // 64-bit
if (length - i < 8) {
ESP_LOGV(TAG, "Invalid 64-bit at %" PRIu32, i);
error = true;
break;
}
uint64_t raw = uint64_t(buffer[i]) | (uint64_t(buffer[i + 1]) << 8) | (uint64_t(buffer[i + 2]) << 16) |
(uint64_t(buffer[i + 3]) << 24) | (uint64_t(buffer[i + 4]) << 32) |
(uint64_t(buffer[i + 5]) << 40) | (uint64_t(buffer[i + 6]) << 48) |
(uint64_t(buffer[i + 7]) << 56);
Proto64Bit value(raw);
bool decoded = false;
// Check regular fields
for (size_t j = 0; j < field_count; j++) {
if (fields[j].field_num == field_id && get_wire_type(fields[j].type) == 1) {
void *field_addr = base + fields[j].offset;
switch (fields[j].type) {
case ProtoFieldType::TYPE_DOUBLE: {
double *val = static_cast<double *>(field_addr);
*val = value.as_double();
decoded = true;
break;
}
case ProtoFieldType::TYPE_FIXED64:
*static_cast<uint64_t *>(field_addr) = value.as_fixed64();
decoded = true;
break;
case ProtoFieldType::TYPE_SFIXED64:
*static_cast<int64_t *>(field_addr) = value.as_sfixed64();
decoded = true;
break;
default:
break;
}
break;
}
}
// Check repeated fields if not found
if (!decoded && repeated_fields) {
for (size_t j = 0; j < repeated_count; j++) {
if (repeated_fields[j].field_num == field_id && get_wire_type(repeated_fields[j].type) == 1) {
void *field_addr = base + repeated_fields[j].offset;
switch (repeated_fields[j].type) {
case ProtoFieldType::TYPE_DOUBLE: {
auto *vec = static_cast<std::vector<double> *>(field_addr);
vec->push_back(value.as_double());
decoded = true;
break;
}
case ProtoFieldType::TYPE_FIXED64: {
auto *vec = static_cast<std::vector<uint64_t> *>(field_addr);
vec->push_back(value.as_fixed64());
decoded = true;
break;
}
case ProtoFieldType::TYPE_SFIXED64: {
auto *vec = static_cast<std::vector<int64_t> *>(field_addr);
vec->push_back(value.as_sfixed64());
decoded = true;
break;
}
default:
break;
}
break;
}
}
}
if (!decoded) {
ESP_LOGV(TAG, "Cannot decode 64-bit field %" PRIu32, field_id);
}
i += 8;
break;
}
default: {
ESP_LOGV(TAG, "Invalid field type %" PRIu32 " at %" PRIu32, field_type, i);
return;
}
}
if (error) {
break;
}
}
}
// Type-based encode implementation
void ProtoMessage::encode_v2(ProtoWriteBuffer buffer) const {
const uint8_t *base = reinterpret_cast<const uint8_t *>(this);
// Get V2 metadata once at the start
const FieldMetaV2 *fields = get_field_metadata_v2();
size_t field_count = get_field_count_v2();
// Encode regular fields
for (size_t i = 0; i < field_count; i++) {
const void *field_addr = base + fields[i].offset;
switch (fields[i].type) {
case ProtoFieldType::TYPE_BOOL: {
const bool *val = static_cast<const bool *>(field_addr);
buffer.encode_bool(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_INT32: {
const int32_t *val = static_cast<const int32_t *>(field_addr);
buffer.encode_int32(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_UINT32: {
const uint32_t *val = static_cast<const uint32_t *>(field_addr);
buffer.encode_uint32(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_INT64: {
const int64_t *val = static_cast<const int64_t *>(field_addr);
buffer.encode_int64(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_UINT64: {
const uint64_t *val = static_cast<const uint64_t *>(field_addr);
buffer.encode_uint64(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SINT32: {
const int32_t *val = static_cast<const int32_t *>(field_addr);
buffer.encode_sint32(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SINT64: {
const int64_t *val = static_cast<const int64_t *>(field_addr);
buffer.encode_sint64(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_ENUM: {
// For enums, treat as uint32 for now
const uint32_t *val = static_cast<const uint32_t *>(field_addr);
buffer.encode_uint32(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_STRING: {
const std::string *val = static_cast<const std::string *>(field_addr);
buffer.encode_string(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_BYTES: {
const std::string *val = static_cast<const std::string *>(field_addr);
buffer.encode_bytes(fields[i].field_num, reinterpret_cast<const uint8_t *>(val->data()), val->size(),
fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_FLOAT: {
const float *val = static_cast<const float *>(field_addr);
buffer.encode_float(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_FIXED32: {
const uint32_t *val = static_cast<const uint32_t *>(field_addr);
buffer.encode_fixed32(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SFIXED32: {
const int32_t *val = static_cast<const int32_t *>(field_addr);
buffer.encode_sfixed32(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_DOUBLE: {
const double *val = static_cast<const double *>(field_addr);
buffer.encode_double(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_FIXED64: {
const uint64_t *val = static_cast<const uint64_t *>(field_addr);
buffer.encode_fixed64(fields[i].field_num, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SFIXED64: {
const int64_t *val = static_cast<const int64_t *>(field_addr);
buffer.encode_sfixed64(fields[i].field_num, *val, fields[i].force_encode);
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);
break;
}
}
}
// Encode repeated fields
const RepeatedFieldMetaV2 *repeated_fields = get_repeated_field_metadata_v2();
size_t repeated_count = get_repeated_field_count_v2();
for (size_t i = 0; i < repeated_count; i++) {
const void *field_addr = base + repeated_fields[i].offset;
switch (repeated_fields[i].type) {
case ProtoFieldType::TYPE_BOOL: {
const auto *vec = static_cast<const std::vector<bool> *>(field_addr);
for (bool val : *vec) {
buffer.encode_bool(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_INT32: {
const auto *vec = static_cast<const std::vector<int32_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_int32(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_UINT32: {
const auto *vec = static_cast<const std::vector<uint32_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_uint32(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_INT64: {
const auto *vec = static_cast<const std::vector<int64_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_int64(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_UINT64: {
const auto *vec = static_cast<const std::vector<uint64_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_uint64(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_SINT32: {
const auto *vec = static_cast<const std::vector<int32_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_sint32(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_SINT64: {
const auto *vec = static_cast<const std::vector<int64_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_sint64(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_ENUM: {
// For repeated enums, treat as uint32 for now
const auto *vec = static_cast<const std::vector<uint32_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_uint32(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_STRING: {
const auto *vec = static_cast<const std::vector<std::string> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_string(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_BYTES: {
const auto *vec = static_cast<const std::vector<std::string> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_bytes(repeated_fields[i].field_num, reinterpret_cast<const uint8_t *>(val.data()), val.size(),
true);
}
break;
}
case ProtoFieldType::TYPE_FLOAT: {
const auto *vec = static_cast<const std::vector<float> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_float(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_FIXED32: {
const auto *vec = static_cast<const std::vector<uint32_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_fixed32(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_SFIXED32: {
const auto *vec = static_cast<const std::vector<int32_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_sfixed32(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_DOUBLE: {
const auto *vec = static_cast<const std::vector<double> *>(field_addr);
for (const auto &val : *vec) {
union {
double value;
uint64_t raw;
} u{};
u.value = val;
buffer.encode_fixed64(repeated_fields[i].field_num, u.raw, true);
}
break;
}
case ProtoFieldType::TYPE_FIXED64: {
const auto *vec = static_cast<const std::vector<uint64_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_fixed64(repeated_fields[i].field_num, val, true);
}
break;
}
case ProtoFieldType::TYPE_SFIXED64: {
const auto *vec = static_cast<const std::vector<int64_t> *>(field_addr);
for (const auto &val : *vec) {
buffer.encode_sfixed64(repeated_fields[i].field_num, val, true);
}
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);
break;
}
}
}
}
// Type-based size calculation implementation
void ProtoMessage::calculate_size_v2(uint32_t &total_size) const {
const uint8_t *base = reinterpret_cast<const uint8_t *>(this);
// Get V2 metadata once at the start
const FieldMetaV2 *fields = get_field_metadata_v2();
size_t field_count = get_field_count_v2();
// Calculate size for regular fields
for (size_t i = 0; i < field_count; i++) {
const void *field_addr = base + fields[i].offset;
switch (fields[i].type) {
case ProtoFieldType::TYPE_BOOL: {
const bool *val = static_cast<const bool *>(field_addr);
ProtoSize::add_bool_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_INT32: {
const int32_t *val = static_cast<const int32_t *>(field_addr);
ProtoSize::add_int32_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_UINT32: {
const uint32_t *val = static_cast<const uint32_t *>(field_addr);
ProtoSize::add_uint32_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_INT64: {
const int64_t *val = static_cast<const int64_t *>(field_addr);
ProtoSize::add_int64_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_UINT64: {
const uint64_t *val = static_cast<const uint64_t *>(field_addr);
ProtoSize::add_uint64_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SINT32: {
const int32_t *val = static_cast<const int32_t *>(field_addr);
ProtoSize::add_sint32_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SINT64: {
const int64_t *val = static_cast<const int64_t *>(field_addr);
ProtoSize::add_sint64_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_ENUM: {
// For enums, treat as uint32 for now
const uint32_t *val = static_cast<const uint32_t *>(field_addr);
ProtoSize::add_enum_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_STRING: {
const std::string *val = static_cast<const std::string *>(field_addr);
ProtoSize::add_string_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_BYTES: {
const std::string *val = static_cast<const std::string *>(field_addr);
ProtoSize::add_string_field(total_size, fields[i].precalced_field_id_size, *val, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_FLOAT: {
const float *val = static_cast<const float *>(field_addr);
ProtoSize::add_fixed_field<4>(total_size, fields[i].precalced_field_id_size,
*val != 0.0f || fields[i].force_encode, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_FIXED32: {
const uint32_t *val = static_cast<const uint32_t *>(field_addr);
ProtoSize::add_fixed_field<4>(total_size, fields[i].precalced_field_id_size,
*val != 0 || fields[i].force_encode, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SFIXED32: {
const int32_t *val = static_cast<const int32_t *>(field_addr);
ProtoSize::add_fixed_field<4>(total_size, fields[i].precalced_field_id_size,
*val != 0 || fields[i].force_encode, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_DOUBLE: {
const double *val = static_cast<const double *>(field_addr);
ProtoSize::add_fixed_field<8>(total_size, fields[i].precalced_field_id_size,
*val != 0.0 || fields[i].force_encode, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_FIXED64: {
const uint64_t *val = static_cast<const uint64_t *>(field_addr);
ProtoSize::add_fixed_field<8>(total_size, fields[i].precalced_field_id_size,
*val != 0 || fields[i].force_encode, fields[i].force_encode);
break;
}
case ProtoFieldType::TYPE_SFIXED64: {
const int64_t *val = static_cast<const int64_t *>(field_addr);
ProtoSize::add_fixed_field<8>(total_size, fields[i].precalced_field_id_size,
*val != 0 || fields[i].force_encode, fields[i].force_encode);
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);
break;
}
}
}
// Calculate size for repeated fields
const RepeatedFieldMetaV2 *repeated_fields = get_repeated_field_metadata_v2();
size_t repeated_count = get_repeated_field_count_v2();
for (size_t i = 0; i < repeated_count; i++) {
const void *field_addr = base + repeated_fields[i].offset;
switch (repeated_fields[i].type) {
case ProtoFieldType::TYPE_BOOL: {
const auto *vec = static_cast<const std::vector<bool> *>(field_addr);
for (bool val : *vec) {
ProtoSize::add_bool_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_INT32: {
const auto *vec = static_cast<const std::vector<int32_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_int32_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_UINT32: {
const auto *vec = static_cast<const std::vector<uint32_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_uint32_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_INT64: {
const auto *vec = static_cast<const std::vector<int64_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_int64_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_UINT64: {
const auto *vec = static_cast<const std::vector<uint64_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_uint64_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_SINT32: {
const auto *vec = static_cast<const std::vector<int32_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_sint32_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_SINT64: {
const auto *vec = static_cast<const std::vector<int64_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_sint64_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_ENUM: {
// For repeated enums, treat as uint32 for now
const auto *vec = static_cast<const std::vector<uint32_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_enum_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_STRING: {
const auto *vec = static_cast<const std::vector<std::string> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_string_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_BYTES: {
const auto *vec = static_cast<const std::vector<std::string> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_string_field(total_size, repeated_fields[i].precalced_field_id_size, val, true);
}
break;
}
case ProtoFieldType::TYPE_FLOAT: {
const auto *vec = static_cast<const std::vector<float> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_fixed_field<4>(total_size, repeated_fields[i].precalced_field_id_size, true, true);
}
break;
}
case ProtoFieldType::TYPE_FIXED32: {
const auto *vec = static_cast<const std::vector<uint32_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_fixed_field<4>(total_size, repeated_fields[i].precalced_field_id_size, true, true);
}
break;
}
case ProtoFieldType::TYPE_SFIXED32: {
const auto *vec = static_cast<const std::vector<int32_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_fixed_field<4>(total_size, repeated_fields[i].precalced_field_id_size, true, true);
}
break;
}
case ProtoFieldType::TYPE_DOUBLE: {
const auto *vec = static_cast<const std::vector<double> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_fixed_field<8>(total_size, repeated_fields[i].precalced_field_id_size, true, true);
}
break;
}
case ProtoFieldType::TYPE_FIXED64: {
const auto *vec = static_cast<const std::vector<uint64_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_fixed_field<8>(total_size, repeated_fields[i].precalced_field_id_size, true, true);
}
break;
}
case ProtoFieldType::TYPE_SFIXED64: {
const auto *vec = static_cast<const std::vector<int64_t> *>(field_addr);
for (const auto &val : *vec) {
ProtoSize::add_fixed_field<8>(total_size, repeated_fields[i].precalced_field_id_size, true, true);
}
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);
break;
}
}
}
}
// Message type handler implementations moved to api_pb2.cpp (generated by Python script)
} // namespace api
} // namespace esphome

View File

@ -16,6 +16,65 @@ namespace api {
// Forward declarations
class ProtoWriteBuffer;
// Enum for all supported field types
enum class ProtoFieldType : uint8_t {
// Varint types (wire type 0)
TYPE_BOOL = 0,
TYPE_INT32,
TYPE_UINT32,
TYPE_INT64,
TYPE_UINT64,
TYPE_SINT32,
TYPE_SINT64,
TYPE_ENUM,
// Length-delimited types (wire type 2)
TYPE_STRING,
TYPE_BYTES,
TYPE_MESSAGE,
// 32-bit types (wire type 5)
TYPE_FLOAT,
TYPE_FIXED32,
TYPE_SFIXED32,
// 64-bit types (wire type 1)
TYPE_DOUBLE,
TYPE_FIXED64,
TYPE_SFIXED64,
};
// Helper to get wire type from field type
constexpr uint8_t get_wire_type(ProtoFieldType type) {
switch (type) {
case ProtoFieldType::TYPE_BOOL:
case ProtoFieldType::TYPE_INT32:
case ProtoFieldType::TYPE_UINT32:
case ProtoFieldType::TYPE_INT64:
case ProtoFieldType::TYPE_UINT64:
case ProtoFieldType::TYPE_SINT32:
case ProtoFieldType::TYPE_SINT64:
case ProtoFieldType::TYPE_ENUM:
return 0; // varint
case ProtoFieldType::TYPE_STRING:
case ProtoFieldType::TYPE_BYTES:
case ProtoFieldType::TYPE_MESSAGE:
return 2; // length-delimited
case ProtoFieldType::TYPE_FLOAT:
case ProtoFieldType::TYPE_FIXED32:
case ProtoFieldType::TYPE_SFIXED32:
return 5; // 32-bit
case ProtoFieldType::TYPE_DOUBLE:
case ProtoFieldType::TYPE_FIXED64:
case ProtoFieldType::TYPE_SFIXED64:
return 1; // 64-bit
}
return 0;
}
// Function pointer types for encoding and size calculation
using EncodeFunc = void (*)(ProtoWriteBuffer &, const void *field_ptr, uint8_t field_num);
using SizeFunc = void (*)(uint32_t &total_size, const void *field_ptr, uint8_t precalced_field_id_size, bool force);
@ -226,6 +285,16 @@ struct FieldMeta {
} decoder;
};
// New type-based metadata structure (smaller and more efficient)
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
uint8_t message_type_id; // For TYPE_MESSAGE/TYPE_ENUM: identifies which type (0 for non-message/enum)
};
class ProtoWriteBuffer {
public:
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
@ -348,6 +417,26 @@ class ProtoWriteBuffer {
}
this->encode_uint64(field_id, uvalue, force);
}
void encode_sfixed32(uint32_t field_id, int32_t value, bool force = false) {
if (!force && value == 0)
return;
this->encode_fixed32(field_id, static_cast<uint32_t>(value), force);
}
void encode_double(uint32_t field_id, double value, bool force = false) {
if (!force && value == 0.0)
return;
union {
double value;
uint64_t raw;
} val{};
val.value = value;
this->encode_fixed64(field_id, val.raw, force);
}
void encode_sfixed64(uint32_t field_id, int64_t value, bool force = false) {
if (!force && value == 0)
return;
this->encode_fixed64(field_id, static_cast<uint64_t>(value), force);
}
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
size_t begin = this->buffer_->size();
@ -388,6 +477,15 @@ struct RepeatedFieldMeta {
} decoder;
};
// New type-based repeated field metadata
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
uint8_t message_type_id; // For TYPE_MESSAGE/TYPE_ENUM: identifies which type (0 for non-message/enum)
};
class ProtoMessage {
public:
virtual ~ProtoMessage() = default;
@ -398,11 +496,22 @@ class ProtoMessage {
virtual const RepeatedFieldMeta *get_repeated_field_metadata() const { return nullptr; }
virtual size_t get_repeated_field_count() const { return 0; }
// V2 metadata getters - default implementations return nullptr/0
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; }
// Encode/decode/calculate_size using metadata
void encode(ProtoWriteBuffer buffer) const;
void decode(const uint8_t *buffer, size_t length);
void calculate_size(uint32_t &total_size) const;
// Type-based implementations using V2 metadata
void decode_v2(const uint8_t *buffer, size_t length);
void encode_v2(ProtoWriteBuffer buffer) const;
void calculate_size_v2(uint32_t &total_size) const;
#ifdef HAS_PROTO_MESSAGE_DUMP
std::string dump() const;
virtual void dump_to(std::string &out) const = 0;
@ -586,5 +695,18 @@ 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

@ -30,6 +30,42 @@ class WireType(IntEnum):
FIXED32 = 5 # fixed32, sfixed32, float
# Message type registry - maps message names to type IDs
MESSAGE_TYPE_REGISTRY = {}
NEXT_MESSAGE_TYPE_ID = 1
def get_message_type_id(message_name):
"""Get or assign a type ID for a message type."""
global NEXT_MESSAGE_TYPE_ID
if message_name not in MESSAGE_TYPE_REGISTRY:
MESSAGE_TYPE_REGISTRY[message_name] = NEXT_MESSAGE_TYPE_ID
NEXT_MESSAGE_TYPE_ID += 1
return MESSAGE_TYPE_REGISTRY[message_name]
# Mapping from protobuf types to our ProtoFieldType enum
PROTO_TYPE_MAP = {
descriptor.FieldDescriptorProto.TYPE_BOOL: "ProtoFieldType::TYPE_BOOL",
descriptor.FieldDescriptorProto.TYPE_INT32: "ProtoFieldType::TYPE_INT32",
descriptor.FieldDescriptorProto.TYPE_UINT32: "ProtoFieldType::TYPE_UINT32",
descriptor.FieldDescriptorProto.TYPE_INT64: "ProtoFieldType::TYPE_INT64",
descriptor.FieldDescriptorProto.TYPE_UINT64: "ProtoFieldType::TYPE_UINT64",
descriptor.FieldDescriptorProto.TYPE_SINT32: "ProtoFieldType::TYPE_SINT32",
descriptor.FieldDescriptorProto.TYPE_SINT64: "ProtoFieldType::TYPE_SINT64",
descriptor.FieldDescriptorProto.TYPE_ENUM: "ProtoFieldType::TYPE_ENUM",
descriptor.FieldDescriptorProto.TYPE_STRING: "ProtoFieldType::TYPE_STRING",
descriptor.FieldDescriptorProto.TYPE_BYTES: "ProtoFieldType::TYPE_BYTES",
descriptor.FieldDescriptorProto.TYPE_MESSAGE: "ProtoFieldType::TYPE_MESSAGE",
descriptor.FieldDescriptorProto.TYPE_FLOAT: "ProtoFieldType::TYPE_FLOAT",
descriptor.FieldDescriptorProto.TYPE_FIXED32: "ProtoFieldType::TYPE_FIXED32",
descriptor.FieldDescriptorProto.TYPE_SFIXED32: "ProtoFieldType::TYPE_SFIXED32",
descriptor.FieldDescriptorProto.TYPE_DOUBLE: "ProtoFieldType::TYPE_DOUBLE",
descriptor.FieldDescriptorProto.TYPE_FIXED64: "ProtoFieldType::TYPE_FIXED64",
descriptor.FieldDescriptorProto.TYPE_SFIXED64: "ProtoFieldType::TYPE_SFIXED64",
}
# Generate with
# protoc --python_out=script/api_protobuf -I esphome/components/api/ api_options.proto
@ -485,6 +521,10 @@ class MessageType(TypeInfo):
def cpp_type(self) -> str:
return self._field.type_name[1:]
@property
def type_name(self) -> str:
return self._field.type_name[1:]
default_value = ""
wire_type = WireType.LENGTH_DELIMITED # Uses wire type 2
@ -1250,6 +1290,8 @@ def build_message_type(
# Generate metadata arrays for all classes using metadata approach
regular_fields = []
repeated_fields = []
regular_fields_v2 = []
repeated_fields_v2 = []
metadata_info = None
if use_metadata:
@ -1335,6 +1377,70 @@ def build_message_type(
"class_name": desc.name,
}
# Also generate V2 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()
if field.type == descriptor.FieldDescriptorProto.TYPE_MESSAGE:
# For messages, get the type ID
message_type_id = get_message_type_id(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}}}"
)
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}}}"
)
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}}}"
)
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()
if field.type == descriptor.FieldDescriptorProto.TYPE_MESSAGE:
# For messages, get the type ID
message_type_id = get_message_type_id(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}}}"
)
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}}}"
)
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)
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}}}"
)
metadata_info["regular_fields_v2"] = regular_fields_v2
metadata_info["repeated_fields_v2"] = repeated_fields_v2
# Only generate decode methods for classes not using metadata approach
if not use_metadata:
if decode_varint:
@ -1405,6 +1511,27 @@ def build_message_type(
else:
public_content.append("static constexpr size_t REPEATED_COUNT = 0;")
# Add V2 metadata arrays
if regular_fields_v2:
public_content.append(
f"static const FieldMetaV2 FIELDS_V2[{len(regular_fields_v2)}];"
)
public_content.append(
f"static constexpr size_t FIELD_COUNT_V2 = {len(regular_fields_v2)};"
)
else:
public_content.append("static constexpr size_t FIELD_COUNT_V2 = 0;")
if repeated_fields_v2:
public_content.append(
f"static const RepeatedFieldMetaV2 REPEATED_FIELDS_V2[{len(repeated_fields_v2)}];"
)
public_content.append(
f"static constexpr size_t REPEATED_COUNT_V2 = {len(repeated_fields_v2)};"
)
else:
public_content.append("static constexpr size_t REPEATED_COUNT_V2 = 0;")
# Add virtual getter methods that return the metadata
public_content.append("// Virtual metadata getters")
if regular_fields:
@ -1431,6 +1558,31 @@ def build_message_type(
"size_t get_repeated_field_count() const override { return REPEATED_COUNT; }"
)
# Add V2 metadata getters
if regular_fields_v2:
public_content.append(
"const FieldMetaV2 *get_field_metadata_v2() const override { return FIELDS_V2; }"
)
else:
public_content.append(
"const FieldMetaV2 *get_field_metadata_v2() const override { return nullptr; }"
)
public_content.append(
"size_t get_field_count_v2() const override { return FIELD_COUNT_V2; }"
)
if repeated_fields_v2:
public_content.append(
"const RepeatedFieldMetaV2 *get_repeated_field_metadata_v2() const override { return REPEATED_FIELDS_V2; }"
)
else:
public_content.append(
"const RepeatedFieldMetaV2 *get_repeated_field_metadata_v2() const override { return nullptr; }"
)
public_content.append(
"size_t get_repeated_field_count_v2() const override { return REPEATED_COUNT_V2; }"
)
# Only generate encode method if there are fields to encode
if encode and not use_metadata:
o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{"
@ -1801,6 +1953,10 @@ namespace api {
mt = file.message_type
# First pass: Register all message types to populate MESSAGE_TYPE_REGISTRY
for m in mt:
get_message_type_id(m.name)
# Collect messages by base class
base_class_groups = collect_messages_by_base_class(mt)
@ -1903,10 +2059,156 @@ namespace api {
cpp += f" {field}\n"
cpp += "};\n"
# Generate V2 metadata arrays
regular_fields_v2 = meta.get("regular_fields_v2", [])
repeated_fields_v2 = meta.get("repeated_fields_v2", [])
if regular_fields_v2:
cpp += f"const FieldMetaV2 {class_name}::FIELDS_V2[{len(regular_fields_v2)}] = {{\n"
for i, field in enumerate(regular_fields_v2):
if i < len(regular_fields_v2) - 1:
cpp += f" {field},\n"
else:
cpp += f" {field}\n"
cpp += "};\n"
if repeated_fields_v2:
cpp += f"const RepeatedFieldMetaV2 {class_name}::REPEATED_FIELDS_V2[{len(repeated_fields_v2)}] = {{\n"
for i, field in enumerate(repeated_fields_v2):
if i < len(repeated_fields_v2) - 1:
cpp += f" {field},\n"
else:
cpp += f" {field}\n"
cpp += "};\n"
# Close last ifdef for metadata
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