ESPHome 2026.8.1
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
24
25// Backward compatibility re-export of heap-allocating helpers.
26// These functions have moved to alloc_helpers.h. External components should
27// update their includes to use #include "esphome/core/alloc_helpers.h" directly.
28// This re-export will be removed in 2026.11.0.
30
31#ifdef USE_ESP8266
32#include <Esp.h>
33#include <pgmspace.h>
34#endif
35
36#ifdef USE_RP2
37#include <Arduino.h>
38#endif
39
40#ifdef USE_ESP32
41#include <esp_heap_caps.h>
42#endif
43
44#if defined(USE_ESP32)
45#include <freertos/FreeRTOS.h>
46#include <freertos/semphr.h>
47#elif defined(USE_LIBRETINY)
48#include <FreeRTOS.h>
49#include <semphr.h>
50#endif
51
52#ifdef USE_HOST
53#include <mutex>
54#endif
55
56#define HOT __attribute__((hot))
57#define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg)))
58#define ESPHOME_ALWAYS_INLINE __attribute__((always_inline))
59#define PACKED __attribute__((packed))
60
61namespace esphome {
62
63// Forward declaration to avoid circular dependency with string_ref.h
64class StringRef;
65
68
69// Keep "using" even after the removal of our backports, to avoid breaking existing code.
70using std::to_string;
71using std::is_trivially_copyable;
72using std::make_unique;
73using std::enable_if_t;
74using std::clamp;
75using std::is_invocable;
76#if __cpp_lib_bit_cast >= 201806
77using std::bit_cast;
78#else
80template<
81 typename To, typename From,
82 enable_if_t<sizeof(To) == sizeof(From) && is_trivially_copyable<From>::value && is_trivially_copyable<To>::value,
83 int> = 0>
84To bit_cast(const From &src) {
85 To dst;
86 memcpy(&dst, &src, sizeof(To));
87 return dst;
88}
89#endif
90
91// clang-format off
92inline float lerp(float completion, float start, float end) = delete; // Please use std::lerp. Notice that it has different order on arguments!
93// clang-format on
94
95// std::byteswap from C++23
96template<typename T> constexpr T byteswap(T n) {
97 T m;
98 for (size_t i = 0; i < sizeof(T); i++)
99 reinterpret_cast<uint8_t *>(&m)[i] = reinterpret_cast<uint8_t *>(&n)[sizeof(T) - 1 - i];
100 return m;
101}
102template<> constexpr uint8_t byteswap(uint8_t n) { return n; }
103#ifdef USE_LIBRETINY
104// LibreTiny's Beken framework redefines __builtin_bswap functions as non-constexpr
105template<> inline uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
106template<> inline uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
107template<> inline uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
108template<> inline int8_t byteswap(int8_t n) { return n; }
109template<> inline int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
110template<> inline int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
111template<> inline int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
112#else
113template<> constexpr uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
114template<> constexpr uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
115template<> constexpr uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
116template<> constexpr int8_t byteswap(int8_t n) { return n; }
117template<> constexpr int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
118template<> constexpr int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
119template<> constexpr int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
120#endif
121
123
126
130
131template<typename T> class ConstVector {
132 public:
133 constexpr ConstVector(const T *data, size_t size) : data_(data), size_(size) {}
134
135 const constexpr T &operator[](size_t i) const { return data_[i]; }
136 constexpr size_t size() const { return size_; }
137 constexpr bool empty() const { return size_ == 0; }
138
139 protected:
140 const T *data_;
141 size_t size_;
142};
143
147template<size_t InlineSize = 8> class SmallInlineBuffer {
148 public:
149 SmallInlineBuffer() = default;
151 if (!this->is_inline_())
152 delete[] this->heap_;
153 }
154
155 // Move constructor
156 SmallInlineBuffer(SmallInlineBuffer &&other) noexcept : len_(other.len_) {
157 if (other.is_inline_()) {
158 memcpy(this->inline_, other.inline_, this->len_);
159 } else {
160 this->heap_ = other.heap_;
161 other.heap_ = nullptr;
162 }
163 other.len_ = 0;
164 }
165
166 // Move assignment
168 if (this != &other) {
169 if (!this->is_inline_())
170 delete[] this->heap_;
171 this->len_ = other.len_;
172 if (other.is_inline_()) {
173 memcpy(this->inline_, other.inline_, this->len_);
174 } else {
175 this->heap_ = other.heap_;
176 other.heap_ = nullptr;
177 }
178 other.len_ = 0;
179 }
180 return *this;
181 }
182
183 // Disable copy (would need deep copy of heap data)
186
187 bool empty() const { return this->len_ == 0; }
188
189 // Conversion to std::span for compatibility with span-based APIs
190 operator std::span<const uint8_t>() const { return std::span<const uint8_t>(this->data(), this->len_); }
191
195 uint8_t *init(size_t size) {
196 // Free existing heap allocation if switching from heap to inline or different heap size
198 delete[] this->heap_;
199 this->heap_ = nullptr; // Defensive: prevent use-after-free if logic changes
200 }
201 // Allocate new heap buffer if needed
202 if (size > InlineSize && (this->is_inline_() || size != this->len_)) {
203 this->heap_ = new uint8_t[size]; // NOLINT(cppcoreguidelines-owning-memory)
204 }
205 this->len_ = size;
206 return this->data();
207 }
208
210 void set(const uint8_t *src, size_t size) { memcpy(this->init(size), src, size); }
211
212 uint8_t *data() { return this->is_inline_() ? this->inline_ : this->heap_; }
213 const uint8_t *data() const { return this->is_inline_() ? this->inline_ : this->heap_; }
214 size_t size() const { return this->len_; }
215
216 protected:
217 bool is_inline_() const { return this->len_ <= InlineSize; }
218
219 size_t len_{0};
220 union {
221 uint8_t inline_[InlineSize]{}; // Zero-init ensures clean initial state
222 uint8_t *heap_;
223 };
224};
225
227template<typename T, size_t N> class StaticVector {
228 public:
229 using value_type = T;
230 using iterator = typename std::array<T, N>::iterator;
231 using const_iterator = typename std::array<T, N>::const_iterator;
232 using reverse_iterator = std::reverse_iterator<iterator>;
233 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
234
235 private:
236 std::array<T, N> data_; // intentionally not value-initialized to avoid memset
237 size_t count_{0};
238
239 public:
240 // Default constructor
241 StaticVector() = default;
242
243 // Iterator range constructor
244 template<typename InputIt> StaticVector(InputIt first, InputIt last) {
245 while (first != last && count_ < N) {
246 data_[count_++] = *first++;
247 }
248 }
249
250 // Initializer list constructor
251 StaticVector(std::initializer_list<T> init) {
252 for (const auto &val : init) {
253 if (count_ >= N)
254 break;
255 data_[count_++] = val;
256 }
257 }
258
259 // Converting constructor from a smaller StaticVector of the same element type
260 template<size_t M> StaticVector(const StaticVector<T, M> &other) : StaticVector(other.begin(), other.end()) {
261 static_assert(M <= N, "Source StaticVector cannot be larger than the destination");
262 }
263
264 // Minimal vector-compatible interface - only what we actually use
265 void push_back(const T &value) {
266 if (count_ < N) {
267 data_[count_++] = value;
268 }
269 }
270
271 // Clear all elements
272 void clear() { count_ = 0; }
273
274 // Assign from iterator range
275 template<typename InputIt> void assign(InputIt first, InputIt last) {
276 count_ = 0;
277 while (first != last && count_ < N) {
278 data_[count_++] = *first++;
279 }
280 }
281
282 // Return reference to next element and increment count (with bounds checking)
284 if (count_ >= N) {
285 // Should never happen with proper size calculation
286 // Return reference to last element to avoid crash
287 return data_[N - 1];
288 }
289 return data_[count_++];
290 }
291
292 size_t size() const { return count_; }
293 bool empty() const { return count_ == 0; }
294
295 // Direct access to underlying data
296 T *data() { return data_.data(); }
297 const T *data() const { return data_.data(); }
298
299 T &operator[](size_t i) { return data_[i]; }
300 const T &operator[](size_t i) const { return data_[i]; }
301
302 // For range-based for loops
303 iterator begin() { return data_.begin(); }
304 iterator end() { return data_.begin() + count_; }
305 const_iterator begin() const { return data_.begin(); }
306 const_iterator end() const { return data_.begin() + count_; }
307
308 // Reverse iterators
313
314 // Conversion to std::span for compatibility with span-based APIs
315 operator std::span<T>() { return std::span<T>(data_.data(), count_); }
316 operator std::span<const T>() const { return std::span<const T>(data_.data(), count_); }
317};
318
326template<typename T, size_t N> class StaticRingBuffer {
327 using index_type = std::conditional_t<(N <= std::numeric_limits<uint8_t>::max()), uint8_t, uint16_t>;
328
329 public:
330 class Iterator {
331 public:
332 Iterator(StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
333 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % N]; }
335 ++pos_;
336 return *this;
337 }
338 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
339
340 private:
341 StaticRingBuffer *buf_;
342 index_type pos_;
343 };
344
346 public:
347 ConstIterator(const StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
348 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % N]; }
350 ++pos_;
351 return *this;
352 }
353 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
354
355 private:
356 const StaticRingBuffer *buf_;
357 index_type pos_;
358 };
359
360 bool push(const T &value) {
361 if (this->count_ >= N) {
362 return false;
363 }
364 this->data_[this->tail_] = value;
365 this->tail_ = (this->tail_ + 1) % N;
366 ++this->count_;
367 return true;
368 }
369
370 void pop() {
371 if (this->count_ > 0) {
372 this->head_ = (this->head_ + 1) % N;
373 --this->count_;
374 }
375 }
376
377 T &front() { return this->data_[this->head_]; }
378 const T &front() const { return this->data_[this->head_]; }
379 index_type size() const { return this->count_; }
380 bool empty() const { return this->count_ == 0; }
381
383 void clear() {
384 this->head_ = 0;
385 this->tail_ = 0;
386 this->count_ = 0;
387 }
388
389 Iterator begin() { return Iterator(this, 0); }
390 Iterator end() { return Iterator(this, this->count_); }
391 ConstIterator begin() const { return ConstIterator(this, 0); }
392 ConstIterator end() const { return ConstIterator(this, this->count_); }
393
394 protected:
395 T data_[N];
396 index_type head_{0};
397 index_type tail_{0};
398 index_type count_{0};
399};
400
405template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()> class FixedRingBuffer {
406 using index_type = std::conditional_t<
407 (MAX_CAPACITY <= std::numeric_limits<uint8_t>::max()), uint8_t,
408 std::conditional_t<(MAX_CAPACITY <= std::numeric_limits<uint16_t>::max()), uint16_t, uint32_t>>;
409
410 public:
411 class Iterator {
412 public:
413 Iterator(FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
414 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
416 ++pos_;
417 return *this;
418 }
419 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
420
421 private:
422 FixedRingBuffer *buf_;
423 index_type pos_;
424 };
425
427 public:
428 ConstIterator(const FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
429 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
431 ++pos_;
432 return *this;
433 }
434 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
435
436 private:
437 const FixedRingBuffer *buf_;
438 index_type pos_;
439 };
440
441 FixedRingBuffer() = default;
443 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
444 ::operator delete(this->data_);
445 } else {
446 delete[] this->data_;
447 }
448 }
449
450 // Disable copy
453
455 void init(index_type capacity) {
456 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
457 // Raw allocation without initialization (elements are written before read)
458 // NOLINTNEXTLINE(bugprone-sizeof-expression)
459 this->data_ = static_cast<T *>(::operator new(capacity * sizeof(T)));
460 } else {
461 this->data_ = new T[capacity];
462 }
463 this->capacity_ = capacity;
464 }
465
467 bool push(const T &value) {
468 if (this->count_ >= this->capacity_)
469 return false;
470 this->data_[this->tail_] = value;
471 this->tail_ = (this->tail_ + 1) % this->capacity_;
472 ++this->count_;
473 return true;
474 }
475
477 void push_overwrite(const T &value) {
478 this->data_[this->tail_] = value;
479 this->tail_ = (this->tail_ + 1) % this->capacity_;
480 if (this->count_ >= this->capacity_) {
481 // Buffer full - advance head to drop oldest, count stays at capacity
482 this->head_ = this->tail_;
483 } else {
484 ++this->count_;
485 }
486 }
487
489 void pop() {
490 if (this->count_ > 0) {
491 this->head_ = (this->head_ + 1) % this->capacity_;
492 --this->count_;
493 }
494 }
495
496 T &front() { return this->data_[this->head_]; }
497 const T &front() const { return this->data_[this->head_]; }
498 index_type size() const { return this->count_; }
499 bool empty() const { return this->count_ == 0; }
500 index_type capacity() const { return this->capacity_; }
501 bool full() const { return this->count_ == this->capacity_; }
502
504 void clear() {
505 this->head_ = 0;
506 this->tail_ = 0;
507 this->count_ = 0;
508 }
509
510 Iterator begin() { return Iterator(this, 0); }
511 Iterator end() { return Iterator(this, this->count_); }
512 ConstIterator begin() const { return ConstIterator(this, 0); }
513 ConstIterator end() const { return ConstIterator(this, this->count_); }
514
515 protected:
516 T *data_{nullptr};
517 index_type head_{0};
518 index_type tail_{0};
519 index_type count_{0};
520 index_type capacity_{0};
521};
522
527template<typename T, size_t N> inline void init_array_from(std::array<T, N> &dest, std::initializer_list<T> src) {
528#ifdef ESPHOME_DEBUG
529 assert(src.size() == N);
530#endif
531 if constexpr (std::is_trivially_copyable_v<T>) {
532 __builtin_memcpy(dest.data(), src.begin(), N * sizeof(T));
533 } else {
534 size_t i = 0;
535 for (const auto &v : src) {
536 dest[i++] = v;
537 }
538 }
539}
540
544template<typename T> class FixedVector {
545 private:
546 T *data_{nullptr};
547 size_t size_{0};
548 size_t capacity_{0};
549
550 // Helper to destroy all elements without freeing memory
551 void destroy_elements_() {
552 // Only call destructors for non-trivially destructible types
553 if constexpr (!std::is_trivially_destructible<T>::value) {
554 for (size_t i = 0; i < size_; i++) {
555 data_[i].~T();
556 }
557 }
558 }
559
560 // Helper to destroy elements and free memory
561 void cleanup_() {
562 if (data_ != nullptr) {
563 destroy_elements_();
564 // Free raw memory
565 ::operator delete(data_);
566 }
567 }
568
569 // Helper to reset pointers after cleanup
570 void reset_() {
571 data_ = nullptr;
572 capacity_ = 0;
573 size_ = 0;
574 }
575
576 // Helper to assign from initializer list (shared by constructor and assignment operator)
577 void assign_from_initializer_list_(std::initializer_list<T> init_list) {
578 init(init_list.size());
579 size_t idx = 0;
580 for (const auto &item : init_list) {
581 new (data_ + idx) T(item);
582 ++idx;
583 }
584 size_ = init_list.size();
585 }
586
587 public:
588 FixedVector() = default;
589
592 FixedVector(std::initializer_list<T> init_list) { assign_from_initializer_list_(init_list); }
593
594 ~FixedVector() { cleanup_(); }
595
596 // Disable copy operations (avoid accidental expensive copies)
597 FixedVector(const FixedVector &) = delete;
599
600 // Enable move semantics (allows use in move-only containers like std::vector)
601 FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
602 other.reset_();
603 }
604
605 // Allow conversion to std::vector
606 operator std::vector<T>() const { return {data_, data_ + size_}; }
607
608 FixedVector &operator=(FixedVector &&other) noexcept {
609 if (this != &other) {
610 // Delete our current data
611 cleanup_();
612 // Take ownership of other's data
613 data_ = other.data_;
614 size_ = other.size_;
615 capacity_ = other.capacity_;
616 // Leave other in valid empty state
617 other.reset_();
618 }
619 return *this;
620 }
621
624 FixedVector &operator=(std::initializer_list<T> init_list) {
625 cleanup_();
626 reset_();
627 assign_from_initializer_list_(init_list);
628 return *this;
629 }
630
631 // Allocate capacity - can be called multiple times to reinit
632 // IMPORTANT: After calling init(), you MUST use push_back() to add elements.
633 // Direct assignment via operator[] does NOT update the size counter.
634 void init(size_t n) {
635 cleanup_();
636 reset_();
637 if (n > 0) {
638 // Allocate raw memory without calling constructors
639 // sizeof(T) is correct here for any type T (value types, pointers, etc.)
640 // NOLINTNEXTLINE(bugprone-sizeof-expression)
641 data_ = static_cast<T *>(::operator new(n * sizeof(T)));
642 capacity_ = n;
643 }
644 }
645
646 // Clear the vector (destroy all elements, reset size to 0, keep capacity)
647 void clear() {
648 destroy_elements_();
649 size_ = 0;
650 }
651
652 // Release all memory (destroys elements and frees memory)
653 void release() {
654 cleanup_();
655 reset_();
656 }
657
661 void push_back(const T &value) {
662 if (size_ < capacity_) {
663 // Use placement new to construct the object in pre-allocated memory
664 new (&data_[size_]) T(value);
665 size_++;
666 }
667 }
668
672 void push_back(T &&value) {
673 if (size_ < capacity_) {
674 // Use placement new to move-construct the object in pre-allocated memory
675 new (&data_[size_]) T(std::move(value));
676 size_++;
677 }
678 }
679
684 template<typename... Args> T &emplace_back(Args &&...args) {
685 // Use placement new to construct the object in pre-allocated memory
686 new (&data_[size_]) T(std::forward<Args>(args)...);
687 size_++;
688 return data_[size_ - 1];
689 }
690
693 T &front() { return data_[0]; }
694 const T &front() const { return data_[0]; }
695
698 T &back() { return data_[size_ - 1]; }
699 const T &back() const { return data_[size_ - 1]; }
700
703 void pop_back() {
704 if constexpr (!std::is_trivially_destructible<T>::value) {
705 data_[size_ - 1].~T();
706 }
707 size_--;
708 }
709
710 size_t size() const { return size_; }
711 bool empty() const { return size_ == 0; }
712 size_t capacity() const { return capacity_; }
713 bool full() const { return size_ == capacity_; }
714
717 T &operator[](size_t i) { return data_[i]; }
718 const T &operator[](size_t i) const { return data_[i]; }
719
722 T &at(size_t i) { return data_[i]; }
723 const T &at(size_t i) const { return data_[i]; }
724
725 // Iterator support for range-based for loops
726 T *begin() { return data_; }
727 T *end() { return data_ + size_; }
728 const T *begin() const { return data_; }
729 const T *end() const { return data_ + size_; }
730};
731
737template<size_t STACK_SIZE, typename T = uint8_t> class SmallBufferWithHeapFallback {
738 public:
740 if (size <= STACK_SIZE) {
741 this->buffer_ = this->stack_buffer_;
742 } else {
743 this->heap_buffer_ = new T[size];
744 this->buffer_ = this->heap_buffer_;
745 }
746 }
747 ~SmallBufferWithHeapFallback() { delete[] this->heap_buffer_; }
748
749 // Delete copy and move operations to prevent double-delete
754
755 T *get() { return this->buffer_; }
756
757 private:
758 T stack_buffer_[STACK_SIZE];
759 T *heap_buffer_{nullptr};
760 T *buffer_;
761};
762
764
767
771int8_t ilog10(float value);
772
776inline float pow10_int(int8_t exp) {
777 float result = 1.0f;
778 if (exp >= 0) {
779 for (int8_t i = 0; i < exp; i++)
780 result *= 10.0f;
781 } else {
782 for (int8_t i = exp; i < 0; i++)
783 result /= 10.0f;
784 }
785 return result;
786}
787
789template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max_out) {
790 return (value - min) * (max_out - min_out) / (max - min) + min_out;
791}
792
794uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc = 0x00, uint8_t poly = 0x8C, bool msb_first = false);
795
797uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001,
798 bool refin = false, bool refout = false);
799uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc = 0, uint16_t poly = 0x1021, bool refin = false,
800 bool refout = false);
801
804uint32_t fnv1_hash(const char *str);
805inline uint32_t fnv1_hash(const std::string &str) { return fnv1_hash(str.c_str()); }
806
808constexpr uint32_t FNV1_OFFSET_BASIS = 2166136261UL;
810constexpr uint32_t FNV1_PRIME = 16777619UL;
811
813template<std::integral T> constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value) {
814 using UnsignedT = std::make_unsigned_t<T>;
815 UnsignedT uvalue = static_cast<UnsignedT>(value);
816 for (size_t i = 0; i < sizeof(T); i++) {
817 hash *= FNV1_PRIME;
818 hash ^= (uvalue >> (i * 8)) & 0xFF;
819 }
820 return hash;
821}
823constexpr uint32_t fnv1_hash_extend(uint32_t hash, const char *str) {
824 if (str) {
825 while (*str) {
826 hash *= FNV1_PRIME;
827 hash ^= *str++;
828 }
829 }
830 return hash;
831}
832inline uint32_t fnv1_hash_extend(uint32_t hash, const std::string &str) { return fnv1_hash_extend(hash, str.c_str()); }
833
835constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str) {
836 if (str) {
837 while (*str) {
838 hash ^= *str++;
839 hash *= FNV1_PRIME;
840 }
841 }
842 return hash;
843}
844inline uint32_t fnv1a_hash_extend(uint32_t hash, const std::string &str) {
845 return fnv1a_hash_extend(hash, str.c_str());
846}
848template<std::integral T> constexpr uint32_t fnv1a_hash_extend(uint32_t hash, T value) {
849 using UnsignedT = std::make_unsigned_t<T>;
850 UnsignedT uvalue = static_cast<UnsignedT>(value);
851 for (size_t i = 0; i < sizeof(T); i++) {
852 hash ^= (uvalue >> (i * 8)) & 0xFF;
853 hash *= FNV1_PRIME;
854 }
855 return hash;
856}
858constexpr uint32_t fnv1a_hash(const char *str) { return fnv1a_hash_extend(FNV1_OFFSET_BASIS, str); }
859inline uint32_t fnv1a_hash(const std::string &str) { return fnv1a_hash(str.c_str()); }
860
861// micros_to_millis<>() lives in its own lightweight header so hal.h can pull it
862// in for inline millis_64() without forcing every TU that includes hal.h to
863// also include the rest of helpers.h.
864
872float random_float();
875bool random_bytes(uint8_t *data, size_t len);
876
878
881
883constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb) {
884 return (static_cast<uint16_t>(msb) << 8) | (static_cast<uint16_t>(lsb));
885}
887constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) {
888 return (static_cast<uint32_t>(byte1) << 16) | (static_cast<uint32_t>(byte2) << 8) | (static_cast<uint32_t>(byte3));
889}
891constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) {
892 return (static_cast<uint32_t>(byte1) << 24) | (static_cast<uint32_t>(byte2) << 16) |
893 (static_cast<uint32_t>(byte3) << 8) | (static_cast<uint32_t>(byte4));
894}
895
897template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> constexpr T encode_value(const uint8_t *bytes) {
898 T val = 0;
899 for (size_t i = 0; i < sizeof(T); i++) {
900 val <<= 8;
901 val |= bytes[i];
902 }
903 return val;
904}
906template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
907constexpr T encode_value(const std::array<uint8_t, sizeof(T)> bytes) {
908 return encode_value<T>(bytes.data());
909}
911template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
912constexpr std::array<uint8_t, sizeof(T)> decode_value(T val) {
913 std::array<uint8_t, sizeof(T)> ret{};
914 for (size_t i = sizeof(T); i > 0; i--) {
915 ret[i - 1] = val & 0xFF;
916 val >>= 8;
917 }
918 return ret;
919}
920
922inline uint8_t reverse_bits(uint8_t x) {
923 x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
924 x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
925 x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
926 return x;
927}
929inline uint16_t reverse_bits(uint16_t x) {
930 return (reverse_bits(static_cast<uint8_t>(x & 0xFF)) << 8) | reverse_bits(static_cast<uint8_t>((x >> 8) & 0xFF));
931}
934 return (reverse_bits(static_cast<uint16_t>(x & 0xFFFF)) << 16) |
935 reverse_bits(static_cast<uint16_t>((x >> 16) & 0xFFFF));
936}
937
939template<typename T> constexpr T convert_big_endian(T val) {
940#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
941 return byteswap(val);
942#else
943 return val;
944#endif
945}
946
948template<typename T> constexpr T convert_little_endian(T val) {
949#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
950 return val;
951#else
952 return byteswap(val);
953#endif
954}
955
957
960
962bool str_equals_case_insensitive(const std::string &a, const std::string &b);
964bool str_equals_case_insensitive(StringRef a, StringRef b);
966inline bool str_equals_case_insensitive(const char *a, const char *b) { return strcasecmp(a, b) == 0; }
967inline bool str_equals_case_insensitive(const std::string &a, const char *b) { return strcasecmp(a.c_str(), b) == 0; }
968inline bool str_equals_case_insensitive(const char *a, const std::string &b) { return strcasecmp(a, b.c_str()) == 0; }
969
971bool str_startswith(const std::string &str, const std::string &start);
973bool str_endswith(const std::string &str, const std::string &end);
974
976bool str_endswith_ignore_case(const char *str, size_t str_len, const char *suffix, size_t suffix_len);
977inline bool str_endswith_ignore_case(const char *str, const char *suffix) {
978 return str_endswith_ignore_case(str, strlen(str), suffix, strlen(suffix));
979}
980inline bool str_endswith_ignore_case(const std::string &str, const char *suffix) {
981 return str_endswith_ignore_case(str.c_str(), str.size(), suffix, strlen(suffix));
982}
983
984// str_truncate moved to alloc_helpers.h - remove this include before 2026.11.0
985
986// str_until, str_lower_case, str_upper_case moved to alloc_helpers.h - remove this comment before 2026.11.0
987
989constexpr char to_snake_case_char(char c) { return (c == ' ') ? '_' : (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }
990// str_snake_case moved to alloc_helpers.h - remove this comment before 2026.11.0
991
993constexpr char to_sanitized_char(char c) {
994 return (c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? c : '_';
995}
996
1006char *str_sanitize_to(char *buffer, size_t buffer_size, const char *str);
1007
1009template<size_t N> inline char *str_sanitize_to(char (&buffer)[N], const char *str) {
1010 return str_sanitize_to(buffer, N, str);
1011}
1012
1013// str_sanitize moved to alloc_helpers.h - remove this comment before 2026.11.0
1014
1019inline uint32_t fnv1_hash_object_id(const char *str, size_t len) {
1021 for (size_t i = 0; i < len; i++) {
1022 hash *= FNV1_PRIME;
1023 // Apply snake_case (space->underscore, uppercase->lowercase) then sanitize
1024 hash ^= static_cast<uint8_t>(to_sanitized_char(to_snake_case_char(str[i])));
1025 }
1026 return hash;
1027}
1028
1029// str_snprintf, str_sprintf moved to alloc_helpers.h - remove this comment before 2026.11.0
1030
1031#ifdef USE_ESP8266
1032// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM)
1033// Format strings must be wrapped with PSTR() macro
1040inline size_t buf_append_printf_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) {
1041 if (pos >= size) {
1042 return size;
1043 }
1044 va_list args;
1045 va_start(args, fmt);
1046 int written = vsnprintf_P(buf + pos, size - pos, fmt, args);
1047 va_end(args);
1048 if (written < 0) {
1049 return pos; // encoding error
1050 }
1051 return std::min(pos + static_cast<size_t>(written), size);
1052}
1053#define buf_append_printf(buf, size, pos, fmt, ...) buf_append_printf_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__)
1054#else
1062__attribute__((format(printf, 4, 5))) inline size_t buf_append_printf(char *buf, size_t size, size_t pos,
1063 const char *fmt, ...) {
1064 if (pos >= size) {
1065 return size;
1066 }
1067 va_list args;
1069 int written = vsnprintf(buf + pos, size - pos, fmt, args);
1071 if (written < 0) {
1072 return pos; // encoding error
1073 }
1074 return std::min(pos + static_cast<size_t>(written), size);
1075}
1076#endif
1077
1078#ifdef USE_ESP8266
1088inline size_t buf_append_str_p(char *buf, size_t size, size_t pos, PGM_P str) {
1089 if (pos >= size) {
1090 return size;
1091 }
1092 size_t remaining = size - pos - 1; // reserve space for null terminator
1093 size_t len = strnlen_P(str, remaining);
1094 memcpy_P(buf + pos, str, len);
1095 pos += len;
1096 buf[pos] = '\0';
1097 return pos;
1098}
1102#define buf_append_str(buf, size, pos, str) buf_append_str_p(buf, size, pos, PSTR(str))
1103#else
1111inline size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str) {
1112 if (pos >= size) {
1113 return size;
1114 }
1115 size_t remaining = size - pos - 1; // reserve space for null terminator
1116 size_t len = 0;
1117 while (len < remaining && str[len] != '\0') {
1118 len++;
1119 }
1120 memcpy(buf + pos, str, len);
1121 pos += len;
1122 buf[pos] = '\0';
1123 return pos;
1124}
1125#endif
1126
1135std::string make_name_with_suffix(const std::string &name, char sep, const char *suffix_ptr, size_t suffix_len);
1136
1145std::string make_name_with_suffix(const char *name, size_t name_len, char sep, const char *suffix_ptr,
1146 size_t suffix_len);
1147
1157size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep,
1158 const char *suffix_ptr, size_t suffix_len);
1159
1161
1164
1166template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1167optional<T> parse_number(const char *str) {
1168 char *end = nullptr;
1169 unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
1170 if (end == str || *end != '\0' || value > std::numeric_limits<T>::max())
1171 return {};
1172 return value;
1173}
1175template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1176optional<T> parse_number(const std::string &str) {
1177 return parse_number<T>(str.c_str());
1178}
1180template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1181optional<T> parse_number(const char *str) {
1182 char *end = nullptr;
1183 signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
1184 if (end == str || *end != '\0' || value < std::numeric_limits<T>::min() || value > std::numeric_limits<T>::max())
1185 return {};
1186 return value;
1187}
1189template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1190optional<T> parse_number(const std::string &str) {
1191 return parse_number<T>(str.c_str());
1192}
1194template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0> optional<T> parse_number(const char *str) {
1195 char *end = nullptr;
1196 float value = ::strtof(str, &end);
1197 if (end == str || *end != '\0' || value == HUGE_VALF)
1198 return {};
1199 return value;
1200}
1202template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
1203optional<T> parse_number(const std::string &str) {
1204 return parse_number<T>(str.c_str());
1205}
1206
1218size_t parse_hex(const char *str, size_t len, uint8_t *data, size_t count);
1220inline bool parse_hex(const char *str, uint8_t *data, size_t count) {
1221 return parse_hex(str, strlen(str), data, count) == 2 * count;
1222}
1224inline bool parse_hex(const std::string &str, uint8_t *data, size_t count) {
1225 return parse_hex(str.c_str(), str.length(), data, count) == 2 * count;
1226}
1228inline bool parse_hex(const char *str, std::vector<uint8_t> &data, size_t count) {
1229 data.resize(count);
1230 return parse_hex(str, strlen(str), data.data(), count) == 2 * count;
1231}
1233inline bool parse_hex(const std::string &str, std::vector<uint8_t> &data, size_t count) {
1234 data.resize(count);
1235 return parse_hex(str.c_str(), str.length(), data.data(), count) == 2 * count;
1236}
1242template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1243optional<T> parse_hex(const char *str, size_t len) {
1244 T val = 0;
1245 if (len > 2 * sizeof(T) || parse_hex(str, len, reinterpret_cast<uint8_t *>(&val), sizeof(T)) == 0)
1246 return {};
1247 return convert_big_endian(val);
1248}
1250template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const char *str) {
1251 return parse_hex<T>(str, strlen(str));
1252}
1254template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const std::string &str) {
1255 return parse_hex<T>(str.c_str(), str.length());
1256}
1257
1260static constexpr uint8_t INVALID_HEX_CHAR = 255;
1261
1262constexpr uint8_t parse_hex_char(char c) {
1263 if (c >= '0' && c <= '9')
1264 return c - '0';
1265 if (c >= 'A' && c <= 'F')
1266 return c - 'A' + 10;
1267 if (c >= 'a' && c <= 'f')
1268 return c - 'a' + 10;
1269 return INVALID_HEX_CHAR;
1270}
1271
1273ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
1274
1276ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
1277
1279ESPHOME_ALWAYS_INLINE inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
1280
1282static constexpr size_t JSON_ESCAPE_MAX_EXPANSION = 6;
1283
1295const char *json_escape_into_buffer(std::span<char> buf, StringRef value, bool short_control_escapes = true);
1296
1299inline char *int8_to_str(char *buf, int8_t val) {
1300 int32_t v = val;
1301 if (v < 0) {
1302 *buf++ = '-';
1303 v = -v;
1304 }
1305 if (v >= 100) {
1306 *buf++ = '1'; // int8 max is 128, so hundreds digit is always 1
1307 v -= 100;
1308 // Must write tens digit (even if 0) after hundreds
1309 int32_t tens = v / 10;
1310 *buf++ = '0' + tens;
1311 v -= tens * 10;
1312 } else if (v >= 10) {
1313 int32_t tens = v / 10;
1314 *buf++ = '0' + tens;
1315 v -= tens * 10;
1316 }
1317 *buf++ = '0' + v;
1318 return buf;
1319}
1320
1325inline char *buf_append_sep_str(char *buf, size_t remaining, char separator, const char *str, size_t str_len) {
1326 if (remaining < 2) {
1327 if (remaining >= 1) {
1328 *buf = '\0';
1329 }
1330 return buf;
1331 }
1332 *buf++ = separator;
1333 remaining--;
1334 size_t copy_len = std::min(str_len, remaining - 1);
1335 memcpy(buf, str, copy_len);
1336 buf += copy_len;
1337 *buf = '\0';
1338 return buf;
1339}
1340
1342inline uint32_t small_pow10(int8_t n) { return n == 3 ? 1000 : n == 2 ? 100 : n == 1 ? 10 : 1; }
1343
1345static constexpr size_t UINT32_MAX_STR_SIZE = 11;
1346
1349char *uint32_to_str_unchecked(char *buf, uint32_t val);
1350
1353inline size_t uint32_to_str(std::span<char, UINT32_MAX_STR_SIZE> buf, uint32_t val) {
1354 char *end = uint32_to_str_unchecked(buf.data(), val);
1355 *end = '\0';
1356 return static_cast<size_t>(end - buf.data());
1357}
1358
1362inline char *frac_to_str_unchecked(char *buf, uint32_t frac, uint32_t divisor) {
1363 while (divisor > 0) {
1364 *buf++ = '0' + static_cast<char>(frac / divisor);
1365 frac %= divisor;
1366 divisor /= 10;
1367 }
1368 return buf;
1369}
1370
1372char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1373
1376template<size_t N> inline char *format_hex_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1377 static_assert(N >= 3, "Buffer must hold at least one hex byte (3 chars)");
1378 return format_hex_to(buffer, N, data, length);
1379}
1380
1382template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1383inline char *format_hex_to(char (&buffer)[N], T val) {
1384 static_assert(N >= sizeof(T) * 2 + 1, "Buffer too small for type");
1386 return format_hex_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1387}
1388
1390template<size_t N> inline char *format_hex_to(char (&buffer)[N], const std::vector<uint8_t> &data) {
1391 return format_hex_to(buffer, data.data(), data.size());
1392}
1393
1395template<size_t N, size_t M> inline char *format_hex_to(char (&buffer)[N], const std::array<uint8_t, M> &data) {
1396 return format_hex_to(buffer, data.data(), data.size());
1397}
1398
1400constexpr size_t format_hex_size(size_t byte_count) { return byte_count * 2 + 1; }
1401
1403constexpr size_t format_hex_prefixed_size(size_t byte_count) { return byte_count * 2 + 3; }
1404
1406template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1407inline char *format_hex_prefixed_to(char (&buffer)[N], T val) {
1408 static_assert(N >= sizeof(T) * 2 + 3, "Buffer too small for prefixed hex");
1409 buffer[0] = '0';
1410 buffer[1] = 'x';
1412 format_hex_to(buffer + 2, N - 2, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1413 return buffer;
1414}
1415
1417template<size_t N> inline char *format_hex_prefixed_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1418 static_assert(N >= 5, "Buffer must hold at least '0x' + one hex byte + null");
1419 buffer[0] = '0';
1420 buffer[1] = 'x';
1421 format_hex_to(buffer + 2, N - 2, data, length);
1422 return buffer;
1423}
1424
1426constexpr size_t format_hex_pretty_size(size_t byte_count) { return byte_count * 3; }
1427
1439char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator = ':');
1440
1442template<size_t N>
1443inline char *format_hex_pretty_to(char (&buffer)[N], const uint8_t *data, size_t length, char separator = ':') {
1444 static_assert(N >= 3, "Buffer must hold at least one hex byte");
1445 return format_hex_pretty_to(buffer, N, data, length, separator);
1446}
1447
1449template<size_t N>
1450inline char *format_hex_pretty_to(char (&buffer)[N], const std::vector<uint8_t> &data, char separator = ':') {
1451 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1452}
1453
1455template<size_t N, size_t M>
1456inline char *format_hex_pretty_to(char (&buffer)[N], const std::array<uint8_t, M> &data, char separator = ':') {
1457 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1458}
1459
1461constexpr size_t format_hex_pretty_uint16_size(size_t count) { return count * 5; }
1462
1476char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint16_t *data, size_t length, char separator = ':');
1477
1479template<size_t N>
1480inline char *format_hex_pretty_to(char (&buffer)[N], const uint16_t *data, size_t length, char separator = ':') {
1481 static_assert(N >= 5, "Buffer must hold at least one hex uint16_t");
1482 return format_hex_pretty_to(buffer, N, data, length, separator);
1483}
1484
1486static constexpr size_t MAC_ADDRESS_SIZE = 6;
1488static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = format_hex_pretty_size(MAC_ADDRESS_SIZE);
1490static constexpr size_t MAC_ADDRESS_BUFFER_SIZE = MAC_ADDRESS_SIZE * 2 + 1;
1491
1493inline char *format_mac_addr_upper(const uint8_t *mac, char *output) {
1494 return format_hex_pretty_to(output, MAC_ADDRESS_PRETTY_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE, ':');
1495}
1496
1498inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) {
1499 format_hex_to(output, MAC_ADDRESS_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE);
1500}
1501
1502// format_mac_address_pretty, format_hex (all overloads) moved to alloc_helpers.h
1503// Remove this comment and the template overloads below before 2026.11.0
1504
1507template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_hex(T val) {
1509 return format_hex(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1510}
1513template<std::size_t N> std::string format_hex(const std::array<uint8_t, N> &data) {
1514 return format_hex(data.data(), data.size());
1515}
1516
1517// format_hex_pretty (all overloads) moved to alloc_helpers.h
1518// Remove this comment and the template overload below before 2026.11.0
1519
1522template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1523std::string format_hex_pretty(T val, char separator = '.', bool show_length = true) {
1525 return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T), separator, show_length);
1526}
1527
1529constexpr size_t format_bin_size(size_t byte_count) { return byte_count * 8 + 1; }
1530
1550char *format_bin_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1551
1553template<size_t N> inline char *format_bin_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1554 static_assert(N >= 9, "Buffer must hold at least one binary byte (9 chars)");
1555 return format_bin_to(buffer, N, data, length);
1556}
1557
1574template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1575inline char *format_bin_to(char (&buffer)[N], T val) {
1576 static_assert(N >= sizeof(T) * 8 + 1, "Buffer too small for type");
1578 return format_bin_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1579}
1580
1581// format_bin moved to alloc_helpers.h - remove this comment and template overload before 2026.11.0
1582
1585template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_bin(T val) {
1587 return format_bin(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1588}
1589
1598ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr);
1599
1600// value_accuracy_to_string moved to alloc_helpers.h - remove this comment before 2026.11.0
1601
1603static constexpr size_t VALUE_ACCURACY_MAX_LEN = 64;
1604
1606size_t value_accuracy_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value, int8_t accuracy_decimals);
1608size_t value_accuracy_with_uom_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value,
1609 int8_t accuracy_decimals, StringRef unit_of_measurement);
1610
1612int8_t step_to_accuracy_decimals(float step);
1613
1614// base64_encode (both overloads), base64_decode (vector overload) moved to alloc_helpers.h
1615// Remove this comment before 2026.11.0
1616size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len);
1617size_t base64_decode(const uint8_t *encoded_data, size_t encoded_len, uint8_t *buf, size_t buf_len);
1618
1623bool base64_decode_int32_vector(const std::string &base64, std::vector<int32_t> &out);
1624
1626
1629
1631// Remove before 2026.9.0
1632ESPDEPRECATED("Use LightState::gamma_correct_lut() instead. Removed in 2026.9.0.", "2026.3.0")
1633float gamma_correct(float value, float gamma);
1635// Remove before 2026.9.0
1636ESPDEPRECATED("Use LightState::gamma_uncorrect_lut() instead. Removed in 2026.9.0.", "2026.3.0")
1637float gamma_uncorrect(float value, float gamma);
1638
1640void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value);
1642void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue);
1643
1645
1648
1650constexpr float celsius_to_fahrenheit(float value) { return value * 1.8f + 32.0f; }
1652constexpr float fahrenheit_to_celsius(float value) { return (value - 32.0f) / 1.8f; }
1653
1654enum class TemperatureUnit : uint8_t {
1655 CELSIUS = 0,
1656 FAHRENHEIT = 1,
1657 KELVIN = 2,
1658};
1659
1661
1664
1670template<typename... X> struct Callback;
1671
1672template<typename... Ts> struct Callback<void(Ts...)> {
1673 // The inline storage path stores callable bytes in ctx_ via memcpy.
1674 // sizeof equality with uintptr_t ensures void* can round-trip arbitrary bit patterns,
1675 // which combined with flat address spaces on all ESPHome targets means no trap representations.
1676 static_assert(sizeof(void *) == sizeof(std::uintptr_t), "void* must be the same size as uintptr_t");
1677
1678 void (*fn_)(void *, Ts...){nullptr};
1679 void *ctx_{nullptr};
1680
1682 void call(Ts... args) const { this->fn_(this->ctx_, std::forward<Ts>(args)...); }
1683
1686 template<typename F> static Callback create(F &&callable) {
1687 using DecayF = std::decay_t<F>;
1688 if constexpr (sizeof(DecayF) <= sizeof(void *) && std::is_trivially_copyable_v<DecayF>) {
1689 // Small trivial callable (e.g. [this]() { this->method(); }) - store inline in ctx.
1690 // Safe under C++20 (P0593R6): byte copy into aligned storage implicitly
1691 // creates objects of implicit-lifetime types (trivially copyable qualifies).
1692 Callback cb; // fn and ctx are zero-initialized by default
1693 // Decay callable to a local variable first. When F is a function reference
1694 // (e.g. void(&)(int)), &callable would point at machine code, not a pointer variable.
1695 DecayF decayed = std::forward<F>(callable);
1696 __builtin_memcpy(&cb.ctx_, &decayed, sizeof(DecayF));
1697 cb.fn_ = [](void *c, Ts... args) {
1698 alignas(DecayF) char buf[sizeof(DecayF)];
1699 __builtin_memcpy(buf, &c, sizeof(DecayF));
1700 (*std::launder(reinterpret_cast<DecayF *>(buf)))(args...);
1701 };
1702 return cb;
1703 } else {
1704 // Large or non-trivial callable - heap allocate.
1705 // Intentionally never freed: callbacks in ESPHome are registered during setup()
1706 // and live for device lifetime. Same lifetime as the previous std::function approach.
1707 auto *stored = new DecayF(std::forward<F>(callable));
1708 return {[](void *c, Ts... args) { (*static_cast<DecayF *>(c))(args...); }, static_cast<void *>(stored)};
1709 }
1710 }
1711};
1712
1714void *callback_manager_grow(void *data, uint16_t size, uint16_t &capacity, size_t elem_size);
1715
1716template<typename... X> class CallbackManager;
1717
1729template<typename... Ts> class CallbackManager<void(Ts...)> {
1730 using CbType = Callback<void(Ts...)>;
1731 static_assert(std::is_trivially_copyable_v<CbType>, "Callback must be trivially copyable");
1732
1733 public:
1734 CallbackManager() = default;
1735 ~CallbackManager() { ::operator delete(this->data_); }
1736
1737 // Non-copyable (would alias data_), movable (for std::map support)
1741 : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
1742 other.data_ = nullptr;
1743 other.size_ = 0;
1744 other.capacity_ = 0;
1745 }
1747 std::swap(this->data_, other.data_);
1748 std::swap(this->size_, other.size_);
1749 std::swap(this->capacity_, other.capacity_);
1750 return *this;
1751 }
1752
1755 template<typename F> void add(F &&callback) { this->add_(CbType::create(std::forward<F>(callback))); }
1756
1758 inline void ESPHOME_ALWAYS_INLINE call(const Ts &...args) {
1759 if (this->size_ != 0) {
1760 for (auto *it = this->data_, *end = it + this->size_; it != end; ++it) {
1761 it->call(args...);
1762 }
1763 }
1764 }
1765 uint16_t size() const { return this->size_; }
1766
1768 void operator()(const Ts &...args) { this->call(args...); }
1769
1770 protected:
1771 template<typename...> friend class LazyCallbackManager;
1774 void add_(CbType cb) {
1775 if (this->size_ == this->capacity_) {
1776 this->data_ =
1777 static_cast<CbType *>(callback_manager_grow(this->data_, this->size_, this->capacity_, sizeof(CbType)));
1778 }
1779 this->data_[this->size_++] = cb;
1780 }
1781 CbType *data_{nullptr};
1782 uint16_t size_{0};
1783 uint16_t capacity_{0};
1784};
1785
1794template<size_t N, typename... X> class StaticCallbackManager;
1795
1796template<size_t N, typename... Ts> class StaticCallbackManager<N, void(Ts...)> {
1797 public:
1800 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
1801
1803 void call(Ts... args) {
1804 for (auto &cb : this->callbacks_)
1805 cb.call(args...);
1806 }
1807 size_t size() const { return this->callbacks_.size(); }
1808
1810 void operator()(Ts... args) { call(args...); }
1811
1812 protected:
1814 void add_(Callback<void(Ts...)> cb) { this->callbacks_.push_back(cb); }
1816};
1817
1818template<typename... X> class LazyCallbackManager;
1819
1835template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
1836 public:
1840 ~LazyCallbackManager() { delete this->callbacks_; }
1841
1842 // Non-copyable and non-movable (entities are never copied or moved)
1847
1849 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
1850
1852 void call(Ts... args) {
1853 if (this->callbacks_) {
1854 this->callbacks_->call(args...);
1855 }
1856 }
1857
1859 size_t size() const { return this->callbacks_ ? this->callbacks_->size() : 0; }
1860
1862 bool empty() const { return !this->callbacks_ || this->callbacks_->size() == 0; }
1863
1865 void operator()(Ts... args) { this->call(args...); }
1866
1867 protected:
1869 void add_(Callback<void(Ts...)> cb) {
1870 if (!this->callbacks_) {
1871 this->callbacks_ = new CallbackManager<void(Ts...)>();
1872 }
1873 this->callbacks_->add_(cb);
1874 }
1875 CallbackManager<void(Ts...)> *callbacks_{nullptr};
1876};
1877
1879template<typename T> class Deduplicator {
1880 public:
1882 bool next(T value) {
1883 if (this->has_value_ && !this->value_unknown_ && this->last_value_ == value) {
1884 return false;
1885 }
1886 this->has_value_ = true;
1887 this->value_unknown_ = false;
1888 this->last_value_ = value;
1889 return true;
1890 }
1893 bool ret = !this->value_unknown_;
1894 this->value_unknown_ = true;
1895 return ret;
1896 }
1898 bool has_value() const { return this->has_value_; }
1899
1900 protected:
1901 bool has_value_{false};
1902 bool value_unknown_{false};
1904};
1905
1907template<typename T> class Parented {
1908 public:
1910 Parented(T *parent) : parent_(parent) {}
1911
1913 T *get_parent() const { return parent_; }
1915 void set_parent(T *parent) { parent_ = parent; }
1916
1917 protected:
1918 T *parent_{nullptr};
1919};
1920
1922
1925
1930class Mutex {
1931 public:
1932 Mutex(const Mutex &) = delete;
1933 Mutex &operator=(const Mutex &) = delete;
1934
1935#if defined(USE_ESP8266) || defined(USE_RP2)
1936 // Single-threaded platforms: inline no-ops so the compiler eliminates all call overhead.
1937 Mutex() = default;
1938 ~Mutex() = default;
1939 void lock() {}
1940 bool try_lock() { return true; }
1941 void unlock() {}
1942#elif defined(USE_ESP32) || defined(USE_LIBRETINY)
1943 // FreeRTOS platforms: inline to avoid out-of-line call overhead.
1944 Mutex() { handle_ = xSemaphoreCreateMutex(); }
1945 ~Mutex() = default;
1946 void lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); }
1947 bool try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; }
1948 void unlock() { xSemaphoreGive(this->handle_); }
1949
1950 private:
1951 SemaphoreHandle_t handle_;
1952#else
1953 Mutex();
1954 ~Mutex();
1955 void lock();
1956 bool try_lock();
1957 void unlock();
1958
1959 private:
1960 // d-pointer to store private data on new platforms
1961 void *handle_; // NOLINT(clang-diagnostic-unused-private-field)
1962#endif
1963};
1964
1970 public:
1971 LockGuard(Mutex &mutex) : mutex_(mutex) { mutex_.lock(); }
1972 ~LockGuard() { mutex_.unlock(); }
1973
1974 private:
1975 Mutex &mutex_;
1976};
1977
1999 public:
2000 InterruptLock();
2002
2003 protected:
2004#if defined(USE_ESP8266) || defined(USE_RP2) || defined(USE_ZEPHYR)
2006#endif
2007};
2008
2018 public:
2019 LwIPLock(const LwIPLock &) = delete;
2020 LwIPLock &operator=(const LwIPLock &) = delete;
2021
2022#if defined(USE_ESP32) || defined(USE_RP2)
2023 // Platforms with potential lwIP core locking — out-of-line implementations in helpers.cpp
2024 LwIPLock();
2025 ~LwIPLock();
2026#else
2027 // No lwIP core locking — inline no-ops (empty bodies instead of = default
2028 // to prevent clang-tidy unused-variable warnings at call sites)
2031#endif
2032};
2033
2040 public:
2042 void start();
2044 void stop();
2045
2047 static bool is_high_frequency() { return num_requests > 0; }
2048
2049 protected:
2050 bool started_{false};
2051 static uint8_t num_requests; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
2052};
2053
2055void get_mac_address_raw(uint8_t *mac); // NOLINT(readability-non-const-parameter)
2056
2057// get_mac_address, get_mac_address_pretty moved to alloc_helpers.h - remove this comment before 2026.11.0
2058
2062void get_mac_address_into_buffer(std::span<char, MAC_ADDRESS_BUFFER_SIZE> buf);
2063
2067const char *get_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
2068
2069#ifdef USE_ESP32
2071void set_mac_address(uint8_t *mac);
2072#endif
2073
2077
2080bool mac_address_is_valid(const uint8_t *mac);
2081
2084
2086
2089
2099template<class T> class RAMAllocator {
2100 public:
2101 using value_type = T;
2102
2103 enum Flags {
2104 NONE = 0, // Perform external allocation and fall back to internal memory
2105 ALLOC_EXTERNAL = 1 << 0, // Perform external allocation only.
2106 ALLOC_INTERNAL = 1 << 1, // Perform internal allocation only.
2107 ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
2108 PREFER_INTERNAL = 1 << 3, // Perform internal allocation and fall back to external memory
2109 };
2110
2111 constexpr RAMAllocator() = default;
2112 constexpr RAMAllocator(uint8_t flags) {
2113 if (flags & PREFER_INTERNAL) {
2115 return;
2116 }
2117 const uint8_t alloc_bits = flags & (ALLOC_INTERNAL | ALLOC_EXTERNAL);
2118 if (alloc_bits != 0) {
2119 this->flags_ = alloc_bits;
2120 return;
2121 }
2122 this->flags_ = ALLOC_INTERNAL | ALLOC_EXTERNAL;
2123 }
2124 template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {}
2125
2126 T *allocate(size_t n) { return this->allocate(n, sizeof(T)); }
2127
2128 T *allocate(size_t n, size_t manual_size) {
2129 size_t size = n * manual_size;
2130 T *ptr = nullptr;
2131#ifdef USE_ESP32
2132 const auto caps = this->get_caps_();
2133 ptr = static_cast<T *>(heap_caps_malloc_prefer(size, 2, caps[0], caps[1]));
2134#else
2135 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2136 ptr = static_cast<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2137#endif
2138 return ptr;
2139 }
2140
2141 T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); }
2142
2143 T *reallocate(T *p, size_t n, size_t manual_size) {
2144 size_t size = n * manual_size;
2145 T *ptr = nullptr;
2146#ifdef USE_ESP32
2147 const auto caps = this->get_caps_();
2148 ptr = static_cast<T *>(heap_caps_realloc_prefer(p, size, 2, caps[0], caps[1]));
2149#else
2150 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2151 ptr = static_cast<T *>(realloc(p, size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2152#endif
2153 return ptr;
2154 }
2155
2156 void deallocate(T *p, size_t n) {
2157 free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2158 }
2159
2163 size_t get_free_heap_size() const {
2164#ifdef USE_ESP8266
2165 return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
2166#elif defined(USE_ESP32)
2167 auto max_internal =
2168 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2169 auto max_external =
2170 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2171 return max_internal + max_external;
2172#elif defined(USE_RP2)
2173 return ::rp2040.getFreeHeap();
2174#elif defined(USE_LIBRETINY)
2175 return lt_heap_get_free();
2176#else
2177 return 100000;
2178#endif
2179 }
2180
2185#ifdef USE_ESP8266
2186 return ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
2187#elif defined(USE_ESP32)
2188 auto max_internal =
2189 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2190 auto max_external =
2191 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2192 return std::max(max_internal, max_external);
2193#else
2194 return this->get_free_heap_size();
2195#endif
2196 }
2197
2198 private:
2199#ifdef USE_ESP32
2205 std::array<uint32_t, 2> get_caps_() const {
2206 constexpr uint32_t external_caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
2207 constexpr uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
2208 if (this->flags_ & PREFER_INTERNAL) {
2209 return {internal_caps, external_caps};
2210 }
2211 const uint32_t primary = (this->flags_ & ALLOC_EXTERNAL) ? external_caps : internal_caps;
2212 const uint32_t fallback = (this->flags_ & ALLOC_INTERNAL) ? internal_caps : external_caps;
2213 return {primary, fallback};
2214 }
2215#endif
2216
2217 uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL};
2218};
2219
2220template<class T> using ExternalRAMAllocator = RAMAllocator<T>;
2221
2226template<typename T, typename U>
2227concept comparable_with = requires(T a, U b) {
2228 { a > b } -> std::convertible_to<bool>;
2229 { a < b } -> std::convertible_to<bool>;
2230};
2231
2232template<std::totally_ordered T, comparable_with<T> U> T clamp_at_least(T value, U min) {
2233 if (value < min)
2234 return min;
2235 return value;
2236}
2237template<std::totally_ordered T, comparable_with<T> U> T clamp_at_most(T value, U max) {
2238 if (value > max)
2239 return max;
2240 return value;
2241}
2242
2245
2250template<typename T, enable_if_t<!std::is_pointer<T>::value, int> = 0> T id(T value) { return value; }
2255template<typename T, enable_if_t<std::is_pointer<T *>::value, int> = 0> T &id(T *value) { return *value; }
2256
2258
2259} // namespace esphome
Heap-allocating helper functions.
uint8_t m
Definition bl0906.h:1
void ESPHOME_ALWAYS_INLINE call(const Ts &...args)
Call all callbacks in this manager.
Definition helpers.h:1758
CallbackManager & operator=(const CallbackManager &)=delete
void operator()(const Ts &...args)
Call all callbacks in this manager.
Definition helpers.h:1768
CallbackManager & operator=(CallbackManager &&other) noexcept
Definition helpers.h:1746
void add(F &&callback)
Add any callable.
Definition helpers.h:1755
CallbackManager(CallbackManager &&other) noexcept
Definition helpers.h:1740
void add_(CbType cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1774
CallbackManager(const CallbackManager &)=delete
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory) Av...
Definition helpers.h:131
const constexpr T & operator[](size_t i) const
Definition helpers.h:135
constexpr bool empty() const
Definition helpers.h:137
constexpr ConstVector(const T *data, size_t size)
Definition helpers.h:133
constexpr size_t size() const
Definition helpers.h:136
Helper class to deduplicate items in a series of values.
Definition helpers.h:1879
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:1882
bool has_value() const
Returns true if this deduplicator has processed any items.
Definition helpers.h:1898
bool next_unknown()
Returns true if the deduplicator's value was previously known.
Definition helpers.h:1892
bool operator!=(const ConstIterator &other) const
Definition helpers.h:434
ConstIterator(const FixedRingBuffer *buf, index_type pos)
Definition helpers.h:428
bool operator!=(const Iterator &other) const
Definition helpers.h:419
Iterator(FixedRingBuffer *buf, index_type pos)
Definition helpers.h:413
Fixed-capacity circular buffer - allocates once at runtime, never reallocates.
Definition helpers.h:405
FixedRingBuffer & operator=(const FixedRingBuffer &)=delete
ConstIterator begin() const
Definition helpers.h:512
bool push(const T &value)
Push a value. Returns false if full.
Definition helpers.h:467
const T & front() const
Definition helpers.h:497
index_type capacity() const
Definition helpers.h:500
void push_overwrite(const T &value)
Push a value, overwriting the oldest if full.
Definition helpers.h:477
void init(index_type capacity)
Allocate capacity - can only be called once.
Definition helpers.h:455
void pop()
Remove the oldest element.
Definition helpers.h:489
void clear()
Clear all elements (reset to empty, keep capacity)
Definition helpers.h:504
FixedRingBuffer(const FixedRingBuffer &)=delete
index_type size() const
Definition helpers.h:498
ConstIterator end() const
Definition helpers.h:513
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Definition helpers.h:544
const T & at(size_t i) const
Definition helpers.h:723
FixedVector(FixedVector &&other) noexcept
Definition helpers.h:601
FixedVector(std::initializer_list< T > init_list)
Constructor from initializer list - allocates exact size needed This enables brace initialization: Fi...
Definition helpers.h:592
const T * begin() const
Definition helpers.h:728
bool full() const
Definition helpers.h:713
FixedVector & operator=(std::initializer_list< T > init_list)
Assignment from initializer list - avoids temporary and move overhead This enables: FixedVector<int> ...
Definition helpers.h:624
T & front()
Access first element (no bounds checking - matches std::vector behavior) Caller must ensure vector is...
Definition helpers.h:693
const T & operator[](size_t i) const
Definition helpers.h:718
T & operator[](size_t i)
Access element without bounds checking (matches std::vector behavior) Caller must ensure index is val...
Definition helpers.h:717
size_t capacity() const
Definition helpers.h:712
T & back()
Access last element (no bounds checking - matches std::vector behavior) Caller must ensure vector is ...
Definition helpers.h:698
bool empty() const
Definition helpers.h:711
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:672
void pop_back()
Remove the last element in place (no reallocation, keeps capacity) Caller must ensure vector is not e...
Definition helpers.h:703
T & emplace_back(Args &&...args)
Emplace element without bounds checking - constructs in-place with arguments Caller must ensure suffi...
Definition helpers.h:684
size_t size() const
Definition helpers.h:710
const T & front() const
Definition helpers.h:694
const T & back() const
Definition helpers.h:699
const T * end() const
Definition helpers.h:729
FixedVector & operator=(FixedVector &&other) noexcept
Definition helpers.h:608
T & at(size_t i)
Access element with bounds checking (matches std::vector behavior) Note: No exception thrown on out o...
Definition helpers.h:722
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:661
void init(size_t n)
Definition helpers.h:634
Helper class to request loop() to be called as fast as possible.
Definition helpers.h:2039
static bool is_high_frequency()
Check whether the loop is running continuously.
Definition helpers.h:2047
void stop()
Stop running the loop continuously.
Definition helpers.cpp:801
void start()
Start running the loop continuously.
Definition helpers.cpp:795
Helper class to disable interrupts.
Definition helpers.h:1998
LazyCallbackManager & operator=(const LazyCallbackManager &)=delete
LazyCallbackManager(const LazyCallbackManager &)=delete
size_t size() const
Return the number of registered callbacks.
Definition helpers.h:1859
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1869
void add(F &&callback)
Add any callable. Allocates the underlying CallbackManager on first use.
Definition helpers.h:1849
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1865
LazyCallbackManager & operator=(LazyCallbackManager &&)=delete
~LazyCallbackManager()
Destructor - clean up allocated CallbackManager if any.
Definition helpers.h:1840
void call(Ts... args)
Call all callbacks in this manager. No-op if no callbacks registered.
Definition helpers.h:1852
bool empty() const
Check if any callbacks are registered.
Definition helpers.h:1862
LazyCallbackManager(LazyCallbackManager &&)=delete
Helper class that wraps a mutex with a RAII-style API.
Definition helpers.h:1969
LockGuard(Mutex &mutex)
Definition helpers.h:1971
Helper class to lock the lwIP TCPIP core when making lwIP API calls from non-TCPIP threads.
Definition helpers.h:2017
LwIPLock(const LwIPLock &)=delete
LwIPLock & operator=(const LwIPLock &)=delete
Mutex implementation, with API based on the unavailable std::mutex.
Definition helpers.h:1930
~Mutex()=default
Definition helpers.cpp:36
void unlock()
Definition helpers.h:1941
Mutex()=default
Definition helpers.cpp:35
bool try_lock()
Definition helpers.h:1940
Mutex(const Mutex &)=delete
Mutex & operator=(const Mutex &)=delete
Helper class to easily give an object a parent of type T.
Definition helpers.h:1907
T * get_parent() const
Get the parent of this object.
Definition helpers.h:1913
Parented(T *parent)
Definition helpers.h:1910
void set_parent(T *parent)
Set the parent of this object.
Definition helpers.h:1915
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2099
constexpr RAMAllocator(uint8_t flags)
Definition helpers.h:2112
T * reallocate(T *p, size_t n, size_t manual_size)
Definition helpers.h:2143
size_t get_free_heap_size() const
Return the total heap space available via this allocator.
Definition helpers.h:2163
T * reallocate(T *p, size_t n)
Definition helpers.h:2141
void deallocate(T *p, size_t n)
Definition helpers.h:2156
size_t get_max_free_block_size() const
Return the maximum size block this allocator could allocate.
Definition helpers.h:2184
T * allocate(size_t n)
Definition helpers.h:2126
constexpr RAMAllocator(const RAMAllocator< U > &other)
Definition helpers.h:2124
T * allocate(size_t n, size_t manual_size)
Definition helpers.h:2128
constexpr RAMAllocator()=default
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:737
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:147
SmallInlineBuffer(const SmallInlineBuffer &)=delete
uint8_t * init(size_t size)
Resize to size bytes of (uninitialized) storage and return a writable pointer to fill.
Definition helpers.h:195
bool is_inline_() const
Definition helpers.h:217
void set(const uint8_t *src, size_t size)
Set buffer contents, allocating heap if needed.
Definition helpers.h:210
SmallInlineBuffer & operator=(const SmallInlineBuffer &)=delete
size_t size() const
Definition helpers.h:214
uint8_t inline_[InlineSize]
Definition helpers.h:221
SmallInlineBuffer & operator=(SmallInlineBuffer &&other) noexcept
Definition helpers.h:167
const uint8_t * data() const
Definition helpers.h:213
SmallInlineBuffer(SmallInlineBuffer &&other) noexcept
Definition helpers.h:156
void add(F &&callback)
Add any callable.
Definition helpers.h:1800
void call(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1803
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1814
StaticVector< Callback< void(Ts...)>, N > callbacks_
Definition helpers.h:1815
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1810
CallbackManager backed by StaticVector for compile-time-known callback counts.
Definition helpers.h:1794
ConstIterator(const StaticRingBuffer *buf, index_type pos)
Definition helpers.h:347
bool operator!=(const ConstIterator &other) const
Definition helpers.h:353
bool operator!=(const Iterator &other) const
Definition helpers.h:338
Iterator(StaticRingBuffer *buf, index_type pos)
Definition helpers.h:332
Fixed-size circular buffer with FIFO semantics and iteration support.
Definition helpers.h:326
bool push(const T &value)
Definition helpers.h:360
ConstIterator begin() const
Definition helpers.h:391
ConstIterator end() const
Definition helpers.h:392
index_type size() const
Definition helpers.h:379
const T & front() const
Definition helpers.h:378
void clear()
Clear all elements (reset to empty)
Definition helpers.h:383
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:227
const_reverse_iterator rend() const
Definition helpers.h:312
size_t size() const
Definition helpers.h:292
reverse_iterator rbegin()
Definition helpers.h:309
const T & operator[](size_t i) const
Definition helpers.h:300
reverse_iterator rend()
Definition helpers.h:310
void push_back(const T &value)
Definition helpers.h:265
bool empty() const
Definition helpers.h:293
void assign(InputIt first, InputIt last)
Definition helpers.h:275
const_reverse_iterator rbegin() const
Definition helpers.h:311
T & operator[](size_t i)
Definition helpers.h:299
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition helpers.h:233
typename std::array< T, N >::iterator iterator
Definition helpers.h:230
typename std::array< T, N >::const_iterator const_iterator
Definition helpers.h:231
std::reverse_iterator< iterator > reverse_iterator
Definition helpers.h:232
const T * data() const
Definition helpers.h:297
const_iterator end() const
Definition helpers.h:306
StaticVector(InputIt first, InputIt last)
Definition helpers.h:244
StaticVector(const StaticVector< T, M > &other)
Definition helpers.h:260
StaticVector(std::initializer_list< T > init)
Definition helpers.h:251
const_iterator begin() const
Definition helpers.h:305
struct @66::@67 __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:2227
uint16_t flags
uint16_t id
int ret
mopeka_std_values val[3]
size_t buf_append_str_p(char *buf, size_t size, size_t pos, PGM_P str)
Safely append a PROGMEM string to buffer, returning new position (capped at size).
Definition helpers.h:1088
T clamp_at_most(T value, U max)
Definition helpers.h:2237
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, returning new position (capped at size).
Definition helpers.h:1111
constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str)
Extend a FNV-1a hash with additional string data.
Definition helpers.h:835
ESPDEPRECATED("Use LightState::gamma_correct_lut() instead. Removed in 2026.9.0.", "2026.3.0") float gamma_correct(float value
Applies gamma correction of gamma to value.
float random_float()
Return a random float between 0 and 1.
Definition helpers.cpp:197
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:1273
float gamma_uncorrect(float value, float gamma)
Definition helpers.cpp:721
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:241
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:939
float gamma_correct(float value, float gamma)
Definition helpers.cpp:713
constexpr char to_sanitized_char(char c)
Sanitize a single char: keep alphanumerics, dashes, underscores; replace others with underscore.
Definition helpers.h:993
bool mac_address_is_valid(const uint8_t *mac)
Check if the MAC address is not all zeros or all ones.
Definition helpers.cpp:827
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:1279
void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output)
Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators)
Definition helpers.h:1498
constexpr uint32_t FNV1_OFFSET_BASIS
FNV-1 32-bit offset basis.
Definition helpers.h:808
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:730
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
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:539
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:1040
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:466
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.
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:948
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:813
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:675
va_end(args)
const void size_t len
Definition hal.h:64
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:1403
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:887
uint32_t small_pow10(int8_t n)
Return 10^n for small non-negative n (0-3) as uint32_t, avoiding float.
Definition helpers.h:1342
std::vector< uint8_t > base64_decode(const std::string &encoded_string)
Decode a base64 string to a byte vector.
char * format_hex_prefixed_to(char(&buffer)[N], T val)
Format an unsigned integer as "0x" prefixed lowercase hex to buffer.
Definition helpers.h:1407
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:274
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:406
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:1353
uint16_t size
Definition helpers.cpp:25
int8_t ilog10(float value)
Compute floor(log10(fabs(value))) using iterative comparison.
Definition helpers.cpp:481
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:1019
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:160
T clamp_at_least(T value, U min)
Definition helpers.h:2232
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:1167
char * frac_to_str_unchecked(char *buf, uint32_t frac, uint32_t divisor)
Write fractional digits with leading zeros to buffer (internal, no size check).
Definition helpers.h:1362
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:569
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:12
size_t size_t pos
Definition helpers.h:1062
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:816
char * buf_append_sep_str(char *buf, size_t remaining, char separator, const char *str, size_t str_len)
Append a separator char and a string to a buffer, respecting remaining space.
Definition helpers.h:1325
void delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
Definition helpers.cpp:851
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.
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:1400
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare strings for equality in case-insensitive manner.
Definition helpers.cpp:201
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:217
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:224
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:527
char * int8_to_str(char *buf, int8_t val)
Write int8 value to buffer without modulo operations.
Definition helpers.h:1299
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:558
const char * json_escape_into_buffer(std::span< char > buf, StringRef value, bool short_control_escapes)
Copy value into buf, escaping the characters that cannot appear raw inside a JSON string literal.
Definition helpers.cpp:338
char * uint32_to_str_unchecked(char *buf, uint32_t val)
Write unsigned 32-bit integer to buffer (internal, no size check).
Definition helpers.cpp:320
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:1426
TemperatureUnit
Definition helpers.h:1654
constexpr uint32_t FNV1_PRIME
FNV-1 32-bit prime.
Definition helpers.h:810
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:897
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:753
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:810
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:891
const void * src
Definition hal.h:64
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:1650
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:883
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:1063
constexpr uint8_t parse_hex_char(char c)
Definition helpers.h:1262
bool str_startswith(const std::string &str, const std::string &start)
Check whether a string starts with a value.
Definition helpers.cpp:208
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:912
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:1529
int written
Definition helpers.h:1069
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition helpers.h:84
constexpr char to_snake_case_char(char c)
Convert a single char to snake_case: lowercase and space to underscore.
Definition helpers.h:989
constexpr float fahrenheit_to_celsius(float value)
Convert degrees Fahrenheit to degrees Celsius.
Definition helpers.h:1652
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Definition helpers.h:922
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:334
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:1461
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:789
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:209
constexpr uint32_t fnv1a_hash(const char *str)
Calculate a FNV-1a hash of str.
Definition helpers.h:858
float gamma
Definition helpers.h:1633
uint16_t uint16_t & capacity
Definition helpers.cpp:25
ParseOnOffState
Return values for parse_on_off().
Definition helpers.h:1591
@ PARSE_ON
Definition helpers.h:1593
@ PARSE_TOGGLE
Definition helpers.h:1595
@ PARSE_OFF
Definition helpers.h:1594
@ PARSE_NONE
Definition helpers.h:1592
float pow10_int(int8_t exp)
Compute 10^exp using iterative multiplication/division.
Definition helpers.h:776
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:261
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:443
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:1493
static void uint32_t
static Callback create(F &&callable)
Create from any callable.
Definition helpers.h:1686
void call(Ts... args) const
Invoke the callback. Only valid on Callbacks created via create(), never on default-constructed insta...
Definition helpers.h:1682
Lightweight type-erased callback (8 bytes on 32-bit) that avoids std::function overhead.
Definition helpers.h:1670
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