Each of these methods called encode_field_raw() then a value encoder,
causing a store-load pair on pos_ between the tag and value writes.
Inline the tag write into the same __restrict__ local scope as the
value write so the compiler can emit tag + value with a single pos_
load at start and store at end. Verified on Xtensa: encode_uint32
now does one load + two writes + one store (was load-store-load-store).
Add sync_debug_check_bounds_() that syncs pos_ from a local pointer
before checking bounds. Use it in encode_varint_raw_64 and
encode_varint_raw_slow_ to restore per-byte bounds checking in
debug mode without breaking the __restrict__ optimization in
production (where it's a no-op).
Previously encode_string called encode_varint_raw(len) then
encode_raw(data, len) as separate methods, each with their own
__restrict__ pos scope. This caused a redundant store-load pair
of pos_ between the two operations.
Inline the length varint write and memcpy under a single local
pos variable so the compiler can keep pos_ in a register across
both operations. Eliminates one load-store pair per string encode.
Apply the same __restrict__ local pointer pattern proven in
encode_varint_raw_64 to all remaining methods that write through
pos_: encode_varint_raw, encode_varint_raw_short, write_raw_byte,
encode_raw, write_tag_and_fixed32, encode_string, encode_bool,
and encode_fixed32.
Each method now hoists pos_ into a __restrict__ local before
writing and stores back once at the end. When the compiler inlines
these into a generated encode() method, it can keep pos_ in a
register across consecutive calls instead of reloading from memory
after every write.
Same optimization as encode_varint_raw_64: hoist pos_ into a
__restrict__ local so the compiler keeps it in a register across
the loop instead of reloading from memory each iteration.
Use a __restrict__ local pointer for the varint write loop so the
compiler can keep it in a register instead of reloading pos_ from
memory on each iteration. Eliminates the store→load dependency
chain that was causing 7 load/store pairs for a typical 48-bit
BLE address varint.
Use a __restrict__ local pointer for the varint write loop so the
compiler can keep it in a register instead of reloading pos_ from
memory on each iteration. Eliminates the store→load dependency
chain that was causing 7 load/store pairs for a typical 48-bit
BLE address varint.
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.
Remove direct include of build_info_data.h from version_text_sensor.cpp
and use the existing App.get_build_time_string() API instead. This
eliminates duplicate ESPHOME_BUILD_TIME_STR and ESPHOME_COMMENT_STR
symbols that were emitted into every translation unit including the
header (static const in a header = one copy per TU).
Now only application.cpp includes build_info_data.h, which also
improves incremental rebuild times: when build info changes, only
application.cpp needs recompiling instead of both application.cpp
and version_text_sensor.cpp.
Save 4 bytes per ActionList instance by removing the tail pointer
used only during setup() for O(1) append. Instead, walk the short
chain (typically 1-5 actions) to find the tail.
This saves 4 bytes per Automation on 32-bit platforms, which adds
up on memory-constrained devices like ESP8266 with many automations.
Template AndCondition, OrCondition, and XorCondition on N so the
condition pointer list is stored in std::array<Condition<Ts...>*, N>
instead of FixedVector. N is set by code generation to match the
exact number of conditions configured in YAML.
Add init_array_from() helper for optimal codegen when initializing
std::array from initializer_list.
Template ValueListFilter, FilterOutValueFilter, and ThrottleWithPriorityFilter
on N (the number of configured values) so the value list is stored in
std::array<TemplatableValue<float>, N> instead of FixedVector.
Non-template helpers value_list_matches_any() and throttle_check_and_update()
keep the iteration loops in the .cpp to avoid template bloat.