mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	[api] Optimize varint encoding performance for Bluetooth proxy efficiency
This commit is contained in:
		| @@ -235,8 +235,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer | ||||
|  | ||||
|   for (const auto &packet : packets) { | ||||
|     // Calculate varint sizes for header layout | ||||
|     uint8_t size_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.payload_size)); | ||||
|     uint8_t type_varint_len = api::ProtoSize::varint(static_cast<uint32_t>(packet.message_type)); | ||||
|     uint8_t size_varint_len = api::ProtoSize::varint(packet.payload_size); | ||||
|     uint8_t type_varint_len = api::ProtoSize::varint(packet.message_type); | ||||
|     uint8_t total_header_len = 1 + size_varint_len + type_varint_len; | ||||
|  | ||||
|     // Calculate where to start writing the header | ||||
| @@ -271,9 +271,8 @@ APIError APIPlaintextFrameHelper::write_protobuf_packets(ProtoWriteBuffer buffer | ||||
|     buf_start[header_offset] = 0x00;  // indicator | ||||
|  | ||||
|     // Encode varints directly into buffer | ||||
|     ProtoVarInt(packet.payload_size).encode_to_buffer_unchecked(buf_start + header_offset + 1, size_varint_len); | ||||
|     ProtoVarInt(packet.message_type) | ||||
|         .encode_to_buffer_unchecked(buf_start + header_offset + 1 + size_varint_len, type_varint_len); | ||||
|     encode_varint_unchecked(buf_start + header_offset + 1, packet.payload_size); | ||||
|     encode_varint_unchecked(buf_start + header_offset + 1 + size_varint_len, packet.message_type); | ||||
|  | ||||
|     // Add iovec for this packet (header + payload) | ||||
|     size_t packet_len = static_cast<size_t>(total_header_len + packet.payload_size); | ||||
|   | ||||
| @@ -124,34 +124,6 @@ class ProtoVarInt { | ||||
|     // with ZigZag encoding | ||||
|     return decode_zigzag64(this->value_); | ||||
|   } | ||||
|   /** | ||||
|    * Encode the varint value to a pre-allocated buffer without bounds checking. | ||||
|    * | ||||
|    * @param buffer The pre-allocated buffer to write the encoded varint to | ||||
|    * @param len The size of the buffer in bytes | ||||
|    * | ||||
|    * @note The caller is responsible for ensuring the buffer is large enough | ||||
|    *       to hold the encoded value. Use ProtoSize::varint() to calculate | ||||
|    *       the exact size needed before calling this method. | ||||
|    * @note No bounds checking is performed for performance reasons. | ||||
|    */ | ||||
|   void encode_to_buffer_unchecked(uint8_t *buffer, size_t len) { | ||||
|     uint64_t val = this->value_; | ||||
|     if (val <= 0x7F) { | ||||
|       buffer[0] = val; | ||||
|       return; | ||||
|     } | ||||
|     size_t i = 0; | ||||
|     while (val && i < len) { | ||||
|       uint8_t temp = val & 0x7F; | ||||
|       val >>= 7; | ||||
|       if (val) { | ||||
|         buffer[i++] = temp | 0x80; | ||||
|       } else { | ||||
|         buffer[i++] = temp; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   void encode(std::vector<uint8_t> &out) { | ||||
|     uint64_t val = this->value_; | ||||
|     if (val <= 0x7F) { | ||||
| @@ -330,6 +302,28 @@ class ProtoWriteBuffer { | ||||
|   std::vector<uint8_t> *buffer_; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Encode a uint16_t value as a varint directly to a buffer without bounds checking | ||||
|  * | ||||
|  * @param buffer The pre-allocated buffer to write the encoded varint to | ||||
|  * @param value The uint16_t value to encode (0-65535) | ||||
|  * | ||||
|  * @note The caller is responsible for ensuring the buffer is large enough (max 3 bytes for uint16_t) | ||||
|  * @note No bounds checking is performed for performance reasons | ||||
|  */ | ||||
| inline void encode_varint_unchecked(uint8_t *buffer, uint16_t value) { | ||||
|   if (value < 128) { | ||||
|     buffer[0] = value; | ||||
|   } else if (value < 16384) { | ||||
|     buffer[0] = (value & 0x7F) | 0x80; | ||||
|     buffer[1] = value >> 7; | ||||
|   } else { | ||||
|     buffer[0] = (value & 0x7F) | 0x80; | ||||
|     buffer[1] = ((value >> 7) & 0x7F) | 0x80; | ||||
|     buffer[2] = value >> 14; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Forward declaration | ||||
| class ProtoSize; | ||||
|  | ||||
| @@ -386,6 +380,33 @@ class ProtoSize { | ||||
|  | ||||
|   uint32_t get_size() const { return total_size_; } | ||||
|  | ||||
|   /** | ||||
|    * @brief Calculates the size in bytes needed to encode a uint8_t value as a varint | ||||
|    * | ||||
|    * @param value The uint8_t value to calculate size for | ||||
|    * @return The number of bytes needed to encode the value (1 or 2) | ||||
|    */ | ||||
|   static constexpr uint8_t varint(uint8_t value) { | ||||
|     // For uint8_t (0-255), we need at most 2 bytes | ||||
|     return (value < 128) ? 1 : 2; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * @brief Calculates the size in bytes needed to encode a uint16_t value as a varint | ||||
|    * | ||||
|    * @param value The uint16_t value to calculate size for | ||||
|    * @return The number of bytes needed to encode the value (1-3) | ||||
|    */ | ||||
|   static constexpr uint8_t varint(uint16_t value) { | ||||
|     // For uint16_t (0-65535), we need at most 3 bytes | ||||
|     if (value < 128) | ||||
|       return 1;  // 7 bits | ||||
|     else if (value < 16384) | ||||
|       return 2;  // 14 bits | ||||
|     else | ||||
|       return 3;  // 15-16 bits | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * @brief Calculates the size in bytes needed to encode a uint32_t value as a varint | ||||
|    * | ||||
| @@ -395,11 +416,9 @@ class ProtoSize { | ||||
|   static constexpr uint32_t varint(uint32_t value) { | ||||
|     // Optimized varint size calculation using leading zeros | ||||
|     // Each 7 bits requires one byte in the varint encoding | ||||
|     if (value < 128) | ||||
|     if (value < 128) { | ||||
|       return 1;  // 7 bits, common case for small values | ||||
|  | ||||
|     // For larger values, count bytes needed based on the position of the highest bit set | ||||
|     if (value < 16384) { | ||||
|     } else if (value < 16384) { | ||||
|       return 2;  // 14 bits | ||||
|     } else if (value < 2097152) { | ||||
|       return 3;  // 21 bits | ||||
| @@ -773,7 +792,7 @@ inline void ProtoWriteBuffer::encode_message(uint32_t field_id, const ProtoMessa | ||||
|   this->buffer_->resize(this->buffer_->size() + varint_length_bytes); | ||||
|  | ||||
|   // Write the length varint directly | ||||
|   ProtoVarInt(msg_length_bytes).encode_to_buffer_unchecked(this->buffer_->data() + begin, varint_length_bytes); | ||||
|   encode_varint_unchecked(this->buffer_->data() + begin, static_cast<uint16_t>(msg_length_bytes)); | ||||
|  | ||||
|   // Now encode the message content - it will append to the buffer | ||||
|   value.encode(*this); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user