mirror of
https://github.com/esphome/esphome.git
synced 2026-08-31 01:56:01 +00:00
[api] Drop connection instead of crashing when buffer allocation fails (#18803)
This commit is contained in:
@@ -1,13 +1,20 @@
|
||||
#include "api_buffer.h"
|
||||
#include <new>
|
||||
|
||||
namespace esphome::api {
|
||||
|
||||
void APIBuffer::grow_(size_t n) {
|
||||
auto new_data = make_buffer(n);
|
||||
bool APIBuffer::grow_(size_t n) {
|
||||
// nothrow (no zero-fill) so OOM is reportable; plain new aborts instead
|
||||
// (NEW_OOM_ABORT on ESP8266 Arduino, exception stub on ESP-IDF).
|
||||
// RAMAllocator is no fit here: unique_ptr needs delete[]-compatible memory.
|
||||
std::unique_ptr<uint8_t[]> new_data(new (std::nothrow) uint8_t[n]);
|
||||
if (new_data == nullptr)
|
||||
return false;
|
||||
if (this->size_)
|
||||
std::memcpy(new_data.get(), this->data_.get(), this->size_);
|
||||
this->data_ = std::move(new_data);
|
||||
this->capacity_ = n;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace esphome::api
|
||||
|
||||
@@ -9,16 +9,6 @@
|
||||
|
||||
namespace esphome::api {
|
||||
|
||||
/// Helper to use make_unique_for_overwrite where available (skips zero-fill),
|
||||
/// falling back to make_unique on older GCC (ESP8266, LibreTiny).
|
||||
inline std::unique_ptr<uint8_t[]> make_buffer(size_t n) {
|
||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
||||
return std::make_unique<uint8_t[]>(n);
|
||||
#else
|
||||
return std::make_unique_for_overwrite<uint8_t[]>(n);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Byte buffer that skips zero-initialization on resize().
|
||||
///
|
||||
/// std::vector<uint8_t>::resize() zero-fills new bytes via memset. For the
|
||||
@@ -36,23 +26,23 @@ inline std::unique_ptr<uint8_t[]> make_buffer(size_t n) {
|
||||
class APIBuffer {
|
||||
public:
|
||||
void clear() { this->size_ = 0; }
|
||||
inline void reserve(size_t n) ESPHOME_ALWAYS_INLINE {
|
||||
if (n > this->capacity_)
|
||||
this->grow_(n);
|
||||
}
|
||||
inline void resize(size_t n) ESPHOME_ALWAYS_INLINE {
|
||||
this->reserve(n);
|
||||
this->size_ = n; // no zero-fill
|
||||
}
|
||||
/// Returns false if allocation fails; the buffer is left unchanged.
|
||||
[[nodiscard]] inline bool reserve(size_t n) ESPHOME_ALWAYS_INLINE { return n <= this->capacity_ || this->grow_(n); }
|
||||
/// Returns false if allocation fails; the buffer is left unchanged. No zero-fill.
|
||||
[[nodiscard]] inline bool resize(size_t n) ESPHOME_ALWAYS_INLINE { return this->reserve_and_resize(n, n); }
|
||||
/// Reserve capacity for max(reserve_size, new_size) bytes, then set size to new_size.
|
||||
/// Single grow_ check regardless of argument order.
|
||||
inline void reserve_and_resize(size_t reserve_size, size_t new_size) ESPHOME_ALWAYS_INLINE {
|
||||
this->reserve(std::max(reserve_size, new_size));
|
||||
/// Returns false if allocation fails; the buffer is left unchanged.
|
||||
[[nodiscard]] inline bool reserve_and_resize(size_t reserve_size, size_t new_size) ESPHOME_ALWAYS_INLINE {
|
||||
if (!this->reserve(std::max(reserve_size, new_size)))
|
||||
return false;
|
||||
this->size_ = new_size;
|
||||
return true;
|
||||
}
|
||||
uint8_t *data() { return this->data_.get(); }
|
||||
const uint8_t *data() const { return this->data_.get(); }
|
||||
size_t size() const { return this->size_; }
|
||||
size_t capacity() const { return this->capacity_; }
|
||||
bool empty() const { return this->size_ == 0; }
|
||||
uint8_t &operator[](size_t i) { return this->data_[i]; }
|
||||
const uint8_t &operator[](size_t i) const { return this->data_[i]; }
|
||||
@@ -64,7 +54,7 @@ class APIBuffer {
|
||||
}
|
||||
|
||||
protected:
|
||||
void grow_(size_t n);
|
||||
bool grow_(size_t n);
|
||||
std::unique_ptr<uint8_t[]> data_;
|
||||
size_t size_{0};
|
||||
size_t capacity_{0};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "api_connection.h"
|
||||
#ifdef USE_API
|
||||
#include "api_connection_buffer.h" // for encode_to_buffer / get_batch_delay_ms_ inlines
|
||||
#include "api_connection_buffer.h" // for the APIServer-dependent APIConnection inlines
|
||||
#ifdef USE_API_NOISE
|
||||
#include "api_frame_helper_noise.h"
|
||||
#endif
|
||||
@@ -2239,10 +2239,17 @@ bool APIConnection::send_message_(uint32_t payload_size, uint16_t message_type,
|
||||
this->log_send_message_(proto_msg->message_name(), proto_msg->dump_to(dump_buf));
|
||||
}
|
||||
#endif
|
||||
if (!this->prepare_first_message_buffer(payload_size)) [[unlikely]] {
|
||||
this->fatal_out_of_memory_();
|
||||
return false;
|
||||
}
|
||||
auto &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||
this->prepare_first_message_buffer(shared_buf, payload_size);
|
||||
size_t write_start = shared_buf.size();
|
||||
shared_buf.resize(write_start + payload_size);
|
||||
#ifdef ESPHOME_DEBUG_API
|
||||
assert(shared_buf.capacity() >= write_start + payload_size);
|
||||
#endif
|
||||
// Capacity reserved above, cannot fail
|
||||
(void) shared_buf.resize(write_start + payload_size);
|
||||
ProtoWriteBuffer buffer{&shared_buf, write_start};
|
||||
encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf));
|
||||
return this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type);
|
||||
@@ -2278,6 +2285,9 @@ void APIConnection::on_no_setup_connection() {
|
||||
this->on_fatal_error();
|
||||
this->log_client_(ESPHOME_LOG_LEVEL_DEBUG, LOG_STR("no connection setup"));
|
||||
}
|
||||
void APIConnection::fatal_out_of_memory_() {
|
||||
this->fatal_error_with_log_(LOG_STR("Out of memory"), APIError::OUT_OF_MEMORY);
|
||||
}
|
||||
void APIConnection::on_fatal_error() {
|
||||
// Don't close socket here - keep it open so getpeername() works for logging
|
||||
// Socket will be closed when client is removed from the list in APIServer::loop()
|
||||
@@ -2292,16 +2302,25 @@ bool APIConnection::schedule_message_front_(EntityBase *entity, uint16_t message
|
||||
bool APIConnection::send_message_smart_(EntityBase *entity, uint16_t message_type, uint8_t estimated_size,
|
||||
uint8_t aux_data_index) {
|
||||
if (this->should_send_immediately_(message_type) && this->helper_->can_write_without_blocking()) {
|
||||
auto &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||
this->prepare_first_message_buffer(shared_buf, estimated_size);
|
||||
// No local for the shared buffer here: keeping it live across
|
||||
// dispatch_message_ costs a register and spills message_type into the
|
||||
// batching path's dedup loop (measured on x86 GCC -Os)
|
||||
if (!this->prepare_first_message_buffer(estimated_size)) [[unlikely]] {
|
||||
this->fatal_out_of_memory_();
|
||||
return false;
|
||||
}
|
||||
DeferredBatch::BatchItem item{entity, message_type, estimated_size, aux_data_index};
|
||||
if (this->dispatch_message_(item, MAX_BATCH_PACKET_SIZE, true) &&
|
||||
this->send_buffer(ProtoWriteBuffer{&shared_buf}, message_type)) {
|
||||
this->send_buffer(ProtoWriteBuffer{&this->parent_->get_shared_buffer_ref()}, message_type)) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
this->log_batch_item_(item);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
// An OOM during the immediate attempt marks the connection for removal;
|
||||
// don't queue more work (schedule_message_'s push_back may allocate again)
|
||||
if (this->flags_.remove) [[unlikely]]
|
||||
return false;
|
||||
}
|
||||
return this->schedule_message_(entity, message_type, estimated_size, aux_data_index);
|
||||
}
|
||||
@@ -2351,7 +2370,11 @@ void APIConnection::process_batch_() {
|
||||
total_estimated_size = MAX_BATCH_PACKET_SIZE;
|
||||
}
|
||||
|
||||
this->prepare_first_message_buffer(shared_buf, header_padding, total_estimated_size);
|
||||
if (!this->prepare_first_message_buffer(header_padding, total_estimated_size)) [[unlikely]] {
|
||||
this->fatal_out_of_memory_();
|
||||
this->clear_batch_();
|
||||
return;
|
||||
}
|
||||
|
||||
// Fast path for single message - buffer already allocated above
|
||||
if (num_items == 1) {
|
||||
@@ -2366,8 +2389,10 @@ void APIConnection::process_batch_() {
|
||||
#endif
|
||||
this->clear_batch_();
|
||||
} else if (payload_size == 0) {
|
||||
// Message too large to fit in available space
|
||||
ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
|
||||
// payload_size == 0 with remove set means encoding hit OOM and the
|
||||
// connection is being dropped; warn only for a genuinely oversized message
|
||||
if (!this->flags_.remove)
|
||||
ESP_LOGW(TAG, "Message too large to send: type=%u", item.message_type);
|
||||
this->clear_batch_();
|
||||
}
|
||||
return;
|
||||
@@ -2430,8 +2455,10 @@ void APIConnection::process_batch_multi_(APIBuffer &shared_buf, size_t num_items
|
||||
|
||||
if (items_processed > 0) {
|
||||
// Add footer space for the last message (for Noise protocol MAC)
|
||||
if (footer_size > 0) {
|
||||
shared_buf.resize(shared_buf.size() + footer_size);
|
||||
if (footer_size > 0 && !shared_buf.resize(shared_buf.size() + footer_size)) [[unlikely]] {
|
||||
this->fatal_out_of_memory_();
|
||||
this->clear_batch_();
|
||||
return;
|
||||
}
|
||||
|
||||
// Send all collected messages
|
||||
|
||||
@@ -352,22 +352,13 @@ class APIConnection final : public APIServerConnectionBase {
|
||||
}
|
||||
}
|
||||
|
||||
void prepare_first_message_buffer(APIBuffer &shared_buf, size_t header_padding, size_t total_size) {
|
||||
shared_buf.clear();
|
||||
// Reserve space for header padding + message + footer
|
||||
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
|
||||
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
|
||||
// Reserve full size but only set initial size to header padding
|
||||
// so message encoding starts at the correct position
|
||||
shared_buf.reserve_and_resize(total_size, header_padding);
|
||||
}
|
||||
/// Clear the shared write buffer and reserve space for the first message.
|
||||
/// Returns false if the allocation fails (out of memory).
|
||||
/// Defined in api_connection_buffer.h (needs APIServer complete).
|
||||
[[nodiscard]] bool prepare_first_message_buffer(size_t header_padding, size_t total_size);
|
||||
|
||||
// Convenience overload - computes frame overhead internally
|
||||
void prepare_first_message_buffer(APIBuffer &shared_buf, size_t payload_size) {
|
||||
const uint8_t header_padding = this->helper_->frame_header_padding();
|
||||
const uint8_t footer_size = this->helper_->frame_footer_size();
|
||||
this->prepare_first_message_buffer(shared_buf, header_padding, payload_size + header_padding + footer_size);
|
||||
}
|
||||
[[nodiscard]] bool prepare_first_message_buffer(size_t payload_size);
|
||||
|
||||
bool try_to_clear_buffer(bool log_out_of_space) {
|
||||
if (this->flags_.remove)
|
||||
@@ -853,6 +844,9 @@ class APIConnection final : public APIServerConnectionBase {
|
||||
this->on_fatal_error();
|
||||
this->log_warning_(message, err);
|
||||
}
|
||||
// Shared cold path for buffer allocation failures — noinline keeps the
|
||||
// OOM handling out of the hot send paths
|
||||
void __attribute__((noinline)) fatal_out_of_memory_();
|
||||
};
|
||||
|
||||
} // namespace esphome::api
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_API
|
||||
|
||||
// Inline APIConnection methods that need APIServer complete. Include this
|
||||
// instead of api_connection.h when calling encode_to_buffer or get_batch_delay_ms_.
|
||||
// Inline APIConnection members that need APIServer complete. Include this
|
||||
// instead of api_connection.h when calling them.
|
||||
|
||||
#include "api_connection.h"
|
||||
#include "api_server.h"
|
||||
@@ -41,7 +41,10 @@ inline uint16_t ESPHOME_ALWAYS_INLINE APIConnection::encode_to_buffer(uint32_t c
|
||||
return 0;
|
||||
|
||||
auto &shared_buf = conn->parent_->get_shared_buffer_ref();
|
||||
shared_buf.resize(shared_buf.size() + to_add);
|
||||
if (!shared_buf.resize(shared_buf.size() + to_add)) [[unlikely]] {
|
||||
conn->fatal_out_of_memory_();
|
||||
return 0;
|
||||
}
|
||||
ProtoWriteBuffer buffer{&shared_buf, shared_buf.size() - calculated_size};
|
||||
encode_fn(msg, buffer PROTO_ENCODE_DEBUG_INIT(&shared_buf));
|
||||
|
||||
@@ -50,5 +53,22 @@ inline uint16_t ESPHOME_ALWAYS_INLINE APIConnection::encode_to_buffer(uint32_t c
|
||||
|
||||
inline uint32_t APIConnection::get_batch_delay_ms_() const { return this->parent_->get_batch_delay(); }
|
||||
|
||||
inline bool APIConnection::prepare_first_message_buffer(size_t header_padding, size_t total_size) {
|
||||
auto &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||
shared_buf.clear();
|
||||
// Reserve space for header padding + message + footer
|
||||
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
|
||||
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
|
||||
// Reserve full size but only set initial size to header padding
|
||||
// so message encoding starts at the correct position
|
||||
return shared_buf.reserve_and_resize(total_size, header_padding);
|
||||
}
|
||||
|
||||
inline bool APIConnection::prepare_first_message_buffer(size_t payload_size) {
|
||||
const uint8_t header_padding = this->helper_->frame_header_padding();
|
||||
const uint8_t footer_size = this->helper_->frame_footer_size();
|
||||
return this->prepare_first_message_buffer(header_padding, payload_size + header_padding + footer_size);
|
||||
}
|
||||
|
||||
} // namespace esphome::api
|
||||
#endif
|
||||
|
||||
@@ -68,7 +68,10 @@ APIError APINoiseFrameHelper::init() {
|
||||
|
||||
// init prologue
|
||||
size_t old_size = prologue_.size();
|
||||
prologue_.resize(old_size + PROLOGUE_INIT_LEN);
|
||||
if (!prologue_.resize(old_size + PROLOGUE_INIT_LEN)) [[unlikely]] {
|
||||
state_ = State::FAILED;
|
||||
return APIError::OUT_OF_MEMORY;
|
||||
}
|
||||
#ifdef USE_ESP8266
|
||||
memcpy_P(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
|
||||
#else
|
||||
@@ -202,7 +205,10 @@ APIError APINoiseFrameHelper::try_read_frame_() {
|
||||
// During handshake, rx_buf_.size() is used in prologue construction, so
|
||||
// the buffer must be exactly msg_size to avoid prologue mismatch.)
|
||||
uint16_t alloc_size = msg_size + (is_data ? RX_BUF_NULL_TERMINATOR : 0);
|
||||
this->rx_buf_.resize(alloc_size);
|
||||
if (!this->rx_buf_.resize(alloc_size)) [[unlikely]] {
|
||||
state_ = State::FAILED;
|
||||
return APIError::OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (rx_buf_len_ < msg_size) {
|
||||
// more data to read
|
||||
@@ -269,7 +275,10 @@ APIError APINoiseFrameHelper::state_action_client_hello_() {
|
||||
// Resize for: existing prologue + 2 size bytes + frame data
|
||||
size_t old_size = this->prologue_.size();
|
||||
size_t rx_size = this->rx_buf_.size();
|
||||
this->prologue_.resize(old_size + 2 + rx_size);
|
||||
if (!this->prologue_.resize(old_size + 2 + rx_size)) [[unlikely]] {
|
||||
state_ = State::FAILED;
|
||||
return APIError::OUT_OF_MEMORY;
|
||||
}
|
||||
this->prologue_[old_size] = (uint8_t) (rx_size >> 8);
|
||||
this->prologue_[old_size + 1] = (uint8_t) rx_size;
|
||||
if (rx_size > 0) {
|
||||
@@ -477,13 +486,15 @@ APIError APINoiseFrameHelper::write_protobuf_packet(uint16_t type, ProtoWriteBuf
|
||||
assert(this->state_ == State::DATA);
|
||||
#endif
|
||||
|
||||
APIBuffer *buf = buffer.get_buffer();
|
||||
// Resize buffer to include footer space for Noise MAC
|
||||
if (this->frame_footer_size_)
|
||||
buffer.get_buffer()->resize(buffer.get_buffer()->size() + this->frame_footer_size_);
|
||||
if (this->frame_footer_size_ && !buf->resize(buf->size() + this->frame_footer_size_)) [[unlikely]] {
|
||||
state_ = State::FAILED;
|
||||
return APIError::OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
uint16_t payload_size =
|
||||
static_cast<uint16_t>(buffer.get_buffer()->size() - HEADER_PADDING - this->frame_footer_size_);
|
||||
uint8_t *buf_start = buffer.get_buffer()->data();
|
||||
uint16_t payload_size = static_cast<uint16_t>(buf->size() - HEADER_PADDING - this->frame_footer_size_);
|
||||
uint8_t *buf_start = buf->data();
|
||||
uint16_t encrypted_len;
|
||||
APIError aerr = this->encrypt_noise_message_(buf_start, payload_size, type, encrypted_len);
|
||||
if (aerr != APIError::OK)
|
||||
|
||||
@@ -172,7 +172,10 @@ APIError APIPlaintextFrameHelper::try_read_frame_() {
|
||||
|
||||
// Reserve space for body (+ null terminator so protobuf StringRef fields
|
||||
// can be safely null-terminated in-place after decode)
|
||||
this->rx_buf_.resize(this->rx_header_parsed_len_ + RX_BUF_NULL_TERMINATOR);
|
||||
if (!this->rx_buf_.resize(this->rx_header_parsed_len_ + RX_BUF_NULL_TERMINATOR)) [[unlikely]] {
|
||||
state_ = State::FAILED;
|
||||
return APIError::OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (rx_buf_len_ < rx_header_parsed_len_) {
|
||||
// more data to read
|
||||
|
||||
@@ -49,7 +49,7 @@ static void Encode_ListEntitiesSensorResponse(benchmark::State &state) {
|
||||
auto msg = make_sensor_response();
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -69,7 +69,7 @@ static void CalcAndEncode_ListEntitiesSensorResponse(benchmark::State &state) {
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
@@ -117,7 +117,7 @@ static void Encode_ListEntitiesBinarySensorResponse(benchmark::State &state) {
|
||||
auto msg = make_binary_sensor_response();
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -137,7 +137,7 @@ static void CalcAndEncode_ListEntitiesBinarySensorResponse(benchmark::State &sta
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
@@ -202,7 +202,7 @@ static void Encode_ListEntitiesLightResponse(benchmark::State &state) {
|
||||
auto msg = make_light_response();
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -222,7 +222,7 @@ static void CalcAndEncode_ListEntitiesLightResponse(benchmark::State &state) {
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ static void Encode_LogResponse_Typical(benchmark::State &state) {
|
||||
msg.level = enums::LOG_LEVEL_DEBUG;
|
||||
msg.set_message(reinterpret_cast<const uint8_t *>(kTypicalLogLine), strlen(kTypicalLogLine));
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -42,7 +42,7 @@ static void Encode_LogResponse_Short(benchmark::State &state) {
|
||||
msg.level = enums::LOG_LEVEL_INFO;
|
||||
msg.set_message(reinterpret_cast<const uint8_t *>(kShortLogLine), strlen(kShortLogLine));
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -84,7 +84,7 @@ static void CalcAndEncode_LogResponse_Typical(benchmark::State &state) {
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ static void CalcAndEncode_LogResponse_Typical_Fresh(benchmark::State &state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
|
||||
@@ -33,7 +33,7 @@ static void PlaintextFrame_WriteSensorState(benchmark::State &state) {
|
||||
// Pre-init buffer to typical TCP MSS size to avoid benchmarking
|
||||
// heap allocation — in real use the buffer is reused across writes.
|
||||
APIBuffer buffer;
|
||||
buffer.reserve(1460);
|
||||
(void) buffer.reserve(1460);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -44,7 +44,7 @@ static void PlaintextFrame_WriteSensorState(benchmark::State &state) {
|
||||
msg.missing_state = false;
|
||||
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(padding + size);
|
||||
(void) buffer.resize(padding + size);
|
||||
ProtoWriteBuffer writer(&buffer, padding);
|
||||
msg.encode(writer);
|
||||
|
||||
@@ -70,7 +70,7 @@ static void PlaintextFrame_WriteBatch5(benchmark::State &state) {
|
||||
// Pre-init buffer to typical TCP MSS size to avoid benchmarking
|
||||
// heap allocation — in real use the buffer is reused across writes.
|
||||
APIBuffer buffer;
|
||||
buffer.reserve(1460);
|
||||
(void) buffer.reserve(1460);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -85,7 +85,7 @@ static void PlaintextFrame_WriteBatch5(benchmark::State &state) {
|
||||
msg.missing_state = false;
|
||||
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(offset + padding + size + footer);
|
||||
(void) buffer.resize(offset + padding + size + footer);
|
||||
ProtoWriteBuffer writer(&buffer, offset + padding);
|
||||
msg.encode(writer);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ static constexpr int kInnerIterations = 2000;
|
||||
template<typename T> static APIBuffer encode_message(const T &msg) {
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
return buffer;
|
||||
|
||||
@@ -19,7 +19,7 @@ static void Encode_SensorStateResponse(benchmark::State &state) {
|
||||
msg.state = 23.5f;
|
||||
msg.missing_state = false;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -60,7 +60,7 @@ static void CalcAndEncode_SensorStateResponse(benchmark::State &state) {
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ static void CalcAndEncode_SensorStateResponse_Fresh(benchmark::State &state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
@@ -103,7 +103,7 @@ static void Encode_BinarySensorStateResponse(benchmark::State &state) {
|
||||
msg.state = true;
|
||||
msg.missing_state = false;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -126,7 +126,7 @@ static void Encode_HelloResponse(benchmark::State &state) {
|
||||
msg.server_info = StringRef::from_lit("esphome v2026.3.0");
|
||||
msg.name = StringRef::from_lit("living-room-sensor");
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -158,7 +158,7 @@ static void Encode_LightStateResponse(benchmark::State &state) {
|
||||
msg.warm_white = 0.0f;
|
||||
msg.effect = StringRef::from_lit("rainbow");
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -243,7 +243,7 @@ static void Encode_DeviceInfoResponse(benchmark::State &state) {
|
||||
auto msg = make_device_info_response();
|
||||
APIBuffer buffer;
|
||||
uint32_t total_size = msg.calculate_size();
|
||||
buffer.resize(total_size);
|
||||
(void) buffer.resize(total_size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -264,7 +264,7 @@ static void CalcAndEncode_DeviceInfoResponse(benchmark::State &state) {
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
@@ -285,7 +285,7 @@ static void CalcAndEncode_DeviceInfoResponse_Fresh(benchmark::State &state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
@@ -335,7 +335,7 @@ static void Encode_BLERawAdvs12(benchmark::State &state) {
|
||||
auto msg = make_ble_raw_advs_12();
|
||||
APIBuffer buffer;
|
||||
uint32_t total_size = msg.calculate_size();
|
||||
buffer.resize(total_size);
|
||||
(void) buffer.resize(total_size);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -355,7 +355,7 @@ static void CalcAndEncode_BLERawAdvs12(benchmark::State &state) {
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
}
|
||||
@@ -372,7 +372,7 @@ static void CalcAndEncode_BLERawAdvs12_Fresh(benchmark::State &state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
APIBuffer buffer;
|
||||
uint32_t size = msg.calculate_size();
|
||||
buffer.resize(size);
|
||||
(void) buffer.resize(size);
|
||||
ProtoWriteBuffer writer(&buffer, 0);
|
||||
msg.encode(writer);
|
||||
benchmark::DoNotOptimize(buffer.data());
|
||||
|
||||
@@ -16,7 +16,7 @@ static constexpr int kInnerIterations = 2000;
|
||||
// Encodes `src` into `out`. Caller owns `out` and must keep it alive across
|
||||
// the decode loop (decoded messages may store pointers back into its bytes).
|
||||
template<typename T> static void encode_into(APIBuffer &out, const T &src) {
|
||||
out.resize(src.calculate_size());
|
||||
(void) out.resize(src.calculate_size());
|
||||
ProtoWriteBuffer writer(&out, 0);
|
||||
src.encode(writer);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ static void Encode_ZWaveProxyFrame(benchmark::State &state) {
|
||||
msg.data = kZWaveFrameData;
|
||||
msg.data_len = sizeof(kZWaveFrameData);
|
||||
APIBuffer buffer;
|
||||
buffer.resize(msg.calculate_size());
|
||||
(void) buffer.resize(msg.calculate_size());
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -111,7 +111,7 @@ static void Encode_SerialProxyDataReceived(benchmark::State &state) {
|
||||
msg.instance = 0;
|
||||
msg.set_data(kSerialPayload, kSerialPayloadSize);
|
||||
APIBuffer buffer;
|
||||
buffer.resize(msg.calculate_size());
|
||||
(void) buffer.resize(msg.calculate_size());
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -171,7 +171,7 @@ static void Encode_InfraredRFReceiveEvent(benchmark::State &state) {
|
||||
msg.key = 0xDEADBEEF;
|
||||
msg.timings = &get_ir_timings_100();
|
||||
APIBuffer buffer;
|
||||
buffer.resize(msg.calculate_size());
|
||||
(void) buffer.resize(msg.calculate_size());
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -254,7 +254,7 @@ static APIBuffer build_infrared_rf_transmit_wire() {
|
||||
put_varint(1);
|
||||
|
||||
APIBuffer buf;
|
||||
buf.resize(len);
|
||||
(void) buf.resize(len);
|
||||
std::memcpy(buf.data(), bytes, len);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ BENCHMARK(ProtoVarInt_Parse_FiveByte);
|
||||
|
||||
static void Encode_Varint_Small(benchmark::State &state) {
|
||||
APIBuffer buffer;
|
||||
buffer.resize(16);
|
||||
(void) buffer.resize(16);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -73,7 +73,7 @@ BENCHMARK(Encode_Varint_Small);
|
||||
|
||||
static void Encode_Varint_Large(benchmark::State &state) {
|
||||
APIBuffer buffer;
|
||||
buffer.resize(16);
|
||||
(void) buffer.resize(16);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
@@ -88,7 +88,7 @@ BENCHMARK(Encode_Varint_Large);
|
||||
|
||||
static void Encode_Varint_MaxUint32(benchmark::State &state) {
|
||||
APIBuffer buffer;
|
||||
buffer.resize(16);
|
||||
(void) buffer.resize(16);
|
||||
|
||||
for (auto _ : state) {
|
||||
for (int i = 0; i < kInnerIterations; i++) {
|
||||
|
||||
@@ -54,7 +54,7 @@ static void verify_mac(uint64_t mac, size_t expected_bytes) {
|
||||
size_t ref_len = reference_encode(mac, ref_buf);
|
||||
|
||||
APIBuffer api_buf;
|
||||
api_buf.resize(16);
|
||||
ASSERT_TRUE(api_buf.resize(16));
|
||||
uint8_t *pos = api_buf.data();
|
||||
#ifdef ESPHOME_DEBUG_API
|
||||
uint8_t *proto_debug_end_ = api_buf.data() + api_buf.size();
|
||||
|
||||
Reference in New Issue
Block a user