1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 07:03:55 +00:00
This commit is contained in:
J. Nick Koston
2025-10-28 21:21:48 -05:00
parent a609343cb6
commit 36ffe21bfd
7 changed files with 109 additions and 30 deletions

View File

@@ -425,7 +425,7 @@ message ListEntitiesFanResponse {
bool disabled_by_default = 9;
string icon = 10 [(field_ifdef) = "USE_ENTITY_ICON"];
EntityCategory entity_category = 11;
repeated string supported_preset_modes = 12 [(container_pointer) = "std::set"];
repeated string supported_preset_modes = 12 [(container_pointer) = "std::vector"];
uint32 device_id = 13 [(field_ifdef) = "USE_DEVICES"];
}
// Deprecated in API version 1.6 - only used in deprecated fields
@@ -1143,7 +1143,7 @@ message ListEntitiesSelectResponse {
reserved 4; // Deprecated: was string unique_id
string icon = 5 [(field_ifdef) = "USE_ENTITY_ICON"];
repeated string options = 6 [(container_pointer) = "std::vector"];
repeated string options = 6 [(container_pointer_no_template) = "FixedVector<const char *>"];
bool disabled_by_default = 7;
EntityCategory entity_category = 8;
uint32 device_id = 9 [(field_ifdef) = "USE_DEVICES"];

View File

@@ -142,6 +142,11 @@ APIError APINoiseFrameHelper::loop() {
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
*/
APIError APINoiseFrameHelper::try_read_frame_() {
// Clear buffer when starting a new frame (rx_buf_len_ == 0 means not resuming after WOULD_BLOCK)
if (this->rx_buf_len_ == 0) {
this->rx_buf_.clear();
}
// read header
if (rx_header_buf_len_ < 3) {
// no header information yet

View File

@@ -54,6 +54,11 @@ APIError APIPlaintextFrameHelper::loop() {
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
*/
APIError APIPlaintextFrameHelper::try_read_frame_() {
// Clear buffer when starting a new frame (rx_buf_len_ == 0 means not resuming after WOULD_BLOCK)
if (this->rx_buf_len_ == 0) {
this->rx_buf_.clear();
}
// read header
while (!rx_header_parsed_) {
// Now that we know when the socket is ready, we can read up to 3 bytes

View File

@@ -1475,8 +1475,8 @@ void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
#ifdef USE_ENTITY_ICON
buffer.encode_string(5, this->icon_ref_);
#endif
for (const auto &it : *this->options) {
buffer.encode_string(6, it, true);
for (const char *it : *this->options) {
buffer.encode_string(6, it, strlen(it), true);
}
buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_uint32(8, static_cast<uint32_t>(this->entity_category));
@@ -1492,8 +1492,8 @@ void ListEntitiesSelectResponse::calculate_size(ProtoSize &size) const {
size.add_length(1, this->icon_ref_.size());
#endif
if (!this->options->empty()) {
for (const auto &it : *this->options) {
size.add_length_force(1, it.size());
for (const char *it : *this->options) {
size.add_length_force(1, strlen(it));
}
}
size.add_bool(1, this->disabled_by_default);

View File

@@ -725,7 +725,7 @@ class ListEntitiesFanResponse final : public InfoResponseProtoMessage {
bool supports_speed{false};
bool supports_direction{false};
int32_t supported_speed_count{0};
const std::set<std::string> *supported_preset_modes{};
const std::vector<std::string> *supported_preset_modes{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
@@ -1534,7 +1534,7 @@ class ListEntitiesSelectResponse final : public InfoResponseProtoMessage {
#ifdef HAS_PROTO_MESSAGE_DUMP
const char *message_name() const override { return "list_entities_select_response"; }
#endif
const std::vector<std::string> *options{};
const FixedVector<const char *> *options{};
void encode(ProtoWriteBuffer buffer) const override;
void calculate_size(ProtoSize &size) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP

View File

@@ -88,6 +88,12 @@ static void dump_field(std::string &out, const char *field_name, StringRef value
out.append("\n");
}
static void dump_field(std::string &out, const char *field_name, const char *value, int indent = 2) {
append_field_prefix(out, field_name, indent);
out.append("'").append(value).append("'");
out.append("\n");
}
template<typename T> static void dump_field(std::string &out, const char *field_name, T value, int indent = 2) {
append_field_prefix(out, field_name, indent);
out.append(proto_enum_to_string<T>(value));

View File

@@ -7,6 +7,7 @@
#include <cassert>
#include <cstring>
#include <type_traits>
#include <vector>
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
@@ -159,22 +160,6 @@ class ProtoVarInt {
}
}
}
void encode(std::vector<uint8_t> &out) {
uint64_t val = this->value_;
if (val <= 0x7F) {
out.push_back(val);
return;
}
while (val) {
uint8_t temp = val & 0x7F;
val >>= 7;
if (val) {
out.push_back(temp | 0x80);
} else {
out.push_back(temp);
}
}
}
protected:
uint64_t value_;
@@ -233,8 +218,86 @@ class ProtoWriteBuffer {
public:
ProtoWriteBuffer(std::vector<uint8_t> *buffer) : buffer_(buffer) {}
void write(uint8_t value) { this->buffer_->push_back(value); }
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
// Single implementation that all overloads delegate to
void encode_varint(uint64_t value) {
auto buffer = this->buffer_;
size_t start = buffer->size();
// Fast paths for common cases (1-3 bytes)
if (value < (1ULL << 7)) {
// 1 byte - very common for field IDs and small lengths
buffer->resize(start + 1);
buffer->data()[start] = static_cast<uint8_t>(value);
return;
}
uint8_t *p;
if (value < (1ULL << 14)) {
// 2 bytes - common for medium field IDs and lengths
buffer->resize(start + 2);
p = buffer->data() + start;
p[0] = (value & 0x7F) | 0x80;
p[1] = (value >> 7) & 0x7F;
return;
}
if (value < (1ULL << 21)) {
// 3 bytes - rare
buffer->resize(start + 3);
p = buffer->data() + start;
p[0] = (value & 0x7F) | 0x80;
p[1] = ((value >> 7) & 0x7F) | 0x80;
p[2] = (value >> 14) & 0x7F;
return;
}
// Rare case: 4-10 byte values - calculate size from bit position
// Value is guaranteed >= (1ULL << 21), so CLZ is safe (non-zero)
uint32_t size;
#if defined(__GNUC__) || defined(__clang__)
// Use compiler intrinsic for efficient bit position lookup
size = (64 - __builtin_clzll(value) + 6) / 7;
#else
// Fallback for compilers without __builtin_clzll
if (value < (1ULL << 28)) {
size = 4;
} else if (value < (1ULL << 35)) {
size = 5;
} else if (value < (1ULL << 42)) {
size = 6;
} else if (value < (1ULL << 49)) {
size = 7;
} else if (value < (1ULL << 56)) {
size = 8;
} else if (value < (1ULL << 63)) {
size = 9;
} else {
size = 10;
}
#endif
buffer->resize(start + size);
p = buffer->data() + start;
size_t bytes = 0;
while (value) {
uint8_t temp = value & 0x7F;
value >>= 7;
p[bytes++] = value ? temp | 0x80 : temp;
}
}
// Common case: uint32_t values (field IDs, lengths, most integers)
void encode_varint(uint32_t value) { this->encode_varint(static_cast<uint64_t>(value)); }
// size_t overload (only enabled if size_t is distinct from uint32_t and uint64_t)
template<typename T>
void encode_varint(T value) requires(std::is_same_v<T, size_t> && !std::is_same_v<size_t, uint32_t> &&
!std::is_same_v<size_t, uint64_t>) {
this->encode_varint(static_cast<uint64_t>(value));
}
// Rare case: ProtoVarInt wrapper
void encode_varint(ProtoVarInt value) { this->encode_varint(value.as_uint64()); }
/**
* Encode a field key (tag/wire type combination).
*
@@ -249,14 +312,14 @@ class ProtoWriteBuffer {
*/
void encode_field_raw(uint32_t field_id, uint32_t type) {
uint32_t val = (field_id << 3) | (type & WIRE_TYPE_MASK);
this->encode_varint_raw(val);
this->encode_varint(val);
}
void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
if (len == 0 && !force)
return;
this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
this->encode_varint_raw(len);
this->encode_varint(len);
// Using resize + memcpy instead of insert provides significant performance improvement:
// ~10-11x faster for 16-32 byte strings, ~3x faster for 64-byte strings
@@ -278,13 +341,13 @@ class ProtoWriteBuffer {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
this->encode_varint_raw(value);
this->encode_varint(value);
}
void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
if (value == 0 && !force)
return;
this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
this->encode_varint_raw(ProtoVarInt(value));
this->encode_varint(value);
}
void encode_bool(uint32_t field_id, bool value, bool force = false) {
if (!value && !force)