Compare commits

..
2 changed files with 27 additions and 35 deletions
+26 -34
View File
@@ -56,48 +56,24 @@ inline constexpr int64_t decode_zigzag64(uint64_t value) {
return (value & 1) ? static_cast<int64_t>(~(value >> 1)) : static_cast<int64_t>(value >> 1);
}
/// Count number of varints in a packed buffer
///
/// A varint always ends on the first byte whose continuation bit (0x80) is clear, so the number
/// of varints is the number of bytes without that bit, i.e. len minus the number of bytes with
/// it. A trailing truncated varint (continuation bit set through the end of the buffer) is not
/// counted, matching the wire format.
///
/// Continuation bits are counted a machine word at a time: for a word w, (w >> 7) & 0x01..01
/// leaves 1 in every byte lane whose high bit was set. The lanes are accumulated in parallel and
/// only folded together once per chunk of 255 words, the most a byte lane can hold without
/// overflowing. Word loads are aligned first: unaligned word access is unsupported (Xtensa) or
/// slow on the platforms ESPHome targets.
/// Count varints in a packed buffer: len minus bytes with the continuation bit, summed a word at a time.
/// Word is a template parameter so tests can cover the 32-bit path on a 64-bit host.
template<typename Word = size_t> inline uint16_t count_packed_varints(const uint8_t *data, size_t len) {
constexpr size_t word_size = sizeof(Word);
constexpr Word lane_ones = ~Word{0} / 0xFF; // 0x01..01
constexpr size_t max_chunk_words = 255; // a byte lane counts at most one per word
const uint8_t *end = data + len;
size_t continuations = 0;
// Leading bytes before the first word boundary
while (data != end && (reinterpret_cast<uintptr_t>(data) & (word_size - 1)) != 0) {
continuations += *data++ >> 7;
}
while (static_cast<size_t>(end - data) >= word_size) {
size_t words = static_cast<size_t>(end - data) / word_size;
if (words > max_chunk_words)
words = max_chunk_words;
const uint8_t *chunk_end = data + words * word_size;
Word lanes = 0;
do {
while (data != end) {
// Unaligned word loads fault on Xtensa
if ((reinterpret_cast<uintptr_t>(data) & (word_size - 1)) == 0 && static_cast<size_t>(end - data) >= word_size) {
Word word;
memcpy(&word, __builtin_assume_aligned(data, word_size), word_size);
lanes += (word >> 7) & lane_ones;
continuations += (((word >> 7) & lane_ones) * lane_ones) >> (word_size * 8 - 8);
data += word_size;
} while (data != chunk_end);
for (size_t shift = 0; shift < word_size * 8; shift += 8) {
continuations += static_cast<size_t>(lanes >> shift) & 0xFFu;
} else {
continuations += *data++ >> 7;
}
}
// Trailing bytes after the last full word
while (data != end) {
continuations += *data++ >> 7;
}
return static_cast<uint16_t>(len - continuations);
}
@@ -407,6 +383,22 @@ class ProtoEncode {
encode_field_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t type) {
return encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, proto_tag(field_id, type));
}
/// Write a field key through the local cursor. Sub-message fields commonly have a 2-byte tag.
[[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
write_tag(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t type) {
uint32_t tag = proto_tag(field_id, type);
#ifdef ESPHOME_DEBUG_API
return encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, tag);
#else
uint8_t *__restrict__ p = pos;
while (tag > 0x7F) {
*p++ = static_cast<uint8_t>(tag | 0x80);
tag >>= 7;
}
*p++ = static_cast<uint8_t>(tag);
return p;
#endif
}
/// Write a single precomputed tag byte. Tag must be < 128.
[[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE
write_raw_byte(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t b) {
@@ -592,7 +584,7 @@ class ProtoEncode {
template<typename T>
[[nodiscard]] static inline uint8_t *encode_sub_message(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM,
uint32_t field_id, const T &value) {
pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 2);
pos = write_tag(pos PROTO_ENCODE_DEBUG_ARG, field_id, WIRE_TYPE_LENGTH_DELIMITED);
return encode_sub_message_body(pos PROTO_ENCODE_DEBUG_ARG, &value, &T::encode_msg);
}
/// Singular sub-message field, skipped when it encodes to nothing.
@@ -602,7 +594,7 @@ class ProtoEncode {
uint32_t nested_size = T::calc_size_msg(&value);
if (nested_size == 0)
return pos;
pos = encode_field_raw(pos PROTO_ENCODE_DEBUG_ARG, field_id, 2);
pos = write_tag(pos PROTO_ENCODE_DEBUG_ARG, field_id, WIRE_TYPE_LENGTH_DELIMITED);
#ifdef ESPHOME_DEBUG_API
uint8_t *end = encode_sized_sub_message_body(pos PROTO_ENCODE_DEBUG_ARG, nested_size, &value, &T::encode_msg);
proto_check_sub_message_size(field_id, nested_size, pos, end);
+1 -1
View File
@@ -12,7 +12,7 @@ pyserial==3.5
platformio==6.2.0
esptool==5.4.0
click==8.5.0
aioesphomeapi==46.7.0
aioesphomeapi==46.8.0
aiohappyeyeballs==2.7.1 # Happy Eyeballs for requests downloads; already pulled in by aioesphomeapi
zeroconf==0.151.5
puremagic==2.2.0