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.
Extend the precomputed-tag approach from fixed32 key fields to all forced
fields with single-byte tags (field IDs 1-15). The code generator now
emits write_raw_byte(tag) followed by the raw encode primitive instead
of calling the full encode_* method.
For varint types (uint32, uint64, sint32, sint64, int64, bool, enum),
this eliminates the zero-check branch and encode_field_raw indirection.
For length-delimited types (bytes, string), it additionally skips the
encode_string wrapper.
Benchmarked on real hardware with BluetoothLERawAdvertisementsResponse
(12 advertisements per message, 10000 iterations):
ESP32 (Xtensa dual-core 240MHz):
encode: 38498 -> 30460 ns/op (-20.9%)
calc+encode: 48479 -> 40458 ns/op (-16.6%)
ESP32-C3 (RISC-V single-core 160MHz):
encode: 54199 -> 40342 ns/op (-25.6%)
calc+encode: 57800 -> 51365 ns/op (-11.1%)
- Combine tag byte + fixed32 value into single write_tag_and_fixed32()
method: pos[0] = tag, memcpy(pos+1, &value, 4), pos += 5
- Extract calculate_tag() from duplicated computation in
calculate_field_id_size() and encode_content
- Combine tag byte + fixed32 value into single write_tag_and_fixed32()
method: pos[0] = tag, memcpy(pos+1, &value, 4), pos += 5
- Extract calculate_tag() from duplicated computation in
calculate_field_id_size() and encode_content
Instead of ALWAYS_INLINE on encode_field_raw (which bloated all
callers), have the code generator precompute the tag byte and emit
write_raw_byte(tag) + write_fixed32_raw(value) directly.
This gives the same tight codegen (single byte store + memcpy) for
key fields without inflating encode_bool/encode_uint32/etc.
+108 bytes flash vs baseline, -48 bytes vs the ALWAYS_INLINE approach.
- Update comments to reflect that ProtoService methods moved to
APIConnection, not APIServerConnectionBase
- Fix comment referring to read_message as "override"
- Wrap #include "api_connection.h" and read_message_ implementation
in #ifdef USE_API guards
The virtual destructor was unnecessary since APIConnection is only
stored as unique_ptr<APIConnection>, never via a base class pointer.
Removing it eliminates the vtable entirely.
Rename read_message to read_message_ per clang-tidy naming convention
for protected methods.
ProtoService was an abstract interface with 6 pure virtual methods,
but APIConnection was the only concrete implementation. Move all
functionality directly into APIConnection and remove the unnecessary
virtual dispatch and vtable overhead.
Move read_message() from APIServerConnectionBase into APIConnection
and drop the virtual keyword from all 64 on_* handler declarations.
Since APIConnection is final and the only subclass, the virtual
dispatch was unnecessary. With read_message and the on_* handlers
in the same class, the compiler can devirtualize the calls and
inline small handlers directly into the switch cases.
Add libretiny and zephyr to PLATFORM_SPECIFIC_COMPONENTS so they
are correctly filtered out when an incompatible platform is selected
for memory impact analysis. Previously, libretiny was missing from
the set, causing it to be included in esp32-idf builds and producing
a misleading comment showing both platforms.
message_name() returned bare string literals that stayed in RAM on
ESP8266. Change return type to const LogString * and wrap with LOG_STR()
so they are stored in flash. Update log_send_message_ to use
LOG_STR_ARG() accordingly.
Also fix clang-tidy: rename append_p_esp8266_ to append_p_esp8266,
add NOLINTNEXTLINE for conditionally-unused dump_field overloads.
message_name() returned bare string literals that stayed in RAM on
ESP8266. Change return type to const LogString * and wrap with LOG_STR()
so they are stored in flash. Update log_send_message_ to use
LOG_STR_ARG() accordingly.
Also fix clang-tidy: rename append_p_esp8266_ to append_p_esp8266,
add NOLINTNEXTLINE for conditionally-unused dump_field overloads.