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>
The LWIP raw socket factory only supports TCP but silently ignored the
type parameter, returning a TCP socket for SOCK_DGRAM requests. This
caused components expecting UDP to fail silently with no indication of
why. Return nullptr with EPROTOTYPE and log an error instead.
When leave_() decremented consumers to 0 without removing the entry,
a subsequent join_() would find it, increment 0->1, and return early
without rejoining the IGMP multicast group. Now join_() only skips
the IGMP rejoin when consumers was already > 0.
Replace std::map<int, int> with std::vector<UniverseConsumer> for
tracking universe reference counts. This eliminates red-black tree
overhead for what is typically 1-10 entries.
Co-Authored-By: J. Nick Koston <nick@koston.org>
The socket abstraction layer on ESP8266/RP2040 (USE_SOCKET_IMPL_LWIP_TCP)
only supports TCP. When E1.31 was migrated from WiFiUDP to sockets, it
silently broke on these platforms since SOCK_DGRAM requests get TCP sockets
that never receive UDP data.
Restore WiFiUDP as the transport on LWIP_TCP platforms while keeping the
socket-based implementation on BSD/LWIP socket platforms (ESP32, etc.).
This follows the same dual-path pattern used by the udp and wake_on_lan
components.
Replace the LogListener abstract class (single pure virtual method) with
a lightweight LogCallback struct containing a function pointer + instance
pointer. This eliminates a vtable sub-table and thunk from every class
that previously inherited LogListener.
Savings per former LogListener implementer:
- 12 bytes vtable (sub-table header + thunk slot)
- 4 bytes vtable (on_log in primary table)
- ~23 bytes thunk code
- ~39 bytes total per class
Affected classes: APIServer, WebServer, MQTTClientComponent, Syslog,
BLENUS, LoggerMessageTrigger (6 classes, ~234 bytes total).
The non-capturing lambdas used at registration sites decay to plain
function pointers at compile time -- zero closure/std::function overhead.
Neither method is overridden anywhere in the codebase.
Removing virtual eliminates one vtable slot per method
from every Component-derived class (~100+ vtables),
saving ~800+ bytes of flash globally.