1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-14 17:22:20 +01:00

Merge branch 'zero_copy' into memory_api

This commit is contained in:
J. Nick Koston
2025-07-20 20:23:15 -10:00
8 changed files with 172 additions and 97 deletions

View File

@@ -225,24 +225,13 @@ void APIConnection::loop() {
if (this->image_reader_ && this->image_reader_->available() && this->helper_->can_write_without_blocking()) {
uint32_t to_send = std::min((size_t) MAX_BATCH_PACKET_SIZE, this->image_reader_->available());
bool done = this->image_reader_->available() == to_send;
uint32_t msg_size = 0;
ProtoSize::add_fixed_field<4>(msg_size, 1, true);
// partial message size calculated manually since its a special case
// 1 for the data field, varint for the data size, and the data itself
msg_size += 1 + ProtoSize::varint(to_send) + to_send;
ProtoSize::add_bool_field(msg_size, 1, done);
auto buffer = this->create_buffer(msg_size);
// fixed32 key = 1;
buffer.encode_fixed32(1, camera::Camera::instance()->get_object_id_hash());
// bytes data = 2;
buffer.encode_bytes(2, this->image_reader_->peek_data_buffer(), to_send);
// bool done = 3;
buffer.encode_bool(3, done);
CameraImageResponse msg;
msg.key = camera::Camera::instance()->get_object_id_hash();
msg.set_data(this->image_reader_->peek_data_buffer(), to_send);
msg.done = done;
bool success = this->send_buffer(buffer, CameraImageResponse::MESSAGE_TYPE);
if (success) {
if (this->send_message_(msg, CameraImageResponse::MESSAGE_TYPE)) {
this->image_reader_->consume_data(to_send);
if (done) {
this->image_reader_->return_image();
@@ -1350,26 +1339,12 @@ void APIConnection::update_command(const UpdateCommandRequest &msg) {
#endif
bool APIConnection::try_send_log_message(int level, const char *tag, const char *line, size_t message_len) {
// Pre-calculate message size to avoid reallocations
uint32_t msg_size = 0;
SubscribeLogsResponse msg;
msg.level = static_cast<enums::LogLevel>(level);
msg.set_message(reinterpret_cast<const uint8_t *>(line), message_len);
msg.send_failed = false;
// Add size for level field (field ID 1, varint type)
// 1 byte for field tag + size of the level varint
msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(level));
// Add size for string field (field ID 3, string type)
// 1 byte for field tag + size of length varint + string length
msg_size += 1 + api::ProtoSize::varint(static_cast<uint32_t>(message_len)) + message_len;
// Create a pre-sized buffer
auto buffer = this->create_buffer(msg_size);
// Encode the message (SubscribeLogsResponse)
buffer.encode_uint32(1, static_cast<uint32_t>(level)); // LogLevel level = 1
buffer.encode_string(3, line, message_len); // string message = 3
// SubscribeLogsResponse - 29
return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
return this->send_message_(msg, SubscribeLogsResponse::MESSAGE_TYPE);
}
void APIConnection::complete_authentication_() {

View File

@@ -822,12 +822,12 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
}
void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, static_cast<uint32_t>(this->level));
buffer.encode_bytes(3, reinterpret_cast<const uint8_t *>(this->message.data()), this->message.size());
buffer.encode_bytes(3, this->message_ptr_, this->message_len_);
buffer.encode_bool(4, this->send_failed);
}
void SubscribeLogsResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_enum_field(total_size, 1, static_cast<uint32_t>(this->level));
ProtoSize::add_string_field(total_size, 1, this->message);
ProtoSize::add_bytes_field(total_size, 1, this->message_len_);
ProtoSize::add_bool_field(total_size, 1, this->send_failed);
}
#ifdef USE_API_NOISE
@@ -1034,7 +1034,7 @@ void ListEntitiesCameraResponse::calculate_size(uint32_t &total_size) const {
}
void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_fixed32(1, this->key);
buffer.encode_bytes(2, reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size());
buffer.encode_bytes(2, this->data_ptr_, this->data_len_);
buffer.encode_bool(3, this->done);
#ifdef USE_DEVICES
buffer.encode_uint32(4, this->device_id);
@@ -1042,7 +1042,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
}
void CameraImageResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_fixed32_field(total_size, 1, this->key);
ProtoSize::add_string_field(total_size, 1, this->data);
ProtoSize::add_bytes_field(total_size, 1, this->data_len_);
ProtoSize::add_bool_field(total_size, 1, this->done);
#ifdef USE_DEVICES
ProtoSize::add_uint32_field(total_size, 1, this->device_id);
@@ -1976,12 +1976,12 @@ bool BluetoothGATTReadRequest::decode_varint(uint32_t field_id, ProtoVarInt valu
void BluetoothGATTReadResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_uint32(2, this->handle);
buffer.encode_bytes(3, reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size());
buffer.encode_bytes(3, this->data_ptr_, this->data_len_);
}
void BluetoothGATTReadResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_uint64_field(total_size, 1, this->address);
ProtoSize::add_uint32_field(total_size, 1, this->handle);
ProtoSize::add_string_field(total_size, 1, this->data);
ProtoSize::add_bytes_field(total_size, 1, this->data_len_);
}
bool BluetoothGATTWriteRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
switch (field_id) {
@@ -2064,12 +2064,12 @@ bool BluetoothGATTNotifyRequest::decode_varint(uint32_t field_id, ProtoVarInt va
void BluetoothGATTNotifyDataResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint64(1, this->address);
buffer.encode_uint32(2, this->handle);
buffer.encode_bytes(3, reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size());
buffer.encode_bytes(3, this->data_ptr_, this->data_len_);
}
void BluetoothGATTNotifyDataResponse::calculate_size(uint32_t &total_size) const {
ProtoSize::add_uint64_field(total_size, 1, this->address);
ProtoSize::add_uint32_field(total_size, 1, this->handle);
ProtoSize::add_string_field(total_size, 1, this->data);
ProtoSize::add_bytes_field(total_size, 1, this->data_len_);
}
void BluetoothConnectionsFreeResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_uint32(1, this->free);
@@ -2268,11 +2268,11 @@ bool VoiceAssistantAudio::decode_length(uint32_t field_id, ProtoLengthDelimited
return true;
}
void VoiceAssistantAudio::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bytes(1, reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size());
buffer.encode_bytes(1, this->data_ptr_, this->data_len_);
buffer.encode_bool(2, this->end);
}
void VoiceAssistantAudio::calculate_size(uint32_t &total_size) const {
ProtoSize::add_string_field(total_size, 1, this->data);
ProtoSize::add_bytes_field(total_size, 1, this->data_len_);
ProtoSize::add_bool_field(total_size, 1, this->end);
}
bool VoiceAssistantTimerEventResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {

View File

@@ -970,7 +970,12 @@ class SubscribeLogsResponse : public ProtoMessage {
const char *message_name() const override { return "subscribe_logs_response"; }
#endif
enums::LogLevel level{};
std::string message{};
const uint8_t *message_ptr_{nullptr};
size_t message_len_{0};
void set_message(const uint8_t *data, size_t len) {
this->message_ptr_ = data;
this->message_len_ = len;
}
bool send_failed{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@@ -1228,7 +1233,12 @@ class CameraImageResponse : public StateResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "camera_image_response"; }
#endif
std::string data{};
const uint8_t *data_ptr_{nullptr};
size_t data_len_{0};
void set_data(const uint8_t *data, size_t len) {
this->data_ptr_ = data;
this->data_len_ = len;
}
bool done{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
@@ -1882,7 +1892,12 @@ class BluetoothGATTReadResponse : public ProtoMessage {
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
const uint8_t *data_ptr_{nullptr};
size_t data_len_{0};
void set_data(const uint8_t *data, size_t len) {
this->data_ptr_ = data;
this->data_len_ = len;
}
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1970,7 +1985,12 @@ class BluetoothGATTNotifyDataResponse : public ProtoMessage {
#endif
uint64_t address{0};
uint32_t handle{0};
std::string data{};
const uint8_t *data_ptr_{nullptr};
size_t data_len_{0};
void set_data(const uint8_t *data, size_t len) {
this->data_ptr_ = data;
this->data_len_ = len;
}
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -2263,7 +2283,13 @@ class VoiceAssistantAudio : public ProtoDecodableMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "voice_assistant_audio"; }
#endif
const uint8_t *data_ptr_{nullptr};
size_t data_len_{0};
std::string data{};
void set_data(const uint8_t *data, size_t len) {
this->data_ptr_ = data;
this->data_len_ = len;
}
bool end{false};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(uint32_t &total_size) const override;

View File

@@ -1668,7 +1668,7 @@ void SubscribeLogsResponse::dump_to(std::string &out) const {
out.append("\n");
out.append(" message: ");
out.append(format_hex_pretty(this->message));
out.append(format_hex_pretty(this->message_ptr_, this->message_len_));
out.append("\n");
out.append(" send_failed: ");
@@ -1681,7 +1681,7 @@ void NoiseEncryptionSetKeyRequest::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("NoiseEncryptionSetKeyRequest {\n");
out.append(" key: ");
out.append(format_hex_pretty(this->key));
out.append(format_hex_pretty(reinterpret_cast<const uint8_t *>(this->key.data()), this->key.size()));
out.append("\n");
out.append("}");
}
@@ -1934,7 +1934,7 @@ void CameraImageResponse::dump_to(std::string &out) const {
out.append("\n");
out.append(" data: ");
out.append(format_hex_pretty(this->data));
out.append(format_hex_pretty(this->data_ptr_, this->data_len_));
out.append("\n");
out.append(" done: ");
@@ -3143,7 +3143,7 @@ void BluetoothGATTReadResponse::dump_to(std::string &out) const {
out.append("\n");
out.append(" data: ");
out.append(format_hex_pretty(this->data));
out.append(format_hex_pretty(this->data_ptr_, this->data_len_));
out.append("\n");
out.append("}");
}
@@ -3165,7 +3165,7 @@ void BluetoothGATTWriteRequest::dump_to(std::string &out) const {
out.append("\n");
out.append(" data: ");
out.append(format_hex_pretty(this->data));
out.append(format_hex_pretty(reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size()));
out.append("\n");
out.append("}");
}
@@ -3197,7 +3197,7 @@ void BluetoothGATTWriteDescriptorRequest::dump_to(std::string &out) const {
out.append("\n");
out.append(" data: ");
out.append(format_hex_pretty(this->data));
out.append(format_hex_pretty(reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size()));
out.append("\n");
out.append("}");
}
@@ -3233,7 +3233,7 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const {
out.append("\n");
out.append(" data: ");
out.append(format_hex_pretty(this->data));
out.append(format_hex_pretty(this->data_ptr_, this->data_len_));
out.append("\n");
out.append("}");
}
@@ -3487,7 +3487,11 @@ void VoiceAssistantAudio::dump_to(std::string &out) const {
__attribute__((unused)) char buffer[64];
out.append("VoiceAssistantAudio {\n");
out.append(" data: ");
out.append(format_hex_pretty(this->data));
if (this->data_ptr_ != nullptr) {
out.append(format_hex_pretty(this->data_ptr_, this->data_len_));
} else {
out.append(format_hex_pretty(reinterpret_cast<const uint8_t *>(this->data.data()), this->data.size()));
}
out.append("\n");
out.append(" end: ");

View File

@@ -527,25 +527,6 @@ class ProtoSize {
total_size += field_id_size + 1;
}
/**
* @brief Calculates and adds the size of a fixed field to the total message size
*
* Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double).
*
* @tparam NumBytes The number of bytes for this fixed field (4 or 8)
* @param is_nonzero Whether the value is non-zero
*/
template<uint32_t NumBytes>
static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero) {
// Skip calculation if value is zero
if (!is_nonzero) {
return; // No need to update total_size
}
// Fixed fields always take exactly NumBytes
total_size += field_id_size + NumBytes;
}
/**
* @brief Calculates and adds the size of a float field to the total message size
*/
@@ -704,6 +685,19 @@ class ProtoSize {
total_size += field_id_size + varint(str_size) + str_size;
}
/**
* @brief Calculates and adds the size of a bytes field to the total message size
*/
static inline void add_bytes_field(uint32_t &total_size, uint32_t field_id_size, size_t len) {
// Skip calculation if bytes is empty
if (len == 0) {
return; // No need to update total_size
}
// Field ID + length varint + data bytes
total_size += field_id_size + varint(static_cast<uint32_t>(len)) + static_cast<uint32_t>(len);
}
/**
* @brief Calculates and adds the size of a nested message field to the total message size
*

View File

@@ -235,9 +235,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTReadResponse resp;
resp.address = this->address_;
resp.handle = param->read.handle;
resp.data.reserve(param->read.value_len);
// Use bulk insert instead of individual push_backs
resp.data.insert(resp.data.end(), param->read.value, param->read.value + param->read.value_len);
resp.set_data(param->read.value, param->read.value_len);
this->proxy_->get_api_connection()->send_message(resp, api::BluetoothGATTReadResponse::MESSAGE_TYPE);
break;
}
@@ -288,9 +286,7 @@ bool BluetoothConnection::gattc_event_handler(esp_gattc_cb_event_t event, esp_ga
api::BluetoothGATTNotifyDataResponse resp;
resp.address = this->address_;
resp.handle = param->notify.handle;
resp.data.reserve(param->notify.value_len);
// Use bulk insert instead of individual push_backs
resp.data.insert(resp.data.end(), param->notify.value, param->notify.value + param->notify.value_len);
resp.set_data(param->notify.value, param->notify.value_len);
this->proxy_->get_api_connection()->send_message(resp, api::BluetoothGATTNotifyDataResponse::MESSAGE_TYPE);
break;
}

View File

@@ -273,7 +273,7 @@ void VoiceAssistant::loop() {
size_t read_bytes = this->ring_buffer_->read((void *) this->send_buffer_, SEND_BUFFER_SIZE, 0);
if (this->audio_mode_ == AUDIO_MODE_API) {
api::VoiceAssistantAudio msg;
msg.data.assign((char *) this->send_buffer_, read_bytes);
msg.set_data(this->send_buffer_, read_bytes);
this->api_client_->send_message(msg, api::VoiceAssistantAudio::MESSAGE_TYPE);
} else {
if (!this->udp_socket_running_) {