No overloads — every method has a unique name matching its data type:
write_raw_fast_buf_ / write_raw_buf_ — single contiguous buffer
write_raw_fast_iov_ / write_raw_iov_ — iovec array
Moving iovec construction into write_raw_fast_ caused an 8% regression
by enlarging the inlined failure path. The 33-byte out-of-line wrapper
keeps the hot inline path minimal.
Callers already guarantee DATA state before calling write_protobuf_packet
(via try_to_clear_buffer) and write_protobuf_messages (via loop processing).
The runtime check is unreachable — replaced with ESPHOME_DEBUG_API assert.
The wrappers called write_raw_inline_ which expanded the full fast
path code, defeating the purpose. Cold callers now call write_raw_slow_
directly with sent=-1, which is the same behavior without duplicating
the fast path at each cold call site.
Hot paths use write_raw_inline_ (ALWAYS_INLINE fast path).
Cold paths (handshake, error handling) use write_raw_ which is
an out-of-line wrapper that calls write_raw_inline_ — same logic,
no code duplication, but the compiler won't expand the fast path
at each cold call site.
Cold paths (write_frame_, bad indicator) no longer need to construct
an iovec just to call the slow path. The single-buffer overload wraps
in iovec internally, keeping call sites clean and avoiding iovec
setup in the caller's stack frame.
write_frame_ (handshake) and bad indicator response are cold paths
that don't benefit from the inlined write_raw_ fast path. Calling
write_raw_slow_ directly avoids expanding the inline fast path code
at these call sites, saving ~70 bytes per site.
Move the happy path (overflow empty + full write succeeds) inline into
the header so it gets inlined at each call site. Both overloads share
a single out-of-line write_raw_slow_ that handles partial writes,
errors, and overflow buffering.
Both write_raw_ overloads now have a clean fast path (direct socket
write) and delegate to a shared noinline enqueue_overflow_ for the
rare overflow case. The multi-iovec path now always calls writev
since single-message callers use the dedicated overload.
The existing write_raw_ takes iovec array + count, but single-message
writes (87-100% of traffic) always pass iovcnt=1. Adding a dedicated
overload that takes (data, len) eliminates iovec construction, the
iovcnt==1 branch, and pointer indirection on the hot path.
Instead of routing single messages through write_protobuf_messages and
branching internally, make write_protobuf_packet a separate virtual
override in each frame helper. The single-message path gets its own
minimal stack frame with no StaticVector allocation, and the batch
path in write_protobuf_messages has no size==1 branch — each caller
picks the right method upfront.
The noinline attribute on the batch path prevented the compiler from
inlining callees like write_plaintext_header and encrypt_noise_message_
into the loop body, causing a 5.6% regression on batch writes. Adding
flatten forces all callees to be inlined within the batch function while
noinline still keeps its large stack frame separate from the single-message
fast path.