mirror of
https://github.com/esphome/esphome.git
synced 2026-10-27 00:12:11 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0523725530 |
No files matched your search
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -646,11 +646,40 @@ size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf
|
||||
}
|
||||
|
||||
size_t base64_decode(const uint8_t *encoded_data, size_t encoded_len, uint8_t *buf, size_t buf_len) {
|
||||
size_t in = 0;
|
||||
size_t out = 0;
|
||||
bool truncated = false;
|
||||
|
||||
// Fast path: a full group of 4 characters becomes 3 bytes with no bit bookkeeping
|
||||
// and a single bounds check. Bails out to the loop below on the last partial group,
|
||||
// on padding ('=') and on any other character outside the alphabet.
|
||||
while (in + 4 <= encoded_len && out + 3 <= buf_len) {
|
||||
uint8_t v0 = base64_char_value(encoded_data[in]);
|
||||
uint8_t v1 = base64_char_value(encoded_data[in + 1]);
|
||||
uint8_t v2 = base64_char_value(encoded_data[in + 2]);
|
||||
uint8_t v3 = base64_char_value(encoded_data[in + 3]);
|
||||
// Alphabet values are <= 63, so any bit above bit 5 means one of the four
|
||||
// characters was INVALID_BASE64_CHAR.
|
||||
if (((v0 | v1 | v2 | v3) & 0xC0) != 0)
|
||||
break;
|
||||
uint32_t group =
|
||||
(static_cast<uint32_t>(v0) << 18) | (static_cast<uint32_t>(v1) << 12) | (static_cast<uint32_t>(v2) << 6) | v3;
|
||||
buf[out] = static_cast<uint8_t>(group >> 16);
|
||||
buf[out + 1] = static_cast<uint8_t>(group >> 8);
|
||||
buf[out + 2] = static_cast<uint8_t>(group);
|
||||
in += 4;
|
||||
out += 3;
|
||||
}
|
||||
|
||||
// Bit accumulator: characters contribute 6 bits each and a byte is emitted as soon
|
||||
// as 8 bits are available. Padding ('=') and any other character outside the
|
||||
// alphabet stop the decode, leaving the leftover bits unused - this matches
|
||||
// zero-padding a trailing partial group, so a group of 2 chars yields 1 byte and a
|
||||
// group of 3 chars yields 2 bytes.
|
||||
uint32_t accum = 0;
|
||||
uint32_t bits = 0;
|
||||
// Stops at '=' or any non-alphabet char; leftover bits of a partial group are dropped.
|
||||
for (size_t in = 0; in < encoded_len; in++) {
|
||||
|
||||
for (; in < encoded_len; in++) {
|
||||
uint8_t value = base64_char_value(encoded_data[in]);
|
||||
if (value == INVALID_BASE64_CHAR)
|
||||
break;
|
||||
@@ -658,13 +687,17 @@ size_t base64_decode(const uint8_t *encoded_data, size_t encoded_len, uint8_t *b
|
||||
bits += 6;
|
||||
if (bits >= 8) {
|
||||
bits -= 8;
|
||||
if (out == buf_len) {
|
||||
ESP_LOGW(TAG, "Base64 decode: buffer too small, truncating");
|
||||
return out;
|
||||
if (out < buf_len) {
|
||||
buf[out++] = static_cast<uint8_t>(accum >> bits);
|
||||
} else {
|
||||
truncated = true;
|
||||
}
|
||||
buf[out++] = static_cast<uint8_t>(accum >> bits);
|
||||
}
|
||||
}
|
||||
|
||||
if (truncated) {
|
||||
ESP_LOGW(TAG, "Base64 decode: buffer too small, truncating");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user