Replace two separate write_raw_byte calls with a single
encode_small_varint(tag, value) call that writes both bytes
with one bounds check. Used for enum fields with max < 128.
When an enum field has max_value < 128 and a single-byte tag, emit
inline write_raw_byte calls instead of calling encode_uint32, which
avoids encode_field_raw and encode_varint_raw function call overhead.
When a repeated enum field has a constant size per element (max < 128),
emit size * constant instead of iterating. This fixes unused variable
warnings from clang-tidy and generates more efficient code.
The protobuf code generator already parses all enum definitions and
knows every enum's maximum value. Use this to automatically apply the
max_value < 128 optimization to all enum fields, eliminating the need
for manual annotation.
Since every enum in api.proto has max < 128, all 58 enum field size
calculations now use constant arithmetic instead of calling varint
size functions.
Builds on #15424 which introduced the max_value optimization.
Replace 3-call sequence (write_raw_byte + write_raw_byte + encode_raw)
with single inlined encode_raw_short_string call for forced string
fields with max_data_length < 128. The compiler can hoist the pos
pointer across all three writes in one function boundary.
Add max_data_length field option for string/bytes fields. When
max_data_length < 128, the codegen emits constant-size length varint
calculations and direct byte writes.
Annotate all entity name and object_id fields with
(max_data_length) = 120 and (force) = true across all 25
ListEntities*Response messages (50 fields).
Generated code changes:
- calculate_size: `calc_length(1, size)` -> `2 + size` (constant)
- encode: `encode_string(N, ref)` -> `write_raw_byte(tag) + write_raw_byte(len) + encode_raw(data, len)`
Eliminates 2 function calls per field per entity list response,
removes zero-check branches, and removes varint size computation.
Refactor the constant-size varint pattern into a reusable helper
method on the TypeInfo base class, used by both UInt32Type (max_value)
and FixedArrayBytesType (fixed_array_size < 128).
Add a max_value field option to api_options.proto that tells the code
generator the maximum value a field can have. When max_value < 128,
the generated calculate_size() uses constant arithmetic instead of
calling varint size functions, and encode() uses direct byte writes
instead of varint encoding.
Also optimize FixedArrayBytesType: when fixed_array_size < 128, the
length varint is always 1 byte, so calculate_size() uses constant
arithmetic and encode() uses write_raw_byte for the length.
Applied to BluetoothLERawAdvertisement.address_type (max_value=4).
Measured on ESP32 (upstairsdesk89proxy):
- BluetoothLERawAdvertisement::calculate_size: 88 → 71 bytes (-19%)
- BluetoothLERawAdvertisement::encode: 199 → 179 bytes (-10%)
- Total BLE proxy hot path: 1807 → 1770 bytes (-37 bytes)
Add encode_varint_raw_short() and ProtoSize::varint_short() that
inline both the 1-byte and 2-byte varint paths, falling back to
the noinline slow path for 3+ bytes.
Use these for sint32 fields (zigzag encoding), where values like
RSSI (-100 to 0) produce zigzag values that are 1-2 bytes. This
avoids a function call for the common case without bloating the
generic encode_varint_raw fast path.
Add CodSpeed benchmarks for BluetoothLERawAdvertisementsResponse
(12 advertisements) covering calculate_size, encode, calc+encode,
and fresh-buffer paths.
Includes a lightweight bluetooth_proxy stub header in
tests/benchmarks/stubs/ so the api component can compile with
USE_BLUETOOTH_PROXY on the host platform without pulling in
ESP32 BLE dependencies.
decode() is never called polymorphically - all call sites in
read_message_() use concrete types. The only indirect call site was
decode_to_message(), which also always knows the concrete type.
Convert decode_to_message() to a template so the concrete type is
preserved, allowing decode() to be non-virtual. The two classes that
override decode() (ExecuteServiceArgument, ExecuteServiceRequest) now
hide the base method, which works since all calls use concrete types.
This removes one vtable slot (4 bytes) from each decodable message
class vtable, saving ~148 bytes of flash.