[api] Share the fixed32 emission between float and fixed32 fields

One helper next to the other precomputed tag paths decides how a
single byte tag fixed32 field is written; the float and fixed32 types
only differ in the value expression. Drop the non forced std::string
encode_string overload, which the generator never emits, and build the
generator tests from one block of field type constants.
This commit is contained in:
J. Nick Koston
2026-09-07 10:33:21 +02:00
parent b77e2441d4
commit 490aca17e6
4 changed files with 47 additions and 43 deletions
+2 -2
View File
@@ -3838,8 +3838,8 @@ uint8_t *DateTimeStateResponse::encode(ProtoWriteBuffer &buffer PROTO_ENCODE_DEB
uint8_t *__restrict__ pos = buffer.get_pos();
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 13, this->key);
pos = ProtoEncode::encode_bool(pos PROTO_ENCODE_DEBUG_ARG, 2, this->missing_state);
if (this->epoch_seconds != 0) [[likely]] {
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 29, this->epoch_seconds);
if (uint32_t raw = this->epoch_seconds; raw != 0) [[likely]] {
pos = ProtoEncode::write_tag_and_fixed32(pos PROTO_ENCODE_DEBUG_ARG, 29, raw);
}
#ifdef USE_DEVICES
pos = ProtoEncode::encode_uint32(pos PROTO_ENCODE_DEBUG_ARG, 4, this->device_id);
-4
View File
@@ -457,10 +457,6 @@ class ProtoEncode {
return pos;
return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, string, len);
}
static inline uint8_t *encode_string(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
const std::string &value) {
return encode_string(pos PROTO_ENCODE_DEBUG_ARG, field_id, value.data(), value.size());
}
static inline uint8_t *encode_string_force(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id,
const std::string &value) {
return encode_string_force(pos PROTO_ENCODE_DEBUG_ARG, field_id, value.data(), value.size());
+28 -23
View File
@@ -324,6 +324,24 @@ class TypeInfo(ABC):
f"pos = ProtoEncode::encode_raw(pos, {data_expr}, {len_expr});"
)
def _encode_fixed32_with_precomputed_tag(self, value_expr: str) -> str | None:
"""Emit a fixed32 field through the shared tag+value writer when the tag is one byte.
Returns None for multi-byte tags, which go through the generic helper.
"""
tag = self.calculate_tag()
if tag >= 128:
return None
if self.force:
return (
f"pos = ProtoEncode::write_tag_and_fixed32(pos, {tag}, {value_expr});"
)
return (
f"if (uint32_t raw = {value_expr}; raw != 0) [[likely]] {{\n"
f" pos = ProtoEncode::write_tag_and_fixed32(pos, {tag}, raw);\n"
"}"
)
@property
def encode_content(self) -> str:
if result := self._encode_with_precomputed_tag(f"this->{self.field_name}"):
@@ -637,18 +655,11 @@ class FloatType(FixedSizeTypeMixin, TypeInfo):
@property
def encode_content(self) -> str:
tag = self.calculate_tag()
if tag >= 128:
return super().encode_content
# Single-byte tag: share the outlined tag+fixed32 writer instead of the generic helper
value = f"float_to_raw(this->{self.field_name})"
if self.force:
return f"pos = ProtoEncode::write_tag_and_fixed32(pos, {tag}, {value});"
return (
f"if (uint32_t raw = {value}; raw != 0) [[likely]] {{\n"
f" pos = ProtoEncode::write_tag_and_fixed32(pos, {tag}, raw);\n"
"}"
)
if result := self._encode_fixed32_with_precomputed_tag(
f"float_to_raw(this->{self.field_name})"
):
return result
return super().encode_content
def dump(self, name: str) -> str:
o = f'snprintf(buffer, sizeof(buffer), "%g", {name});\n'
@@ -786,17 +797,11 @@ class Fixed32Type(FixedSizeTypeMixin, TypeInfo):
@property
def encode_content(self) -> str:
tag = self.calculate_tag()
if tag >= 128:
return super().encode_content
# Single-byte tag: share the outlined tag+fixed32 writer instead of the generic helper
if self.force:
return f"pos = ProtoEncode::write_tag_and_fixed32(pos, {tag}, this->{self.field_name});"
return (
f"if (this->{self.field_name} != 0) [[likely]] {{\n"
f" pos = ProtoEncode::write_tag_and_fixed32(pos, {tag}, this->{self.field_name});\n"
"}"
)
if result := self._encode_fixed32_with_precomputed_tag(
f"this->{self.field_name}"
):
return result
return super().encode_content
def get_size_calculation(self, name: str, force: bool = False) -> str:
field_id_size = self.calculate_field_id_size()
@@ -45,7 +45,14 @@ UINT64 = descriptor_pb2.FieldDescriptorProto.TYPE_UINT64
INT64 = descriptor_pb2.FieldDescriptorProto.TYPE_INT64
SINT64 = descriptor_pb2.FieldDescriptorProto.TYPE_SINT64
UINT32 = descriptor_pb2.FieldDescriptorProto.TYPE_UINT32
INT32 = descriptor_pb2.FieldDescriptorProto.TYPE_INT32
SINT32 = descriptor_pb2.FieldDescriptorProto.TYPE_SINT32
FIXED64 = descriptor_pb2.FieldDescriptorProto.TYPE_FIXED64
FIXED32 = descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32
FLOAT = descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT
BOOL = descriptor_pb2.FieldDescriptorProto.TYPE_BOOL
STRING = descriptor_pb2.FieldDescriptorProto.TYPE_STRING
BYTES = descriptor_pb2.FieldDescriptorProto.TYPE_BYTES
def test_no_varint64_fields() -> None:
@@ -127,16 +134,16 @@ def _encode_field(
SCALAR_TYPES = [
descriptor_pb2.FieldDescriptorProto.TYPE_BOOL,
descriptor_pb2.FieldDescriptorProto.TYPE_UINT32,
descriptor_pb2.FieldDescriptorProto.TYPE_INT32,
descriptor_pb2.FieldDescriptorProto.TYPE_UINT64,
descriptor_pb2.FieldDescriptorProto.TYPE_INT64,
descriptor_pb2.FieldDescriptorProto.TYPE_SINT32,
descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT,
descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32,
descriptor_pb2.FieldDescriptorProto.TYPE_STRING,
descriptor_pb2.FieldDescriptorProto.TYPE_BYTES,
BOOL,
UINT32,
INT32,
UINT64,
INT64,
SINT32,
FLOAT,
FIXED32,
STRING,
BYTES,
]
@@ -165,10 +172,6 @@ def test_forced_fields_use_the_force_overload_or_raw_writes(field_type: int) ->
), content
FLOAT = descriptor_pb2.FieldDescriptorProto.TYPE_FLOAT
FIXED32 = descriptor_pb2.FieldDescriptorProto.TYPE_FIXED32
@pytest.mark.parametrize("field_type", [FLOAT, FIXED32])
def test_single_byte_tag_fixed32_shares_the_outlined_writer(field_type: int) -> None:
unconditional = _encode_field(field_type, force=True)