mirror of
https://github.com/esphome/esphome.git
synced 2026-09-02 02:56:01 +00:00
[api] Use StringRef for user service string arguments
Replace std::string with StringRef (non-owning string view) for user service string arguments in YAML-generated services. This avoids unnecessary heap allocation when the protobuf decode buffer already contains the string data. Key changes: - Frame helpers reserve +1 byte in rx_buf_ so string fields can be safely null-terminated in-place after decode - Add (null_terminate) protobuf field option to target only fields that need it (ExecuteServiceArgument.string_ and HomeAssistantStateResponse.state) - Add StringRef template specializations for get_execute_arg_value and to_service_arg_type - Python codegen uses StringRef for string service args, with automatic std::string fallback when deferred actions (delay, wait_until, script.wait) are present in the action chain - Add deferred flag to action registry for detecting actions that store trigger args for later execution - Simplify HomeAssistantStateResponse handler by removing SmallBufferWithHeapFallback copy (state is null-terminated in-place) - Add compare() method to StringRef for external component compatibility Saves ~240 bytes of flash on ESP8266 by eliminating std::string template instantiations for user service string arguments.
This commit is contained in:
@@ -2020,6 +2020,8 @@ def build_message_type(
|
||||
|
||||
# Collect fixed_vector fields for custom decode generation
|
||||
fixed_vector_fields = []
|
||||
# Collect fields with (null_terminate) = true option
|
||||
null_terminate_fields = []
|
||||
|
||||
for field in desc.field:
|
||||
# Skip deprecated fields completely
|
||||
@@ -2062,6 +2064,10 @@ def build_message_type(
|
||||
|
||||
ti = create_field_type_info(field, needs_decode, needs_encode)
|
||||
|
||||
# Collect fields with (null_terminate) = true for post-decode null-termination
|
||||
if needs_decode and get_field_opt(field, pb.null_terminate, False):
|
||||
null_terminate_fields.append(ti.field_name)
|
||||
|
||||
# Skip field declarations for fields that are in the base class
|
||||
# but include their encode/decode logic
|
||||
if field.name not in common_field_names:
|
||||
@@ -2168,8 +2174,8 @@ def build_message_type(
|
||||
prot = "bool decode_64bit(uint32_t field_id, Proto64Bit value) override;"
|
||||
protected_content.insert(0, prot)
|
||||
|
||||
# Generate custom decode() override for messages with FixedVector fields
|
||||
if fixed_vector_fields:
|
||||
# Generate custom decode() override for messages with FixedVector or null_terminate fields
|
||||
if fixed_vector_fields or null_terminate_fields:
|
||||
# Generate the decode() implementation in cpp
|
||||
o = f"void {desc.name}::decode(const uint8_t *buffer, size_t length) {{\n"
|
||||
# Count and init each FixedVector field
|
||||
@@ -2178,6 +2184,13 @@ def build_message_type(
|
||||
o += f" this->{field_name}.init(count_{field_name});\n"
|
||||
# Call parent decode to populate the fields
|
||||
o += " ProtoDecodableMessage::decode(buffer, length);\n"
|
||||
# Null-terminate fields marked with (null_terminate) = true in-place.
|
||||
# Safe: decode is complete, byte after string was already parsed (next field tag)
|
||||
# or is the +1 reserved byte at end of rx_buf_.
|
||||
for field_name in null_terminate_fields:
|
||||
o += f" if (!this->{field_name}.empty()) {{\n"
|
||||
o += f" const_cast<char *>(this->{field_name}.c_str())[this->{field_name}.size()] = '\\0';\n"
|
||||
o += " }\n"
|
||||
o += "}\n"
|
||||
cpp += o
|
||||
# Generate the decode() declaration in header (public method)
|
||||
|
||||
Reference in New Issue
Block a user