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 runtime powf() calls in light gamma correction with
pre-computed uint16[256] PROGMEM lookup tables generated at
Python codegen time. The gamma value is a compile-time constant,
so the tables can be computed once and shared across all lights
with the same gamma value.
This eliminates powf/ieee754_powf (~2.3KB) from the binary.
Two 512-byte PROGMEM tables (forward + reverse) are shared per
unique gamma value, for a net savings of ~1.3KB flash and zero
RAM impact.
The LUT uses linear interpolation between table entries,
achieving zero PWM errors at both 8-bit and 16-bit resolution
compared to the old powf-based approach.
Breaking change: gamma parameter removed from
LightColorValues::as_*() methods since gamma correction is now
applied externally via LightState::gamma_correct_lut().
gamma_correct() and gamma_uncorrect() in helpers.h are
deprecated (removal in 2026.12.0).
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.
Extend the clk_mode deprecation period to 2026.7.0 and improve the
warning message to show the exact replacement YAML the user needs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructure the defer queue processing loop to merge lock acquisitions.
Previously each item required two separate lock/unlock pairs: one to
move the item out of the queue, and one to recycle it after execution.
This totaled 2N+1 lock acquisitions for N items.
By combining the recycle of each item with the move-out of the next
item under a single lock hold, the total drops to N+1. The callback
still executes without the lock held to prevent deadlocks.
Also adds an early return when the queue is empty to avoid taking
the lock at all in the common case (nothing deferred). The lockless
check is safe because the main loop is the single consumer of
defer_queue_front_, and a stale size() read from a concurrent push
can only cause us to see fewer items — they will be processed on
the next loop iteration.
Additionally outlines the rare defer queue compaction path into a
separate noinline compact_defer_queue_locked_() to keep cold code
out of the hot instruction cache lines.
Resolve conflict in component.cpp: keep destructor removed (moved
to header as = default) and keep #ifdef USE_SETUP_PRIORITY_OVERRIDE
guard from dev.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>