ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
helpers.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cassert>
6#include <cmath>
7#include <cstdarg>
8#include <cstdint>
9#include <cstdio>
10#include <cstring>
11#include <functional>
12#include <iterator>
13#include <limits>
14#include <memory>
15#include <span>
16#include <string>
17#include <type_traits>
18#include <vector>
19#include <concepts>
20#include <strings.h>
21
23
24#ifdef USE_ESP8266
25#include <Esp.h>
26#include <pgmspace.h>
27#endif
28
29#ifdef USE_RP2040
30#include <Arduino.h>
31#endif
32
33#ifdef USE_ESP32
34#include <esp_heap_caps.h>
35#endif
36
37#if defined(USE_ESP32)
38#include <freertos/FreeRTOS.h>
39#include <freertos/semphr.h>
40#elif defined(USE_LIBRETINY)
41#include <FreeRTOS.h>
42#include <semphr.h>
43#endif
44
45#ifdef USE_HOST
46#include <mutex>
47#endif
48
49#define HOT __attribute__((hot))
50#define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg)))
51#define ESPHOME_ALWAYS_INLINE __attribute__((always_inline))
52#define PACKED __attribute__((packed))
53
54namespace esphome {
55
56// Forward declaration to avoid circular dependency with string_ref.h
57class StringRef;
58
61
62// Keep "using" even after the removal of our backports, to avoid breaking existing code.
63using std::to_string;
64using std::is_trivially_copyable;
65using std::make_unique;
66using std::enable_if_t;
67using std::clamp;
68using std::is_invocable;
69#if __cpp_lib_bit_cast >= 201806
70using std::bit_cast;
71#else
73template<
74 typename To, typename From,
75 enable_if_t<sizeof(To) == sizeof(From) && is_trivially_copyable<From>::value && is_trivially_copyable<To>::value,
76 int> = 0>
77To bit_cast(const From &src) {
78 To dst;
79 memcpy(&dst, &src, sizeof(To));
80 return dst;
81}
82#endif
83
84// clang-format off
85inline float lerp(float completion, float start, float end) = delete; // Please use std::lerp. Notice that it has different order on arguments!
86// clang-format on
87
88// std::byteswap from C++23
89template<typename T> constexpr T byteswap(T n) {
90 T m;
91 for (size_t i = 0; i < sizeof(T); i++)
92 reinterpret_cast<uint8_t *>(&m)[i] = reinterpret_cast<uint8_t *>(&n)[sizeof(T) - 1 - i];
93 return m;
94}
95template<> constexpr uint8_t byteswap(uint8_t n) { return n; }
96#ifdef USE_LIBRETINY
97// LibreTiny's Beken framework redefines __builtin_bswap functions as non-constexpr
98template<> inline uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
99template<> inline uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
100template<> inline uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
101template<> inline int8_t byteswap(int8_t n) { return n; }
102template<> inline int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
103template<> inline int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
104template<> inline int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
105#else
106template<> constexpr uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
107template<> constexpr uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
108template<> constexpr uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
109template<> constexpr int8_t byteswap(int8_t n) { return n; }
110template<> constexpr int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
111template<> constexpr int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
112template<> constexpr int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
113#endif
114
116
119
123
124template<typename T> class ConstVector {
125 public:
126 constexpr ConstVector(const T *data, size_t size) : data_(data), size_(size) {}
127
128 const constexpr T &operator[](size_t i) const { return data_[i]; }
129 constexpr size_t size() const { return size_; }
130 constexpr bool empty() const { return size_ == 0; }
131
132 protected:
133 const T *data_;
134 size_t size_;
135};
136
140template<size_t InlineSize = 8> class SmallInlineBuffer {
141 public:
142 SmallInlineBuffer() = default;
144 if (!this->is_inline_())
145 delete[] this->heap_;
146 }
147
148 // Move constructor
149 SmallInlineBuffer(SmallInlineBuffer &&other) noexcept : len_(other.len_) {
150 if (other.is_inline_()) {
151 memcpy(this->inline_, other.inline_, this->len_);
152 } else {
153 this->heap_ = other.heap_;
154 other.heap_ = nullptr;
155 }
156 other.len_ = 0;
157 }
158
159 // Move assignment
161 if (this != &other) {
162 if (!this->is_inline_())
163 delete[] this->heap_;
164 this->len_ = other.len_;
165 if (other.is_inline_()) {
166 memcpy(this->inline_, other.inline_, this->len_);
167 } else {
168 this->heap_ = other.heap_;
169 other.heap_ = nullptr;
170 }
171 other.len_ = 0;
172 }
173 return *this;
174 }
175
176 // Disable copy (would need deep copy of heap data)
179
181 void set(const uint8_t *src, size_t size) {
182 // Free existing heap allocation if switching from heap to inline or different heap size
184 delete[] this->heap_;
185 this->heap_ = nullptr; // Defensive: prevent use-after-free if logic changes
186 }
187 // Allocate new heap buffer if needed
188 if (size > InlineSize && (this->is_inline_() || size != this->len_)) {
189 this->heap_ = new uint8_t[size]; // NOLINT(cppcoreguidelines-owning-memory)
190 }
191 this->len_ = size;
192 memcpy(this->data(), src, size);
193 }
194
195 uint8_t *data() { return this->is_inline_() ? this->inline_ : this->heap_; }
196 const uint8_t *data() const { return this->is_inline_() ? this->inline_ : this->heap_; }
197 size_t size() const { return this->len_; }
198
199 protected:
200 bool is_inline_() const { return this->len_ <= InlineSize; }
201
202 size_t len_{0};
203 union {
204 uint8_t inline_[InlineSize]{}; // Zero-init ensures clean initial state
205 uint8_t *heap_;
206 };
207};
208
210template<typename T, size_t N> class StaticVector {
211 public:
212 using value_type = T;
213 using iterator = typename std::array<T, N>::iterator;
214 using const_iterator = typename std::array<T, N>::const_iterator;
215 using reverse_iterator = std::reverse_iterator<iterator>;
216 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
217
218 private:
219 std::array<T, N> data_; // intentionally not value-initialized to avoid memset
220 size_t count_{0};
221
222 public:
223 // Default constructor
224 StaticVector() = default;
225
226 // Iterator range constructor
227 template<typename InputIt> StaticVector(InputIt first, InputIt last) {
228 while (first != last && count_ < N) {
229 data_[count_++] = *first++;
230 }
231 }
232
233 // Initializer list constructor
234 StaticVector(std::initializer_list<T> init) {
235 for (const auto &val : init) {
236 if (count_ >= N)
237 break;
238 data_[count_++] = val;
239 }
240 }
241
242 // Minimal vector-compatible interface - only what we actually use
243 void push_back(const T &value) {
244 if (count_ < N) {
245 data_[count_++] = value;
246 }
247 }
248
249 // Clear all elements
250 void clear() { count_ = 0; }
251
252 // Assign from iterator range
253 template<typename InputIt> void assign(InputIt first, InputIt last) {
254 count_ = 0;
255 while (first != last && count_ < N) {
256 data_[count_++] = *first++;
257 }
258 }
259
260 // Return reference to next element and increment count (with bounds checking)
262 if (count_ >= N) {
263 // Should never happen with proper size calculation
264 // Return reference to last element to avoid crash
265 return data_[N - 1];
266 }
267 return data_[count_++];
268 }
269
270 size_t size() const { return count_; }
271 bool empty() const { return count_ == 0; }
272
273 // Direct access to underlying data
274 T *data() { return data_.data(); }
275 const T *data() const { return data_.data(); }
276
277 T &operator[](size_t i) { return data_[i]; }
278 const T &operator[](size_t i) const { return data_[i]; }
279
280 // For range-based for loops
281 iterator begin() { return data_.begin(); }
282 iterator end() { return data_.begin() + count_; }
283 const_iterator begin() const { return data_.begin(); }
284 const_iterator end() const { return data_.begin() + count_; }
285
286 // Reverse iterators
291
292 // Conversion to std::span for compatibility with span-based APIs
293 operator std::span<T>() { return std::span<T>(data_.data(), count_); }
294 operator std::span<const T>() const { return std::span<const T>(data_.data(), count_); }
295};
296
304template<typename T, size_t N> class StaticRingBuffer {
305 using index_type = std::conditional_t<(N <= std::numeric_limits<uint8_t>::max()), uint8_t, uint16_t>;
306
307 public:
308 class Iterator {
309 public:
310 Iterator(StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
311 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % N]; }
313 ++pos_;
314 return *this;
315 }
316 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
317
318 private:
319 StaticRingBuffer *buf_;
320 index_type pos_;
321 };
322
324 public:
325 ConstIterator(const StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
326 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % N]; }
328 ++pos_;
329 return *this;
330 }
331 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
332
333 private:
334 const StaticRingBuffer *buf_;
335 index_type pos_;
336 };
337
338 bool push(const T &value) {
339 if (this->count_ >= N) {
340 return false;
341 }
342 this->data_[this->tail_] = value;
343 this->tail_ = (this->tail_ + 1) % N;
344 ++this->count_;
345 return true;
346 }
347
348 void pop() {
349 if (this->count_ > 0) {
350 this->head_ = (this->head_ + 1) % N;
351 --this->count_;
352 }
353 }
354
355 T &front() { return this->data_[this->head_]; }
356 const T &front() const { return this->data_[this->head_]; }
357 index_type size() const { return this->count_; }
358 bool empty() const { return this->count_ == 0; }
359
361 void clear() {
362 this->head_ = 0;
363 this->tail_ = 0;
364 this->count_ = 0;
365 }
366
367 Iterator begin() { return Iterator(this, 0); }
368 Iterator end() { return Iterator(this, this->count_); }
369 ConstIterator begin() const { return ConstIterator(this, 0); }
370 ConstIterator end() const { return ConstIterator(this, this->count_); }
371
372 protected:
373 T data_[N];
374 index_type head_{0};
375 index_type tail_{0};
376 index_type count_{0};
377};
378
383template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()> class FixedRingBuffer {
384 using index_type = std::conditional_t<
385 (MAX_CAPACITY <= std::numeric_limits<uint8_t>::max()), uint8_t,
386 std::conditional_t<(MAX_CAPACITY <= std::numeric_limits<uint16_t>::max()), uint16_t, uint32_t>>;
387
388 public:
389 class Iterator {
390 public:
391 Iterator(FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
392 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
394 ++pos_;
395 return *this;
396 }
397 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
398
399 private:
400 FixedRingBuffer *buf_;
401 index_type pos_;
402 };
403
405 public:
406 ConstIterator(const FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
407 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
409 ++pos_;
410 return *this;
411 }
412 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
413
414 private:
415 const FixedRingBuffer *buf_;
416 index_type pos_;
417 };
418
419 FixedRingBuffer() = default;
421 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
422 ::operator delete(this->data_);
423 } else {
424 delete[] this->data_;
425 }
426 }
427
428 // Disable copy
431
433 void init(index_type capacity) {
434 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
435 // Raw allocation without initialization (elements are written before read)
436 // NOLINTNEXTLINE(bugprone-sizeof-expression)
437 this->data_ = static_cast<T *>(::operator new(capacity * sizeof(T)));
438 } else {
439 this->data_ = new T[capacity];
440 }
441 this->capacity_ = capacity;
442 }
443
445 bool push(const T &value) {
446 if (this->count_ >= this->capacity_)
447 return false;
448 this->data_[this->tail_] = value;
449 this->tail_ = (this->tail_ + 1) % this->capacity_;
450 ++this->count_;
451 return true;
452 }
453
455 void push_overwrite(const T &value) {
456 this->data_[this->tail_] = value;
457 this->tail_ = (this->tail_ + 1) % this->capacity_;
458 if (this->count_ >= this->capacity_) {
459 // Buffer full - advance head to drop oldest, count stays at capacity
460 this->head_ = this->tail_;
461 } else {
462 ++this->count_;
463 }
464 }
465
467 void pop() {
468 if (this->count_ > 0) {
469 this->head_ = (this->head_ + 1) % this->capacity_;
470 --this->count_;
471 }
472 }
473
474 T &front() { return this->data_[this->head_]; }
475 const T &front() const { return this->data_[this->head_]; }
476 index_type size() const { return this->count_; }
477 bool empty() const { return this->count_ == 0; }
478 index_type capacity() const { return this->capacity_; }
479 bool full() const { return this->count_ == this->capacity_; }
480
482 void clear() {
483 this->head_ = 0;
484 this->tail_ = 0;
485 this->count_ = 0;
486 }
487
488 Iterator begin() { return Iterator(this, 0); }
489 Iterator end() { return Iterator(this, this->count_); }
490 ConstIterator begin() const { return ConstIterator(this, 0); }
491 ConstIterator end() const { return ConstIterator(this, this->count_); }
492
493 protected:
494 T *data_{nullptr};
495 index_type head_{0};
496 index_type tail_{0};
497 index_type count_{0};
498 index_type capacity_{0};
499};
500
505template<typename T, size_t N> inline void init_array_from(std::array<T, N> &dest, std::initializer_list<T> src) {
506#ifdef ESPHOME_DEBUG
507 assert(src.size() == N);
508#endif
509 if constexpr (std::is_trivially_copyable_v<T>) {
510 __builtin_memcpy(dest.data(), src.begin(), N * sizeof(T));
511 } else {
512 size_t i = 0;
513 for (const auto &v : src) {
514 dest[i++] = v;
515 }
516 }
517}
518
522template<typename T> class FixedVector {
523 private:
524 T *data_{nullptr};
525 size_t size_{0};
526 size_t capacity_{0};
527
528 // Helper to destroy all elements without freeing memory
529 void destroy_elements_() {
530 // Only call destructors for non-trivially destructible types
531 if constexpr (!std::is_trivially_destructible<T>::value) {
532 for (size_t i = 0; i < size_; i++) {
533 data_[i].~T();
534 }
535 }
536 }
537
538 // Helper to destroy elements and free memory
539 void cleanup_() {
540 if (data_ != nullptr) {
541 destroy_elements_();
542 // Free raw memory
543 ::operator delete(data_);
544 }
545 }
546
547 // Helper to reset pointers after cleanup
548 void reset_() {
549 data_ = nullptr;
550 capacity_ = 0;
551 size_ = 0;
552 }
553
554 // Helper to assign from initializer list (shared by constructor and assignment operator)
555 void assign_from_initializer_list_(std::initializer_list<T> init_list) {
556 init(init_list.size());
557 size_t idx = 0;
558 for (const auto &item : init_list) {
559 new (data_ + idx) T(item);
560 ++idx;
561 }
562 size_ = init_list.size();
563 }
564
565 public:
566 FixedVector() = default;
567
570 FixedVector(std::initializer_list<T> init_list) { assign_from_initializer_list_(init_list); }
571
572 ~FixedVector() { cleanup_(); }
573
574 // Disable copy operations (avoid accidental expensive copies)
575 FixedVector(const FixedVector &) = delete;
577
578 // Enable move semantics (allows use in move-only containers like std::vector)
579 FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
580 other.reset_();
581 }
582
583 // Allow conversion to std::vector
584 operator std::vector<T>() const { return {data_, data_ + size_}; }
585
586 FixedVector &operator=(FixedVector &&other) noexcept {
587 if (this != &other) {
588 // Delete our current data
589 cleanup_();
590 // Take ownership of other's data
591 data_ = other.data_;
592 size_ = other.size_;
593 capacity_ = other.capacity_;
594 // Leave other in valid empty state
595 other.reset_();
596 }
597 return *this;
598 }
599
602 FixedVector &operator=(std::initializer_list<T> init_list) {
603 cleanup_();
604 reset_();
605 assign_from_initializer_list_(init_list);
606 return *this;
607 }
608
609 // Allocate capacity - can be called multiple times to reinit
610 // IMPORTANT: After calling init(), you MUST use push_back() to add elements.
611 // Direct assignment via operator[] does NOT update the size counter.
612 void init(size_t n) {
613 cleanup_();
614 reset_();
615 if (n > 0) {
616 // Allocate raw memory without calling constructors
617 // sizeof(T) is correct here for any type T (value types, pointers, etc.)
618 // NOLINTNEXTLINE(bugprone-sizeof-expression)
619 data_ = static_cast<T *>(::operator new(n * sizeof(T)));
620 capacity_ = n;
621 }
622 }
623
624 // Clear the vector (destroy all elements, reset size to 0, keep capacity)
625 void clear() {
626 destroy_elements_();
627 size_ = 0;
628 }
629
630 // Release all memory (destroys elements and frees memory)
631 void release() {
632 cleanup_();
633 reset_();
634 }
635
639 void push_back(const T &value) {
640 if (size_ < capacity_) {
641 // Use placement new to construct the object in pre-allocated memory
642 new (&data_[size_]) T(value);
643 size_++;
644 }
645 }
646
650 void push_back(T &&value) {
651 if (size_ < capacity_) {
652 // Use placement new to move-construct the object in pre-allocated memory
653 new (&data_[size_]) T(std::move(value));
654 size_++;
655 }
656 }
657
662 template<typename... Args> T &emplace_back(Args &&...args) {
663 // Use placement new to construct the object in pre-allocated memory
664 new (&data_[size_]) T(std::forward<Args>(args)...);
665 size_++;
666 return data_[size_ - 1];
667 }
668
671 T &front() { return data_[0]; }
672 const T &front() const { return data_[0]; }
673
676 T &back() { return data_[size_ - 1]; }
677 const T &back() const { return data_[size_ - 1]; }
678
679 size_t size() const { return size_; }
680 bool empty() const { return size_ == 0; }
681 size_t capacity() const { return capacity_; }
682 bool full() const { return size_ == capacity_; }
683
686 T &operator[](size_t i) { return data_[i]; }
687 const T &operator[](size_t i) const { return data_[i]; }
688
691 T &at(size_t i) { return data_[i]; }
692 const T &at(size_t i) const { return data_[i]; }
693
694 // Iterator support for range-based for loops
695 T *begin() { return data_; }
696 T *end() { return data_ + size_; }
697 const T *begin() const { return data_; }
698 const T *end() const { return data_ + size_; }
699};
700
706template<size_t STACK_SIZE, typename T = uint8_t> class SmallBufferWithHeapFallback {
707 public:
709 if (size <= STACK_SIZE) {
710 this->buffer_ = this->stack_buffer_;
711 } else {
712 this->heap_buffer_ = new T[size];
713 this->buffer_ = this->heap_buffer_;
714 }
715 }
716 ~SmallBufferWithHeapFallback() { delete[] this->heap_buffer_; }
717
718 // Delete copy and move operations to prevent double-delete
723
724 T *get() { return this->buffer_; }
725
726 private:
727 T stack_buffer_[STACK_SIZE];
728 T *heap_buffer_{nullptr};
729 T *buffer_;
730};
731
733
736
740inline float pow10_int(int8_t exp) {
741 float result = 1.0f;
742 if (exp >= 0) {
743 for (int8_t i = 0; i < exp; i++)
744 result *= 10.0f;
745 } else {
746 for (int8_t i = exp; i < 0; i++)
747 result /= 10.0f;
748 }
749 return result;
750}
751
753template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max_out) {
754 return (value - min) * (max_out - min_out) / (max - min) + min_out;
755}
756
758uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc = 0x00, uint8_t poly = 0x8C, bool msb_first = false);
759
761uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001,
762 bool refin = false, bool refout = false);
763uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc = 0, uint16_t poly = 0x1021, bool refin = false,
764 bool refout = false);
765
768uint32_t fnv1_hash(const char *str);
769inline uint32_t fnv1_hash(const std::string &str) { return fnv1_hash(str.c_str()); }
770
772constexpr uint32_t FNV1_OFFSET_BASIS = 2166136261UL;
774constexpr uint32_t FNV1_PRIME = 16777619UL;
775
777template<std::integral T> constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value) {
778 using UnsignedT = std::make_unsigned_t<T>;
779 UnsignedT uvalue = static_cast<UnsignedT>(value);
780 for (size_t i = 0; i < sizeof(T); i++) {
781 hash *= FNV1_PRIME;
782 hash ^= (uvalue >> (i * 8)) & 0xFF;
783 }
784 return hash;
785}
787constexpr uint32_t fnv1_hash_extend(uint32_t hash, const char *str) {
788 if (str) {
789 while (*str) {
790 hash *= FNV1_PRIME;
791 hash ^= *str++;
792 }
793 }
794 return hash;
795}
796inline uint32_t fnv1_hash_extend(uint32_t hash, const std::string &str) { return fnv1_hash_extend(hash, str.c_str()); }
797
799constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str) {
800 if (str) {
801 while (*str) {
802 hash ^= *str++;
803 hash *= FNV1_PRIME;
804 }
805 }
806 return hash;
807}
808inline uint32_t fnv1a_hash_extend(uint32_t hash, const std::string &str) {
809 return fnv1a_hash_extend(hash, str.c_str());
810}
812template<std::integral T> constexpr uint32_t fnv1a_hash_extend(uint32_t hash, T value) {
813 using UnsignedT = std::make_unsigned_t<T>;
814 UnsignedT uvalue = static_cast<UnsignedT>(value);
815 for (size_t i = 0; i < sizeof(T); i++) {
816 hash ^= (uvalue >> (i * 8)) & 0xFF;
817 hash *= FNV1_PRIME;
818 }
819 return hash;
820}
822constexpr uint32_t fnv1a_hash(const char *str) { return fnv1a_hash_extend(FNV1_OFFSET_BASIS, str); }
823inline uint32_t fnv1a_hash(const std::string &str) { return fnv1a_hash(str.c_str()); }
824
848template<typename ReturnT = uint32_t> inline constexpr ESPHOME_ALWAYS_INLINE ReturnT micros_to_millis(uint64_t us) {
849 constexpr uint32_t d = 125U;
850 constexpr uint32_t q = static_cast<uint32_t>((1ULL << 32) / d); // 34359738
851 constexpr uint32_t r = static_cast<uint32_t>((1ULL << 32) % d); // 46
852 // 1000 = 8 * 125; divide-by-8 is a free shift
853 uint64_t x = us >> 3;
854 uint32_t lo = static_cast<uint32_t>(x);
855 uint32_t hi = static_cast<uint32_t>(x >> 32);
856 // Combine remainder term: hi * (2^32 % 125) + lo
857 uint32_t adj = hi * r + lo;
858 // If adj overflowed, the true value is 2^32 + adj; apply the identity again
859 // static_cast<ReturnT>(hi) widens to 64-bit when ReturnT=uint64_t, preserving upper bits of hi*q
860 return static_cast<ReturnT>(hi) * q + (adj < lo ? (adj + r) / d + q : adj / d);
861}
862
870float random_float();
873bool random_bytes(uint8_t *data, size_t len);
874
876
879
881constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb) {
882 return (static_cast<uint16_t>(msb) << 8) | (static_cast<uint16_t>(lsb));
883}
885constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) {
886 return (static_cast<uint32_t>(byte1) << 16) | (static_cast<uint32_t>(byte2) << 8) | (static_cast<uint32_t>(byte3));
887}
889constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) {
890 return (static_cast<uint32_t>(byte1) << 24) | (static_cast<uint32_t>(byte2) << 16) |
891 (static_cast<uint32_t>(byte3) << 8) | (static_cast<uint32_t>(byte4));
892}
893
895template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> constexpr T encode_value(const uint8_t *bytes) {
896 T val = 0;
897 for (size_t i = 0; i < sizeof(T); i++) {
898 val <<= 8;
899 val |= bytes[i];
900 }
901 return val;
902}
904template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
905constexpr T encode_value(const std::array<uint8_t, sizeof(T)> bytes) {
906 return encode_value<T>(bytes.data());
907}
909template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
910constexpr std::array<uint8_t, sizeof(T)> decode_value(T val) {
911 std::array<uint8_t, sizeof(T)> ret{};
912 for (size_t i = sizeof(T); i > 0; i--) {
913 ret[i - 1] = val & 0xFF;
914 val >>= 8;
915 }
916 return ret;
917}
918
920inline uint8_t reverse_bits(uint8_t x) {
921 x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
922 x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
923 x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
924 return x;
925}
927inline uint16_t reverse_bits(uint16_t x) {
928 return (reverse_bits(static_cast<uint8_t>(x & 0xFF)) << 8) | reverse_bits(static_cast<uint8_t>((x >> 8) & 0xFF));
929}
932 return (reverse_bits(static_cast<uint16_t>(x & 0xFFFF)) << 16) |
933 reverse_bits(static_cast<uint16_t>((x >> 16) & 0xFFFF));
934}
935
937template<typename T> constexpr T convert_big_endian(T val) {
938#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
939 return byteswap(val);
940#else
941 return val;
942#endif
943}
944
946template<typename T> constexpr T convert_little_endian(T val) {
947#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
948 return val;
949#else
950 return byteswap(val);
951#endif
952}
953
955
958
960bool str_equals_case_insensitive(const std::string &a, const std::string &b);
962bool str_equals_case_insensitive(StringRef a, StringRef b);
964inline bool str_equals_case_insensitive(const char *a, const char *b) { return strcasecmp(a, b) == 0; }
965inline bool str_equals_case_insensitive(const std::string &a, const char *b) { return strcasecmp(a.c_str(), b) == 0; }
966inline bool str_equals_case_insensitive(const char *a, const std::string &b) { return strcasecmp(a, b.c_str()) == 0; }
967
969bool str_startswith(const std::string &str, const std::string &start);
971bool str_endswith(const std::string &str, const std::string &end);
972
974bool str_endswith_ignore_case(const char *str, size_t str_len, const char *suffix, size_t suffix_len);
975inline bool str_endswith_ignore_case(const char *str, const char *suffix) {
976 return str_endswith_ignore_case(str, strlen(str), suffix, strlen(suffix));
977}
978inline bool str_endswith_ignore_case(const std::string &str, const char *suffix) {
979 return str_endswith_ignore_case(str.c_str(), str.size(), suffix, strlen(suffix));
980}
981
984std::string str_truncate(const std::string &str, size_t length);
985
988std::string str_until(const char *str, char ch);
990std::string str_until(const std::string &str, char ch);
991
993std::string str_lower_case(const std::string &str);
996std::string str_upper_case(const std::string &str);
997
999constexpr char to_snake_case_char(char c) { return (c == ' ') ? '_' : (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }
1002std::string str_snake_case(const std::string &str);
1003
1005constexpr char to_sanitized_char(char c) {
1006 return (c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? c : '_';
1007}
1008
1018char *str_sanitize_to(char *buffer, size_t buffer_size, const char *str);
1019
1021template<size_t N> inline char *str_sanitize_to(char (&buffer)[N], const char *str) {
1022 return str_sanitize_to(buffer, N, str);
1023}
1024
1027std::string str_sanitize(const std::string &str);
1028
1033inline uint32_t fnv1_hash_object_id(const char *str, size_t len) {
1035 for (size_t i = 0; i < len; i++) {
1036 hash *= FNV1_PRIME;
1037 // Apply snake_case (space->underscore, uppercase->lowercase) then sanitize
1038 hash ^= static_cast<uint8_t>(to_sanitized_char(to_snake_case_char(str[i])));
1039 }
1040 return hash;
1041}
1042
1045std::string __attribute__((format(printf, 1, 3))) str_snprintf(const char *fmt, size_t len, ...);
1046
1049std::string __attribute__((format(printf, 1, 2))) str_sprintf(const char *fmt, ...);
1050
1051#ifdef USE_ESP8266
1052// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM)
1053// Format strings must be wrapped with PSTR() macro
1060inline size_t buf_append_printf_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) {
1061 if (pos >= size) {
1062 return size;
1063 }
1064 va_list args;
1065 va_start(args, fmt);
1066 int written = vsnprintf_P(buf + pos, size - pos, fmt, args);
1067 va_end(args);
1068 if (written < 0) {
1069 return pos; // encoding error
1070 }
1071 return std::min(pos + static_cast<size_t>(written), size);
1072}
1073#define buf_append_printf(buf, size, pos, fmt, ...) buf_append_printf_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__)
1074#else
1082__attribute__((format(printf, 4, 5))) inline size_t buf_append_printf(char *buf, size_t size, size_t pos,
1083 const char *fmt, ...) {
1084 if (pos >= size) {
1085 return size;
1086 }
1087 va_list args;
1089 int written = vsnprintf(buf + pos, size - pos, fmt, args);
1091 if (written < 0) {
1092 return pos; // encoding error
1093 }
1094 return std::min(pos + static_cast<size_t>(written), size);
1095}
1096#endif
1097
1105inline size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str) {
1106 if (pos >= size) {
1107 return size;
1108 }
1109 size_t remaining = size - pos - 1; // reserve space for null terminator
1110 size_t len = strlen(str);
1111 if (len > remaining) {
1112 len = remaining;
1113 }
1114 memcpy(buf + pos, str, len);
1115 pos += len;
1116 buf[pos] = '\0';
1117 return pos;
1118}
1119
1128std::string make_name_with_suffix(const std::string &name, char sep, const char *suffix_ptr, size_t suffix_len);
1129
1138std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr,
1139 size_t suffix_len);
1140
1150size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep,
1151 const char *suffix_ptr, size_t suffix_len);
1152
1154
1157
1159template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1160optional<T> parse_number(const char *str) {
1161 char *end = nullptr;
1162 unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
1163 if (end == str || *end != '\0' || value > std::numeric_limits<T>::max())
1164 return {};
1165 return value;
1166}
1168template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1169optional<T> parse_number(const std::string &str) {
1170 return parse_number<T>(str.c_str());
1171}
1173template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1174optional<T> parse_number(const char *str) {
1175 char *end = nullptr;
1176 signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
1177 if (end == str || *end != '\0' || value < std::numeric_limits<T>::min() || value > std::numeric_limits<T>::max())
1178 return {};
1179 return value;
1180}
1182template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1183optional<T> parse_number(const std::string &str) {
1184 return parse_number<T>(str.c_str());
1185}
1187template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0> optional<T> parse_number(const char *str) {
1188 char *end = nullptr;
1189 float value = ::strtof(str, &end);
1190 if (end == str || *end != '\0' || value == HUGE_VALF)
1191 return {};
1192 return value;
1193}
1195template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
1196optional<T> parse_number(const std::string &str) {
1197 return parse_number<T>(str.c_str());
1198}
1199
1211size_t parse_hex(const char *str, size_t len, uint8_t *data, size_t count);
1213inline bool parse_hex(const char *str, uint8_t *data, size_t count) {
1214 return parse_hex(str, strlen(str), data, count) == 2 * count;
1215}
1217inline bool parse_hex(const std::string &str, uint8_t *data, size_t count) {
1218 return parse_hex(str.c_str(), str.length(), data, count) == 2 * count;
1219}
1221inline bool parse_hex(const char *str, std::vector<uint8_t> &data, size_t count) {
1222 data.resize(count);
1223 return parse_hex(str, strlen(str), data.data(), count) == 2 * count;
1224}
1226inline bool parse_hex(const std::string &str, std::vector<uint8_t> &data, size_t count) {
1227 data.resize(count);
1228 return parse_hex(str.c_str(), str.length(), data.data(), count) == 2 * count;
1229}
1235template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1236optional<T> parse_hex(const char *str, size_t len) {
1237 T val = 0;
1238 if (len > 2 * sizeof(T) || parse_hex(str, len, reinterpret_cast<uint8_t *>(&val), sizeof(T)) == 0)
1239 return {};
1240 return convert_big_endian(val);
1241}
1243template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const char *str) {
1244 return parse_hex<T>(str, strlen(str));
1245}
1247template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const std::string &str) {
1248 return parse_hex<T>(str.c_str(), str.length());
1249}
1250
1253static constexpr uint8_t INVALID_HEX_CHAR = 255;
1254
1255constexpr uint8_t parse_hex_char(char c) {
1256 if (c >= '0' && c <= '9')
1257 return c - '0';
1258 if (c >= 'A' && c <= 'F')
1259 return c - 'A' + 10;
1260 if (c >= 'a' && c <= 'f')
1261 return c - 'a' + 10;
1262 return INVALID_HEX_CHAR;
1263}
1264
1266ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
1267
1269ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
1270
1272ESPHOME_ALWAYS_INLINE inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
1273
1276inline char *int8_to_str(char *buf, int8_t val) {
1277 int32_t v = val;
1278 if (v < 0) {
1279 *buf++ = '-';
1280 v = -v;
1281 }
1282 if (v >= 100) {
1283 *buf++ = '1'; // int8 max is 128, so hundreds digit is always 1
1284 v -= 100;
1285 // Must write tens digit (even if 0) after hundreds
1286 int32_t tens = v / 10;
1287 *buf++ = '0' + tens;
1288 v -= tens * 10;
1289 } else if (v >= 10) {
1290 int32_t tens = v / 10;
1291 *buf++ = '0' + tens;
1292 v -= tens * 10;
1293 }
1294 *buf++ = '0' + v;
1295 return buf;
1296}
1297
1299static constexpr size_t UINT32_MAX_STR_SIZE = 11;
1300
1303char *uint32_to_str_unchecked(char *buf, uint32_t val);
1304
1307inline size_t uint32_to_str(std::span<char, UINT32_MAX_STR_SIZE> buf, uint32_t val) {
1308 char *end = uint32_to_str_unchecked(buf.data(), val);
1309 *end = '\0';
1310 return static_cast<size_t>(end - buf.data());
1311}
1312
1314char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1315
1318template<size_t N> inline char *format_hex_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1319 static_assert(N >= 3, "Buffer must hold at least one hex byte (3 chars)");
1320 return format_hex_to(buffer, N, data, length);
1321}
1322
1324template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1325inline char *format_hex_to(char (&buffer)[N], T val) {
1326 static_assert(N >= sizeof(T) * 2 + 1, "Buffer too small for type");
1328 return format_hex_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1329}
1330
1332template<size_t N> inline char *format_hex_to(char (&buffer)[N], const std::vector<uint8_t> &data) {
1333 return format_hex_to(buffer, data.data(), data.size());
1334}
1335
1337template<size_t N, size_t M> inline char *format_hex_to(char (&buffer)[N], const std::array<uint8_t, M> &data) {
1338 return format_hex_to(buffer, data.data(), data.size());
1339}
1340
1342constexpr size_t format_hex_size(size_t byte_count) { return byte_count * 2 + 1; }
1343
1345constexpr size_t format_hex_prefixed_size(size_t byte_count) { return byte_count * 2 + 3; }
1346
1348template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1349inline char *format_hex_prefixed_to(char (&buffer)[N], T val) {
1350 static_assert(N >= sizeof(T) * 2 + 3, "Buffer too small for prefixed hex");
1351 buffer[0] = '0';
1352 buffer[1] = 'x';
1354 format_hex_to(buffer + 2, N - 2, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1355 return buffer;
1356}
1357
1359template<size_t N> inline char *format_hex_prefixed_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1360 static_assert(N >= 5, "Buffer must hold at least '0x' + one hex byte + null");
1361 buffer[0] = '0';
1362 buffer[1] = 'x';
1363 format_hex_to(buffer + 2, N - 2, data, length);
1364 return buffer;
1365}
1366
1368constexpr size_t format_hex_pretty_size(size_t byte_count) { return byte_count * 3; }
1369
1381char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator = ':');
1382
1384template<size_t N>
1385inline char *format_hex_pretty_to(char (&buffer)[N], const uint8_t *data, size_t length, char separator = ':') {
1386 static_assert(N >= 3, "Buffer must hold at least one hex byte");
1387 return format_hex_pretty_to(buffer, N, data, length, separator);
1388}
1389
1391template<size_t N>
1392inline char *format_hex_pretty_to(char (&buffer)[N], const std::vector<uint8_t> &data, char separator = ':') {
1393 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1394}
1395
1397template<size_t N, size_t M>
1398inline char *format_hex_pretty_to(char (&buffer)[N], const std::array<uint8_t, M> &data, char separator = ':') {
1399 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1400}
1401
1403constexpr size_t format_hex_pretty_uint16_size(size_t count) { return count * 5; }
1404
1418char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint16_t *data, size_t length, char separator = ':');
1419
1421template<size_t N>
1422inline char *format_hex_pretty_to(char (&buffer)[N], const uint16_t *data, size_t length, char separator = ':') {
1423 static_assert(N >= 5, "Buffer must hold at least one hex uint16_t");
1424 return format_hex_pretty_to(buffer, N, data, length, separator);
1425}
1426
1428static constexpr size_t MAC_ADDRESS_SIZE = 6;
1430static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = format_hex_pretty_size(MAC_ADDRESS_SIZE);
1432static constexpr size_t MAC_ADDRESS_BUFFER_SIZE = MAC_ADDRESS_SIZE * 2 + 1;
1433
1435inline char *format_mac_addr_upper(const uint8_t *mac, char *output) {
1436 return format_hex_pretty_to(output, MAC_ADDRESS_PRETTY_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE, ':');
1437}
1438
1440inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) {
1441 format_hex_to(output, MAC_ADDRESS_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE);
1442}
1443
1447std::string format_mac_address_pretty(const uint8_t mac[6]);
1451std::string format_hex(const uint8_t *data, size_t length);
1455std::string format_hex(const std::vector<uint8_t> &data);
1459template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_hex(T val) {
1461 return format_hex(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1462}
1466template<std::size_t N> std::string format_hex(const std::array<uint8_t, N> &data) {
1467 return format_hex(data.data(), data.size());
1468}
1469
1498std::string format_hex_pretty(const uint8_t *data, size_t length, char separator = '.', bool show_length = true);
1499
1523std::string format_hex_pretty(const uint16_t *data, size_t length, char separator = '.', bool show_length = true);
1524
1549std::string format_hex_pretty(const std::vector<uint8_t> &data, char separator = '.', bool show_length = true);
1550
1574std::string format_hex_pretty(const std::vector<uint16_t> &data, char separator = '.', bool show_length = true);
1575
1599std::string format_hex_pretty(const std::string &data, char separator = '.', bool show_length = true);
1600
1627template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1628std::string format_hex_pretty(T val, char separator = '.', bool show_length = true) {
1630 return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T), separator, show_length);
1631}
1632
1634constexpr size_t format_bin_size(size_t byte_count) { return byte_count * 8 + 1; }
1635
1655char *format_bin_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1656
1658template<size_t N> inline char *format_bin_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1659 static_assert(N >= 9, "Buffer must hold at least one binary byte (9 chars)");
1660 return format_bin_to(buffer, N, data, length);
1661}
1662
1679template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1680inline char *format_bin_to(char (&buffer)[N], T val) {
1681 static_assert(N >= sizeof(T) * 8 + 1, "Buffer too small for type");
1683 return format_bin_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1684}
1685
1689std::string format_bin(const uint8_t *data, size_t length);
1693template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_bin(T val) {
1695 return format_bin(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1696}
1697
1706ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr);
1707
1709ESPDEPRECATED("Allocates heap memory. Use value_accuracy_to_buf() instead. Removed in 2026.7.0.", "2026.1.0")
1710std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
1711
1713static constexpr size_t VALUE_ACCURACY_MAX_LEN = 64;
1714
1716size_t value_accuracy_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value, int8_t accuracy_decimals);
1718size_t value_accuracy_with_uom_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value,
1719 int8_t accuracy_decimals, StringRef unit_of_measurement);
1720
1722int8_t step_to_accuracy_decimals(float step);
1723
1724std::string base64_encode(const uint8_t *buf, size_t buf_len);
1725std::string base64_encode(const std::vector<uint8_t> &buf);
1726
1727std::vector<uint8_t> base64_decode(const std::string &encoded_string);
1728size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len);
1729size_t base64_decode(const uint8_t *encoded_data, size_t encoded_len, uint8_t *buf, size_t buf_len);
1730
1735bool base64_decode_int32_vector(const std::string &base64, std::vector<int32_t> &out);
1736
1738
1741
1743// Remove before 2026.9.0
1744ESPDEPRECATED("Use LightState::gamma_correct_lut() instead. Removed in 2026.9.0.", "2026.3.0")
1745float gamma_correct(float value, float gamma);
1747// Remove before 2026.9.0
1748ESPDEPRECATED("Use LightState::gamma_uncorrect_lut() instead. Removed in 2026.9.0.", "2026.3.0")
1749float gamma_uncorrect(float value, float gamma);
1750
1752void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value);
1754void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue);
1755
1757
1760
1762constexpr float celsius_to_fahrenheit(float value) { return value * 1.8f + 32.0f; }
1764constexpr float fahrenheit_to_celsius(float value) { return (value - 32.0f) / 1.8f; }
1765
1767
1770
1776template<typename... X> struct Callback;
1777
1778template<typename... Ts> struct Callback<void(Ts...)> {
1779 // The inline storage path stores callable bytes in ctx_ via memcpy.
1780 // sizeof equality with uintptr_t ensures void* can round-trip arbitrary bit patterns,
1781 // which combined with flat address spaces on all ESPHome targets means no trap representations.
1782 static_assert(sizeof(void *) == sizeof(std::uintptr_t), "void* must be the same size as uintptr_t");
1783
1784 void (*fn_)(void *, Ts...){nullptr};
1785 void *ctx_{nullptr};
1786
1788 void call(Ts... args) const { this->fn_(this->ctx_, args...); }
1789
1792 template<typename F> static Callback create(F &&callable) {
1793 using DecayF = std::decay_t<F>;
1794 if constexpr (sizeof(DecayF) <= sizeof(void *) && std::is_trivially_copyable_v<DecayF>) {
1795 // Small trivial callable (e.g. [this]() { this->method(); }) - store inline in ctx.
1796 // Safe under C++20 (P0593R6): byte copy into aligned storage implicitly
1797 // creates objects of implicit-lifetime types (trivially copyable qualifies).
1798 Callback cb; // fn and ctx are zero-initialized by default
1799 // Decay callable to a local variable first. When F is a function reference
1800 // (e.g. void(&)(int)), &callable would point at machine code, not a pointer variable.
1801 DecayF decayed = std::forward<F>(callable);
1802 __builtin_memcpy(&cb.ctx_, &decayed, sizeof(DecayF));
1803 cb.fn_ = [](void *c, Ts... args) {
1804 alignas(DecayF) char buf[sizeof(DecayF)];
1805 __builtin_memcpy(buf, &c, sizeof(DecayF));
1806 (*std::launder(reinterpret_cast<DecayF *>(buf)))(args...);
1807 };
1808 return cb;
1809 } else {
1810 // Large or non-trivial callable - heap allocate.
1811 // Intentionally never freed: callbacks in ESPHome are registered during setup()
1812 // and live for device lifetime. Same lifetime as the previous std::function approach.
1813 auto *stored = new DecayF(std::forward<F>(callable));
1814 return {[](void *c, Ts... args) { (*static_cast<DecayF *>(c))(args...); }, static_cast<void *>(stored)};
1815 }
1816 }
1817};
1818
1820void *callback_manager_grow(void *data, uint16_t size, uint16_t &capacity, size_t elem_size);
1821
1822template<typename... X> class CallbackManager;
1823
1835template<typename... Ts> class CallbackManager<void(Ts...)> {
1836 using CbType = Callback<void(Ts...)>;
1837 static_assert(std::is_trivially_copyable_v<CbType>, "Callback must be trivially copyable");
1838
1839 public:
1840 CallbackManager() = default;
1841 ~CallbackManager() { ::operator delete(this->data_); }
1842
1843 // Non-copyable (would alias data_), movable (for std::map support)
1847 : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
1848 other.data_ = nullptr;
1849 other.size_ = 0;
1850 other.capacity_ = 0;
1851 }
1853 std::swap(this->data_, other.data_);
1854 std::swap(this->size_, other.size_);
1855 std::swap(this->capacity_, other.capacity_);
1856 return *this;
1857 }
1858
1861 template<typename F> void add(F &&callback) { this->add_(CbType::create(std::forward<F>(callback))); }
1862
1864 inline void ESPHOME_ALWAYS_INLINE call(Ts... args) {
1865 if (this->size_ != 0) {
1866 for (auto *it = this->data_, *end = it + this->size_; it != end; ++it) {
1867 it->call(args...);
1868 }
1869 }
1870 }
1871 uint16_t size() const { return this->size_; }
1872
1874 void operator()(Ts... args) { this->call(args...); }
1875
1876 protected:
1877 template<typename...> friend class LazyCallbackManager;
1880 void add_(CbType cb) {
1881 if (this->size_ == this->capacity_) {
1882 this->data_ =
1883 static_cast<CbType *>(callback_manager_grow(this->data_, this->size_, this->capacity_, sizeof(CbType)));
1884 }
1885 this->data_[this->size_++] = cb;
1886 }
1887 CbType *data_{nullptr};
1888 uint16_t size_{0};
1889 uint16_t capacity_{0};
1890};
1891
1900template<size_t N, typename... X> class StaticCallbackManager;
1901
1902template<size_t N, typename... Ts> class StaticCallbackManager<N, void(Ts...)> {
1903 public:
1906 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
1907
1909 void call(Ts... args) {
1910 for (auto &cb : this->callbacks_)
1911 cb.call(args...);
1912 }
1913 size_t size() const { return this->callbacks_.size(); }
1914
1916 void operator()(Ts... args) { call(args...); }
1917
1918 protected:
1920 void add_(Callback<void(Ts...)> cb) { this->callbacks_.push_back(cb); }
1922};
1923
1924template<typename... X> class LazyCallbackManager;
1925
1941template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
1942 public:
1946 ~LazyCallbackManager() { delete this->callbacks_; }
1947
1948 // Non-copyable and non-movable (entities are never copied or moved)
1953
1955 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
1956
1958 void call(Ts... args) {
1959 if (this->callbacks_) {
1960 this->callbacks_->call(args...);
1961 }
1962 }
1963
1965 size_t size() const { return this->callbacks_ ? this->callbacks_->size() : 0; }
1966
1968 bool empty() const { return !this->callbacks_ || this->callbacks_->size() == 0; }
1969
1971 void operator()(Ts... args) { this->call(args...); }
1972
1973 protected:
1975 void add_(Callback<void(Ts...)> cb) {
1976 if (!this->callbacks_) {
1977 this->callbacks_ = new CallbackManager<void(Ts...)>();
1978 }
1979 this->callbacks_->add_(cb);
1980 }
1981 CallbackManager<void(Ts...)> *callbacks_{nullptr};
1982};
1983
1985template<typename T> class Deduplicator {
1986 public:
1988 bool next(T value) {
1989 if (this->has_value_ && !this->value_unknown_ && this->last_value_ == value) {
1990 return false;
1991 }
1992 this->has_value_ = true;
1993 this->value_unknown_ = false;
1994 this->last_value_ = value;
1995 return true;
1996 }
1999 bool ret = !this->value_unknown_;
2000 this->value_unknown_ = true;
2001 return ret;
2002 }
2004 bool has_value() const { return this->has_value_; }
2005
2006 protected:
2007 bool has_value_{false};
2008 bool value_unknown_{false};
2010};
2011
2013template<typename T> class Parented {
2014 public:
2016 Parented(T *parent) : parent_(parent) {}
2017
2019 T *get_parent() const { return parent_; }
2021 void set_parent(T *parent) { parent_ = parent; }
2022
2023 protected:
2024 T *parent_{nullptr};
2025};
2026
2028
2031
2036class Mutex {
2037 public:
2038 Mutex(const Mutex &) = delete;
2039 Mutex &operator=(const Mutex &) = delete;
2040
2041#if defined(USE_ESP8266) || defined(USE_RP2040)
2042 // Single-threaded platforms: inline no-ops so the compiler eliminates all call overhead.
2043 Mutex() = default;
2044 ~Mutex() = default;
2045 void lock() {}
2046 bool try_lock() { return true; }
2047 void unlock() {}
2048#elif defined(USE_ESP32) || defined(USE_LIBRETINY)
2049 // FreeRTOS platforms: inline to avoid out-of-line call overhead.
2050 Mutex() { handle_ = xSemaphoreCreateMutex(); }
2051 ~Mutex() = default;
2052 void lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); }
2053 bool try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; }
2054 void unlock() { xSemaphoreGive(this->handle_); }
2055
2056 private:
2057 SemaphoreHandle_t handle_;
2058#else
2059 Mutex();
2060 ~Mutex();
2061 void lock();
2062 bool try_lock();
2063 void unlock();
2064
2065 private:
2066 // d-pointer to store private data on new platforms
2067 void *handle_; // NOLINT(clang-diagnostic-unused-private-field)
2068#endif
2069};
2070
2076 public:
2077 LockGuard(Mutex &mutex) : mutex_(mutex) { mutex_.lock(); }
2078 ~LockGuard() { mutex_.unlock(); }
2079
2080 private:
2081 Mutex &mutex_;
2082};
2083
2105 public:
2106 InterruptLock();
2108
2109 protected:
2110#if defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_ZEPHYR)
2112#endif
2113};
2114
2124 public:
2125 LwIPLock(const LwIPLock &) = delete;
2126 LwIPLock &operator=(const LwIPLock &) = delete;
2127
2128#if defined(USE_ESP32) || defined(USE_RP2040)
2129 // Platforms with potential lwIP core locking — out-of-line implementations in helpers.cpp
2130 LwIPLock();
2131 ~LwIPLock();
2132#else
2133 // No lwIP core locking — inline no-ops (empty bodies instead of = default
2134 // to prevent clang-tidy unused-variable warnings at call sites)
2137#endif
2138};
2139
2146 public:
2148 void start();
2150 void stop();
2151
2153 static bool is_high_frequency() { return num_requests > 0; }
2154
2155 protected:
2156 bool started_{false};
2157 static uint8_t num_requests; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
2158};
2159
2161void get_mac_address_raw(uint8_t *mac); // NOLINT(readability-non-const-parameter)
2162
2166std::string get_mac_address();
2167
2171std::string get_mac_address_pretty();
2172
2176void get_mac_address_into_buffer(std::span<char, MAC_ADDRESS_BUFFER_SIZE> buf);
2177
2181const char *get_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
2182
2183#ifdef USE_ESP32
2185void set_mac_address(uint8_t *mac);
2186#endif
2187
2191
2194bool mac_address_is_valid(const uint8_t *mac);
2195
2198
2200
2203
2212template<class T> class RAMAllocator {
2213 public:
2214 using value_type = T;
2215
2216 enum Flags {
2217 NONE = 0, // Perform external allocation and fall back to internal memory
2218 ALLOC_EXTERNAL = 1 << 0, // Perform external allocation only.
2219 ALLOC_INTERNAL = 1 << 1, // Perform internal allocation only.
2220 ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
2221 };
2222
2223 constexpr RAMAllocator() = default;
2224 constexpr RAMAllocator(uint8_t flags)
2227 template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {}
2228
2229 T *allocate(size_t n) { return this->allocate(n, sizeof(T)); }
2230
2231 T *allocate(size_t n, size_t manual_size) {
2232 size_t size = n * manual_size;
2233 T *ptr = nullptr;
2234#ifdef USE_ESP32
2235 if (this->flags_ & Flags::ALLOC_EXTERNAL) {
2236 ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
2237 }
2238 if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) {
2239 ptr = static_cast<T *>(heap_caps_malloc(size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
2240 }
2241#else
2242 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2243 ptr = static_cast<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2244#endif
2245 return ptr;
2246 }
2247
2248 T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); }
2249
2250 T *reallocate(T *p, size_t n, size_t manual_size) {
2251 size_t size = n * manual_size;
2252 T *ptr = nullptr;
2253#ifdef USE_ESP32
2254 if (this->flags_ & Flags::ALLOC_EXTERNAL) {
2255 ptr = static_cast<T *>(heap_caps_realloc(p, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
2256 }
2257 if (ptr == nullptr && this->flags_ & Flags::ALLOC_INTERNAL) {
2258 ptr = static_cast<T *>(heap_caps_realloc(p, size, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT));
2259 }
2260#else
2261 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2262 ptr = static_cast<T *>(realloc(p, size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2263#endif
2264 return ptr;
2265 }
2266
2267 void deallocate(T *p, size_t n) {
2268 free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2269 }
2270
2274 size_t get_free_heap_size() const {
2275#ifdef USE_ESP8266
2276 return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
2277#elif defined(USE_ESP32)
2278 auto max_internal =
2279 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2280 auto max_external =
2281 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2282 return max_internal + max_external;
2283#elif defined(USE_RP2040)
2284 return ::rp2040.getFreeHeap();
2285#elif defined(USE_LIBRETINY)
2286 return lt_heap_get_free();
2287#else
2288 return 100000;
2289#endif
2290 }
2291
2296#ifdef USE_ESP8266
2297 return ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
2298#elif defined(USE_ESP32)
2299 auto max_internal =
2300 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2301 auto max_external =
2302 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2303 return std::max(max_internal, max_external);
2304#else
2305 return this->get_free_heap_size();
2306#endif
2307 }
2308
2309 private:
2310 uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL};
2311};
2312
2313template<class T> using ExternalRAMAllocator = RAMAllocator<T>;
2314
2319template<typename T, typename U>
2320concept comparable_with = requires(T a, U b) {
2321 { a > b } -> std::convertible_to<bool>;
2322 { a < b } -> std::convertible_to<bool>;
2323};
2324
2325template<std::totally_ordered T, comparable_with<T> U> T clamp_at_least(T value, U min) {
2326 if (value < min)
2327 return min;
2328 return value;
2329}
2330template<std::totally_ordered T, comparable_with<T> U> T clamp_at_most(T value, U max) {
2331 if (value > max)
2332 return max;
2333 return value;
2334}
2335
2338
2343template<typename T, enable_if_t<!std::is_pointer<T>::value, int> = 0> T id(T value) { return value; }
2348template<typename T, enable_if_t<std::is_pointer<T *>::value, int> = 0> T &id(T *value) { return *value; }
2349
2351
2352} // namespace esphome
uint8_t m
Definition bl0906.h:1
CallbackManager & operator=(const CallbackManager &)=delete
CallbackManager & operator=(CallbackManager &&other) noexcept
Definition helpers.h:1852
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1874
void add(F &&callback)
Add any callable.
Definition helpers.h:1861
CallbackManager(CallbackManager &&other) noexcept
Definition helpers.h:1846
void add_(CbType cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1880
CallbackManager(const CallbackManager &)=delete
void ESPHOME_ALWAYS_INLINE call(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1864
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory) Av...
Definition helpers.h:124
const constexpr T & operator[](size_t i) const
Definition helpers.h:128
constexpr bool empty() const
Definition helpers.h:130
constexpr ConstVector(const T *data, size_t size)
Definition helpers.h:126
constexpr size_t size() const
Definition helpers.h:129
Helper class to deduplicate items in a series of values.
Definition helpers.h:1985
bool next(T value)
Feeds the next item in the series to the deduplicator and returns false if this is a duplicate.
Definition helpers.h:1988
bool has_value() const
Returns true if this deduplicator has processed any items.
Definition helpers.h:2004
bool next_unknown()
Returns true if the deduplicator's value was previously known.
Definition helpers.h:1998
bool operator!=(const ConstIterator &other) const
Definition helpers.h:412
ConstIterator(const FixedRingBuffer *buf, index_type pos)
Definition helpers.h:406
bool operator!=(const Iterator &other) const
Definition helpers.h:397
Iterator(FixedRingBuffer *buf, index_type pos)
Definition helpers.h:391
Fixed-capacity circular buffer - allocates once at runtime, never reallocates.
Definition helpers.h:383
FixedRingBuffer & operator=(const FixedRingBuffer &)=delete
ConstIterator begin() const
Definition helpers.h:490
bool push(const T &value)
Push a value. Returns false if full.
Definition helpers.h:445
const T & front() const
Definition helpers.h:475
index_type capacity() const
Definition helpers.h:478
void push_overwrite(const T &value)
Push a value, overwriting the oldest if full.
Definition helpers.h:455
void init(index_type capacity)
Allocate capacity - can only be called once.
Definition helpers.h:433
void pop()
Remove the oldest element.
Definition helpers.h:467
void clear()
Clear all elements (reset to empty, keep capacity)
Definition helpers.h:482
FixedRingBuffer(const FixedRingBuffer &)=delete
index_type size() const
Definition helpers.h:476
ConstIterator end() const
Definition helpers.h:491
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:522
const T & at(size_t i) const
Definition helpers.h:692
FixedVector(FixedVector &&other) noexcept
Definition helpers.h:579
FixedVector(std::initializer_list< T > init_list)
Constructor from initializer list - allocates exact size needed This enables brace initialization: Fi...
Definition helpers.h:570
const T * begin() const
Definition helpers.h:697
bool full() const
Definition helpers.h:682
FixedVector & operator=(std::initializer_list< T > init_list)
Assignment from initializer list - avoids temporary and move overhead This enables: FixedVector<int> ...
Definition helpers.h:602
T & front()
Access first element (no bounds checking - matches std::vector behavior) Caller must ensure vector is...
Definition helpers.h:671
const T & operator[](size_t i) const
Definition helpers.h:687
T & operator[](size_t i)
Access element without bounds checking (matches std::vector behavior) Caller must ensure index is val...
Definition helpers.h:686
size_t capacity() const
Definition helpers.h:681
T & back()
Access last element (no bounds checking - matches std::vector behavior) Caller must ensure vector is ...
Definition helpers.h:676
bool empty() const
Definition helpers.h:680
FixedVector & operator=(const FixedVector &)=delete
FixedVector(const FixedVector &)=delete
void push_back(T &&value)
Add element by move without bounds checking Caller must ensure sufficient capacity was allocated via ...
Definition helpers.h:650
T & emplace_back(Args &&...args)
Emplace element without bounds checking - constructs in-place with arguments Caller must ensure suffi...
Definition helpers.h:662
size_t size() const
Definition helpers.h:679
const T & front() const
Definition helpers.h:672
const T & back() const
Definition helpers.h:677
const T * end() const
Definition helpers.h:698
FixedVector & operator=(FixedVector &&other) noexcept
Definition helpers.h:586
T & at(size_t i)
Access element with bounds checking (matches std::vector behavior) Note: No exception thrown on out o...
Definition helpers.h:691
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:639
void init(size_t n)
Definition helpers.h:612
Helper class to request loop() to be called as fast as possible.
Definition helpers.h:2145
static bool is_high_frequency()
Check whether the loop is running continuously.
Definition helpers.h:2153
void stop()
Stop running the loop continuously.
Definition helpers.cpp:847
void start()
Start running the loop continuously.
Definition helpers.cpp:841
Helper class to disable interrupts.
Definition helpers.h:2104
LazyCallbackManager & operator=(const LazyCallbackManager &)=delete
LazyCallbackManager(const LazyCallbackManager &)=delete
size_t size() const
Return the number of registered callbacks.
Definition helpers.h:1965
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1975
void add(F &&callback)
Add any callable. Allocates the underlying CallbackManager on first use.
Definition helpers.h:1955
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1971
LazyCallbackManager & operator=(LazyCallbackManager &&)=delete
~LazyCallbackManager()
Destructor - clean up allocated CallbackManager if any.
Definition helpers.h:1946
void call(Ts... args)
Call all callbacks in this manager. No-op if no callbacks registered.
Definition helpers.h:1958
bool empty() const
Check if any callbacks are registered.
Definition helpers.h:1968
LazyCallbackManager(LazyCallbackManager &&)=delete
Helper class that wraps a mutex with a RAII-style API.
Definition helpers.h:2075
LockGuard(Mutex &mutex)
Definition helpers.h:2077
Helper class to lock the lwIP TCPIP core when making lwIP API calls from non-TCPIP threads.
Definition helpers.h:2123
LwIPLock(const LwIPLock &)=delete
LwIPLock & operator=(const LwIPLock &)=delete
Mutex implementation, with API based on the unavailable std::mutex.
Definition helpers.h:2036
~Mutex()=default
Definition helpers.cpp:36
void unlock()
Definition helpers.h:2047
Mutex()=default
Definition helpers.cpp:35
bool try_lock()
Definition helpers.h:2046
Mutex(const Mutex &)=delete
Mutex & operator=(const Mutex &)=delete
Helper class to easily give an object a parent of type T.
Definition helpers.h:2013
T * get_parent() const
Get the parent of this object.
Definition helpers.h:2019
Parented(T *parent)
Definition helpers.h:2016
void set_parent(T *parent)
Set the parent of this object.
Definition helpers.h:2021
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2212
constexpr RAMAllocator(uint8_t flags)
Definition helpers.h:2224
T * reallocate(T *p, size_t n, size_t manual_size)
Definition helpers.h:2250
size_t get_free_heap_size() const
Return the total heap space available via this allocator.
Definition helpers.h:2274
T * reallocate(T *p, size_t n)
Definition helpers.h:2248
void deallocate(T *p, size_t n)
Definition helpers.h:2267
size_t get_max_free_block_size() const
Return the maximum size block this allocator could allocate.
Definition helpers.h:2295
T * allocate(size_t n)
Definition helpers.h:2229
constexpr RAMAllocator(const RAMAllocator< U > &other)
Definition helpers.h:2227
T * allocate(size_t n, size_t manual_size)
Definition helpers.h:2231
constexpr RAMAllocator()=default
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:706
SmallBufferWithHeapFallback(const SmallBufferWithHeapFallback &)=delete
SmallBufferWithHeapFallback & operator=(SmallBufferWithHeapFallback &&)=delete
SmallBufferWithHeapFallback & operator=(const SmallBufferWithHeapFallback &)=delete
SmallBufferWithHeapFallback(SmallBufferWithHeapFallback &&)=delete
Small buffer optimization - stores data inline when small, heap-allocates for large data This avoids ...
Definition helpers.h:140
SmallInlineBuffer(const SmallInlineBuffer &)=delete
bool is_inline_() const
Definition helpers.h:200
void set(const uint8_t *src, size_t size)
Set buffer contents, allocating heap if needed.
Definition helpers.h:181
SmallInlineBuffer & operator=(const SmallInlineBuffer &)=delete
size_t size() const
Definition helpers.h:197
uint8_t inline_[InlineSize]
Definition helpers.h:204
SmallInlineBuffer & operator=(SmallInlineBuffer &&other) noexcept
Definition helpers.h:160
const uint8_t * data() const
Definition helpers.h:196
SmallInlineBuffer(SmallInlineBuffer &&other) noexcept
Definition helpers.h:149
void add(F &&callback)
Add any callable.
Definition helpers.h:1906
void call(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1909
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1920
StaticVector< Callback< void(Ts...)>, N > callbacks_
Definition helpers.h:1921
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1916
CallbackManager backed by StaticVector for compile-time-known callback counts.
Definition helpers.h:1900
ConstIterator(const StaticRingBuffer *buf, index_type pos)
Definition helpers.h:325
bool operator!=(const ConstIterator &other) const
Definition helpers.h:331
bool operator!=(const Iterator &other) const
Definition helpers.h:316
Iterator(StaticRingBuffer *buf, index_type pos)
Definition helpers.h:310
Fixed-size circular buffer with FIFO semantics and iteration support.
Definition helpers.h:304
bool push(const T &value)
Definition helpers.h:338
ConstIterator begin() const
Definition helpers.h:369
ConstIterator end() const
Definition helpers.h:370
index_type size() const
Definition helpers.h:357
const T & front() const
Definition helpers.h:356
void clear()
Clear all elements (reset to empty)
Definition helpers.h:361
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:210
const_reverse_iterator rend() const
Definition helpers.h:290
size_t size() const
Definition helpers.h:270
reverse_iterator rbegin()
Definition helpers.h:287
const T & operator[](size_t i) const
Definition helpers.h:278
reverse_iterator rend()
Definition helpers.h:288
void push_back(const T &value)
Definition helpers.h:243
bool empty() const
Definition helpers.h:271
void assign(InputIt first, InputIt last)
Definition helpers.h:253
const_reverse_iterator rbegin() const
Definition helpers.h:289
T & operator[](size_t i)
Definition helpers.h:277
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition helpers.h:216
typename std::array< T, N >::iterator iterator
Definition helpers.h:213
typename std::array< T, N >::const_iterator const_iterator
Definition helpers.h:214
std::reverse_iterator< iterator > reverse_iterator
Definition helpers.h:215
const T * data() const
Definition helpers.h:275
const_iterator end() const
Definition helpers.h:284
StaticVector(InputIt first, InputIt last)
Definition helpers.h:227
StaticVector(std::initializer_list< T > init)
Definition helpers.h:234
const_iterator begin() const
Definition helpers.h:283
struct @65::@66 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:32
Functions to constrain the range of arithmetic values.
Definition helpers.h:2320
uint16_t flags
uint16_t id
mopeka_std_values val[3]
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
T clamp_at_most(T value, U max)
Definition helpers.h:2330
bool random_bytes(uint8_t *data, size_t len)
Generate len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
Definition helpers.cpp:20
size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str)
Safely append a string to buffer without format parsing, returning new position (capped at size).
Definition helpers.h:1105
constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str)
Extend a FNV-1a hash with additional string data.
Definition helpers.h:799
float random_float()
Return a random float between 0 and 1.
Definition helpers.cpp:198
const char int const __FlashStringHelper * format
Definition log.h:74
ESPHOME_ALWAYS_INLINE char format_hex_char(uint8_t v, char base)
Convert a nibble (0-15) to hex char with specified base ('a' for lowercase, 'A' for uppercase)
Definition helpers.h:1266
float gamma_uncorrect(float value, float gamma)
Definition helpers.cpp:767
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
Definition helpers.cpp:86
size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep, const char *suffix_ptr, size_t suffix_len)
Zero-allocation version: format name + separator + suffix directly into buffer.
Definition helpers.cpp:300
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals)
Definition helpers.cpp:540
constexpr T convert_big_endian(T val)
Convert a value between host byte order and big endian (most significant byte first) order.
Definition helpers.h:937
float gamma_correct(float value, float gamma)
Definition helpers.cpp:759
constexpr char to_sanitized_char(char c)
Sanitize a single char: keep alphanumerics, dashes, underscores; replace others with underscore.
Definition helpers.h:1005
bool mac_address_is_valid(const uint8_t *mac)
Check if the MAC address is not all zeros or all ones.
Definition helpers.cpp:884
ESPHOME_ALWAYS_INLINE char format_hex_pretty_char(uint8_t v)
Convert a nibble (0-15) to uppercase hex char (used for pretty printing)
Definition helpers.h:1272
void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output)
Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators)
Definition helpers.h:1440
constexpr uint32_t FNV1_OFFSET_BASIS
FNV-1 32-bit offset basis.
Definition helpers.h:772
void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value)
Convert red, green and blue (all 0-1) values to hue (0-360), saturation (0-1) and value (0-1).
Definition helpers.cpp:776
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
Definition helpers.cpp:401
uint16_t uint16_t size_t elem_size
Definition helpers.cpp:26
size_t value_accuracy_to_buf(std::span< char, VALUE_ACCURACY_MAX_LEN > buf, float value, int8_t accuracy_decimals)
Format value with accuracy to buffer, returns chars written (excluding null)
Definition helpers.cpp:546
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
Definition helpers.cpp:240
ParseOnOffState parse_on_off(const char *str, const char *on, const char *off)
Parse a string that contains either on, off or toggle.
Definition helpers.cpp:510
const char int const __FlashStringHelper va_list args
Definition log.h:74
std::string format_bin(const uint8_t *data, size_t length)
Format the byte array data of length len in binary.
Definition helpers.cpp:503
constexpr T convert_little_endian(T val)
Convert a value between host byte order and little endian (least significant byte first) order.
Definition helpers.h:946
std::string str_sanitize(const std::string &str)
Sanitizes the input string by removing all characters but alphanumerics, dashes and underscores.
Definition helpers.cpp:261
constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value)
Extend a FNV-1 hash with an integer (hashes each byte).
Definition helpers.h:777
bool base64_decode_int32_vector(const std::string &base64, std::vector< int32_t > &out)
Decode base64/base64url string directly into vector of little-endian int32 values.
Definition helpers.cpp:721
va_end(args)
std::string size_t len
Definition helpers.h:1045
constexpr size_t format_hex_prefixed_size(size_t byte_count)
Calculate buffer size needed for format_hex_prefixed_to: "0xXXXXXXXX...\0" = bytes * 2 + 3.
Definition helpers.h:1345
constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3)
Encode a 24-bit value given three bytes in most to least significant byte order.
Definition helpers.h:885
char * format_hex_prefixed_to(char(&buffer)[N], T val)
Format an unsigned integer as "0x" prefixed lowercase hex to buffer.
Definition helpers.h:1349
bool has_custom_mac_address()
Check if a custom MAC address is set (ESP32 & variants)
Definition helpers.cpp:110
size_t parse_hex(const char *str, size_t length, uint8_t *data, size_t count)
Parse bytes from a hex-encoded string into a byte array.
Definition helpers.cpp:333
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
Definition helpers.cpp:409
size_t uint32_to_str(std::span< char, UINT32_MAX_STR_SIZE > buf, uint32_t val)
Write unsigned 32-bit integer to buffer with compile-time size check.
Definition helpers.h:1307
uint16_t size
Definition helpers.cpp:25
uint32_t fnv1_hash_object_id(const char *str, size_t len)
Calculate FNV-1 hash of a string while applying snake_case + sanitize transformations.
Definition helpers.h:1033
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:161
T clamp_at_least(T value, U min)
Definition helpers.h:2325
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:1160
std::string get_mac_address_pretty()
Get the device MAC address as a string, in colon-separated uppercase hex notation.
Definition helpers.cpp:862
std::string str_snprintf(const char *fmt, size_t len,...)
Definition helpers.cpp:267
void set_mac_address(uint8_t *mac)
Set the MAC address to use from the provided byte array (6 bytes).
Definition helpers.cpp:108
int8_t step_to_accuracy_decimals(float step)
Derive accuracy in decimals from an increment step.
Definition helpers.cpp:570
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:12
size_t size_t pos
Definition helpers.h:1082
const char * get_mac_address_pretty_into_buffer(std::span< char, MAC_ADDRESS_PRETTY_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in colon-separated uppercase hex notation.
Definition helpers.cpp:873
void IRAM_ATTR HOT delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
Definition helpers.cpp:908
std::string str_upper_case(const std::string &str)
Convert the string to upper case.
Definition helpers.cpp:241
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
Definition helpers.cpp:457
constexpr size_t format_hex_size(size_t byte_count)
Calculate buffer size needed for format_hex_to: "XXXXXXXX...\0" = bytes * 2 + 1.
Definition helpers.h:1342
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare strings for equality in case-insensitive manner.
Definition helpers.cpp:202
std::string str_until(const char *str, char ch)
Extract the part of the string until either the first occurrence of the specified character,...
Definition helpers.cpp:227
bool str_endswith_ignore_case(const char *str, size_t str_len, const char *suffix, size_t suffix_len)
Case-insensitive check if string ends with suffix (no heap allocation).
Definition helpers.cpp:218
constexpr ESPHOME_ALWAYS_INLINE ReturnT micros_to_millis(uint64_t us)
Convert a 64-bit microsecond count to milliseconds without calling __udivdi3 (software 64-bit divide,...
Definition helpers.h:848
char * str_sanitize_to(char *buffer, size_t buffer_size, const char *str)
Sanitize a string to buffer, keeping only alphanumerics, dashes, and underscores.
Definition helpers.cpp:249
void init_array_from(std::array< T, N > &dest, std::initializer_list< T > src)
Initialize a std::array from an initializer_list.
Definition helpers.h:505
char * int8_to_str(char *buf, int8_t val)
Write int8 value to buffer without modulo operations.
Definition helpers.h:1276
std::string format_mac_address_pretty(const uint8_t *mac)
Definition helpers.cpp:344
size_t value_accuracy_with_uom_to_buf(std::span< char, VALUE_ACCURACY_MAX_LEN > buf, float value, int8_t accuracy_decimals, StringRef unit_of_measurement)
Format value with accuracy and UOM to buffer, returns chars written (excluding null)
Definition helpers.cpp:556
char * uint32_to_str_unchecked(char *buf, uint32_t val)
Write unsigned 32-bit integer to buffer (internal, no size check).
Definition helpers.cpp:383
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
Definition helpers.h:1368
std::string base64_encode(const std::vector< uint8_t > &buf)
Definition helpers.cpp:609
constexpr uint32_t FNV1_PRIME
FNV-1 32-bit prime.
Definition helpers.h:774
constexpr T encode_value(const uint8_t *bytes)
Encode a value from its constituent bytes (from most to least significant) in an array with length si...
Definition helpers.h:895
void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue)
Convert hue (0-360), saturation (0-1) and value (0-1) to red, green and blue (all 0-1).
Definition helpers.cpp:799
void get_mac_address_into_buffer(std::span< char, MAC_ADDRESS_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in lowercase hex notation.
Definition helpers.cpp:867
uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t poly, bool refin, bool refout)
Definition helpers.cpp:126
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:889
uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc, uint8_t poly, bool msb_first)
Calculate a CRC-8 checksum of data with size len.
Definition helpers.cpp:59
constexpr float celsius_to_fahrenheit(float value)
Convert degrees Celsius to degrees Fahrenheit.
Definition helpers.h:1762
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:281
size_t size_t const char va_start(args, fmt)
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:881
void get_mac_address_raw(uint8_t *mac)
Get the device MAC address as raw bytes, written into the provided byte array (6 bytes).
Definition helpers.cpp:74
size_t size_t const char * fmt
Definition helpers.h:1083
constexpr uint8_t parse_hex_char(char c)
Definition helpers.h:1255
bool str_startswith(const std::string &str, const std::string &start)
Check whether a string starts with a value.
Definition helpers.cpp:209
constexpr std::array< uint8_t, sizeof(T)> decode_value(T val)
Decode a value into its constituent bytes (from most to least significant).
Definition helpers.h:910
std::string get_mac_address()
Get the device MAC address as a string, in lowercase hex notation.
Definition helpers.cpp:854
constexpr size_t format_bin_size(size_t byte_count)
Calculate buffer size needed for format_bin_to: "01234567...\0" = bytes * 8 + 1.
Definition helpers.h:1634
int written
Definition helpers.h:1089
struct ESPDEPRECATED("Use std::index_sequence instead. Removed in 2026.6.0", "2025.12.0") seq
Definition automation.h:26
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition helpers.h:77
constexpr char to_snake_case_char(char c)
Convert a single char to snake_case: lowercase and space to underscore.
Definition helpers.h:999
constexpr float fahrenheit_to_celsius(float value)
Convert degrees Fahrenheit to degrees Celsius.
Definition helpers.h:1764
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Definition helpers.h:920
char * format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length)
Format byte array as lowercase hex to buffer (base implementation).
Definition helpers.cpp:397
std::string str_snake_case(const std::string &str)
Convert the string to snake case (lowercase with underscores).
Definition helpers.cpp:242
float lerp(float completion, float start, float end)=delete
constexpr size_t format_hex_pretty_uint16_size(size_t count)
Calculate buffer size needed for format_hex_pretty_to with uint16_t data: "XXXX:XXXX:....
Definition helpers.h:1403
T remap(U value, U min, U max, T min_out, T max_out)
Remap value from the range (min, max) to (min_out, max_out).
Definition helpers.h:753
void * callback_manager_grow(void *data, uint16_t size, uint16_t &capacity, size_t elem_size)
Grow a CallbackManager's backing array to exactly size+1. Defined in helpers.cpp.
bool str_endswith(const std::string &str, const std::string &end)
Check whether a string ends with a value.
Definition helpers.cpp:210
constexpr uint32_t fnv1a_hash(const char *str)
Calculate a FNV-1a hash of str.
Definition helpers.h:822
std::string size_t std::string size_t buf_append_printf_p(char *buf, size_t size, size_t pos, PGM_P fmt,...)
Safely append formatted string to buffer, returning new position (capped at size).
Definition helpers.h:1060
float gamma
Definition helpers.h:1745
size_t base64_decode(const std::string &encoded_string, uint8_t *buf, size_t buf_len)
Definition helpers.cpp:649
uint16_t uint16_t & capacity
Definition helpers.cpp:25
ParseOnOffState
Return values for parse_on_off().
Definition helpers.h:1699
@ PARSE_ON
Definition helpers.h:1701
@ PARSE_TOGGLE
Definition helpers.h:1703
@ PARSE_OFF
Definition helpers.h:1702
@ PARSE_NONE
Definition helpers.h:1700
float pow10_int(int8_t exp)
Compute 10^exp using iterative multiplication/division.
Definition helpers.h:740
std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr, size_t suffix_len)
Optimized string concatenation: name + separator + suffix (const char* overload) Uses a fixed stack b...
Definition helpers.cpp:320
char * format_bin_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length)
Format byte array as binary string to buffer.
Definition helpers.cpp:482
char * format_mac_addr_upper(const uint8_t *mac, char *output)
Format MAC address as XX:XX:XX:XX:XX:XX (uppercase, colon separators)
Definition helpers.h:1435
std::string str_truncate(const std::string &str, size_t length)
Truncate a string to a specific length.
Definition helpers.cpp:224
static void uint32_t
static Callback create(F &&callable)
Create from any callable.
Definition helpers.h:1792
void call(Ts... args) const
Invoke the callback. Only valid on Callbacks created via create(), never on default-constructed insta...
Definition helpers.h:1788
Lightweight type-erased callback (8 bytes on 32-bit) that avoids std::function overhead.
Definition helpers.h:1776
uint8_t end[39]
Definition sun_gtil2.cpp:17
void byteswap()
uint16_t length
Definition tt21100.cpp:0
uint16_t x
Definition tt21100.cpp:5