mirror of
https://github.com/esphome/esphome.git
synced 2025-09-02 03:12:20 +01: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