[api] Skip state_action_() call in noise data path

The noise frame helper was calling state_action_() (447 bytes) on every
read_packet() and write_protobuf_messages() call. In the DATA state
(steady-state after handshake), this function falls through all handshake
checks and returns OK — pure overhead on every encrypted packet.

Add check_data_state_() inline helper that replaces the call with a
single byte-load + branch instruction in the hot path. The handshake
state machine continues to be driven by loop() as before.

Also applies consistent state checking to the plaintext frame helper.
This commit is contained in:
J. Nick Koston
2026-03-08 11:27:40 -10:00
parent ad5811280a
commit 956977bd4b
3 changed files with 22 additions and 21 deletions
+11
View File
@@ -232,6 +232,17 @@ class APIFrameHelper {
EXPLICIT_REJECT = 8, // Noise only
};
// Fast inline check for read_packet/write_protobuf_messages hot path.
// In DATA state, state_action_() is a no-op (falls through all checks to return OK).
// This avoids a 447-byte function call on every packet during normal operation.
inline APIError ESPHOME_ALWAYS_INLINE check_data_state_() const {
if (state_ == State::DATA)
return APIError::OK;
if (state_ == State::CLOSED || state_ == State::FAILED)
return APIError::BAD_STATE;
return APIError::WOULD_BLOCK;
}
// Containers (size varies, but typically 12+ bytes on 32-bit)
std::array<std::unique_ptr<SendBuffer>, API_MAX_SEND_QUEUE> tx_buf_;
std::vector<uint8_t> rx_buf_;
@@ -397,14 +397,9 @@ void APINoiseFrameHelper::send_explicit_handshake_reject_(const LogString *reaso
state_ = orig_state;
}
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
APIError aerr = this->state_action_();
if (aerr != APIError::OK) {
APIError aerr = this->check_data_state_();
if (aerr != APIError::OK)
return aerr;
}
if (this->state_ != State::DATA) {
return APIError::WOULD_BLOCK;
}
aerr = this->try_read_frame_();
if (aerr != APIError::OK)
@@ -461,14 +456,9 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuff
}
APIError APINoiseFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffer, std::span<const MessageInfo> messages) {
APIError aerr = state_action_();
if (aerr != APIError::OK) {
APIError aerr = this->check_data_state_();
if (aerr != APIError::OK)
return aerr;
}
if (state_ != State::DATA) {
return APIError::WOULD_BLOCK;
}
if (messages.empty()) {
return APIError::OK;
@@ -195,11 +195,11 @@ APIError APIPlaintextFrameHelper::try_read_frame_() {
}
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
if (this->state_ != State::DATA) {
return APIError::WOULD_BLOCK;
}
APIError aerr = this->check_data_state_();
if (aerr != APIError::OK)
return aerr;
APIError aerr = this->try_read_frame_();
aerr = this->try_read_frame_();
if (aerr != APIError::OK) {
if (aerr == APIError::BAD_INDICATOR) {
// Make sure to tell the remote that we don't
@@ -244,9 +244,9 @@ APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWrite
APIError APIPlaintextFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffer,
std::span<const MessageInfo> messages) {
if (state_ != State::DATA) {
return APIError::BAD_STATE;
}
APIError aerr = this->check_data_state_();
if (aerr != APIError::OK)
return aerr;
if (messages.empty()) {
return APIError::OK;