[api] Add single-pass encode_sub_message to eliminate redundant calculate_size() calls

Replace encode_message with encode_sub_message for protobuf submessage encoding.

For repeated submessage fields, encode_sub_message uses a backpatch approach:
writes field tag, reserves 1 byte for length varint, encodes the body, then
backpatches the actual length. For bodies >= 128 bytes, shifts the body forward
to make room for a multi-byte varint. This eliminates 2 of 3 calculate_size()
calls per repeated submessage element.

For singular submessage fields, encode_sub_message uses calculate_size() upfront
to skip empty submessages without writing to the buffer, preserving the debug
size check.

For the BLE advertisement proxy hot path (16 advertisements per batch), this
reduces calculate_size() calls from 48 to 16 per flush.
This commit is contained in:
J. Nick Koston
2026-03-06 14:20:54 -10:00
parent d8deb2255d
commit b38af3d6ec
4 changed files with 111 additions and 49 deletions
+7 -10
View File
@@ -690,15 +690,12 @@ class MessageType(TypeInfo):
@property
def encode_func(self) -> str:
return "encode_message"
return "encode_optional_sub_message"
@property
def encode_content(self) -> str:
# Singular message fields pass force=false (skip empty messages)
# The default for encode_nested_message is force=true (for repeated fields)
return (
f"buffer.{self.encode_func}({self.number}, this->{self.field_name}, false);"
)
# Singular message fields skip encoding when empty
return f"buffer.{self.encode_func}({self.number}, this->{self.field_name});"
@property
def decode_length(self) -> str:
@@ -1322,9 +1319,9 @@ class FixedArrayRepeatedType(TypeInfo):
"""Helper to generate encode statement for a single element."""
if isinstance(self._ti, EnumType):
return f"buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>({element}), true);"
# MessageType.encode_message doesn't have a force parameter
# Repeated message elements use encode_sub_message (force=true is default)
if isinstance(self._ti, MessageType):
return f"buffer.{self._ti.encode_func}({self.number}, {element});"
return f"buffer.encode_sub_message({self.number}, {element});"
return f"buffer.{self._ti.encode_func}({self.number}, {element}, true);"
@property
@@ -1650,9 +1647,9 @@ class RepeatedTypeInfo(TypeInfo):
"""Helper to generate encode call for a single element."""
if isinstance(self._ti, EnumType):
return f"buffer.{self._ti.encode_func}({self.number}, static_cast<uint32_t>({element}), true);"
# MessageType.encode_message doesn't have a force parameter
# Repeated message elements use encode_sub_message (force=true is default)
if isinstance(self._ti, MessageType):
return f"buffer.{self._ti.encode_func}({self.number}, {element});"
return f"buffer.encode_sub_message({self.number}, {element});"
return f"buffer.{self._ti.encode_func}({self.number}, {element}, true);"
@property