mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	merge
This commit is contained in:
		| @@ -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"]; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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)); | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user