ESPHome 2026.11.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 <cstddef>
9#include <cstdint>
10#include <cstdio>
11#include <cstdlib>
12#include <cstring>
13#include <functional>
14#include <iterator>
15#include <limits>
16#include <memory>
17#include <new>
18#include <numeric>
19#include <span>
20#include <string>
21#include <type_traits>
22#include <utility>
23#include <vector>
24#include <concepts>
25#include <strings.h>
26
29
30#ifdef USE_ESP8266
31#include <Esp.h>
32#include <pgmspace.h>
33#endif
34
35#ifdef USE_RP2
36#include <Arduino.h>
37#endif
38
39#ifdef USE_ESP32
40#include <esp_system.h>
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
130template<typename T, bool Owning = false> class ConstVector {
131 public:
132 using value_type = T;
133
134 constexpr ConstVector() = default;
135 constexpr ConstVector(const T *data, size_t size) : data_(data), size_(size) {}
136
137 const T *begin() const { return this->data_; }
138 const T *end() const { return this->data_ + this->size_; }
139 const T *data() const { return this->data_; }
140 constexpr size_t size() const { return this->size_; }
141 constexpr bool empty() const { return this->size_ == 0; }
142 const constexpr T &operator[](size_t i) const { return this->data_[i]; }
143 const T &at(size_t i) const { return this->data_[i]; }
144
145 protected:
146 const T *data_{nullptr};
147 size_t size_{0};
148};
149
153template<typename T> class ConstVector<T, true> {
154 static_assert(std::is_trivially_copyable_v<T> && sizeof(T) % sizeof(uint32_t) == 0,
155 "ConstVector elements must be whole words so ESP8266 can read them from flash");
156
157 public:
158 using value_type = T;
159
160 constexpr ConstVector() = default;
161 constexpr ConstVector(const T *data, size_t size) : data_(data), size_(size) {}
162 ConstVector(const ConstVector &) = delete;
164 ~ConstVector() { this->release_(); }
165
166 const T *begin() const { return this->data_; }
167 const T *end() const { return this->data_ + this->size(); }
168 const T *data() const { return this->data_; }
169 size_t size() const { return this->size_ & ~OWNED_BIT; }
170 bool empty() const { return this->size() == 0; }
171 const T &operator[](size_t index) const { return this->data_[index]; }
172 const T &at(size_t index) const { return this->data_[index]; }
173
176 void assign_static(const T *data, size_t size) {
177 this->data_ = data;
178 this->size_ = size;
179 }
181 void assign_copy(const T *data, size_t size) {
182 auto *table = new T[size]; // NOLINT(cppcoreguidelines-owning-memory)
183 std::copy(data, data + size, table);
184 this->release_();
185 this->data_ = table;
186 this->size_ = size | OWNED_BIT;
187 }
188
189 protected:
190 static constexpr size_t OWNED_BIT = size_t{1} << (sizeof(size_t) * 8 - 1);
191
192 void release_() {
193 if (this->size_ & OWNED_BIT)
194 delete[] this->data_; // NOLINT(cppcoreguidelines-owning-memory)
195 }
196
197 const T *data_{nullptr};
198 size_t size_{0}; // top bit set when data_ is an owned heap copy
199};
200static_assert(sizeof(ConstVector<const char *, true>) == 2 * sizeof(void *),
201 "ConstVector must stay a pointer and a size");
202
206template<size_t InlineSize = 8> class SmallInlineBuffer {
207 public:
208 SmallInlineBuffer() = default;
210 if (!this->is_inline_())
211 delete[] this->heap_;
212 }
213
214 // Move constructor
215 SmallInlineBuffer(SmallInlineBuffer &&other) noexcept : len_(other.len_) {
216 if (other.is_inline_()) {
217 memcpy(this->inline_, other.inline_, this->len_);
218 } else {
219 this->heap_ = other.heap_;
220 other.heap_ = nullptr;
221 }
222 other.len_ = 0;
223 }
224
225 // Move assignment
227 if (this != &other) {
228 if (!this->is_inline_())
229 delete[] this->heap_;
230 this->len_ = other.len_;
231 if (other.is_inline_()) {
232 memcpy(this->inline_, other.inline_, this->len_);
233 } else {
234 this->heap_ = other.heap_;
235 other.heap_ = nullptr;
236 }
237 other.len_ = 0;
238 }
239 return *this;
240 }
241
242 // Disable copy (would need deep copy of heap data)
245
246 bool empty() const { return this->len_ == 0; }
247
248 // Conversion to std::span for compatibility with span-based APIs
249 operator std::span<const uint8_t>() const { return std::span<const uint8_t>(this->data(), this->len_); }
250
254 uint8_t *init(size_t size) {
255 // Free existing heap allocation if switching from heap to inline or different heap size
257 delete[] this->heap_;
258 this->heap_ = nullptr; // Defensive: prevent use-after-free if logic changes
259 }
260 // Allocate new heap buffer if needed
261 if (size > InlineSize && (this->is_inline_() || size != this->len_)) {
262 this->heap_ = new uint8_t[size]; // NOLINT(cppcoreguidelines-owning-memory)
263 }
264 this->len_ = size;
265 return this->data();
266 }
267
269 void set(const uint8_t *src, size_t size) { memcpy(this->init(size), src, size); }
270
271 uint8_t *data() { return this->is_inline_() ? this->inline_ : this->heap_; }
272 const uint8_t *data() const { return this->is_inline_() ? this->inline_ : this->heap_; }
273 size_t size() const { return this->len_; }
274
275 protected:
276 bool is_inline_() const { return this->len_ <= InlineSize; }
277
278 size_t len_{0};
279 union {
280 uint8_t inline_[InlineSize]{}; // Zero-init ensures clean initial state
281 uint8_t *heap_;
282 };
283};
284
286template<typename T, size_t N> class StaticVector {
287 public:
288 using value_type = T;
289 using iterator = typename std::array<T, N>::iterator;
290 using const_iterator = typename std::array<T, N>::const_iterator;
291 using reverse_iterator = std::reverse_iterator<iterator>;
292 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
293
294 private:
295 std::array<T, N> data_; // intentionally not value-initialized to avoid memset
296 size_t count_{0};
297
298 public:
299 // User provided, not "= default": otherwise `StaticVector<...> x_{}` members
300 // value-initialize and memset data_, defeating the comment above.
301 constexpr StaticVector() noexcept {}
302
303 // Iterator range constructor
304 template<typename InputIt> StaticVector(InputIt first, InputIt last) {
305 while (first != last && count_ < N) {
306 data_[count_++] = *first++;
307 }
308 }
309
310 // Initializer list constructor
311 StaticVector(std::initializer_list<T> init) {
312 for (const auto &val : init) {
313 if (count_ >= N)
314 break;
315 data_[count_++] = val;
316 }
317 }
318
319 // Converting constructor from a smaller StaticVector of the same element type
320 template<size_t M> StaticVector(const StaticVector<T, M> &other) : StaticVector(other.begin(), other.end()) {
321 static_assert(M <= N, "Source StaticVector cannot be larger than the destination");
322 }
323
324 // Minimal vector-compatible interface - only what we actually use
325 void push_back(const T &value) {
326 if (count_ < N) {
327 data_[count_++] = value;
328 }
329 }
330
331 // Clear all elements
332 void clear() { count_ = 0; }
333 // Set the element count, capped at N. Elements are neither initialized when growing nor destroyed when
334 // shrinking; release owning elements before shrinking past them.
335 void resize(size_t n) { count_ = n < N ? n : N; }
336
337 // Assign from iterator range
338 template<typename InputIt> void assign(InputIt first, InputIt last) {
339 count_ = 0;
340 while (first != last && count_ < N) {
341 data_[count_++] = *first++;
342 }
343 }
344
345 // Return reference to next element and increment count (with bounds checking)
347 if (count_ >= N) {
348 // Should never happen with proper size calculation
349 // Return reference to last element to avoid crash
350 return data_[N - 1];
351 }
352 return data_[count_++];
353 }
354
355 size_t size() const { return count_; }
356 static constexpr size_t capacity() { return N; }
357 bool empty() const { return count_ == 0; }
358
359 // Direct access to underlying data
360 T *data() { return data_.data(); }
361 const T *data() const { return data_.data(); }
362
363 T &operator[](size_t i) { return data_[i]; }
364 const T &operator[](size_t i) const { return data_[i]; }
365
366 // For range-based for loops
367 iterator begin() { return data_.begin(); }
368 iterator end() { return data_.begin() + count_; }
369 const_iterator begin() const { return data_.begin(); }
370 const_iterator end() const { return data_.begin() + count_; }
371
372 // Reverse iterators
377
378 // Conversion to std::span for compatibility with span-based APIs
379 operator std::span<T>() { return std::span<T>(data_.data(), count_); }
380 operator std::span<const T>() const { return std::span<const T>(data_.data(), count_); }
381};
382
390template<typename T, size_t N> class StaticRingBuffer {
391 using index_type = std::conditional_t<(N <= std::numeric_limits<uint8_t>::max()), uint8_t, uint16_t>;
392
393 public:
394 class Iterator {
395 public:
396 Iterator(StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
397 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % N]; }
399 ++pos_;
400 return *this;
401 }
402 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
403
404 private:
405 StaticRingBuffer *buf_;
406 index_type pos_;
407 };
408
410 public:
411 ConstIterator(const StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
412 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % N]; }
414 ++pos_;
415 return *this;
416 }
417 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
418
419 private:
420 const StaticRingBuffer *buf_;
421 index_type pos_;
422 };
423
424 bool push(const T &value) {
425 if (this->count_ >= N) {
426 return false;
427 }
428 this->data_[this->tail_] = value;
429 this->tail_ = (this->tail_ + 1) % N;
430 ++this->count_;
431 return true;
432 }
433
434 void pop() {
435 if (this->count_ > 0) {
436 this->head_ = (this->head_ + 1) % N;
437 --this->count_;
438 }
439 }
440
441 T &front() { return this->data_[this->head_]; }
442 const T &front() const { return this->data_[this->head_]; }
443 index_type size() const { return this->count_; }
444 bool empty() const { return this->count_ == 0; }
445
447 void clear() {
448 this->head_ = 0;
449 this->tail_ = 0;
450 this->count_ = 0;
451 }
452
453 Iterator begin() { return Iterator(this, 0); }
454 Iterator end() { return Iterator(this, this->count_); }
455 ConstIterator begin() const { return ConstIterator(this, 0); }
456 ConstIterator end() const { return ConstIterator(this, this->count_); }
457
458 protected:
459 T data_[N];
460 index_type head_{0};
461 index_type tail_{0};
462 index_type count_{0};
463};
464
469template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()> class FixedRingBuffer {
470 using index_type = std::conditional_t<
471 (MAX_CAPACITY <= std::numeric_limits<uint8_t>::max()), uint8_t,
472 std::conditional_t<(MAX_CAPACITY <= std::numeric_limits<uint16_t>::max()), uint16_t, uint32_t>>;
473
474 public:
475 class Iterator {
476 public:
477 Iterator(FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
478 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
480 ++pos_;
481 return *this;
482 }
483 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
484
485 private:
486 FixedRingBuffer *buf_;
487 index_type pos_;
488 };
489
491 public:
492 ConstIterator(const FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
493 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
495 ++pos_;
496 return *this;
497 }
498 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
499
500 private:
501 const FixedRingBuffer *buf_;
502 index_type pos_;
503 };
504
505 FixedRingBuffer() = default;
507 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
508 ::operator delete(this->data_);
509 } else {
510 delete[] this->data_;
511 }
512 }
513
514 // Disable copy
517
519 void init(index_type capacity) {
520 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
521 // Raw allocation without initialization (elements are written before read)
522 // NOLINTNEXTLINE(bugprone-sizeof-expression)
523 this->data_ = static_cast<T *>(::operator new(capacity * sizeof(T)));
524 } else {
525 this->data_ = new T[capacity];
526 }
527 this->capacity_ = capacity;
528 }
529
531 bool push(const T &value) {
532 if (this->count_ >= this->capacity_)
533 return false;
534 this->data_[this->tail_] = value;
535 this->tail_ = (this->tail_ + 1) % this->capacity_;
536 ++this->count_;
537 return true;
538 }
539
541 void push_overwrite(const T &value) {
542 this->data_[this->tail_] = value;
543 this->tail_ = (this->tail_ + 1) % this->capacity_;
544 if (this->count_ >= this->capacity_) {
545 // Buffer full - advance head to drop oldest, count stays at capacity
546 this->head_ = this->tail_;
547 } else {
548 ++this->count_;
549 }
550 }
551
553 void pop() {
554 if (this->count_ > 0) {
555 this->head_ = (this->head_ + 1) % this->capacity_;
556 --this->count_;
557 }
558 }
559
560 T &front() { return this->data_[this->head_]; }
561 const T &front() const { return this->data_[this->head_]; }
562 index_type size() const { return this->count_; }
563 bool empty() const { return this->count_ == 0; }
564 index_type capacity() const { return this->capacity_; }
565 bool full() const { return this->count_ == this->capacity_; }
566
568 void clear() {
569 this->head_ = 0;
570 this->tail_ = 0;
571 this->count_ = 0;
572 }
573
574 Iterator begin() { return Iterator(this, 0); }
575 Iterator end() { return Iterator(this, this->count_); }
576 ConstIterator begin() const { return ConstIterator(this, 0); }
577 ConstIterator end() const { return ConstIterator(this, this->count_); }
578
580 const T &operator[](index_type pos) const {
581 size_t index = static_cast<size_t>(this->head_) + pos;
582 if (index >= this->capacity_)
583 index -= this->capacity_;
584 return this->data_[index];
585 }
586
587 protected:
588 T *data_{nullptr};
589 index_type head_{0};
590 index_type tail_{0};
591 index_type count_{0};
592 index_type capacity_{0};
593};
594
599template<typename T, size_t N> inline void init_array_from(std::array<T, N> &dest, std::initializer_list<T> src) {
600#ifdef ESPHOME_DEBUG
601 assert(src.size() == N);
602#endif
603 if constexpr (std::is_trivially_copyable_v<T>) {
604 __builtin_memcpy(dest.data(), src.begin(), N * sizeof(T));
605 } else {
606 size_t i = 0;
607 for (const auto &v : src) {
608 dest[i++] = v;
609 }
610 }
611}
612
613// Abort with a reason that reaches the panic output on ESP32. Elsewhere the literal is dropped
614// before it can land in rodata, which is RAM on ESP8266
615#ifdef USE_ESP32
616#define ESPHOME_ABORT_WITH_REASON(reason) esp_system_abort(reason)
617#else
618#define ESPHOME_ABORT_WITH_REASON(reason) abort()
619#endif
620
624template<typename T> class FixedVector {
625 private:
626 T *data_{nullptr};
627 size_t size_{0};
628 size_t capacity_{0};
629
630 // Helper to destroy all elements without freeing memory
631 void destroy_elements_() {
632 // Only call destructors for non-trivially destructible types
633 if constexpr (!std::is_trivially_destructible<T>::value) {
634 for (size_t i = 0; i < size_; i++) {
635 data_[i].~T();
636 }
637 }
638 }
639
640 // Helper to destroy elements and free memory
641 void cleanup_() {
642 if (data_ != nullptr) {
643 destroy_elements_();
644 free(data_); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
645 }
646 }
647
648 // Helper to reset pointers after cleanup
649 void reset_() {
650 data_ = nullptr;
651 capacity_ = 0;
652 size_ = 0;
653 }
654
655 // Helper to assign from initializer list (shared by constructor and assignment operator)
656 void assign_from_initializer_list_(std::initializer_list<T> init_list) {
657 init(init_list.size());
658 size_t idx = 0;
659 for (const auto &item : init_list) {
660 new (data_ + idx) T(item);
661 ++idx;
662 }
663 size_ = init_list.size();
664 }
665
666 public:
667 FixedVector() = default;
668
671 FixedVector(std::initializer_list<T> init_list) { assign_from_initializer_list_(init_list); }
672
673 ~FixedVector() { cleanup_(); }
674
675 // Disable copy operations (avoid accidental expensive copies)
676 FixedVector(const FixedVector &) = delete;
678
679 // Enable move semantics (allows use in move-only containers like std::vector)
680 FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
681 other.reset_();
682 }
683
684 // Allow conversion to std::vector
685 operator std::vector<T>() const { return {data_, data_ + size_}; }
686
687 FixedVector &operator=(FixedVector &&other) noexcept {
688 if (this != &other) {
689 // Delete our current data
690 cleanup_();
691 // Take ownership of other's data
692 data_ = other.data_;
693 size_ = other.size_;
694 capacity_ = other.capacity_;
695 // Leave other in valid empty state
696 other.reset_();
697 }
698 return *this;
699 }
700
703 FixedVector &operator=(std::initializer_list<T> init_list) {
704 cleanup_();
705 reset_();
706 assign_from_initializer_list_(init_list);
707 return *this;
708 }
709
710 // Allocate capacity - can be called multiple times to reinit
711 // IMPORTANT: After calling init(), you MUST use push_back() to add elements.
712 // Direct assignment via operator[] does NOT update the size counter.
713 // Aborts on exhaustion; use try_init() to handle failure.
714 void init(size_t n) {
715 if (!try_init(n))
716 ESPHOME_ABORT_WITH_REASON("FixedVector: out of memory");
717 }
718
719 // Same as init(), but returns false when memory is exhausted; the previous storage is freed either way
720 bool try_init(size_t n) {
721 cleanup_();
722 reset_();
723 if (n == 0)
724 return true;
725 if (n > SIZE_MAX / sizeof(T))
726 return false; // the byte count would wrap into a small block
727 // sizeof(T) is correct here for any type T (value types, pointers, etc.)
728 // NOLINTNEXTLINE(bugprone-sizeof-expression,cppcoreguidelines-no-malloc,cppcoreguidelines-owning-memory)
729 data_ = static_cast<T *>(malloc(n * sizeof(T)));
730 if (data_ == nullptr)
731 return false;
732 capacity_ = n;
733 return true;
734 }
735
736 // Clear the vector (destroy all elements, reset size to 0, keep capacity)
737 void clear() {
738 destroy_elements_();
739 size_ = 0;
740 }
741
742 // Release all memory (destroys elements and frees memory)
743 void release() {
744 cleanup_();
745 reset_();
746 }
747
751 void push_back(const T &value) {
752 if (size_ < capacity_) {
753 // Use placement new to construct the object in pre-allocated memory
754 new (&data_[size_]) T(value);
755 size_++;
756 }
757 }
758
762 void push_back(T &&value) {
763 if (size_ < capacity_) {
764 // Use placement new to move-construct the object in pre-allocated memory
765 new (&data_[size_]) T(std::move(value));
766 size_++;
767 }
768 }
769
774 template<typename... Args> T &emplace_back(Args &&...args) {
775 // Use placement new to construct the object in pre-allocated memory
776 new (&data_[size_]) T(std::forward<Args>(args)...);
777 size_++;
778 return data_[size_ - 1];
779 }
780
783 T &front() { return data_[0]; }
784 const T &front() const { return data_[0]; }
785
788 T &back() { return data_[size_ - 1]; }
789 const T &back() const { return data_[size_ - 1]; }
790
793 void pop_back() {
794 if constexpr (!std::is_trivially_destructible<T>::value) {
795 data_[size_ - 1].~T();
796 }
797 size_--;
798 }
799
800 size_t size() const { return size_; }
801 bool empty() const { return size_ == 0; }
802 size_t capacity() const { return capacity_; }
803 bool full() const { return size_ == capacity_; }
804
807 T &operator[](size_t i) { return data_[i]; }
808 const T &operator[](size_t i) const { return data_[i]; }
809
812 T &at(size_t i) { return data_[i]; }
813 const T &at(size_t i) const { return data_[i]; }
814
815 // Iterator support for range-based for loops
816 T *begin() { return data_; }
817 T *end() { return data_ + size_; }
818 const T *begin() const { return data_; }
819 const T *end() const { return data_ + size_; }
820};
821
827template<size_t STACK_SIZE, typename T = uint8_t> class SmallBufferWithHeapFallback {
828 public:
830 static_assert(std::is_trivially_default_constructible_v<T> && std::is_trivially_destructible_v<T>,
831 "the heap fallback leaves elements unconstructed");
832 if (size <= STACK_SIZE) {
833 this->buffer_ = this->stack_buffer_;
834 } else {
835 if (size <= SIZE_MAX / sizeof(T)) {
836 // NOLINTNEXTLINE(bugprone-sizeof-expression,cppcoreguidelines-no-malloc,cppcoreguidelines-owning-memory)
837 this->heap_buffer_ = static_cast<T *>(malloc(size * sizeof(T)));
838 }
839 // Callers write through get() unchecked, so exhaustion aborts like the new[] it replaces
840 if (this->heap_buffer_ == nullptr)
841 ESPHOME_ABORT_WITH_REASON("SmallBufferWithHeapFallback: out of memory");
842 this->buffer_ = this->heap_buffer_;
843 }
844 }
845 ~SmallBufferWithHeapFallback() { free(this->heap_buffer_); } // NOLINT(cppcoreguidelines-no-malloc)
846
847 // Delete copy and move operations to prevent double-delete
852
853 T *get() { return this->buffer_; }
854
855 private:
856 T stack_buffer_[STACK_SIZE];
857 T *heap_buffer_{nullptr};
858 T *buffer_;
859};
860
862
865
869int8_t ilog10(float value);
870
874inline float pow10_int(int8_t exp) {
875 float result = 1.0f;
876 if (exp >= 0) {
877 for (int8_t i = 0; i < exp; i++)
878 result *= 10.0f;
879 } else {
880 for (int8_t i = exp; i < 0; i++)
881 result /= 10.0f;
882 }
883 return result;
884}
885
887template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max_out) {
888 return (value - min) * (max_out - min_out) / (max - min) + min_out;
889}
890
894constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum = 0) {
895 return std::accumulate(data, data + len, sum);
896}
897
899constexpr uint16_t sum16_bytes(const uint8_t *data, size_t len, uint16_t sum = 0) {
900 return std::accumulate(data, data + len, sum);
901}
902
904constexpr uint8_t xor_bytes(const uint8_t *data, size_t len, uint8_t init = 0) {
905 return std::accumulate(data, data + len, init, std::bit_xor<>());
906}
907
909uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc = 0x00, uint8_t poly = 0x8C, bool msb_first = false);
910
912uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001,
913 bool refin = false, bool refout = false);
914uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc = 0, uint16_t poly = 0x1021, bool refin = false,
915 bool refout = false);
916
919uint32_t fnv1_hash(const char *str);
920inline uint32_t fnv1_hash(const std::string &str) { return fnv1_hash(str.c_str()); }
921
923constexpr uint32_t FNV1_OFFSET_BASIS = 2166136261UL;
925constexpr uint32_t FNV1_PRIME = 16777619UL;
926
928template<std::integral T> constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value) {
929 using UnsignedT = std::make_unsigned_t<T>;
930 UnsignedT uvalue = static_cast<UnsignedT>(value);
931 for (size_t i = 0; i < sizeof(T); i++) {
932 hash *= FNV1_PRIME;
933 hash ^= (uvalue >> (i * 8)) & 0xFF;
934 }
935 return hash;
936}
938constexpr uint32_t fnv1_hash_extend(uint32_t hash, const char *str) {
939 if (str) {
940 while (*str) {
941 hash *= FNV1_PRIME;
942 hash ^= *str++;
943 }
944 }
945 return hash;
946}
947inline uint32_t fnv1_hash_extend(uint32_t hash, const std::string &str) { return fnv1_hash_extend(hash, str.c_str()); }
948
950constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str) {
951 if (str) {
952 while (*str) {
953 hash ^= *str++;
954 hash *= FNV1_PRIME;
955 }
956 }
957 return hash;
958}
959inline uint32_t fnv1a_hash_extend(uint32_t hash, const std::string &str) {
960 return fnv1a_hash_extend(hash, str.c_str());
961}
963template<std::integral T> constexpr uint32_t fnv1a_hash_extend(uint32_t hash, T value) {
964 using UnsignedT = std::make_unsigned_t<T>;
965 UnsignedT uvalue = static_cast<UnsignedT>(value);
966 for (size_t i = 0; i < sizeof(T); i++) {
967 hash ^= (uvalue >> (i * 8)) & 0xFF;
968 hash *= FNV1_PRIME;
969 }
970 return hash;
971}
973constexpr uint32_t fnv1a_hash(const char *str) { return fnv1a_hash_extend(FNV1_OFFSET_BASIS, str); }
974inline uint32_t fnv1a_hash(const std::string &str) { return fnv1a_hash(str.c_str()); }
975
976// micros_to_millis<>() lives in its own lightweight header so hal.h can pull it
977// in for inline millis_64() without forcing every TU that includes hal.h to
978// also include the rest of helpers.h.
979
987float random_float();
990bool random_bytes(uint8_t *data, size_t len);
991
993
996
998constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb) {
999 return (static_cast<uint16_t>(msb) << 8) | (static_cast<uint16_t>(lsb));
1000}
1002constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) {
1003 return (static_cast<uint32_t>(byte1) << 16) | (static_cast<uint32_t>(byte2) << 8) | (static_cast<uint32_t>(byte3));
1004}
1006constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) {
1007 return (static_cast<uint32_t>(byte1) << 24) | (static_cast<uint32_t>(byte2) << 16) |
1008 (static_cast<uint32_t>(byte3) << 8) | (static_cast<uint32_t>(byte4));
1009}
1010
1012template<unsigned BITS> constexpr int32_t sign_extend(uint32_t value) {
1013 static_assert(BITS > 0 && BITS <= 32, "BITS must be 1 to 32");
1014 return static_cast<int32_t>(value << (32 - BITS)) >> (32 - BITS);
1015}
1016
1019template<typename T, enable_if_t<std::is_integral<T>::value, int> = 0> constexpr T encode_value(const uint8_t *bytes) {
1020 if constexpr (sizeof(T) == 1) {
1021 return static_cast<T>(bytes[0]);
1022 } else {
1023 std::make_unsigned_t<T> val = 0;
1024 for (size_t i = 0; i < sizeof(T); i++) {
1025 val <<= 8;
1026 val |= bytes[i];
1027 }
1028 return static_cast<T>(val);
1029 }
1030}
1032template<typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
1033constexpr T encode_value(const std::array<uint8_t, sizeof(T)> bytes) {
1034 return encode_value<T>(bytes.data());
1035}
1038template<typename T, enable_if_t<std::is_integral<T>::value, int> = 0>
1039constexpr T encode_value_le(const uint8_t *bytes) {
1040 // Loops here grow 16-bit callers by up to 48 bytes on ESP8266 through different inlining
1041 if constexpr (sizeof(T) == 1) {
1042 return static_cast<T>(bytes[0]);
1043 } else if constexpr (sizeof(T) == 2) {
1044 return static_cast<T>(encode_uint16(bytes[1], bytes[0]));
1045 } else {
1046 std::make_unsigned_t<T> val = 0;
1047 for (size_t i = sizeof(T); i > 0; i--) {
1048 val <<= 8;
1049 val |= bytes[i - 1];
1050 }
1051 return static_cast<T>(val);
1052 }
1053}
1055template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1056constexpr std::array<uint8_t, sizeof(T)> decode_value(T val) {
1057 std::array<uint8_t, sizeof(T)> ret{};
1058 for (size_t i = sizeof(T); i > 0; i--) {
1059 ret[i - 1] = val & 0xFF;
1060 val >>= 8;
1061 }
1062 return ret;
1063}
1064
1066constexpr uint8_t bcd_to_uint8(uint8_t bcd) { return (bcd >> 4) * 10 + (bcd & 0x0F); }
1068constexpr uint8_t uint8_to_bcd(uint8_t value) { return ((value / 10) << 4) | (value % 10); }
1069
1072template<typename T> ESPHOME_ALWAYS_INLINE constexpr void assign_bits(T &value, std::type_identity_t<T> mask, bool on) {
1073 if (on) {
1074 value |= mask;
1075 } else {
1076 value &= ~mask;
1077 }
1078}
1079
1081inline uint8_t reverse_bits(uint8_t x) {
1082 x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
1083 x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
1084 x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
1085 return x;
1086}
1088inline uint16_t reverse_bits(uint16_t x) {
1089 return (reverse_bits(static_cast<uint8_t>(x & 0xFF)) << 8) | reverse_bits(static_cast<uint8_t>((x >> 8) & 0xFF));
1090}
1093 return (reverse_bits(static_cast<uint16_t>(x & 0xFFFF)) << 16) |
1094 reverse_bits(static_cast<uint16_t>((x >> 16) & 0xFFFF));
1095}
1096
1098template<typename T> constexpr T convert_big_endian(T val) {
1099#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1100 return byteswap(val);
1101#else
1102 return val;
1103#endif
1104}
1105
1107template<typename T> constexpr T convert_little_endian(T val) {
1108#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1109 return val;
1110#else
1111 return byteswap(val);
1112#endif
1113}
1114
1117inline bool constant_time_equals(const uint8_t *a, const uint8_t *b, size_t len) {
1118 volatile uint8_t diff = 0;
1119 for (size_t i = 0; i < len; i++) {
1120 diff = diff | (a[i] ^ b[i]);
1121 }
1122 return diff == 0;
1123}
1124
1126
1129
1131bool str_equals_case_insensitive(const std::string &a, const std::string &b);
1133bool str_equals_case_insensitive(StringRef a, StringRef b);
1135inline bool str_equals_case_insensitive(const char *a, const char *b) { return strcasecmp(a, b) == 0; }
1136inline bool str_equals_case_insensitive(const std::string &a, const char *b) { return strcasecmp(a.c_str(), b) == 0; }
1137inline bool str_equals_case_insensitive(const char *a, const std::string &b) { return strcasecmp(a, b.c_str()) == 0; }
1138
1140bool str_startswith(const std::string &str, const std::string &start);
1142bool str_endswith(const std::string &str, const std::string &end);
1143
1145bool str_endswith_ignore_case(const char *str, size_t str_len, const char *suffix, size_t suffix_len);
1146inline bool str_endswith_ignore_case(const char *str, const char *suffix) {
1147 return str_endswith_ignore_case(str, strlen(str), suffix, strlen(suffix));
1148}
1149inline bool str_endswith_ignore_case(const std::string &str, const char *suffix) {
1150 return str_endswith_ignore_case(str.c_str(), str.size(), suffix, strlen(suffix));
1151}
1152
1154bool str_contains_ignore_case_fallback(const char *haystack, const char *needle);
1155
1156#ifdef USE_ESP8266
1159bool str_contains_ignore_case_p(const char *haystack, PGM_P needle);
1163#define str_contains_ignore_case(haystack, needle) str_contains_ignore_case_p(haystack, PSTR(needle))
1164#else
1166inline bool str_contains_ignore_case(const char *haystack, const char *needle) {
1167 if (!needle || !haystack) {
1168 return false;
1169 }
1170
1171// strcasestr is a GNU extension: newlib only declares it when _GNU_SOURCE is set.
1172// ESP32/host builds get it from their framework or from g++ on Linux;
1173// LibreTiny, RP2 and Zephyr do not, so they use the hand-rolled fallback.
1174#if defined(USE_LIBRETINY) || defined(USE_RP2) || defined(USE_ZEPHYR)
1175 return str_contains_ignore_case_fallback(haystack, needle);
1176#else // defined(USE_LIBRETINY) || defined(USE_RP2) || defined(USE_ZEPHYR)
1177 return strcasestr(haystack, needle) != nullptr;
1178#endif // defined(USE_LIBRETINY) || defined(USE_RP2) || defined(USE_ZEPHYR)
1179}
1180#endif // USE_ESP8266
1181
1183constexpr char to_snake_case_char(char c) { return (c == ' ') ? '_' : (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }
1184
1186constexpr char to_sanitized_char(char c) {
1187 return (c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? c : '_';
1188}
1189
1199char *str_sanitize_to(char *buffer, size_t buffer_size, const char *str);
1200
1202template<size_t N> inline char *str_sanitize_to(char (&buffer)[N], const char *str) {
1203 return str_sanitize_to(buffer, N, str);
1204}
1205
1211template<typename ReadByte> inline uint32_t fnv1_hash_object_id(size_t len, ReadByte read_byte) {
1213 for (size_t i = 0; i < len; i++) {
1214 hash *= FNV1_PRIME;
1215 // Apply snake_case (space->underscore, uppercase->lowercase) then sanitize
1216 hash ^= static_cast<uint8_t>(to_sanitized_char(to_snake_case_char(read_byte(i))));
1217 }
1218 return hash;
1219}
1220inline uint32_t fnv1_hash_object_id(const char *str, size_t len) {
1221 return fnv1_hash_object_id(len, [str](size_t i) { return str[i]; });
1222}
1223
1224#ifdef USE_ESP8266
1225// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM)
1226// Format strings must be wrapped with PSTR() macro
1233inline size_t buf_append_printf_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) {
1234 if (pos >= size) {
1235 return size;
1236 }
1237 va_list args;
1238 va_start(args, fmt);
1239 int written = vsnprintf_P(buf + pos, size - pos, fmt, args);
1240 va_end(args);
1241 if (written < 0) {
1242 return pos; // encoding error
1243 }
1244 return std::min(pos + static_cast<size_t>(written), size);
1245}
1246#define buf_append_printf(buf, size, pos, fmt, ...) buf_append_printf_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__)
1247#else
1255__attribute__((format(printf, 4, 5))) inline size_t buf_append_printf(char *buf, size_t size, size_t pos,
1256 const char *fmt, ...) {
1257 if (pos >= size) {
1258 return size;
1259 }
1260 va_list args;
1262 int written = vsnprintf(buf + pos, size - pos, fmt, args);
1263 va_end(args);
1264 if (written < 0) {
1265 return pos; // encoding error
1266 }
1267 return std::min(pos + static_cast<size_t>(written), size);
1268}
1269#endif
1270
1271#ifdef USE_ESP8266
1281inline size_t buf_append_str_p(char *buf, size_t size, size_t pos, PGM_P str) {
1282 if (pos >= size) {
1283 return size;
1284 }
1285 size_t remaining = size - pos - 1; // reserve space for null terminator
1286 size_t len = strnlen_P(str, remaining);
1287 memcpy_P(buf + pos, str, len);
1288 pos += len;
1289 buf[pos] = '\0';
1290 return pos;
1291}
1295#define buf_append_str(buf, size, pos, str) buf_append_str_p(buf, size, pos, PSTR(str))
1296#else
1304inline size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str) {
1305 if (pos >= size) {
1306 return size;
1307 }
1308 size_t remaining = size - pos - 1; // reserve space for null terminator
1309 size_t len = 0;
1310 while (len < remaining && str[len] != '\0') {
1311 len++;
1312 }
1313 memcpy(buf + pos, str, len);
1314 pos += len;
1315 buf[pos] = '\0';
1316 return pos;
1317}
1318#endif
1319
1321static constexpr size_t MAX_NAME_WITH_SUFFIX_SIZE = 128;
1322
1332size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep,
1333 const char *suffix_ptr, size_t suffix_len);
1334
1336static constexpr size_t MAC_ADDRESS_SUFFIX_WITH_SEPARATOR_LEN = 7;
1337
1343size_t truncate_name_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, size_t keep_suffix_len);
1344
1346
1349
1351template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1352optional<T> parse_number(const char *str) {
1353 char *end = nullptr;
1354 unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
1355 if (end == str || *end != '\0' || value > std::numeric_limits<T>::max())
1356 return {};
1357 return value;
1358}
1360template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1361optional<T> parse_number(const std::string &str) {
1362 return parse_number<T>(str.c_str());
1363}
1365template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1366optional<T> parse_number(const char *str) {
1367 char *end = nullptr;
1368 signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
1369 if (end == str || *end != '\0' || value < std::numeric_limits<T>::min() || value > std::numeric_limits<T>::max())
1370 return {};
1371 return value;
1372}
1374template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1375optional<T> parse_number(const std::string &str) {
1376 return parse_number<T>(str.c_str());
1377}
1379optional<float> parse_float(const char *str);
1381template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0> optional<T> parse_number(const char *str) {
1382 return parse_float(str);
1383}
1385template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
1386optional<T> parse_number(const std::string &str) {
1387 return parse_number<T>(str.c_str());
1388}
1389
1401size_t parse_hex(const char *str, size_t len, uint8_t *data, size_t count);
1403inline bool parse_hex(const char *str, uint8_t *data, size_t count) {
1404 return parse_hex(str, strlen(str), data, count) == 2 * count;
1405}
1407inline bool parse_hex(const std::string &str, uint8_t *data, size_t count) {
1408 return parse_hex(str.c_str(), str.length(), data, count) == 2 * count;
1409}
1411inline bool parse_hex(const char *str, std::vector<uint8_t> &data, size_t count) {
1412 data.resize(count);
1413 return parse_hex(str, strlen(str), data.data(), count) == 2 * count;
1414}
1416inline bool parse_hex(const std::string &str, std::vector<uint8_t> &data, size_t count) {
1417 data.resize(count);
1418 return parse_hex(str.c_str(), str.length(), data.data(), count) == 2 * count;
1419}
1425template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1426optional<T> parse_hex(const char *str, size_t len) {
1427 T val = 0;
1428 if (len > 2 * sizeof(T) || parse_hex(str, len, reinterpret_cast<uint8_t *>(&val), sizeof(T)) == 0)
1429 return {};
1430 return convert_big_endian(val);
1431}
1433template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const char *str) {
1434 return parse_hex<T>(str, strlen(str));
1435}
1437template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const std::string &str) {
1438 return parse_hex<T>(str.c_str(), str.length());
1439}
1440
1443static constexpr uint8_t INVALID_HEX_CHAR = 255;
1444
1445constexpr uint8_t parse_hex_char(char c) {
1446 if (c >= '0' && c <= '9')
1447 return c - '0';
1448 if (c >= 'A' && c <= 'F')
1449 return c - 'A' + 10;
1450 if (c >= 'a' && c <= 'f')
1451 return c - 'a' + 10;
1452 return INVALID_HEX_CHAR;
1453}
1454
1456ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
1457
1459ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
1460
1462ESPHOME_ALWAYS_INLINE inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
1463
1465static constexpr size_t JSON_ESCAPE_MAX_EXPANSION = 6;
1466
1478const char *json_escape_into_buffer(std::span<char> buf, StringRef value, bool short_control_escapes = true);
1479
1482inline char *int8_to_str(char *buf, int8_t val) {
1483 int32_t v = val;
1484 if (v < 0) {
1485 *buf++ = '-';
1486 v = -v;
1487 }
1488 if (v >= 100) {
1489 *buf++ = '1'; // int8 max is 128, so hundreds digit is always 1
1490 v -= 100;
1491 // Must write tens digit (even if 0) after hundreds
1492 int32_t tens = v / 10;
1493 *buf++ = '0' + tens;
1494 v -= tens * 10;
1495 } else if (v >= 10) {
1496 int32_t tens = v / 10;
1497 *buf++ = '0' + tens;
1498 v -= tens * 10;
1499 }
1500 *buf++ = '0' + v;
1501 return buf;
1502}
1503
1508inline char *buf_append_sep_str(char *buf, size_t remaining, char separator, const char *str, size_t str_len) {
1509 if (remaining < 2) {
1510 if (remaining >= 1) {
1511 *buf = '\0';
1512 }
1513 return buf;
1514 }
1515 *buf++ = separator;
1516 remaining--;
1517 size_t copy_len = std::min(str_len, remaining - 1);
1518 memcpy(buf, str, copy_len);
1519 buf += copy_len;
1520 *buf = '\0';
1521 return buf;
1522}
1523
1525inline uint32_t small_pow10(int8_t n) { return n == 4 ? 10000 : n == 3 ? 1000 : n == 2 ? 100 : n == 1 ? 10 : 1; }
1526
1528static constexpr size_t UINT32_MAX_STR_SIZE = 11;
1529
1532char *uint32_to_str_unchecked(char *buf, uint32_t val);
1533
1536inline size_t uint32_to_str(std::span<char, UINT32_MAX_STR_SIZE> buf, uint32_t val) {
1537 char *end = uint32_to_str_unchecked(buf.data(), val);
1538 *end = '\0';
1539 return static_cast<size_t>(end - buf.data());
1540}
1541
1545inline char *frac_to_str_unchecked(char *buf, uint32_t frac, uint32_t divisor) {
1546 while (divisor > 0) {
1547 *buf++ = '0' + static_cast<char>(frac / divisor);
1548 frac %= divisor;
1549 divisor /= 10;
1550 }
1551 return buf;
1552}
1553
1555char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1556
1559template<size_t N> inline char *format_hex_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1560 static_assert(N >= 3, "Buffer must hold at least one hex byte (3 chars)");
1561 return format_hex_to(buffer, N, data, length);
1562}
1563
1565template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1566inline char *format_hex_to(char (&buffer)[N], T val) {
1567 static_assert(N >= sizeof(T) * 2 + 1, "Buffer too small for type");
1569 return format_hex_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1570}
1571
1573template<size_t N> inline char *format_hex_to(char (&buffer)[N], const std::vector<uint8_t> &data) {
1574 return format_hex_to(buffer, data.data(), data.size());
1575}
1576
1578template<size_t N, size_t M> inline char *format_hex_to(char (&buffer)[N], const std::array<uint8_t, M> &data) {
1579 return format_hex_to(buffer, data.data(), data.size());
1580}
1581
1583constexpr size_t format_hex_size(size_t byte_count) { return byte_count * 2 + 1; }
1584
1586constexpr size_t format_hex_prefixed_size(size_t byte_count) { return byte_count * 2 + 3; }
1587
1589template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1590inline char *format_hex_prefixed_to(char (&buffer)[N], T val) {
1591 static_assert(N >= sizeof(T) * 2 + 3, "Buffer too small for prefixed hex");
1592 buffer[0] = '0';
1593 buffer[1] = 'x';
1595 format_hex_to(buffer + 2, N - 2, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1596 return buffer;
1597}
1598
1600template<size_t N> inline char *format_hex_prefixed_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1601 static_assert(N >= 5, "Buffer must hold at least '0x' + one hex byte + null");
1602 buffer[0] = '0';
1603 buffer[1] = 'x';
1604 format_hex_to(buffer + 2, N - 2, data, length);
1605 return buffer;
1606}
1607
1609constexpr size_t format_hex_pretty_size(size_t byte_count) { return byte_count * 3; }
1610
1622char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator = ':');
1623
1625template<size_t N>
1626inline char *format_hex_pretty_to(char (&buffer)[N], const uint8_t *data, size_t length, char separator = ':') {
1627 static_assert(N >= 3, "Buffer must hold at least one hex byte");
1628 return format_hex_pretty_to(buffer, N, data, length, separator);
1629}
1630
1632template<size_t N>
1633inline char *format_hex_pretty_to(char (&buffer)[N], const std::vector<uint8_t> &data, char separator = ':') {
1634 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1635}
1636
1638template<size_t N, size_t M>
1639inline char *format_hex_pretty_to(char (&buffer)[N], const std::array<uint8_t, M> &data, char separator = ':') {
1640 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1641}
1642
1644constexpr size_t format_hex_pretty_uint16_size(size_t count) { return count * 5; }
1645
1659char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint16_t *data, size_t length, char separator = ':');
1660
1662template<size_t N>
1663inline char *format_hex_pretty_to(char (&buffer)[N], const uint16_t *data, size_t length, char separator = ':') {
1664 static_assert(N >= 5, "Buffer must hold at least one hex uint16_t");
1665 return format_hex_pretty_to(buffer, N, data, length, separator);
1666}
1667
1669static constexpr size_t MAC_ADDRESS_SIZE = 6;
1671static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = format_hex_pretty_size(MAC_ADDRESS_SIZE);
1673static constexpr size_t MAC_ADDRESS_BUFFER_SIZE = MAC_ADDRESS_SIZE * 2 + 1;
1674
1676inline char *format_mac_addr_upper(const uint8_t *mac, char *output) {
1677 return format_hex_pretty_to(output, MAC_ADDRESS_PRETTY_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE, ':');
1678}
1679
1681inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) {
1682 format_hex_to(output, MAC_ADDRESS_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE);
1683}
1684
1686constexpr size_t format_bin_size(size_t byte_count) { return byte_count * 8 + 1; }
1687
1707char *format_bin_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1708
1710template<size_t N> inline char *format_bin_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1711 static_assert(N >= 9, "Buffer must hold at least one binary byte (9 chars)");
1712 return format_bin_to(buffer, N, data, length);
1713}
1714
1731template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1732inline char *format_bin_to(char (&buffer)[N], T val) {
1733 static_assert(N >= sizeof(T) * 8 + 1, "Buffer too small for type");
1735 return format_bin_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1736}
1737
1746ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr);
1747
1749static constexpr size_t VALUE_ACCURACY_MAX_LEN = 64;
1750
1752size_t value_accuracy_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value, int8_t accuracy_decimals);
1754size_t value_accuracy_with_uom_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value,
1755 int8_t accuracy_decimals, StringRef unit_of_measurement);
1756
1758int8_t step_to_accuracy_decimals(float step);
1759
1760size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len);
1761size_t base64_decode(const uint8_t *encoded_data, size_t encoded_len, uint8_t *buf, size_t buf_len);
1762
1767bool base64_decode_int32_vector(const std::string &base64, std::vector<int32_t> &out);
1768
1770
1773
1775void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value);
1777void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue);
1778
1780
1783
1785constexpr float celsius_to_fahrenheit(float value) { return value * 1.8f + 32.0f; }
1787constexpr float fahrenheit_to_celsius(float value) { return (value - 32.0f) / 1.8f; }
1788
1789enum class TemperatureUnit : uint8_t {
1790 CELSIUS = 0,
1791 FAHRENHEIT = 1,
1792 KELVIN = 2,
1793};
1794
1796
1799
1806template<typename... X> struct Callback;
1807
1808template<typename... Ts> struct Callback<void(Ts...)> {
1809 // The inline storage path stores callable bytes in ctx_ via memcpy.
1810 // sizeof equality with uintptr_t ensures void* can round-trip arbitrary bit patterns,
1811 // which combined with flat address spaces on all ESPHome targets means no trap representations.
1812 static_assert(sizeof(void *) == sizeof(std::uintptr_t), "void* must be the same size as uintptr_t");
1813
1814 void (*fn_)(void *, Ts...){nullptr};
1815 void *ctx_{nullptr};
1816
1818 void call(Ts... args) const { this->fn_(this->ctx_, std::forward<Ts>(args)...); }
1819
1822 template<typename F> static constexpr bool fits_inline() {
1823 using DecayF = std::decay_t<F>;
1824 return sizeof(DecayF) <= sizeof(void *) && std::is_trivially_copyable_v<DecayF> &&
1825 std::is_invocable_v<const DecayF &, Ts...>;
1826 }
1827
1830 template<typename F> static Callback create(F &&callable) {
1831 using DecayF = std::decay_t<F>;
1832 if constexpr (fits_inline<F>()) {
1833 // Small trivial callable (e.g. [this]() { this->method(); }) - store inline in ctx.
1834 // Safe under C++20 (P0593R6): byte copy into aligned storage implicitly
1835 // creates objects of implicit-lifetime types (trivially copyable qualifies).
1836 Callback cb; // fn and ctx are zero-initialized by default
1837 // Decay callable to a local variable first. When F is a function reference
1838 // (e.g. void(&)(int)), &callable would point at machine code, not a pointer variable.
1839 DecayF decayed = std::forward<F>(callable);
1840 __builtin_memcpy(&cb.ctx_, &decayed, sizeof(DecayF));
1841 cb.fn_ = [](void *c, Ts... args) {
1842 alignas(DecayF) char buf[sizeof(DecayF)];
1843 __builtin_memcpy(buf, &c, sizeof(DecayF));
1844 (*std::launder(reinterpret_cast<DecayF *>(buf)))(args...);
1845 };
1846 return cb;
1847 } else {
1848 // Large or non-trivial callable - heap allocate.
1849 // Intentionally never freed: callbacks in ESPHome are registered during setup()
1850 // and live for device lifetime. Same lifetime as the previous std::function approach.
1851 auto *stored = new DecayF(std::forward<F>(callable));
1852 return {[](void *c, Ts... args) { (*static_cast<DecayF *>(c))(args...); }, static_cast<void *>(stored)};
1853 }
1854 }
1855
1857 struct Box {
1858 void (*free)(Box *box);
1859 };
1860
1862 template<typename F> static Callback create_boxed(F &&callable) {
1863 struct Boxed : Box {
1864 std::decay_t<F> fn;
1865 };
1866 auto *box = new Boxed{{[](Box *b) { delete static_cast<Boxed *>(b); }}, // NOLINT(cppcoreguidelines-owning-memory)
1867 std::forward<F>(callable)};
1868 return {[](void *c, Ts... args) { static_cast<Boxed *>(static_cast<Box *>(c))->fn(args...); },
1869 static_cast<Box *>(box)};
1870 }
1871
1873 void free_boxed() const {
1874 auto *box = static_cast<Box *>(this->ctx_);
1875 box->free(box);
1876 }
1877};
1878
1880void *callback_manager_grow(void *data, uint16_t size, uint16_t &capacity, size_t elem_size);
1881
1882template<typename... X> class CallbackManager;
1883
1895template<typename... Ts> class CallbackManager<void(Ts...)> {
1896 using CbType = Callback<void(Ts...)>;
1897 static_assert(std::is_trivially_copyable_v<CbType>, "Callback must be trivially copyable");
1898
1899 public:
1900 CallbackManager() = default;
1901 ~CallbackManager() { ::operator delete(this->data_); }
1902
1903 // Non-copyable (would alias data_), movable (for std::map support)
1907 : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
1908 other.data_ = nullptr;
1909 other.size_ = 0;
1910 other.capacity_ = 0;
1911 }
1913 std::swap(this->data_, other.data_);
1914 std::swap(this->size_, other.size_);
1915 std::swap(this->capacity_, other.capacity_);
1916 return *this;
1917 }
1918
1921 template<typename F> void add(F &&callback) { this->add_(CbType::create(std::forward<F>(callback))); }
1922
1924 inline void ESPHOME_ALWAYS_INLINE call(const Ts &...args) {
1925 if (this->size_ != 0) {
1926 for (auto *it = this->data_, *end = it + this->size_; it != end; ++it) {
1927 it->call(args...);
1928 }
1929 }
1930 }
1931 uint16_t size() const { return this->size_; }
1932
1934 void operator()(const Ts &...args) { this->call(args...); }
1935
1936 protected:
1937 template<typename...> friend class LazyCallbackManager;
1940 void add_(CbType cb) {
1941 if (this->size_ == this->capacity_) {
1942 this->data_ =
1943 static_cast<CbType *>(callback_manager_grow(this->data_, this->size_, this->capacity_, sizeof(CbType)));
1944 }
1945 this->data_[this->size_++] = cb;
1946 }
1947 CbType *data_{nullptr};
1948 uint16_t size_{0};
1949 uint16_t capacity_{0};
1950};
1951
1960template<size_t N, typename... X> class StaticCallbackManager;
1961
1962template<size_t N, typename... Ts> class StaticCallbackManager<N, void(Ts...)> {
1963 public:
1966 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
1967
1969 void call(Ts... args) {
1970 for (auto &cb : this->callbacks_)
1971 cb.call(args...);
1972 }
1973 size_t size() const { return this->callbacks_.size(); }
1974
1976 void operator()(Ts... args) { call(args...); }
1977
1978 protected:
1980 void add_(Callback<void(Ts...)> cb) { this->callbacks_.push_back(cb); }
1982};
1983
1984template<typename... X> class LazyCallbackManager;
1985
2001template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
2002 public:
2006 ~LazyCallbackManager() { delete this->callbacks_; }
2007
2008 // Non-copyable and non-movable (entities are never copied or moved)
2013
2015 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
2016
2018 void call(Ts... args) {
2019 if (this->callbacks_) {
2020 this->callbacks_->call(args...);
2021 }
2022 }
2023
2025 size_t size() const { return this->callbacks_ ? this->callbacks_->size() : 0; }
2026
2028 bool empty() const { return !this->callbacks_ || this->callbacks_->size() == 0; }
2029
2031 void operator()(Ts... args) { this->call(args...); }
2032
2033 protected:
2035 void add_(Callback<void(Ts...)> cb) {
2036 if (!this->callbacks_) {
2037 this->callbacks_ = new CallbackManager<void(Ts...)>();
2038 }
2039 this->callbacks_->add_(cb);
2040 }
2041 CallbackManager<void(Ts...)> *callbacks_{nullptr};
2042};
2043
2045template<typename T> class Deduplicator {
2046 public:
2048 bool next(T value) {
2049 if (this->has_value_ && !this->value_unknown_ && this->last_value_ == value) {
2050 return false;
2051 }
2052 this->has_value_ = true;
2053 this->value_unknown_ = false;
2054 this->last_value_ = value;
2055 return true;
2056 }
2059 bool ret = !this->value_unknown_;
2060 this->value_unknown_ = true;
2061 return ret;
2062 }
2064 bool has_value() const { return this->has_value_; }
2065
2066 protected:
2067 bool has_value_{false};
2068 bool value_unknown_{false};
2070};
2071
2073template<typename T> class Parented {
2074 public:
2076 Parented(T *parent) : parent_(parent) {}
2077
2079 T *get_parent() const { return parent_; }
2081 void set_parent(T *parent) { parent_ = parent; }
2082
2083 protected:
2084 T *parent_{nullptr};
2085};
2086
2088
2091
2096class Mutex {
2097 public:
2098 Mutex(const Mutex &) = delete;
2099 Mutex &operator=(const Mutex &) = delete;
2100
2101#if defined(USE_ESP8266) || defined(USE_RP2)
2102 // Single-threaded platforms: inline no-ops so the compiler eliminates all call overhead.
2103 Mutex() = default;
2104 ~Mutex() = default;
2105 void lock() {}
2106 bool try_lock() { return true; }
2107 void unlock() {}
2108#elif defined(USE_ESP32) || defined(USE_LIBRETINY)
2109 // FreeRTOS platforms: inline to avoid out-of-line call overhead.
2110 Mutex() { handle_ = xSemaphoreCreateMutex(); }
2111 ~Mutex() = default;
2112 void lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); }
2113 bool try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; }
2114 void unlock() { xSemaphoreGive(this->handle_); }
2115
2116 private:
2117 SemaphoreHandle_t handle_;
2118#else
2119 Mutex();
2120 ~Mutex();
2121 void lock();
2122 bool try_lock();
2123 void unlock();
2124
2125 private:
2126 // d-pointer to store private data on new platforms
2127 void *handle_; // NOLINT(clang-diagnostic-unused-private-field)
2128#endif
2129};
2130
2136 public:
2137 LockGuard(Mutex &mutex) : mutex_(mutex) { mutex_.lock(); }
2138 ~LockGuard() { mutex_.unlock(); }
2139
2140 private:
2141 Mutex &mutex_;
2142};
2143
2165 public:
2166 InterruptLock();
2168
2169 protected:
2170#if defined(USE_ESP8266) || defined(USE_RP2) || defined(USE_ZEPHYR)
2172#endif
2173};
2174
2184 public:
2185 LwIPLock(const LwIPLock &) = delete;
2186 LwIPLock &operator=(const LwIPLock &) = delete;
2187
2188#if defined(USE_ESP32) || defined(USE_RP2)
2189 // Platforms with potential lwIP core locking — out-of-line implementations in helpers.cpp
2190 LwIPLock();
2191 ~LwIPLock();
2192#else
2193 // No lwIP core locking — inline no-ops (empty bodies instead of = default
2194 // to prevent clang-tidy unused-variable warnings at call sites)
2197#endif
2198};
2199
2200#if defined(USE_ESP8266) && F_CPU != 160000000L
2201// Forward decl from <user_interface.h>
2202// NOLINTNEXTLINE(readability-redundant-declaration)
2203extern "C" bool system_update_cpu_freq(uint8_t freq);
2204#endif
2205
2213 public:
2216#if defined(USE_ESP8266) && F_CPU != 160000000L
2219#else
2220 // Not = default, so clang-tidy does not flag unused variables at call sites
2223#endif
2224};
2225
2232 public:
2234 void start();
2236 void stop();
2237
2239 static bool is_high_frequency() { return num_requests > 0; }
2240
2241 protected:
2242 bool started_{false};
2243 static uint8_t num_requests; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
2244};
2245
2247void get_mac_address_raw(uint8_t *mac); // NOLINT(readability-non-const-parameter)
2248
2252void get_mac_address_into_buffer(std::span<char, MAC_ADDRESS_BUFFER_SIZE> buf);
2253
2257const char *get_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
2258
2259#ifdef USE_ESP32
2261void set_mac_address(uint8_t *mac);
2262
2266bool get_custom_mac_address(uint8_t *mac);
2267#endif
2268
2272
2275bool mac_address_is_valid(const uint8_t *mac);
2276
2279
2281
2284
2285template<typename T> struct RAMDeleter;
2287template<typename T> using RAMUniquePtr = std::unique_ptr<T, RAMDeleter<T>>;
2288
2298template<class T> class RAMAllocator {
2299 public:
2300 using value_type = T;
2301
2302 enum Flags {
2303 NONE = 0, // Perform external allocation and fall back to internal memory
2304 ALLOC_EXTERNAL = 1 << 0, // Perform external allocation only.
2305 ALLOC_INTERNAL = 1 << 1, // Perform internal allocation only.
2306 ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
2307 PREFER_INTERNAL = 1 << 3, // Perform internal allocation and fall back to external memory
2308 };
2309
2310 constexpr RAMAllocator() = default;
2311 constexpr RAMAllocator(uint8_t flags) {
2312 if (flags & PREFER_INTERNAL) {
2314 return;
2315 }
2316 const uint8_t alloc_bits = flags & (ALLOC_INTERNAL | ALLOC_EXTERNAL);
2317 if (alloc_bits != 0) {
2318 this->flags_ = alloc_bits;
2319 return;
2320 }
2321 this->flags_ = ALLOC_INTERNAL | ALLOC_EXTERNAL;
2322 }
2323 template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {}
2324
2325 T *allocate(size_t n) { return this->allocate(n, sizeof(T)); }
2326
2327 T *allocate(size_t n, size_t manual_size) {
2328 size_t size = n * manual_size;
2329 T *ptr = nullptr;
2330#ifdef USE_ESP32
2331 const auto caps = this->get_caps_();
2332 ptr = static_cast<T *>(heap_caps_malloc_prefer(size, caps.num, caps.primary, caps.fallback));
2333#else
2334 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2335 ptr = static_cast<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2336#endif
2337 return ptr;
2338 }
2339
2340 T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); }
2341
2342 T *reallocate(T *p, size_t n, size_t manual_size) {
2343 size_t size = n * manual_size;
2344 T *ptr = nullptr;
2345#ifdef USE_ESP32
2346 const auto caps = this->get_caps_();
2347 ptr = static_cast<T *>(heap_caps_realloc_prefer(p, size, caps.num, caps.primary, caps.fallback));
2348#else
2349 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2350 ptr = static_cast<T *>(realloc(p, size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2351#endif
2352 return ptr;
2353 }
2354
2355 void deallocate(T *p, size_t n) {
2356 free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2357 }
2358
2361 template<typename... Args> RAMUniquePtr<T> make_unique(Args &&...args) {
2362 static_assert(alignof(T) <= alignof(std::max_align_t), "malloc storage cannot hold an over aligned type");
2363 T *p = this->allocate(1);
2364 if (p == nullptr)
2365 return {};
2366 // ::new so a class scoped operator new cannot hide the global placement form
2367 return RAMUniquePtr<T>(::new (p) T(std::forward<Args>(args)...));
2368 }
2369
2372 static_assert(std::is_trivially_default_constructible_v<T>, "elements are left unconstructed");
2373 static_assert(alignof(T) <= alignof(std::max_align_t), "malloc storage cannot hold an over aligned type");
2374 if (n == 0 || n > SIZE_MAX / sizeof(T))
2375 return {};
2376 return RAMUniquePtr<T[]>(this->allocate(n));
2377 }
2378
2382 size_t get_free_heap_size() const {
2383#ifdef USE_ESP8266
2384 return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
2385#elif defined(USE_ESP32)
2386 auto max_internal =
2387 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2388 auto max_external =
2389 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2390 return max_internal + max_external;
2391#elif defined(USE_RP2)
2392 return ::rp2040.getFreeHeap();
2393#elif defined(USE_LIBRETINY)
2394 return lt_heap_get_free();
2395#else
2396 return 100000;
2397#endif
2398 }
2399
2404#ifdef USE_ESP8266
2405 return ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
2406#elif defined(USE_ESP32)
2407 auto max_internal =
2408 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2409 auto max_external =
2410 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2411 return std::max(max_internal, max_external);
2412#else
2413 return this->get_free_heap_size();
2414#endif
2415 }
2416
2417 private:
2418#ifdef USE_ESP32
2419 struct Caps {
2420 uint32_t primary;
2421 uint32_t fallback;
2422 uint8_t num; // distinct capability sets to hand to heap_caps_*_prefer
2423 constexpr Caps(uint32_t primary_caps, uint32_t fallback_caps)
2424 : primary(primary_caps), fallback(fallback_caps), num(primary_caps == fallback_caps ? 1 : 2) {}
2425 };
2426
2433 constexpr Caps get_caps_() const {
2434 constexpr uint32_t external_caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
2435 constexpr uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
2436 if (this->flags_ & PREFER_INTERNAL) {
2437 return {internal_caps, external_caps};
2438 }
2439 const uint32_t primary = (this->flags_ & ALLOC_EXTERNAL) ? external_caps : internal_caps;
2440 const uint32_t fallback = (this->flags_ & ALLOC_INTERNAL) ? internal_caps : external_caps;
2441 return {primary, fallback};
2442 }
2443#endif
2444
2445 uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL};
2446};
2447
2448template<class T> using ExternalRAMAllocator = RAMAllocator<T>;
2449
2451template<typename T> struct RAMDeleter {
2452 void operator()(T *p) const {
2453 p->~T();
2455 }
2456};
2458template<typename T> struct RAMDeleter<T[]> {
2459 static_assert(std::is_trivially_destructible_v<T>, "RAMUniquePtr<T[]> is for trivially destructible elements");
2460 void operator()(T *p) const { RAMAllocator<T>().deallocate(p, 1); }
2461};
2462
2467template<typename T, typename U>
2468concept comparable_with = requires(T a, U b) {
2469 { a > b } -> std::convertible_to<bool>;
2470 { a < b } -> std::convertible_to<bool>;
2471};
2472
2473template<std::totally_ordered T, comparable_with<T> U> T clamp_at_least(T value, U min) {
2474 if (value < min)
2475 return min;
2476 return value;
2477}
2478template<std::totally_ordered T, comparable_with<T> U> T clamp_at_most(T value, U max) {
2479 if (value > max)
2480 return max;
2481 return value;
2482}
2483
2486
2491template<typename T, enable_if_t<!std::is_pointer<T>::value, int> = 0> T id(T value) { return value; }
2496template<typename T, enable_if_t<std::is_pointer<T *>::value, int> = 0> T &id(T *value) { return *value; }
2497
2499
2500} // namespace esphome
uint8_t m
Definition bl0906.h:1
void ESPHOME_ALWAYS_INLINE call(const Ts &...args)
Call all callbacks in this manager.
Definition helpers.h:1924
CallbackManager & operator=(const CallbackManager &)=delete
void operator()(const Ts &...args)
Call all callbacks in this manager.
Definition helpers.h:1934
CallbackManager & operator=(CallbackManager &&other) noexcept
Definition helpers.h:1912
void add(F &&callback)
Add any callable.
Definition helpers.h:1921
CallbackManager(CallbackManager &&other) noexcept
Definition helpers.h:1906
void add_(CbType cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1940
CallbackManager(const CallbackManager &)=delete
const T & operator[](size_t index) const
Definition helpers.h:171
const T & at(size_t index) const
Definition helpers.h:172
constexpr ConstVector()=default
const T * begin() const
Definition helpers.h:166
void assign_static(const T *data, size_t size)
Codegen only: call before any runtime copy; it does not free a previous owned copy (generated setup()...
Definition helpers.h:176
constexpr ConstVector(const T *data, size_t size)
Definition helpers.h:161
ConstVector(const ConstVector &)=delete
ConstVector & operator=(const ConstVector &)=delete
void assign_copy(const T *data, size_t size)
Copies the list into a heap array this owns, freeing a previous owned copy.
Definition helpers.h:181
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory).
Definition helpers.h:130
constexpr bool empty() const
Definition helpers.h:141
const T * begin() const
Definition helpers.h:137
constexpr ConstVector(const T *data, size_t size)
Definition helpers.h:135
const T & at(size_t i) const
Definition helpers.h:143
const constexpr T & operator[](size_t i) const
Definition helpers.h:142
const T * data() const
Definition helpers.h:139
constexpr size_t size() const
Definition helpers.h:140
constexpr ConstVector()=default
const T * end() const
Definition helpers.h:138
Runs the CPU at 160 MHz while alive.
Definition helpers.h:2212
CpuFrequencyBoost & operator=(const CpuFrequencyBoost &)=delete
CpuFrequencyBoost(const CpuFrequencyBoost &)=delete
Helper class to deduplicate items in a series of values.
Definition helpers.h:2045
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:2048
bool has_value() const
Returns true if this deduplicator has processed any items.
Definition helpers.h:2064
bool next_unknown()
Returns true if the deduplicator's value was previously known.
Definition helpers.h:2058
bool operator!=(const ConstIterator &other) const
Definition helpers.h:498
ConstIterator(const FixedRingBuffer *buf, index_type pos)
Definition helpers.h:492
bool operator!=(const Iterator &other) const
Definition helpers.h:483
Iterator(FixedRingBuffer *buf, index_type pos)
Definition helpers.h:477
Fixed-capacity circular buffer - allocates once at runtime, never reallocates.
Definition helpers.h:469
FixedRingBuffer & operator=(const FixedRingBuffer &)=delete
ConstIterator begin() const
Definition helpers.h:576
bool push(const T &value)
Push a value. Returns false if full.
Definition helpers.h:531
const T & front() const
Definition helpers.h:561
index_type capacity() const
Definition helpers.h:564
void push_overwrite(const T &value)
Push a value, overwriting the oldest if full.
Definition helpers.h:541
void init(index_type capacity)
Allocate capacity - can only be called once.
Definition helpers.h:519
const T & operator[](index_type pos) const
The element pos places after the oldest one; the sum is widened so a small index type cannot truncate...
Definition helpers.h:580
void pop()
Remove the oldest element.
Definition helpers.h:553
void clear()
Clear all elements (reset to empty, keep capacity)
Definition helpers.h:568
FixedRingBuffer(const FixedRingBuffer &)=delete
index_type size() const
Definition helpers.h:562
ConstIterator end() const
Definition helpers.h:577
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Definition helpers.h:624
const T & at(size_t i) const
Definition helpers.h:813
FixedVector(FixedVector &&other) noexcept
Definition helpers.h:680
FixedVector(std::initializer_list< T > init_list)
Constructor from initializer list - allocates exact size needed This enables brace initialization: Fi...
Definition helpers.h:671
const T * begin() const
Definition helpers.h:818
bool full() const
Definition helpers.h:803
FixedVector & operator=(std::initializer_list< T > init_list)
Assignment from initializer list - avoids temporary and move overhead This enables: FixedVector<int> ...
Definition helpers.h:703
T & front()
Access first element (no bounds checking - matches std::vector behavior) Caller must ensure vector is...
Definition helpers.h:783
const T & operator[](size_t i) const
Definition helpers.h:808
T & operator[](size_t i)
Access element without bounds checking (matches std::vector behavior) Caller must ensure index is val...
Definition helpers.h:807
size_t capacity() const
Definition helpers.h:802
T & back()
Access last element (no bounds checking - matches std::vector behavior) Caller must ensure vector is ...
Definition helpers.h:788
bool empty() const
Definition helpers.h:801
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:762
void pop_back()
Remove the last element in place (no reallocation, keeps capacity) Caller must ensure vector is not e...
Definition helpers.h:793
T & emplace_back(Args &&...args)
Emplace element without bounds checking - constructs in-place with arguments Caller must ensure suffi...
Definition helpers.h:774
size_t size() const
Definition helpers.h:800
const T & front() const
Definition helpers.h:784
const T & back() const
Definition helpers.h:789
const T * end() const
Definition helpers.h:819
bool try_init(size_t n)
Definition helpers.h:720
FixedVector & operator=(FixedVector &&other) noexcept
Definition helpers.h:687
T & at(size_t i)
Access element with bounds checking (matches std::vector behavior) Note: No exception thrown on out o...
Definition helpers.h:812
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:751
void init(size_t n)
Definition helpers.h:714
Helper class to request loop() to be called as fast as possible.
Definition helpers.h:2231
static bool is_high_frequency()
Check whether the loop is running continuously.
Definition helpers.h:2239
void stop()
Stop running the loop continuously.
Definition helpers.cpp:841
void start()
Start running the loop continuously.
Definition helpers.cpp:835
Helper class to disable interrupts.
Definition helpers.h:2164
LazyCallbackManager & operator=(const LazyCallbackManager &)=delete
LazyCallbackManager(const LazyCallbackManager &)=delete
size_t size() const
Return the number of registered callbacks.
Definition helpers.h:2025
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:2035
void add(F &&callback)
Add any callable. Allocates the underlying CallbackManager on first use.
Definition helpers.h:2015
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:2031
LazyCallbackManager & operator=(LazyCallbackManager &&)=delete
~LazyCallbackManager()
Destructor - clean up allocated CallbackManager if any.
Definition helpers.h:2006
void call(Ts... args)
Call all callbacks in this manager. No-op if no callbacks registered.
Definition helpers.h:2018
bool empty() const
Check if any callbacks are registered.
Definition helpers.h:2028
LazyCallbackManager(LazyCallbackManager &&)=delete
Helper class that wraps a mutex with a RAII-style API.
Definition helpers.h:2135
LockGuard(Mutex &mutex)
Definition helpers.h:2137
Helper class to lock the lwIP TCPIP core when making lwIP API calls from non-TCPIP threads.
Definition helpers.h:2183
LwIPLock(const LwIPLock &)=delete
LwIPLock & operator=(const LwIPLock &)=delete
Mutex implementation, with API based on the unavailable std::mutex.
Definition helpers.h:2096
~Mutex()=default
Definition helpers.cpp:40
void unlock()
Definition helpers.h:2107
Mutex()=default
Definition helpers.cpp:39
bool try_lock()
Definition helpers.h:2106
Mutex(const Mutex &)=delete
Mutex & operator=(const Mutex &)=delete
Helper class to easily give an object a parent of type T.
Definition helpers.h:2073
T * get_parent() const
Get the parent of this object.
Definition helpers.h:2079
Parented(T *parent)
Definition helpers.h:2076
void set_parent(T *parent)
Set the parent of this object.
Definition helpers.h:2081
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2298
constexpr RAMAllocator(uint8_t flags)
Definition helpers.h:2311
T * reallocate(T *p, size_t n, size_t manual_size)
Definition helpers.h:2342
size_t get_free_heap_size() const
Return the total heap space available via this allocator.
Definition helpers.h:2382
T * reallocate(T *p, size_t n)
Definition helpers.h:2340
void deallocate(T *p, size_t n)
Definition helpers.h:2355
RAMUniquePtr< T > make_unique(Args &&...args)
Value initialize one T; empty on exhaustion.
Definition helpers.h:2361
size_t get_max_free_block_size() const
Return the maximum size block this allocator could allocate.
Definition helpers.h:2403
T * allocate(size_t n)
Definition helpers.h:2325
constexpr RAMAllocator(const RAMAllocator< U > &other)
Definition helpers.h:2323
RAMUniquePtr< T[]> make_unique_array_for_overwrite(size_t n)
n elements left uninitialized, as std::make_unique_for_overwrite does; empty on exhaustion,...
Definition helpers.h:2371
T * allocate(size_t n, size_t manual_size)
Definition helpers.h:2327
constexpr RAMAllocator()=default
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:827
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:206
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:254
bool is_inline_() const
Definition helpers.h:276
void set(const uint8_t *src, size_t size)
Set buffer contents, allocating heap if needed.
Definition helpers.h:269
SmallInlineBuffer & operator=(const SmallInlineBuffer &)=delete
size_t size() const
Definition helpers.h:273
uint8_t inline_[InlineSize]
Definition helpers.h:280
SmallInlineBuffer & operator=(SmallInlineBuffer &&other) noexcept
Definition helpers.h:226
const uint8_t * data() const
Definition helpers.h:272
SmallInlineBuffer(SmallInlineBuffer &&other) noexcept
Definition helpers.h:215
void add(F &&callback)
Add any callable.
Definition helpers.h:1966
void call(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1969
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1980
StaticVector< Callback< void(Ts...)>, N > callbacks_
Definition helpers.h:1981
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1976
CallbackManager backed by StaticVector for compile-time-known callback counts.
Definition helpers.h:1960
ConstIterator(const StaticRingBuffer *buf, index_type pos)
Definition helpers.h:411
bool operator!=(const ConstIterator &other) const
Definition helpers.h:417
bool operator!=(const Iterator &other) const
Definition helpers.h:402
Iterator(StaticRingBuffer *buf, index_type pos)
Definition helpers.h:396
Fixed-size circular buffer with FIFO semantics and iteration support.
Definition helpers.h:390
bool push(const T &value)
Definition helpers.h:424
ConstIterator begin() const
Definition helpers.h:455
ConstIterator end() const
Definition helpers.h:456
index_type size() const
Definition helpers.h:443
const T & front() const
Definition helpers.h:442
void clear()
Clear all elements (reset to empty)
Definition helpers.h:447
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:286
const_reverse_iterator rend() const
Definition helpers.h:376
size_t size() const
Definition helpers.h:355
constexpr StaticVector() noexcept
Definition helpers.h:301
reverse_iterator rbegin()
Definition helpers.h:373
const T & operator[](size_t i) const
Definition helpers.h:364
reverse_iterator rend()
Definition helpers.h:374
void push_back(const T &value)
Definition helpers.h:325
bool empty() const
Definition helpers.h:357
static constexpr size_t capacity()
Definition helpers.h:356
void assign(InputIt first, InputIt last)
Definition helpers.h:338
const_reverse_iterator rbegin() const
Definition helpers.h:375
T & operator[](size_t i)
Definition helpers.h:363
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition helpers.h:292
typename std::array< T, N >::iterator iterator
Definition helpers.h:289
typename std::array< T, N >::const_iterator const_iterator
Definition helpers.h:290
std::reverse_iterator< iterator > reverse_iterator
Definition helpers.h:291
const T * data() const
Definition helpers.h:361
const_iterator end() const
Definition helpers.h:370
StaticVector(InputIt first, InputIt last)
Definition helpers.h:304
StaticVector(const StaticVector< T, M > &other)
Definition helpers.h:320
StaticVector(std::initializer_list< T > init)
Definition helpers.h:311
void resize(size_t n)
Definition helpers.h:335
const_iterator begin() const
Definition helpers.h:369
struct @66::@67 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:45
Functions to constrain the range of arithmetic values.
Definition helpers.h:2468
uint16_t flags
uint16_t id
std::vector< uint8_t > & out
Definition gif.cpp:49
unsigned count
Definition gif.cpp:51
uint32_t buffer
Definition gif.cpp:50
int ret
const char * format
mopeka_std_values val[3]
value[LAYOUT.day_index()]
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:1281
T clamp_at_most(T value, U max)
Definition helpers.h:2478
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
constexpr uint8_t xor_bytes(const uint8_t *data, size_t len, uint8_t init=0)
XOR the bytes of data with size len, starting from init.
Definition helpers.h:904
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:1304
constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str)
Extend a FNV-1a hash with additional string data.
Definition helpers.h:950
float random_float()
Return a random float between 0 and 1.
Definition helpers.cpp:206
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:1456
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:95
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)
Format name + separator + suffix directly into buffer without heap allocation.
Definition helpers.cpp:277
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:1098
size_t truncate_name_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, size_t keep_suffix_len)
Copy a name into a buffer, cutting it when it does not fit.
Definition helpers.cpp:297
bool str_contains_ignore_case(const char *haystack, const char *needle)
Case-insensitive check if needle string is contained in haystack (no heap allocation).
Definition helpers.h:1166
bool system_update_cpu_freq(uint8_t freq)
std::unique_ptr< T, RAMDeleter< T > > RAMUniquePtr
unique_ptr over RAMAllocator storage
Definition helpers.h:2287
constexpr char to_sanitized_char(char c)
Sanitize a single char: keep alphanumerics, dashes, underscores; replace others with underscore.
Definition helpers.h:1186
bool mac_address_is_valid(const uint8_t *mac)
Check if the MAC address is not all zeros or all ones.
Definition helpers.cpp:865
constexpr uint8_t uint8_to_bcd(uint8_t value)
Encode a value from 0 to 99 as a two-digit packed BCD byte, e.g. 59 to 0x59.
Definition helpers.h:1068
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:1462
void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output)
Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators)
Definition helpers.h:1681
constexpr uint32_t FNV1_OFFSET_BASIS
FNV-1 32-bit offset basis.
Definition helpers.h:923
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:770
uint16_t uint16_t size_t elem_size
Definition helpers.cpp:35
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:625
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:1233
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:554
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
Definition helpers.h:894
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:1107
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:928
ESPHOME_ALWAYS_INLINE constexpr void assign_bits(T &value, std::type_identity_t< T > mask, bool on)
Set the bits of mask in value when on is true, clear them otherwise.
Definition helpers.h:1072
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:732
va_end(args)
const void size_t len
Definition hal.h:68
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:1586
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:1002
uint32_t small_pow10(int8_t n)
Return 10^n for small non-negative n (0-4) as uint32_t, avoiding float.
Definition helpers.h:1525
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:1590
bool has_custom_mac_address()
Check if a custom MAC address is set (ESP32 & variants)
Definition helpers.cpp:119
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:370
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:498
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:1536
uint16_t size
Definition helpers.cpp:34
bool str_contains_ignore_case_fallback(const char *haystack, const char *needle)
Fallback implementation for case insensitive substring comparison.
Definition helpers.cpp:232
uint32_t fnv1_hash_object_id(size_t len, ReadByte read_byte)
Calculate FNV-1 hash of a string while applying snake_case + sanitize transformations.
Definition helpers.h:1211
int8_t ilog10(float value)
Compute floor(log10(fabs(value))) using iterative comparison.
Definition helpers.cpp:569
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:169
constexpr T encode_value_le(const uint8_t *bytes)
Encode a value from its constituent bytes (from least to most significant) in an array with length si...
Definition helpers.h:1039
T clamp_at_least(T value, U min)
Definition helpers.h:2473
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:1352
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:1545
void set_mac_address(uint8_t *mac)
Set the MAC address to use from the provided byte array (6 bytes).
Definition helpers.cpp:117
int8_t step_to_accuracy_decimals(float step)
Derive accuracy in decimals from an increment step.
Definition helpers.cpp:655
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:12
size_t size_t pos
Definition helpers.h:1255
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:854
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:1508
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:889
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:1583
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare strings for equality in case-insensitive manner.
Definition helpers.cpp:210
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:226
constexpr uint8_t bcd_to_uint8(uint8_t bcd)
Decode a two-digit packed BCD byte (tens in the high nibble) to its value, e.g. 0x59 to 59.
Definition helpers.h:1066
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:265
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:599
char * int8_to_str(char *buf, int8_t val)
Write int8 value to buffer without modulo operations.
Definition helpers.h:1482
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:644
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:432
char * uint32_to_str_unchecked(char *buf, uint32_t val)
Write unsigned 32-bit integer to buffer (internal, no size check).
Definition helpers.cpp:414
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:1609
TemperatureUnit
Definition helpers.h:1789
constexpr uint32_t FNV1_PRIME
FNV-1 32-bit prime.
Definition helpers.h:925
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:1019
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:793
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:848
uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t poly, bool refin, bool refout)
Definition helpers.cpp:135
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:1006
const void * src
Definition hal.h:68
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:68
constexpr int32_t sign_extend(uint32_t value)
Sign-extend the low BITS bits of value, e.g. sign_extend<24>(0xFFFFFE) == -2. Higher bits are ignored...
Definition helpers.h:1012
constexpr float celsius_to_fahrenheit(float value)
Convert degrees Celsius to degrees Fahrenheit.
Definition helpers.h:1785
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:998
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:87
size_t size_t const char * fmt
Definition helpers.h:1256
constexpr uint8_t parse_hex_char(char c)
Definition helpers.h:1445
bool str_startswith(const std::string &str, const std::string &start)
Check whether a string starts with a value.
Definition helpers.cpp:217
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:1056
bool constant_time_equals(const uint8_t *a, const uint8_t *b, size_t len)
Compare len bytes of a and b in a time that does not depend on where they first differ.
Definition helpers.h:1117
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:1686
int written
Definition helpers.h:1262
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:1183
constexpr float fahrenheit_to_celsius(float value)
Convert degrees Fahrenheit to degrees Celsius.
Definition helpers.h:1787
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Definition helpers.h:1081
optional< float > parse_float(const char *str)
Parse a decimal floating-point number; plain decimals skip the libc parser.
Definition helpers.cpp:326
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:428
float lerp(float completion, float start, float end)=delete
constexpr uint16_t sum16_bytes(const uint8_t *data, size_t len, uint16_t sum=0)
Add up the bytes of data with size len, truncated to 16 bits.
Definition helpers.h:899
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:1644
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:887
bool get_custom_mac_address(uint8_t *mac)
Read the custom MAC address from eFuse into the provided byte array (6 bytes).
Definition helpers.cpp:75
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:218
constexpr uint32_t fnv1a_hash(const char *str)
Calculate a FNV-1a hash of str.
Definition helpers.h:973
bool str_contains_ignore_case_p(const char *haystack, PGM_P needle)
ESP8266 internal implementation reading the needle from flash — prefer the str_contains_ignore_case m...
Definition helpers.cpp:248
uint16_t uint16_t & capacity
Definition helpers.cpp:34
ParseOnOffState
Return values for parse_on_off().
Definition helpers.h:1739
@ PARSE_ON
Definition helpers.h:1741
@ PARSE_TOGGLE
Definition helpers.h:1743
@ PARSE_OFF
Definition helpers.h:1742
@ PARSE_NONE
Definition helpers.h:1740
float pow10_int(int8_t exp)
Compute 10^exp using iterative multiplication/division.
Definition helpers.h:874
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:533
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:1676
static void uint32_t
Heap home of a callable stored by create_boxed(); the header knows how to free it.
Definition helpers.h:1857
static Callback create(F &&callable)
Create from any callable.
Definition helpers.h:1830
void free_boxed() const
Free the callable of a Callback made by create_boxed(); the Callback must not be called afterwards.
Definition helpers.h:1873
static constexpr bool fits_inline()
Whether create() stores F inline in the ctx pointer.
Definition helpers.h:1822
static Callback create_boxed(F &&callable)
Store any callable on the heap with a deleter, for an owner that can later free_boxed() it.
Definition helpers.h:1862
void call(Ts... args) const
Invoke the callback. Only valid on Callbacks created via create(), never on default-constructed insta...
Definition helpers.h:1818
Lightweight type-erased callback (8 bytes on 32-bit) that avoids std::function overhead.
Definition helpers.h:1806
void operator()(T *p) const
Definition helpers.h:2460
Destroys and frees RAMAllocator storage. Not convertible: free() needs the address malloc returned.
Definition helpers.h:2451
void operator()(T *p) const
Definition helpers.h:2452
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