- Add early return when size_==0 to avoid nullptr+0 pointer
arithmetic (UB per C++ standard, even though all ESPHome
targets treat it as no-op)
- Fix debug assert: size < UINT16_MAX allows capacity up to 65535
Since Callback is trivially copyable regardless of signature,
the grow logic is type-independent. Move it to a concrete
function in helpers.cpp using void*/memcpy, eliminating the
template definition from the header entirely.
On some ESP32 boards (especially cheap clones), the eFuse custom MAC
area contains random garbage that passes the existing all-zeros/all-ones
validation. Additionally, esp_efuse_mac_get_default() can fail with CRC
errors, but the return value was being ignored, causing garbage MAC
addresses to be advertised via mDNS.
This caused Home Assistant to report false "MAC address changed" device
conflicts on every boot.
Two fixes:
- Check return values from eFuse MAC read functions and add a fallback
chain: custom MAC -> default MAC -> raw eFuse bytes -> zeroed MAC.
- Reject multicast MACs (bit 0 of first byte set) in mac_address_is_valid()
since device MACs must always be unicast.
Closes https://github.com/esphome/esphome/issues/14501
Move is_high_frequency() from out-of-line definition in helpers.cpp
to inline in the header. This allows the compiler to inline the
trivial check (num_requests > 0) at the call site in
Application::loop(), avoiding a function call every loop iteration.
Same pattern as the decode side - the bit manipulation was
duplicated in the main loop and tail path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
base64_find_char() safely handles any input including 0,
returning 0 via strchr fallback - no special contract needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fold the output copy loop into base64_decode_quad_ since both
call sites use the same truncation-checked copy logic, differing
only in count (3 vs i-1).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The base64 character-to-byte conversion and output copy loop were
duplicated in both the main decode loop and the tail/padding path.
Extract into a shared static helper to reduce flash usage (-64 bytes
on ESP32) and eliminate duplicated logic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The base64 character-to-byte conversion was duplicated in both the
main decode loop and the tail/padding path. Extract into a shared
static helper to reduce flash usage (-64 bytes on ESP32).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a ci-custom.py check that prevents powf() from being introduced
into esphome/core/ and base entity platform components (sensor, light,
climate, etc.). These files are linked into every build, and powf
pulls in __ieee754_powf (~2.3KB flash).
Existing legitimate uses of powf with non-integer exponents (gamma
correction in helpers.cpp) are excluded. New uses can opt out with
// NOLINT if truly necessary.
Add shared pow10_int() helper to helpers.h that computes 10^exp
using iterative multiplication/division instead of powf.
Replace powf(10, exp) calls in:
- sensor/filter.cpp: ValueListFilter and RoundFilter
- helpers.cpp: normalize_accuracy_decimals (refactored to use
pow10_int for the general case, keeping -1/-2 fast paths)
Eliminates powf/__ieee754_powf from builds where sensor filters
are the only remaining powf call site.
Replace powf(10.0f, accuracy_decimals) with integer divisor computation
to avoid pulling in __ieee754_powf (~2.2KB) on builds where this is
the only powf call site.
Tested on ESP8266 with web_server: -3.1KB flash, -16 bytes RAM.