1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-31 15:12:06 +00:00

Merge branch 'integration' into memory_api

This commit is contained in:
J. Nick Koston
2025-10-15 15:51:19 -10:00
3 changed files with 23 additions and 15 deletions

View File

@@ -22,7 +22,7 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
} }
uint32_t tag = res->as_uint32(); uint32_t tag = res->as_uint32();
uint32_t field_type = tag & 0b111; uint32_t field_type = tag & WIRE_TYPE_MASK;
uint32_t field_id = tag >> 3; uint32_t field_id = tag >> 3;
ptr += consumed; ptr += consumed;
@@ -33,7 +33,7 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
// Skip field data based on wire type // Skip field data based on wire type
switch (field_type) { switch (field_type) {
case 0: { // VarInt - parse and skip case WIRE_TYPE_VARINT: { // VarInt - parse and skip
res = ProtoVarInt::parse(ptr, end - ptr, &consumed); res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
if (!res.has_value()) { if (!res.has_value()) {
return count; // Invalid data, return what we have return count; // Invalid data, return what we have
@@ -41,7 +41,7 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
ptr += consumed; ptr += consumed;
break; break;
} }
case 2: { // Length-delimited - parse length and skip data case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited - parse length and skip data
res = ProtoVarInt::parse(ptr, end - ptr, &consumed); res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
if (!res.has_value()) { if (!res.has_value()) {
return count; return count;
@@ -54,7 +54,7 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
ptr += field_length; ptr += field_length;
break; break;
} }
case 5: { // 32-bit - skip 4 bytes case WIRE_TYPE_FIXED32: { // 32-bit - skip 4 bytes
if (ptr + 4 > end) { if (ptr + 4 > end) {
return count; return count;
} }
@@ -85,12 +85,12 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
} }
uint32_t tag = res->as_uint32(); uint32_t tag = res->as_uint32();
uint32_t field_type = tag & 0b111; uint32_t field_type = tag & WIRE_TYPE_MASK;
uint32_t field_id = tag >> 3; uint32_t field_id = tag >> 3;
ptr += consumed; ptr += consumed;
switch (field_type) { switch (field_type) {
case 0: { // VarInt case WIRE_TYPE_VARINT: { // VarInt
res = ProtoVarInt::parse(ptr, end - ptr, &consumed); res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
if (!res.has_value()) { if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer)); ESP_LOGV(TAG, "Invalid VarInt at offset %ld", (long) (ptr - buffer));
@@ -102,7 +102,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
ptr += consumed; ptr += consumed;
break; break;
} }
case 2: { // Length-delimited case WIRE_TYPE_LENGTH_DELIMITED: { // Length-delimited
res = ProtoVarInt::parse(ptr, end - ptr, &consumed); res = ProtoVarInt::parse(ptr, end - ptr, &consumed);
if (!res.has_value()) { if (!res.has_value()) {
ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer)); ESP_LOGV(TAG, "Invalid Length Delimited at offset %ld", (long) (ptr - buffer));
@@ -120,7 +120,7 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
ptr += field_length; ptr += field_length;
break; break;
} }
case 5: { // 32-bit case WIRE_TYPE_FIXED32: { // 32-bit
if (ptr + 4 > end) { if (ptr + 4 > end) {
ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer)); ESP_LOGV(TAG, "Out-of-bounds Fixed32-bit at offset %ld", (long) (ptr - buffer));
return; return;

View File

@@ -15,6 +15,13 @@
namespace esphome::api { namespace esphome::api {
// Protocol Buffer wire type constants
// See https://protobuf.dev/programming-guides/encoding/#structure
constexpr uint8_t WIRE_TYPE_VARINT = 0; // int32, int64, uint32, uint64, sint32, sint64, bool, enum
constexpr uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2; // string, bytes, embedded messages, packed repeated fields
constexpr uint8_t WIRE_TYPE_FIXED32 = 5; // fixed32, sfixed32, float
constexpr uint8_t WIRE_TYPE_MASK = 0b111; // Mask to extract wire type from tag
// Helper functions for ZigZag encoding/decoding // Helper functions for ZigZag encoding/decoding
inline constexpr uint32_t encode_zigzag32(int32_t value) { inline constexpr uint32_t encode_zigzag32(int32_t value) {
return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31)); return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
@@ -241,7 +248,7 @@ class ProtoWriteBuffer {
* Following https://protobuf.dev/programming-guides/encoding/#structure * Following https://protobuf.dev/programming-guides/encoding/#structure
*/ */
void encode_field_raw(uint32_t field_id, uint32_t type) { void encode_field_raw(uint32_t field_id, uint32_t type) {
uint32_t val = (field_id << 3) | (type & 0b111); uint32_t val = (field_id << 3) | (type & WIRE_TYPE_MASK);
this->encode_varint_raw(val); this->encode_varint_raw(val);
} }
void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) { void encode_string(uint32_t field_id, const char *string, size_t len, bool force = false) {
@@ -493,7 +500,7 @@ class ProtoSize {
* @return The number of bytes needed to encode the field ID and wire type * @return The number of bytes needed to encode the field ID and wire type
*/ */
static constexpr uint32_t field(uint32_t field_id, uint32_t type) { static constexpr uint32_t field(uint32_t field_id, uint32_t type) {
uint32_t tag = (field_id << 3) | (type & 0b111); uint32_t tag = (field_id << 3) | (type & WIRE_TYPE_MASK);
return varint(tag); return varint(tag);
} }

View File

@@ -11,6 +11,7 @@ from typing import Any
import aioesphomeapi.api_options_pb2 as pb import aioesphomeapi.api_options_pb2 as pb
import google.protobuf.descriptor_pb2 as descriptor import google.protobuf.descriptor_pb2 as descriptor
from google.protobuf.descriptor_pb2 import FieldDescriptorProto
class WireType(IntEnum): class WireType(IntEnum):
@@ -148,7 +149,7 @@ class TypeInfo(ABC):
@property @property
def repeated(self) -> bool: def repeated(self) -> bool:
"""Check if the field is repeated.""" """Check if the field is repeated."""
return self._field.label == 3 return self._field.label == FieldDescriptorProto.LABEL_REPEATED
@property @property
def wire_type(self) -> WireType: def wire_type(self) -> WireType:
@@ -337,7 +338,7 @@ def create_field_type_info(
needs_encode: bool = True, needs_encode: bool = True,
) -> TypeInfo: ) -> TypeInfo:
"""Create the appropriate TypeInfo instance for a field, handling repeated fields and custom options.""" """Create the appropriate TypeInfo instance for a field, handling repeated fields and custom options."""
if field.label == 3: # repeated if field.label == FieldDescriptorProto.LABEL_REPEATED:
# Check if this repeated field has fixed_array_with_length_define option # Check if this repeated field has fixed_array_with_length_define option
if ( if (
fixed_size := get_field_opt(field, pb.fixed_array_with_length_define) fixed_size := get_field_opt(field, pb.fixed_array_with_length_define)
@@ -1890,7 +1891,7 @@ def build_message_type(
# Validate that fixed_array_size is only used in encode-only messages # Validate that fixed_array_size is only used in encode-only messages
if ( if (
needs_decode needs_decode
and field.label == 3 and field.label == FieldDescriptorProto.LABEL_REPEATED
and get_field_opt(field, pb.fixed_array_size) is not None and get_field_opt(field, pb.fixed_array_size) is not None
): ):
raise ValueError( raise ValueError(
@@ -1903,7 +1904,7 @@ def build_message_type(
# Validate that fixed_array_with_length_define is only used in encode-only messages # Validate that fixed_array_with_length_define is only used in encode-only messages
if ( if (
needs_decode needs_decode
and field.label == 3 and field.label == FieldDescriptorProto.LABEL_REPEATED
and get_field_opt(field, pb.fixed_array_with_length_define) is not None and get_field_opt(field, pb.fixed_array_with_length_define) is not None
): ):
raise ValueError( raise ValueError(
@@ -1916,7 +1917,7 @@ def build_message_type(
# Collect fixed_vector repeated fields for custom decode generation # Collect fixed_vector repeated fields for custom decode generation
if ( if (
needs_decode needs_decode
and field.label == 3 and field.label == FieldDescriptorProto.LABEL_REPEATED
and get_field_opt(field, pb.fixed_vector, False) and get_field_opt(field, pb.fixed_vector, False)
): ):
fixed_vector_fields.append((field.name, field.number)) fixed_vector_fields.append((field.name, field.number))