[api] Restore single-buffer write_raw_ overload to keep inline failure path small

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.
This commit is contained in:
J. Nick Koston
2026-03-29 12:01:06 -10:00
parent 1ede6d2e60
commit 52c297206a
4 changed files with 15 additions and 11 deletions
+9 -2
View File
@@ -112,8 +112,15 @@ APIError APIFrameHelper::drain_overflow_and_handle_errors_() {
return APIError::OK;
}
// Slow path: handles partial writes, errors, and overflow buffering.
// Called when the inline fast path in the header couldn't complete the write.
// Single-buffer write path: wraps in iovec and delegates.
APIError APIFrameHelper::write_raw_(const void *data, uint16_t len, ssize_t sent) {
struct iovec iov = {const_cast<void *>(data), len};
return this->write_raw_(&iov, 1, len, sent);
}
// Handles partial writes, errors, and overflow buffering.
// Called when the inline fast path in the header couldn't complete the write,
// or directly from cold paths (handshake, error handling).
// sent == -1 means either the fast path write returned -1, or there was overflow backlog.
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, uint16_t total_write_len, ssize_t sent) {
#ifdef HELPER_LOG_PACKETS
+3 -3
View File
@@ -202,8 +202,7 @@ class APIFrameHelper {
if (sent == static_cast<ssize_t>(len)) [[likely]]
return APIError::OK;
}
struct iovec iov = {const_cast<void *>(data), len};
return this->write_raw_(&iov, 1, len, sent);
return this->write_raw_(data, len, sent);
}
inline APIError ESPHOME_ALWAYS_INLINE write_raw_fast_(const struct iovec *iov, int iovcnt, uint16_t total_write_len) {
ssize_t sent = -1;
@@ -215,7 +214,8 @@ class APIFrameHelper {
return this->write_raw_(iov, iovcnt, total_write_len, sent);
}
// Slow path (out-of-line): handle partial writes, errors, overflow buffering
// Out-of-line write paths: handle partial writes, errors, overflow buffering
APIError write_raw_(const void *data, uint16_t len, ssize_t sent = -1);
APIError write_raw_(const struct iovec *iov, int iovcnt, uint16_t total_write_len, ssize_t sent = -1);
// Socket ownership (4 bytes on 32-bit, 8 bytes on 64-bit)
@@ -537,8 +537,7 @@ APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
header[2] = (uint8_t) len;
if (len == 0) {
struct iovec iov = {header, 3};
return this->write_raw_(&iov, 1, 3);
return this->write_raw_(header, 3);
}
struct iovec iov[2];
iov[0].iov_base = header;
@@ -219,13 +219,11 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
"Bad indicator byte";
char msg[INDICATOR_MSG_SIZE];
memcpy_P(msg, MSG_PROGMEM, INDICATOR_MSG_SIZE);
struct iovec iov = {msg, INDICATOR_MSG_SIZE};
this->write_raw_(&iov, 1, INDICATOR_MSG_SIZE);
this->write_raw_(msg, INDICATOR_MSG_SIZE);
#else
static const char MSG[] = "\x00"
"Bad indicator byte";
struct iovec iov = {const_cast<char *>(MSG), INDICATOR_MSG_SIZE};
this->write_raw_(&iov, 1, INDICATOR_MSG_SIZE);
this->write_raw_(MSG, INDICATOR_MSG_SIZE);
#endif
}
return aerr;