|
|
|
|
@@ -57,6 +57,16 @@ inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Encode a varint directly into a pre-allocated buffer.
|
|
|
|
|
/// Caller must ensure buffer has space (use ProtoSize::varint() to calculate).
|
|
|
|
|
inline void encode_varint_to_buffer(uint32_t val, uint8_t *buffer) {
|
|
|
|
|
while (val > 0x7F) {
|
|
|
|
|
*buffer++ = static_cast<uint8_t>(val | 0x80);
|
|
|
|
|
val >>= 7;
|
|
|
|
|
}
|
|
|
|
|
*buffer = static_cast<uint8_t>(val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* StringRef Ownership Model for API Protocol Messages
|
|
|
|
|
* ===================================================
|
|
|
|
|
@@ -93,17 +103,17 @@ class ProtoVarInt {
|
|
|
|
|
ProtoVarInt() : value_(0) {}
|
|
|
|
|
explicit ProtoVarInt(uint64_t value) : value_(value) {}
|
|
|
|
|
|
|
|
|
|
/// Parse a varint from buffer. consumed must be a valid pointer (not null).
|
|
|
|
|
static optional<ProtoVarInt> parse(const uint8_t *buffer, uint32_t len, uint32_t *consumed) {
|
|
|
|
|
if (len == 0) {
|
|
|
|
|
if (consumed != nullptr)
|
|
|
|
|
*consumed = 0;
|
|
|
|
|
#ifdef ESPHOME_DEBUG_API
|
|
|
|
|
assert(consumed != nullptr);
|
|
|
|
|
#endif
|
|
|
|
|
if (len == 0)
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Most common case: single-byte varint (values 0-127)
|
|
|
|
|
if ((buffer[0] & 0x80) == 0) {
|
|
|
|
|
if (consumed != nullptr)
|
|
|
|
|
*consumed = 1;
|
|
|
|
|
*consumed = 1;
|
|
|
|
|
return ProtoVarInt(buffer[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -122,14 +132,11 @@ class ProtoVarInt {
|
|
|
|
|
result |= uint64_t(val & 0x7F) << uint64_t(bitpos);
|
|
|
|
|
bitpos += 7;
|
|
|
|
|
if ((val & 0x80) == 0) {
|
|
|
|
|
if (consumed != nullptr)
|
|
|
|
|
*consumed = i + 1;
|
|
|
|
|
*consumed = i + 1;
|
|
|
|
|
return ProtoVarInt(result);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (consumed != nullptr)
|
|
|
|
|
*consumed = 0;
|
|
|
|
|
return {}; // Incomplete or invalid varint
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -153,50 +160,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) {
|
|
|
|
|
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_;
|
|
|
|
|
@@ -256,8 +219,20 @@ 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)); }
|
|
|
|
|
void encode_varint_raw(uint32_t value) {
|
|
|
|
|
while (value > 0x7F) {
|
|
|
|
|
this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
|
|
|
|
|
value >>= 7;
|
|
|
|
|
}
|
|
|
|
|
this->buffer_->push_back(static_cast<uint8_t>(value));
|
|
|
|
|
}
|
|
|
|
|
void encode_varint_raw_64(uint64_t value) {
|
|
|
|
|
while (value > 0x7F) {
|
|
|
|
|
this->buffer_->push_back(static_cast<uint8_t>(value | 0x80));
|
|
|
|
|
value >>= 7;
|
|
|
|
|
}
|
|
|
|
|
this->buffer_->push_back(static_cast<uint8_t>(value));
|
|
|
|
|
}
|
|
|
|
|
/**
|
|
|
|
|
* Encode a field key (tag/wire type combination).
|
|
|
|
|
*
|
|
|
|
|
@@ -307,13 +282,13 @@ class ProtoWriteBuffer {
|
|
|
|
|
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_raw_64(value);
|
|
|
|
|
}
|
|
|
|
|
void encode_bool(uint32_t field_id, bool value, bool force = false) {
|
|
|
|
|
if (!value && !force)
|
|
|
|
|
return;
|
|
|
|
|
this->encode_field_raw(field_id, 0); // type 0: Varint - bool
|
|
|
|
|
this->write(0x01);
|
|
|
|
|
this->buffer_->push_back(value ? 0x01 : 0x00);
|
|
|
|
|
}
|
|
|
|
|
void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
|
|
|
|
|
if (value == 0 && !force)
|
|
|
|
|
@@ -938,13 +913,15 @@ 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_to_buffer(msg_length_bytes, this->buffer_->data() + begin);
|
|
|
|
|
|
|
|
|
|
// Now encode the message content - it will append to the buffer
|
|
|
|
|
value.encode(*this);
|
|
|
|
|
|
|
|
|
|
#ifdef ESPHOME_DEBUG_API
|
|
|
|
|
// Verify that the encoded size matches what we calculated
|
|
|
|
|
assert(this->buffer_->size() == begin + varint_length_bytes + msg_length_bytes);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implementation of decode_to_message - must be after ProtoDecodableMessage is defined
|
|
|
|
|
|