Consistency with frame helper code — makes the uint64→uint32 narrowing
explicit on BLE builds where proto_varint_value_t is uint64_t.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Cast msg_size/type_varint.value to uint32_t in HELPER_LOG to match PRIu32
format (proto_varint_value_t is uint64_t on BLE builds)
- Remove unused is_varint64 field from api_protobuf.py TypeInfo classes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address code review feedback:
- Remove as_uint16() and as_uint32() accessors from ProtoVarIntResult
- Use .value directly with static_cast where narrowing is needed
- Fix ESP_LOGV truncation: use PRIu64 with static_cast<uint64_t> for BLE builds
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After moving decode_varint to raw proto_varint_value_t, the type-
conversion accessors (as_bool, as_int32, as_sint32, as_uint64, etc.)
on ProtoVarIntResult are dead code. ProtoVarInt itself is now only
used as a static method container for parse(), so remove its
constructors, instance accessors, and value_ member.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace per-override #ifdef USE_API_VARINT64 conditionals with a
single type alias proto_varint_value_t, eliminating preprocessor
blocks from every decode_varint signature in api_pb2.cpp/h.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass uint32_t (or uint64_t when USE_API_VARINT64 is defined) directly
to decode_varint() instead of the ProtoVarIntResult struct. This
eliminates accessor method overhead in each of the 51 overrides,
replacing value.as_uint32() with direct value usage, value.as_bool()
with value != 0, etc. No vtable growth - single virtual method with
conditionally-typed parameter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
parse_non_empty() has no len==0 check (with debug assert) for callers
that guarantee len >= 1 (e.g. after while (ptr < end)).
parse() adds the len==0 guard and delegates to parse_non_empty() for
callers where the buffer may be empty (e.g. value parse after tag
advance in decode(), frame helper header parsing).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All callers guarantee len > 0 (decode loop checks ptr < end,
header parse checks minimum bytes). Replace runtime check with
ESPHOME_DEBUG_API assert. This removes the len check from the
inline entirely, saving one branch per call site.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the len==0 check from the inlined fast path into parse_slow(),
saving one branch + one return-value setup per inline site (~6-8
bytes per call site on Xtensa/xtensa-lx106).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rename parse_slow_ -> parse_slow and parse_wide_ -> parse_wide
(static methods should not have trailing underscore per clang-tidy)
- Add PROTO_VARINT_PARSE_FAILED constant for consumed field sentinel
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace optional<ProtoVarInt> + consumed pointer with ProtoVarIntResult
struct that returns value + consumed count directly. This eliminates
memory stores through a pointer on the fast path (single-byte varints
< 128), keeping everything in registers.
The parse() method is now ESPHOME_ALWAYS_INLINE with the multi-byte
slow path outlined to parse_slow_(). The common case for protobuf
field tags, small enums, booleans, and typical message sizes/types
is a simple high-bit check + register return with no function call.
Measured on ESP32 (Xtensa): try_read_frame_ grows only +12 bytes
(320 → 332) for two inlined parse sites, while eliminating two
function calls per message on the hot path.
ESP8266's older Arduino GCC doesn't optimize small memcpy into single
load/store instructions, making the memcpy path 16 bytes larger than
byte-at-a-time on that platform.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
libsodium's autoconf-generated config normally sets NATIVE_LITTLE_ENDIAN,
but PlatformIO skips autoconf. Without this define, libsodium falls back
to byte-at-a-time load/store operations in poly1305 and chacha20 instead
of optimized 32-bit memcpy. This saves ~340 bytes of flash and improves
encryption/decryption throughput on every API noise packet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
libsodium's autoconf-generated config normally sets NATIVE_LITTLE_ENDIAN,
but PlatformIO skips autoconf. Without this define, libsodium falls back
to byte-at-a-time load/store operations in poly1305 and chacha20 instead
of optimized 32-bit memcpy. This saves ~340 bytes of flash and improves
encryption/decryption throughput on every API noise packet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
libsodium's autoconf-generated config normally sets NATIVE_LITTLE_ENDIAN,
but PlatformIO skips autoconf. Without this define, libsodium falls back
to byte-at-a-time load/store operations in poly1305 and chacha20 instead
of optimized 32-bit memcpy. This saves ~340 bytes of flash and improves
encryption/decryption throughput on every API noise packet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the common-case checks (flags_.remove and can_write_without_blocking)
into an inline method in the header, leaving only the slow path (delay,
loop, retry) in the .cpp file. This avoids a function call on every
send_buffer() invocation when the TX buffer is already clear.