From 9f774b382701c5276ba4d4271d24f28693b9fe3b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 16 Mar 2026 12:43:17 -1000 Subject: [PATCH] Inline empty() fast path, out-of-line drain+error handling Keep the cheap overflow_buf_.empty() check inline at all call sites. Move try_drain + HELPER_LOG + error handling into a single out-of-line drain_overflow_and_handle_errors_() used by both write_raw_ and the subclass loop() methods. Eliminates the duplicated drain logic. --- esphome/components/api/api_frame_helper.cpp | 17 ++++++++++++----- esphome/components/api/api_frame_helper.h | 14 +++++--------- .../components/api/api_frame_helper_noise.cpp | 9 +++++---- .../api/api_frame_helper_plaintext.cpp | 9 +++++---- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index d89154f4570..ec326edbd62 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -100,6 +100,15 @@ const LogString *api_error_to_logstr(APIError err) { return LOG_STR("UNKNOWN"); } +APIError APIFrameHelper::drain_overflow_and_handle_errors_() { + if (this->overflow_buf_.try_drain(this->socket_.get()) == -1) { + HELPER_LOG("Socket write failed with errno %d", errno); + if (this->check_socket_write_err_(errno) != APIError::WOULD_BLOCK) + return APIError::SOCKET_WRITE_FAILED; + } + return APIError::OK; +} + // Write data to socket, overflow to backlog buffer if LWIP TCP send buffer is full. // Returns OK if all data was sent or successfully queued. // Returns SOCKET_WRITE_FAILED on hard error (sets state to FAILED). @@ -114,11 +123,9 @@ APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_ // Drain any existing backlog first if (!this->overflow_buf_.empty()) [[unlikely]] { - if (this->overflow_buf_.try_drain(this->socket_.get()) == -1) { - HELPER_LOG("Socket write failed with errno %d", errno); - if (this->check_socket_write_err_(errno) != APIError::WOULD_BLOCK) - return APIError::SOCKET_WRITE_FAILED; - } + APIError err = this->drain_overflow_and_handle_errors_(); + if (err != APIError::OK) + return err; } // If backlog is clear, try direct send diff --git a/esphome/components/api/api_frame_helper.h b/esphome/components/api/api_frame_helper.h index ccbb9fa077a..72ccf8aa562 100644 --- a/esphome/components/api/api_frame_helper.h +++ b/esphome/components/api/api_frame_helper.h @@ -190,15 +190,11 @@ class APIFrameHelper { } protected: - // Drain any backlogged overflow data to the socket. - // Returns OK even for WOULD_BLOCK to avoid connection termination. - APIError try_drain_overflow_buffer_() { - if (!this->overflow_buf_.empty() && this->overflow_buf_.try_drain(this->socket_.get()) == -1) { - if (this->check_socket_write_err_(errno) != APIError::WOULD_BLOCK) - return APIError::SOCKET_WRITE_FAILED; - } - return APIError::OK; - } + // Drain backlogged overflow data to the socket and handle errors. + // Called when overflow_buf_.empty() is false. Out-of-line to keep the + // fast path (empty check) inline at call sites. + // Returns OK for transient errors (WOULD_BLOCK), SOCKET_WRITE_FAILED for hard errors. + APIError drain_overflow_and_handle_errors_(); // Common implementation for writing raw data to socket APIError write_raw_(const struct iovec *iov, int iovcnt, uint16_t total_write_len); diff --git a/esphome/components/api/api_frame_helper_noise.cpp b/esphome/components/api/api_frame_helper_noise.cpp index 9690266971a..d73b1147c55 100644 --- a/esphome/components/api/api_frame_helper_noise.cpp +++ b/esphome/components/api/api_frame_helper_noise.cpp @@ -153,11 +153,12 @@ APIError APINoiseFrameHelper::loop() { } } - APIError err = this->try_drain_overflow_buffer_(); - if (err != APIError::OK) { - HELPER_LOG("Overflow drain failed with errno %d", errno); + if (!this->overflow_buf_.empty()) [[unlikely]] { + APIError err = this->drain_overflow_and_handle_errors_(); + if (err != APIError::OK) + return err; } - return err; + return APIError::OK; } /** Read a packet into the rx_buf_. diff --git a/esphome/components/api/api_frame_helper_plaintext.cpp b/esphome/components/api/api_frame_helper_plaintext.cpp index 1790d50727e..1ed1b9c8e3c 100644 --- a/esphome/components/api/api_frame_helper_plaintext.cpp +++ b/esphome/components/api/api_frame_helper_plaintext.cpp @@ -64,11 +64,12 @@ APIError APIPlaintextFrameHelper::loop() { if (state_ != State::DATA) { return APIError::BAD_STATE; } - APIError err = this->try_drain_overflow_buffer_(); - if (err != APIError::OK) { - HELPER_LOG("Overflow drain failed with errno %d", errno); + if (!this->overflow_buf_.empty()) [[unlikely]] { + APIError err = this->drain_overflow_and_handle_errors_(); + if (err != APIError::OK) + return err; } - return err; + return APIError::OK; } /** Read a packet into the rx_buf_.