mirror of
https://github.com/esphome/esphome.git
synced 2026-07-10 08:55:36 +00:00
Split parse() into parse() and parse_non_empty()
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>
This commit is contained in:
@@ -129,7 +129,8 @@ APIError APIPlaintextFrameHelper::try_read_frame_() {
|
||||
// Skip indicator byte at position 0
|
||||
uint8_t varint_pos = 1;
|
||||
|
||||
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos);
|
||||
// rx_header_buf_pos_ >= 3 and varint_pos == 1, so len >= 2
|
||||
auto msg_size_varint = ProtoVarInt::parse_non_empty(&rx_header_buf_[varint_pos], rx_header_buf_pos_ - varint_pos);
|
||||
if (!msg_size_varint.has_value()) {
|
||||
// not enough data there yet
|
||||
continue;
|
||||
|
||||
@@ -63,8 +63,8 @@ uint32_t ProtoDecodableMessage::count_repeated_field(const uint8_t *buffer, size
|
||||
const uint8_t *end = buffer + length;
|
||||
|
||||
while (ptr < end) {
|
||||
// Parse field header (tag)
|
||||
auto res = ProtoVarInt::parse(ptr, end - ptr);
|
||||
// Parse field header (tag) - ptr < end guarantees len >= 1
|
||||
auto res = ProtoVarInt::parse_non_empty(ptr, end - ptr);
|
||||
if (!res.has_value()) {
|
||||
break; // Invalid data, stop counting
|
||||
}
|
||||
@@ -208,8 +208,8 @@ void ProtoDecodableMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
const uint8_t *end = buffer + length;
|
||||
|
||||
while (ptr < end) {
|
||||
// Parse field header
|
||||
auto res = ProtoVarInt::parse(ptr, end - ptr);
|
||||
// Parse field header - ptr < end guarantees len >= 1
|
||||
auto res = ProtoVarInt::parse_non_empty(ptr, end - ptr);
|
||||
if (!res.has_value()) {
|
||||
ESP_LOGV(TAG, "Invalid field start at offset %ld", (long) (ptr - buffer));
|
||||
return;
|
||||
|
||||
@@ -132,9 +132,9 @@ class ProtoVarInt {
|
||||
|
||||
/// Parse a varint from buffer. Caller must ensure len >= 1.
|
||||
/// Returns result with consumed=0 on failure (truncated multi-byte varint).
|
||||
static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse(const uint8_t *buffer, uint32_t len) {
|
||||
static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse_non_empty(const uint8_t *buffer, uint32_t len) {
|
||||
#ifdef ESPHOME_DEBUG_API
|
||||
assert(len > 0); // All callers guarantee len > 0
|
||||
assert(len > 0);
|
||||
#endif
|
||||
// Fast path: single-byte varints (0-127) are the most common case
|
||||
// (booleans, small enums, field tags, small message sizes/types).
|
||||
@@ -143,6 +143,14 @@ class ProtoVarInt {
|
||||
return parse_slow(buffer, len);
|
||||
}
|
||||
|
||||
/// Parse a varint from buffer (safe for empty buffers).
|
||||
/// Returns result with consumed=0 on failure (empty buffer or truncated varint).
|
||||
static inline ProtoVarIntResult ESPHOME_ALWAYS_INLINE parse(const uint8_t *buffer, uint32_t len) {
|
||||
if (len == 0)
|
||||
return {0, PROTO_VARINT_PARSE_FAILED};
|
||||
return parse_non_empty(buffer, len);
|
||||
}
|
||||
|
||||
protected:
|
||||
// Slow path for multi-byte varints (>= 128), outlined to keep fast path small
|
||||
static ProtoVarIntResult parse_slow(const uint8_t *buffer, uint32_t len) __attribute__((noinline));
|
||||
|
||||
Reference in New Issue
Block a user