ESPHome 2026.10.0
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 <span>
19#include <string>
20#include <type_traits>
21#include <utility>
22#include <vector>
23#include <concepts>
24#include <strings.h>
25
28
29// Backward compatibility re-export of heap-allocating helpers.
30// These functions have moved to alloc_helpers.h. External components should
31// update their includes to use #include "esphome/core/alloc_helpers.h" directly.
32// This re-export will be removed in 2026.11.0.
34
35#ifdef USE_ESP8266
36#include <Esp.h>
37#include <pgmspace.h>
38#endif
39
40#ifdef USE_RP2
41#include <Arduino.h>
42#endif
43
44#ifdef USE_ESP32
45#include <esp_system.h>
46#include <esp_heap_caps.h>
47#endif
48
49#if defined(USE_ESP32)
50#include <freertos/FreeRTOS.h>
51#include <freertos/semphr.h>
52#elif defined(USE_LIBRETINY)
53#include <FreeRTOS.h>
54#include <semphr.h>
55#endif
56
57#ifdef USE_HOST
58#include <mutex>
59#endif
60
61#define HOT __attribute__((hot))
62#define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg)))
63#define ESPHOME_ALWAYS_INLINE __attribute__((always_inline))
64#define PACKED __attribute__((packed))
65
66namespace esphome {
67
68// Forward declaration to avoid circular dependency with string_ref.h
69class StringRef;
70
73
74// Keep "using" even after the removal of our backports, to avoid breaking existing code.
75using std::to_string;
76using std::is_trivially_copyable;
77using std::make_unique;
78using std::enable_if_t;
79using std::clamp;
80using std::is_invocable;
81#if __cpp_lib_bit_cast >= 201806
82using std::bit_cast;
83#else
85template<
86 typename To, typename From,
87 enable_if_t<sizeof(To) == sizeof(From) && is_trivially_copyable<From>::value && is_trivially_copyable<To>::value,
88 int> = 0>
89To bit_cast(const From &src) {
90 To dst;
91 memcpy(&dst, &src, sizeof(To));
92 return dst;
93}
94#endif
95
96// clang-format off
97inline float lerp(float completion, float start, float end) = delete; // Please use std::lerp. Notice that it has different order on arguments!
98// clang-format on
99
100// std::byteswap from C++23
101template<typename T> constexpr T byteswap(T n) {
102 T m;
103 for (size_t i = 0; i < sizeof(T); i++)
104 reinterpret_cast<uint8_t *>(&m)[i] = reinterpret_cast<uint8_t *>(&n)[sizeof(T) - 1 - i];
105 return m;
106}
107template<> constexpr uint8_t byteswap(uint8_t n) { return n; }
108#ifdef USE_LIBRETINY
109// LibreTiny's Beken framework redefines __builtin_bswap functions as non-constexpr
110template<> inline uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
111template<> inline uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
112template<> inline uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
113template<> inline int8_t byteswap(int8_t n) { return n; }
114template<> inline int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
115template<> inline int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
116template<> inline int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
117#else
118template<> constexpr uint16_t byteswap(uint16_t n) { return __builtin_bswap16(n); }
119template<> constexpr uint32_t byteswap(uint32_t n) { return __builtin_bswap32(n); }
120template<> constexpr uint64_t byteswap(uint64_t n) { return __builtin_bswap64(n); }
121template<> constexpr int8_t byteswap(int8_t n) { return n; }
122template<> constexpr int16_t byteswap(int16_t n) { return __builtin_bswap16(n); }
123template<> constexpr int32_t byteswap(int32_t n) { return __builtin_bswap32(n); }
124template<> constexpr int64_t byteswap(int64_t n) { return __builtin_bswap64(n); }
125#endif
126
128
131
135template<typename T, bool Owning = false> class ConstVector {
136 public:
137 using value_type = T;
138
139 constexpr ConstVector() = default;
140 constexpr ConstVector(const T *data, size_t size) : data_(data), size_(size) {}
141
142 const T *begin() const { return this->data_; }
143 const T *end() const { return this->data_ + this->size_; }
144 const T *data() const { return this->data_; }
145 constexpr size_t size() const { return this->size_; }
146 constexpr bool empty() const { return this->size_ == 0; }
147 const constexpr T &operator[](size_t i) const { return this->data_[i]; }
148 const T &at(size_t i) const { return this->data_[i]; }
149
150 protected:
151 const T *data_{nullptr};
152 size_t size_{0};
153};
154
158template<typename T> class ConstVector<T, true> {
159 static_assert(std::is_trivially_copyable_v<T> && sizeof(T) % sizeof(uint32_t) == 0,
160 "ConstVector elements must be whole words so ESP8266 can read them from flash");
161
162 public:
163 using value_type = T;
164
165 constexpr ConstVector() = default;
166 constexpr ConstVector(const T *data, size_t size) : data_(data), size_(size) {}
167 ConstVector(const ConstVector &) = delete;
169 ~ConstVector() { this->release_(); }
170
171 const T *begin() const { return this->data_; }
172 const T *end() const { return this->data_ + this->size(); }
173 const T *data() const { return this->data_; }
174 size_t size() const { return this->size_ & ~OWNED_BIT; }
175 bool empty() const { return this->size() == 0; }
176 const T &operator[](size_t index) const { return this->data_[index]; }
177 const T &at(size_t index) const { return this->data_[index]; }
178
181 void assign_static(const T *data, size_t size) {
182 this->data_ = data;
183 this->size_ = size;
184 }
186 void assign_copy(const T *data, size_t size) {
187 auto *table = new T[size]; // NOLINT(cppcoreguidelines-owning-memory)
188 std::copy(data, data + size, table);
189 this->release_();
190 this->data_ = table;
191 this->size_ = size | OWNED_BIT;
192 }
193
194 protected:
195 static constexpr size_t OWNED_BIT = size_t{1} << (sizeof(size_t) * 8 - 1);
196
197 void release_() {
198 if (this->size_ & OWNED_BIT)
199 delete[] this->data_; // NOLINT(cppcoreguidelines-owning-memory)
200 }
201
202 const T *data_{nullptr};
203 size_t size_{0}; // top bit set when data_ is an owned heap copy
204};
205static_assert(sizeof(ConstVector<const char *, true>) == 2 * sizeof(void *),
206 "ConstVector must stay a pointer and a size");
207
211template<size_t InlineSize = 8> class SmallInlineBuffer {
212 public:
213 SmallInlineBuffer() = default;
215 if (!this->is_inline_())
216 delete[] this->heap_;
217 }
218
219 // Move constructor
220 SmallInlineBuffer(SmallInlineBuffer &&other) noexcept : len_(other.len_) {
221 if (other.is_inline_()) {
222 memcpy(this->inline_, other.inline_, this->len_);
223 } else {
224 this->heap_ = other.heap_;
225 other.heap_ = nullptr;
226 }
227 other.len_ = 0;
228 }
229
230 // Move assignment
232 if (this != &other) {
233 if (!this->is_inline_())
234 delete[] this->heap_;
235 this->len_ = other.len_;
236 if (other.is_inline_()) {
237 memcpy(this->inline_, other.inline_, this->len_);
238 } else {
239 this->heap_ = other.heap_;
240 other.heap_ = nullptr;
241 }
242 other.len_ = 0;
243 }
244 return *this;
245 }
246
247 // Disable copy (would need deep copy of heap data)
250
251 bool empty() const { return this->len_ == 0; }
252
253 // Conversion to std::span for compatibility with span-based APIs
254 operator std::span<const uint8_t>() const { return std::span<const uint8_t>(this->data(), this->len_); }
255
259 uint8_t *init(size_t size) {
260 // Free existing heap allocation if switching from heap to inline or different heap size
262 delete[] this->heap_;
263 this->heap_ = nullptr; // Defensive: prevent use-after-free if logic changes
264 }
265 // Allocate new heap buffer if needed
266 if (size > InlineSize && (this->is_inline_() || size != this->len_)) {
267 this->heap_ = new uint8_t[size]; // NOLINT(cppcoreguidelines-owning-memory)
268 }
269 this->len_ = size;
270 return this->data();
271 }
272
274 void set(const uint8_t *src, size_t size) { memcpy(this->init(size), src, size); }
275
276 uint8_t *data() { return this->is_inline_() ? this->inline_ : this->heap_; }
277 const uint8_t *data() const { return this->is_inline_() ? this->inline_ : this->heap_; }
278 size_t size() const { return this->len_; }
279
280 protected:
281 bool is_inline_() const { return this->len_ <= InlineSize; }
282
283 size_t len_{0};
284 union {
285 uint8_t inline_[InlineSize]{}; // Zero-init ensures clean initial state
286 uint8_t *heap_;
287 };
288};
289
291template<typename T, size_t N> class StaticVector {
292 public:
293 using value_type = T;
294 using iterator = typename std::array<T, N>::iterator;
295 using const_iterator = typename std::array<T, N>::const_iterator;
296 using reverse_iterator = std::reverse_iterator<iterator>;
297 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
298
299 private:
300 std::array<T, N> data_; // intentionally not value-initialized to avoid memset
301 size_t count_{0};
302
303 public:
304 // User provided, not "= default": otherwise `StaticVector<...> x_{}` members
305 // value-initialize and memset data_, defeating the comment above.
306 constexpr StaticVector() noexcept {}
307
308 // Iterator range constructor
309 template<typename InputIt> StaticVector(InputIt first, InputIt last) {
310 while (first != last && count_ < N) {
311 data_[count_++] = *first++;
312 }
313 }
314
315 // Initializer list constructor
316 StaticVector(std::initializer_list<T> init) {
317 for (const auto &val : init) {
318 if (count_ >= N)
319 break;
320 data_[count_++] = val;
321 }
322 }
323
324 // Converting constructor from a smaller StaticVector of the same element type
325 template<size_t M> StaticVector(const StaticVector<T, M> &other) : StaticVector(other.begin(), other.end()) {
326 static_assert(M <= N, "Source StaticVector cannot be larger than the destination");
327 }
328
329 // Minimal vector-compatible interface - only what we actually use
330 void push_back(const T &value) {
331 if (count_ < N) {
332 data_[count_++] = value;
333 }
334 }
335
336 // Clear all elements
337 void clear() { count_ = 0; }
338 // Set the element count, capped at N. Elements are neither initialized when growing nor destroyed when
339 // shrinking; release owning elements before shrinking past them.
340 void resize(size_t n) { count_ = n < N ? n : N; }
341
342 // Assign from iterator range
343 template<typename InputIt> void assign(InputIt first, InputIt last) {
344 count_ = 0;
345 while (first != last && count_ < N) {
346 data_[count_++] = *first++;
347 }
348 }
349
350 // Return reference to next element and increment count (with bounds checking)
352 if (count_ >= N) {
353 // Should never happen with proper size calculation
354 // Return reference to last element to avoid crash
355 return data_[N - 1];
356 }
357 return data_[count_++];
358 }
359
360 size_t size() const { return count_; }
361 static constexpr size_t capacity() { return N; }
362 bool empty() const { return count_ == 0; }
363
364 // Direct access to underlying data
365 T *data() { return data_.data(); }
366 const T *data() const { return data_.data(); }
367
368 T &operator[](size_t i) { return data_[i]; }
369 const T &operator[](size_t i) const { return data_[i]; }
370
371 // For range-based for loops
372 iterator begin() { return data_.begin(); }
373 iterator end() { return data_.begin() + count_; }
374 const_iterator begin() const { return data_.begin(); }
375 const_iterator end() const { return data_.begin() + count_; }
376
377 // Reverse iterators
382
383 // Conversion to std::span for compatibility with span-based APIs
384 operator std::span<T>() { return std::span<T>(data_.data(), count_); }
385 operator std::span<const T>() const { return std::span<const T>(data_.data(), count_); }
386};
387
395template<typename T, size_t N> class StaticRingBuffer {
396 using index_type = std::conditional_t<(N <= std::numeric_limits<uint8_t>::max()), uint8_t, uint16_t>;
397
398 public:
399 class Iterator {
400 public:
401 Iterator(StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
402 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % N]; }
404 ++pos_;
405 return *this;
406 }
407 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
408
409 private:
410 StaticRingBuffer *buf_;
411 index_type pos_;
412 };
413
415 public:
416 ConstIterator(const StaticRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
417 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % N]; }
419 ++pos_;
420 return *this;
421 }
422 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
423
424 private:
425 const StaticRingBuffer *buf_;
426 index_type pos_;
427 };
428
429 bool push(const T &value) {
430 if (this->count_ >= N) {
431 return false;
432 }
433 this->data_[this->tail_] = value;
434 this->tail_ = (this->tail_ + 1) % N;
435 ++this->count_;
436 return true;
437 }
438
439 void pop() {
440 if (this->count_ > 0) {
441 this->head_ = (this->head_ + 1) % N;
442 --this->count_;
443 }
444 }
445
446 T &front() { return this->data_[this->head_]; }
447 const T &front() const { return this->data_[this->head_]; }
448 index_type size() const { return this->count_; }
449 bool empty() const { return this->count_ == 0; }
450
452 void clear() {
453 this->head_ = 0;
454 this->tail_ = 0;
455 this->count_ = 0;
456 }
457
458 Iterator begin() { return Iterator(this, 0); }
459 Iterator end() { return Iterator(this, this->count_); }
460 ConstIterator begin() const { return ConstIterator(this, 0); }
461 ConstIterator end() const { return ConstIterator(this, this->count_); }
462
463 protected:
464 T data_[N];
465 index_type head_{0};
466 index_type tail_{0};
467 index_type count_{0};
468};
469
474template<typename T, size_t MAX_CAPACITY = std::numeric_limits<uint16_t>::max()> class FixedRingBuffer {
475 using index_type = std::conditional_t<
476 (MAX_CAPACITY <= std::numeric_limits<uint8_t>::max()), uint8_t,
477 std::conditional_t<(MAX_CAPACITY <= std::numeric_limits<uint16_t>::max()), uint16_t, uint32_t>>;
478
479 public:
480 class Iterator {
481 public:
482 Iterator(FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
483 T &operator*() { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
485 ++pos_;
486 return *this;
487 }
488 bool operator!=(const Iterator &other) const { return pos_ != other.pos_; }
489
490 private:
491 FixedRingBuffer *buf_;
492 index_type pos_;
493 };
494
496 public:
497 ConstIterator(const FixedRingBuffer *buf, index_type pos) : buf_(buf), pos_(pos) {}
498 const T &operator*() const { return buf_->data_[(buf_->head_ + pos_) % buf_->capacity_]; }
500 ++pos_;
501 return *this;
502 }
503 bool operator!=(const ConstIterator &other) const { return pos_ != other.pos_; }
504
505 private:
506 const FixedRingBuffer *buf_;
507 index_type pos_;
508 };
509
510 FixedRingBuffer() = default;
512 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
513 ::operator delete(this->data_);
514 } else {
515 delete[] this->data_;
516 }
517 }
518
519 // Disable copy
522
524 void init(index_type capacity) {
525 if constexpr (std::is_trivially_copyable<T>::value && std::is_trivially_default_constructible<T>::value) {
526 // Raw allocation without initialization (elements are written before read)
527 // NOLINTNEXTLINE(bugprone-sizeof-expression)
528 this->data_ = static_cast<T *>(::operator new(capacity * sizeof(T)));
529 } else {
530 this->data_ = new T[capacity];
531 }
532 this->capacity_ = capacity;
533 }
534
536 bool push(const T &value) {
537 if (this->count_ >= this->capacity_)
538 return false;
539 this->data_[this->tail_] = value;
540 this->tail_ = (this->tail_ + 1) % this->capacity_;
541 ++this->count_;
542 return true;
543 }
544
546 void push_overwrite(const T &value) {
547 this->data_[this->tail_] = value;
548 this->tail_ = (this->tail_ + 1) % this->capacity_;
549 if (this->count_ >= this->capacity_) {
550 // Buffer full - advance head to drop oldest, count stays at capacity
551 this->head_ = this->tail_;
552 } else {
553 ++this->count_;
554 }
555 }
556
558 void pop() {
559 if (this->count_ > 0) {
560 this->head_ = (this->head_ + 1) % this->capacity_;
561 --this->count_;
562 }
563 }
564
565 T &front() { return this->data_[this->head_]; }
566 const T &front() const { return this->data_[this->head_]; }
567 index_type size() const { return this->count_; }
568 bool empty() const { return this->count_ == 0; }
569 index_type capacity() const { return this->capacity_; }
570 bool full() const { return this->count_ == this->capacity_; }
571
573 void clear() {
574 this->head_ = 0;
575 this->tail_ = 0;
576 this->count_ = 0;
577 }
578
579 Iterator begin() { return Iterator(this, 0); }
580 Iterator end() { return Iterator(this, this->count_); }
581 ConstIterator begin() const { return ConstIterator(this, 0); }
582 ConstIterator end() const { return ConstIterator(this, this->count_); }
583
584 protected:
585 T *data_{nullptr};
586 index_type head_{0};
587 index_type tail_{0};
588 index_type count_{0};
589 index_type capacity_{0};
590};
591
596template<typename T, size_t N> inline void init_array_from(std::array<T, N> &dest, std::initializer_list<T> src) {
597#ifdef ESPHOME_DEBUG
598 assert(src.size() == N);
599#endif
600 if constexpr (std::is_trivially_copyable_v<T>) {
601 __builtin_memcpy(dest.data(), src.begin(), N * sizeof(T));
602 } else {
603 size_t i = 0;
604 for (const auto &v : src) {
605 dest[i++] = v;
606 }
607 }
608}
609
610// Abort with a reason that reaches the panic output on ESP32. Elsewhere the literal is dropped
611// before it can land in rodata, which is RAM on ESP8266
612#ifdef USE_ESP32
613#define ESPHOME_ABORT_WITH_REASON(reason) esp_system_abort(reason)
614#else
615#define ESPHOME_ABORT_WITH_REASON(reason) abort()
616#endif
617
621template<typename T> class FixedVector {
622 private:
623 T *data_{nullptr};
624 size_t size_{0};
625 size_t capacity_{0};
626
627 // Helper to destroy all elements without freeing memory
628 void destroy_elements_() {
629 // Only call destructors for non-trivially destructible types
630 if constexpr (!std::is_trivially_destructible<T>::value) {
631 for (size_t i = 0; i < size_; i++) {
632 data_[i].~T();
633 }
634 }
635 }
636
637 // Helper to destroy elements and free memory
638 void cleanup_() {
639 if (data_ != nullptr) {
640 destroy_elements_();
641 free(data_); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
642 }
643 }
644
645 // Helper to reset pointers after cleanup
646 void reset_() {
647 data_ = nullptr;
648 capacity_ = 0;
649 size_ = 0;
650 }
651
652 // Helper to assign from initializer list (shared by constructor and assignment operator)
653 void assign_from_initializer_list_(std::initializer_list<T> init_list) {
654 init(init_list.size());
655 size_t idx = 0;
656 for (const auto &item : init_list) {
657 new (data_ + idx) T(item);
658 ++idx;
659 }
660 size_ = init_list.size();
661 }
662
663 public:
664 FixedVector() = default;
665
668 FixedVector(std::initializer_list<T> init_list) { assign_from_initializer_list_(init_list); }
669
670 ~FixedVector() { cleanup_(); }
671
672 // Disable copy operations (avoid accidental expensive copies)
673 FixedVector(const FixedVector &) = delete;
675
676 // Enable move semantics (allows use in move-only containers like std::vector)
677 FixedVector(FixedVector &&other) noexcept : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
678 other.reset_();
679 }
680
681 // Allow conversion to std::vector
682 operator std::vector<T>() const { return {data_, data_ + size_}; }
683
684 FixedVector &operator=(FixedVector &&other) noexcept {
685 if (this != &other) {
686 // Delete our current data
687 cleanup_();
688 // Take ownership of other's data
689 data_ = other.data_;
690 size_ = other.size_;
691 capacity_ = other.capacity_;
692 // Leave other in valid empty state
693 other.reset_();
694 }
695 return *this;
696 }
697
700 FixedVector &operator=(std::initializer_list<T> init_list) {
701 cleanup_();
702 reset_();
703 assign_from_initializer_list_(init_list);
704 return *this;
705 }
706
707 // Allocate capacity - can be called multiple times to reinit
708 // IMPORTANT: After calling init(), you MUST use push_back() to add elements.
709 // Direct assignment via operator[] does NOT update the size counter.
710 // Aborts on exhaustion; use try_init() to handle failure.
711 void init(size_t n) {
712 if (!try_init(n))
713 ESPHOME_ABORT_WITH_REASON("FixedVector: out of memory");
714 }
715
716 // Same as init(), but returns false when memory is exhausted; the previous storage is freed either way
717 bool try_init(size_t n) {
718 cleanup_();
719 reset_();
720 if (n == 0)
721 return true;
722 if (n > SIZE_MAX / sizeof(T))
723 return false; // the byte count would wrap into a small block
724 // sizeof(T) is correct here for any type T (value types, pointers, etc.)
725 // NOLINTNEXTLINE(bugprone-sizeof-expression,cppcoreguidelines-no-malloc,cppcoreguidelines-owning-memory)
726 data_ = static_cast<T *>(malloc(n * sizeof(T)));
727 if (data_ == nullptr)
728 return false;
729 capacity_ = n;
730 return true;
731 }
732
733 // Clear the vector (destroy all elements, reset size to 0, keep capacity)
734 void clear() {
735 destroy_elements_();
736 size_ = 0;
737 }
738
739 // Release all memory (destroys elements and frees memory)
740 void release() {
741 cleanup_();
742 reset_();
743 }
744
748 void push_back(const T &value) {
749 if (size_ < capacity_) {
750 // Use placement new to construct the object in pre-allocated memory
751 new (&data_[size_]) T(value);
752 size_++;
753 }
754 }
755
759 void push_back(T &&value) {
760 if (size_ < capacity_) {
761 // Use placement new to move-construct the object in pre-allocated memory
762 new (&data_[size_]) T(std::move(value));
763 size_++;
764 }
765 }
766
771 template<typename... Args> T &emplace_back(Args &&...args) {
772 // Use placement new to construct the object in pre-allocated memory
773 new (&data_[size_]) T(std::forward<Args>(args)...);
774 size_++;
775 return data_[size_ - 1];
776 }
777
780 T &front() { return data_[0]; }
781 const T &front() const { return data_[0]; }
782
785 T &back() { return data_[size_ - 1]; }
786 const T &back() const { return data_[size_ - 1]; }
787
790 void pop_back() {
791 if constexpr (!std::is_trivially_destructible<T>::value) {
792 data_[size_ - 1].~T();
793 }
794 size_--;
795 }
796
797 size_t size() const { return size_; }
798 bool empty() const { return size_ == 0; }
799 size_t capacity() const { return capacity_; }
800 bool full() const { return size_ == capacity_; }
801
804 T &operator[](size_t i) { return data_[i]; }
805 const T &operator[](size_t i) const { return data_[i]; }
806
809 T &at(size_t i) { return data_[i]; }
810 const T &at(size_t i) const { return data_[i]; }
811
812 // Iterator support for range-based for loops
813 T *begin() { return data_; }
814 T *end() { return data_ + size_; }
815 const T *begin() const { return data_; }
816 const T *end() const { return data_ + size_; }
817};
818
824template<size_t STACK_SIZE, typename T = uint8_t> class SmallBufferWithHeapFallback {
825 public:
827 static_assert(std::is_trivially_default_constructible_v<T> && std::is_trivially_destructible_v<T>,
828 "the heap fallback leaves elements unconstructed");
829 if (size <= STACK_SIZE) {
830 this->buffer_ = this->stack_buffer_;
831 } else {
832 if (size <= SIZE_MAX / sizeof(T)) {
833 // NOLINTNEXTLINE(bugprone-sizeof-expression,cppcoreguidelines-no-malloc,cppcoreguidelines-owning-memory)
834 this->heap_buffer_ = static_cast<T *>(malloc(size * sizeof(T)));
835 }
836 // Callers write through get() unchecked, so exhaustion aborts like the new[] it replaces
837 if (this->heap_buffer_ == nullptr)
838 ESPHOME_ABORT_WITH_REASON("SmallBufferWithHeapFallback: out of memory");
839 this->buffer_ = this->heap_buffer_;
840 }
841 }
842 ~SmallBufferWithHeapFallback() { free(this->heap_buffer_); } // NOLINT(cppcoreguidelines-no-malloc)
843
844 // Delete copy and move operations to prevent double-delete
849
850 T *get() { return this->buffer_; }
851
852 private:
853 T stack_buffer_[STACK_SIZE];
854 T *heap_buffer_{nullptr};
855 T *buffer_;
856};
857
859
862
866int8_t ilog10(float value);
867
871inline float pow10_int(int8_t exp) {
872 float result = 1.0f;
873 if (exp >= 0) {
874 for (int8_t i = 0; i < exp; i++)
875 result *= 10.0f;
876 } else {
877 for (int8_t i = exp; i < 0; i++)
878 result /= 10.0f;
879 }
880 return result;
881}
882
884template<typename T, typename U> T remap(U value, U min, U max, T min_out, T max_out) {
885 return (value - min) * (max_out - min_out) / (max - min) + min_out;
886}
887
889uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc = 0x00, uint8_t poly = 0x8C, bool msb_first = false);
890
892uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc = 0xffff, uint16_t reverse_poly = 0xa001,
893 bool refin = false, bool refout = false);
894uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc = 0, uint16_t poly = 0x1021, bool refin = false,
895 bool refout = false);
896
899uint32_t fnv1_hash(const char *str);
900inline uint32_t fnv1_hash(const std::string &str) { return fnv1_hash(str.c_str()); }
901
903constexpr uint32_t FNV1_OFFSET_BASIS = 2166136261UL;
905constexpr uint32_t FNV1_PRIME = 16777619UL;
906
908template<std::integral T> constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value) {
909 using UnsignedT = std::make_unsigned_t<T>;
910 UnsignedT uvalue = static_cast<UnsignedT>(value);
911 for (size_t i = 0; i < sizeof(T); i++) {
912 hash *= FNV1_PRIME;
913 hash ^= (uvalue >> (i * 8)) & 0xFF;
914 }
915 return hash;
916}
918constexpr uint32_t fnv1_hash_extend(uint32_t hash, const char *str) {
919 if (str) {
920 while (*str) {
921 hash *= FNV1_PRIME;
922 hash ^= *str++;
923 }
924 }
925 return hash;
926}
927inline uint32_t fnv1_hash_extend(uint32_t hash, const std::string &str) { return fnv1_hash_extend(hash, str.c_str()); }
928
930constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str) {
931 if (str) {
932 while (*str) {
933 hash ^= *str++;
934 hash *= FNV1_PRIME;
935 }
936 }
937 return hash;
938}
939inline uint32_t fnv1a_hash_extend(uint32_t hash, const std::string &str) {
940 return fnv1a_hash_extend(hash, str.c_str());
941}
943template<std::integral T> constexpr uint32_t fnv1a_hash_extend(uint32_t hash, T value) {
944 using UnsignedT = std::make_unsigned_t<T>;
945 UnsignedT uvalue = static_cast<UnsignedT>(value);
946 for (size_t i = 0; i < sizeof(T); i++) {
947 hash ^= (uvalue >> (i * 8)) & 0xFF;
948 hash *= FNV1_PRIME;
949 }
950 return hash;
951}
953constexpr uint32_t fnv1a_hash(const char *str) { return fnv1a_hash_extend(FNV1_OFFSET_BASIS, str); }
954inline uint32_t fnv1a_hash(const std::string &str) { return fnv1a_hash(str.c_str()); }
955
956// micros_to_millis<>() lives in its own lightweight header so hal.h can pull it
957// in for inline millis_64() without forcing every TU that includes hal.h to
958// also include the rest of helpers.h.
959
967float random_float();
970bool random_bytes(uint8_t *data, size_t len);
971
973
976
978constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb) {
979 return (static_cast<uint16_t>(msb) << 8) | (static_cast<uint16_t>(lsb));
980}
982constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3) {
983 return (static_cast<uint32_t>(byte1) << 16) | (static_cast<uint32_t>(byte2) << 8) | (static_cast<uint32_t>(byte3));
984}
986constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4) {
987 return (static_cast<uint32_t>(byte1) << 24) | (static_cast<uint32_t>(byte2) << 16) |
988 (static_cast<uint32_t>(byte3) << 8) | (static_cast<uint32_t>(byte4));
989}
990
992template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> constexpr T encode_value(const uint8_t *bytes) {
993 T val = 0;
994 for (size_t i = 0; i < sizeof(T); i++) {
995 val <<= 8;
996 val |= bytes[i];
997 }
998 return val;
999}
1001template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1002constexpr T encode_value(const std::array<uint8_t, sizeof(T)> bytes) {
1003 return encode_value<T>(bytes.data());
1004}
1006template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1007constexpr std::array<uint8_t, sizeof(T)> decode_value(T val) {
1008 std::array<uint8_t, sizeof(T)> ret{};
1009 for (size_t i = sizeof(T); i > 0; i--) {
1010 ret[i - 1] = val & 0xFF;
1011 val >>= 8;
1012 }
1013 return ret;
1014}
1015
1017inline uint8_t reverse_bits(uint8_t x) {
1018 x = ((x & 0xAA) >> 1) | ((x & 0x55) << 1);
1019 x = ((x & 0xCC) >> 2) | ((x & 0x33) << 2);
1020 x = ((x & 0xF0) >> 4) | ((x & 0x0F) << 4);
1021 return x;
1022}
1024inline uint16_t reverse_bits(uint16_t x) {
1025 return (reverse_bits(static_cast<uint8_t>(x & 0xFF)) << 8) | reverse_bits(static_cast<uint8_t>((x >> 8) & 0xFF));
1026}
1029 return (reverse_bits(static_cast<uint16_t>(x & 0xFFFF)) << 16) |
1030 reverse_bits(static_cast<uint16_t>((x >> 16) & 0xFFFF));
1031}
1032
1034template<typename T> constexpr T convert_big_endian(T val) {
1035#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1036 return byteswap(val);
1037#else
1038 return val;
1039#endif
1040}
1041
1043template<typename T> constexpr T convert_little_endian(T val) {
1044#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1045 return val;
1046#else
1047 return byteswap(val);
1048#endif
1049}
1050
1052
1055
1057bool str_equals_case_insensitive(const std::string &a, const std::string &b);
1059bool str_equals_case_insensitive(StringRef a, StringRef b);
1061inline bool str_equals_case_insensitive(const char *a, const char *b) { return strcasecmp(a, b) == 0; }
1062inline bool str_equals_case_insensitive(const std::string &a, const char *b) { return strcasecmp(a.c_str(), b) == 0; }
1063inline bool str_equals_case_insensitive(const char *a, const std::string &b) { return strcasecmp(a, b.c_str()) == 0; }
1064
1066bool str_startswith(const std::string &str, const std::string &start);
1068bool str_endswith(const std::string &str, const std::string &end);
1069
1071bool str_endswith_ignore_case(const char *str, size_t str_len, const char *suffix, size_t suffix_len);
1072inline bool str_endswith_ignore_case(const char *str, const char *suffix) {
1073 return str_endswith_ignore_case(str, strlen(str), suffix, strlen(suffix));
1074}
1075inline bool str_endswith_ignore_case(const std::string &str, const char *suffix) {
1076 return str_endswith_ignore_case(str.c_str(), str.size(), suffix, strlen(suffix));
1077}
1078
1080bool str_contains_ignore_case_fallback(const char *haystack, const char *needle);
1081
1082#ifdef USE_ESP8266
1085bool str_contains_ignore_case_p(const char *haystack, PGM_P needle);
1089#define str_contains_ignore_case(haystack, needle) str_contains_ignore_case_p(haystack, PSTR(needle))
1090#else
1092inline bool str_contains_ignore_case(const char *haystack, const char *needle) {
1093 if (!needle || !haystack) {
1094 return false;
1095 }
1096
1097// strcasestr is a GNU extension: newlib only declares it when _GNU_SOURCE is set.
1098// ESP32/host builds get it from their framework or from g++ on Linux;
1099// LibreTiny, RP2 and Zephyr do not, so they use the hand-rolled fallback.
1100#if defined(USE_LIBRETINY) || defined(USE_RP2) || defined(USE_ZEPHYR)
1101 return str_contains_ignore_case_fallback(haystack, needle);
1102#else // defined(USE_LIBRETINY) || defined(USE_RP2) || defined(USE_ZEPHYR)
1103 return strcasestr(haystack, needle) != nullptr;
1104#endif // defined(USE_LIBRETINY) || defined(USE_RP2) || defined(USE_ZEPHYR)
1105}
1106#endif // USE_ESP8266
1107
1108// str_truncate moved to alloc_helpers.h - remove this include before 2026.11.0
1109
1110// str_until, str_lower_case, str_upper_case moved to alloc_helpers.h - remove this comment before 2026.11.0
1111
1113constexpr char to_snake_case_char(char c) { return (c == ' ') ? '_' : (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; }
1114// str_snake_case moved to alloc_helpers.h - remove this comment before 2026.11.0
1115
1117constexpr char to_sanitized_char(char c) {
1118 return (c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) ? c : '_';
1119}
1120
1130char *str_sanitize_to(char *buffer, size_t buffer_size, const char *str);
1131
1133template<size_t N> inline char *str_sanitize_to(char (&buffer)[N], const char *str) {
1134 return str_sanitize_to(buffer, N, str);
1135}
1136
1137// str_sanitize moved to alloc_helpers.h - remove this comment before 2026.11.0
1138
1143inline uint32_t fnv1_hash_object_id(const char *str, size_t len) {
1145 for (size_t i = 0; i < len; i++) {
1146 hash *= FNV1_PRIME;
1147 // Apply snake_case (space->underscore, uppercase->lowercase) then sanitize
1148 hash ^= static_cast<uint8_t>(to_sanitized_char(to_snake_case_char(str[i])));
1149 }
1150 return hash;
1151}
1152
1153// str_snprintf, str_sprintf moved to alloc_helpers.h - remove this comment before 2026.11.0
1154
1155#ifdef USE_ESP8266
1156// ESP8266: Use vsnprintf_P to keep format strings in flash (PROGMEM)
1157// Format strings must be wrapped with PSTR() macro
1164inline size_t buf_append_printf_p(char *buf, size_t size, size_t pos, PGM_P fmt, ...) {
1165 if (pos >= size) {
1166 return size;
1167 }
1168 va_list args;
1169 va_start(args, fmt);
1170 int written = vsnprintf_P(buf + pos, size - pos, fmt, args);
1171 va_end(args);
1172 if (written < 0) {
1173 return pos; // encoding error
1174 }
1175 return std::min(pos + static_cast<size_t>(written), size);
1176}
1177#define buf_append_printf(buf, size, pos, fmt, ...) buf_append_printf_p(buf, size, pos, PSTR(fmt), ##__VA_ARGS__)
1178#else
1186__attribute__((format(printf, 4, 5))) inline size_t buf_append_printf(char *buf, size_t size, size_t pos,
1187 const char *fmt, ...) {
1188 if (pos >= size) {
1189 return size;
1190 }
1191 va_list args;
1193 int written = vsnprintf(buf + pos, size - pos, fmt, args);
1194 va_end(args);
1195 if (written < 0) {
1196 return pos; // encoding error
1197 }
1198 return std::min(pos + static_cast<size_t>(written), size);
1199}
1200#endif
1201
1202#ifdef USE_ESP8266
1212inline size_t buf_append_str_p(char *buf, size_t size, size_t pos, PGM_P str) {
1213 if (pos >= size) {
1214 return size;
1215 }
1216 size_t remaining = size - pos - 1; // reserve space for null terminator
1217 size_t len = strnlen_P(str, remaining);
1218 memcpy_P(buf + pos, str, len);
1219 pos += len;
1220 buf[pos] = '\0';
1221 return pos;
1222}
1226#define buf_append_str(buf, size, pos, str) buf_append_str_p(buf, size, pos, PSTR(str))
1227#else
1235inline size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str) {
1236 if (pos >= size) {
1237 return size;
1238 }
1239 size_t remaining = size - pos - 1; // reserve space for null terminator
1240 size_t len = 0;
1241 while (len < remaining && str[len] != '\0') {
1242 len++;
1243 }
1244 memcpy(buf + pos, str, len);
1245 pos += len;
1246 buf[pos] = '\0';
1247 return pos;
1248}
1249#endif
1250
1252static constexpr size_t MAX_NAME_WITH_SUFFIX_SIZE = 128;
1253
1263size_t make_name_with_suffix_to(char *buffer, size_t buffer_size, const char *name, size_t name_len, char sep,
1264 const char *suffix_ptr, size_t suffix_len);
1265
1267
1270
1272template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1273optional<T> parse_number(const char *str) {
1274 char *end = nullptr;
1275 unsigned long value = ::strtoul(str, &end, 10); // NOLINT(google-runtime-int)
1276 if (end == str || *end != '\0' || value > std::numeric_limits<T>::max())
1277 return {};
1278 return value;
1279}
1281template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_unsigned<T>::value), int> = 0>
1282optional<T> parse_number(const std::string &str) {
1283 return parse_number<T>(str.c_str());
1284}
1286template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1287optional<T> parse_number(const char *str) {
1288 char *end = nullptr;
1289 signed long value = ::strtol(str, &end, 10); // NOLINT(google-runtime-int)
1290 if (end == str || *end != '\0' || value < std::numeric_limits<T>::min() || value > std::numeric_limits<T>::max())
1291 return {};
1292 return value;
1293}
1295template<typename T, enable_if_t<(std::is_integral<T>::value && std::is_signed<T>::value), int> = 0>
1296optional<T> parse_number(const std::string &str) {
1297 return parse_number<T>(str.c_str());
1298}
1300template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0> optional<T> parse_number(const char *str) {
1301 char *end = nullptr;
1302 float value = ::strtof(str, &end);
1303 if (end == str || *end != '\0' || value == HUGE_VALF)
1304 return {};
1305 return value;
1306}
1308template<typename T, enable_if_t<(std::is_same<T, float>::value), int> = 0>
1309optional<T> parse_number(const std::string &str) {
1310 return parse_number<T>(str.c_str());
1311}
1312
1324size_t parse_hex(const char *str, size_t len, uint8_t *data, size_t count);
1326inline bool parse_hex(const char *str, uint8_t *data, size_t count) {
1327 return parse_hex(str, strlen(str), data, count) == 2 * count;
1328}
1330inline bool parse_hex(const std::string &str, uint8_t *data, size_t count) {
1331 return parse_hex(str.c_str(), str.length(), data, count) == 2 * count;
1332}
1334inline bool parse_hex(const char *str, std::vector<uint8_t> &data, size_t count) {
1335 data.resize(count);
1336 return parse_hex(str, strlen(str), data.data(), count) == 2 * count;
1337}
1339inline bool parse_hex(const std::string &str, std::vector<uint8_t> &data, size_t count) {
1340 data.resize(count);
1341 return parse_hex(str.c_str(), str.length(), data.data(), count) == 2 * count;
1342}
1348template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1349optional<T> parse_hex(const char *str, size_t len) {
1350 T val = 0;
1351 if (len > 2 * sizeof(T) || parse_hex(str, len, reinterpret_cast<uint8_t *>(&val), sizeof(T)) == 0)
1352 return {};
1353 return convert_big_endian(val);
1354}
1356template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const char *str) {
1357 return parse_hex<T>(str, strlen(str));
1358}
1360template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> optional<T> parse_hex(const std::string &str) {
1361 return parse_hex<T>(str.c_str(), str.length());
1362}
1363
1366static constexpr uint8_t INVALID_HEX_CHAR = 255;
1367
1368constexpr uint8_t parse_hex_char(char c) {
1369 if (c >= '0' && c <= '9')
1370 return c - '0';
1371 if (c >= 'A' && c <= 'F')
1372 return c - 'A' + 10;
1373 if (c >= 'a' && c <= 'f')
1374 return c - 'a' + 10;
1375 return INVALID_HEX_CHAR;
1376}
1377
1379ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v, char base) { return v >= 10 ? base + (v - 10) : '0' + v; }
1380
1382ESPHOME_ALWAYS_INLINE inline char format_hex_char(uint8_t v) { return format_hex_char(v, 'a'); }
1383
1385ESPHOME_ALWAYS_INLINE inline char format_hex_pretty_char(uint8_t v) { return format_hex_char(v, 'A'); }
1386
1388static constexpr size_t JSON_ESCAPE_MAX_EXPANSION = 6;
1389
1401const char *json_escape_into_buffer(std::span<char> buf, StringRef value, bool short_control_escapes = true);
1402
1405inline char *int8_to_str(char *buf, int8_t val) {
1406 int32_t v = val;
1407 if (v < 0) {
1408 *buf++ = '-';
1409 v = -v;
1410 }
1411 if (v >= 100) {
1412 *buf++ = '1'; // int8 max is 128, so hundreds digit is always 1
1413 v -= 100;
1414 // Must write tens digit (even if 0) after hundreds
1415 int32_t tens = v / 10;
1416 *buf++ = '0' + tens;
1417 v -= tens * 10;
1418 } else if (v >= 10) {
1419 int32_t tens = v / 10;
1420 *buf++ = '0' + tens;
1421 v -= tens * 10;
1422 }
1423 *buf++ = '0' + v;
1424 return buf;
1425}
1426
1431inline char *buf_append_sep_str(char *buf, size_t remaining, char separator, const char *str, size_t str_len) {
1432 if (remaining < 2) {
1433 if (remaining >= 1) {
1434 *buf = '\0';
1435 }
1436 return buf;
1437 }
1438 *buf++ = separator;
1439 remaining--;
1440 size_t copy_len = std::min(str_len, remaining - 1);
1441 memcpy(buf, str, copy_len);
1442 buf += copy_len;
1443 *buf = '\0';
1444 return buf;
1445}
1446
1448inline uint32_t small_pow10(int8_t n) { return n == 3 ? 1000 : n == 2 ? 100 : n == 1 ? 10 : 1; }
1449
1451static constexpr size_t UINT32_MAX_STR_SIZE = 11;
1452
1455char *uint32_to_str_unchecked(char *buf, uint32_t val);
1456
1459inline size_t uint32_to_str(std::span<char, UINT32_MAX_STR_SIZE> buf, uint32_t val) {
1460 char *end = uint32_to_str_unchecked(buf.data(), val);
1461 *end = '\0';
1462 return static_cast<size_t>(end - buf.data());
1463}
1464
1468inline char *frac_to_str_unchecked(char *buf, uint32_t frac, uint32_t divisor) {
1469 while (divisor > 0) {
1470 *buf++ = '0' + static_cast<char>(frac / divisor);
1471 frac %= divisor;
1472 divisor /= 10;
1473 }
1474 return buf;
1475}
1476
1478char *format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1479
1482template<size_t N> inline char *format_hex_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1483 static_assert(N >= 3, "Buffer must hold at least one hex byte (3 chars)");
1484 return format_hex_to(buffer, N, data, length);
1485}
1486
1488template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1489inline char *format_hex_to(char (&buffer)[N], T val) {
1490 static_assert(N >= sizeof(T) * 2 + 1, "Buffer too small for type");
1492 return format_hex_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1493}
1494
1496template<size_t N> inline char *format_hex_to(char (&buffer)[N], const std::vector<uint8_t> &data) {
1497 return format_hex_to(buffer, data.data(), data.size());
1498}
1499
1501template<size_t N, size_t M> inline char *format_hex_to(char (&buffer)[N], const std::array<uint8_t, M> &data) {
1502 return format_hex_to(buffer, data.data(), data.size());
1503}
1504
1506constexpr size_t format_hex_size(size_t byte_count) { return byte_count * 2 + 1; }
1507
1509constexpr size_t format_hex_prefixed_size(size_t byte_count) { return byte_count * 2 + 3; }
1510
1512template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1513inline char *format_hex_prefixed_to(char (&buffer)[N], T val) {
1514 static_assert(N >= sizeof(T) * 2 + 3, "Buffer too small for prefixed hex");
1515 buffer[0] = '0';
1516 buffer[1] = 'x';
1518 format_hex_to(buffer + 2, N - 2, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1519 return buffer;
1520}
1521
1523template<size_t N> inline char *format_hex_prefixed_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1524 static_assert(N >= 5, "Buffer must hold at least '0x' + one hex byte + null");
1525 buffer[0] = '0';
1526 buffer[1] = 'x';
1527 format_hex_to(buffer + 2, N - 2, data, length);
1528 return buffer;
1529}
1530
1532constexpr size_t format_hex_pretty_size(size_t byte_count) { return byte_count * 3; }
1533
1545char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator = ':');
1546
1548template<size_t N>
1549inline char *format_hex_pretty_to(char (&buffer)[N], const uint8_t *data, size_t length, char separator = ':') {
1550 static_assert(N >= 3, "Buffer must hold at least one hex byte");
1551 return format_hex_pretty_to(buffer, N, data, length, separator);
1552}
1553
1555template<size_t N>
1556inline char *format_hex_pretty_to(char (&buffer)[N], const std::vector<uint8_t> &data, char separator = ':') {
1557 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1558}
1559
1561template<size_t N, size_t M>
1562inline char *format_hex_pretty_to(char (&buffer)[N], const std::array<uint8_t, M> &data, char separator = ':') {
1563 return format_hex_pretty_to(buffer, data.data(), data.size(), separator);
1564}
1565
1567constexpr size_t format_hex_pretty_uint16_size(size_t count) { return count * 5; }
1568
1582char *format_hex_pretty_to(char *buffer, size_t buffer_size, const uint16_t *data, size_t length, char separator = ':');
1583
1585template<size_t N>
1586inline char *format_hex_pretty_to(char (&buffer)[N], const uint16_t *data, size_t length, char separator = ':') {
1587 static_assert(N >= 5, "Buffer must hold at least one hex uint16_t");
1588 return format_hex_pretty_to(buffer, N, data, length, separator);
1589}
1590
1592static constexpr size_t MAC_ADDRESS_SIZE = 6;
1594static constexpr size_t MAC_ADDRESS_PRETTY_BUFFER_SIZE = format_hex_pretty_size(MAC_ADDRESS_SIZE);
1596static constexpr size_t MAC_ADDRESS_BUFFER_SIZE = MAC_ADDRESS_SIZE * 2 + 1;
1597
1599inline char *format_mac_addr_upper(const uint8_t *mac, char *output) {
1600 return format_hex_pretty_to(output, MAC_ADDRESS_PRETTY_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE, ':');
1601}
1602
1604inline void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output) {
1605 format_hex_to(output, MAC_ADDRESS_BUFFER_SIZE, mac, MAC_ADDRESS_SIZE);
1606}
1607
1608// format_mac_address_pretty, format_hex (all overloads) moved to alloc_helpers.h
1609// Remove this comment and the template overloads below before 2026.11.0
1610
1613template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_hex(T val) {
1615 return format_hex(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1616}
1619template<std::size_t N> std::string format_hex(const std::array<uint8_t, N> &data) {
1620 return format_hex(data.data(), data.size());
1621}
1622
1623// format_hex_pretty (all overloads) moved to alloc_helpers.h
1624// Remove this comment and the template overload below before 2026.11.0
1625
1628template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1629std::string format_hex_pretty(T val, char separator = '.', bool show_length = true) {
1631 return format_hex_pretty(reinterpret_cast<uint8_t *>(&val), sizeof(T), separator, show_length);
1632}
1633
1635constexpr size_t format_bin_size(size_t byte_count) { return byte_count * 8 + 1; }
1636
1656char *format_bin_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length);
1657
1659template<size_t N> inline char *format_bin_to(char (&buffer)[N], const uint8_t *data, size_t length) {
1660 static_assert(N >= 9, "Buffer must hold at least one binary byte (9 chars)");
1661 return format_bin_to(buffer, N, data, length);
1662}
1663
1680template<size_t N, typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0>
1681inline char *format_bin_to(char (&buffer)[N], T val) {
1682 static_assert(N >= sizeof(T) * 8 + 1, "Buffer too small for type");
1684 return format_bin_to(buffer, reinterpret_cast<const uint8_t *>(&val), sizeof(T));
1685}
1686
1687// format_bin moved to alloc_helpers.h - remove this comment and template overload before 2026.11.0
1688
1691template<typename T, enable_if_t<std::is_unsigned<T>::value, int> = 0> std::string format_bin(T val) {
1693 return format_bin(reinterpret_cast<uint8_t *>(&val), sizeof(T));
1694}
1695
1704ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const char *off = nullptr);
1705
1706// value_accuracy_to_string moved to alloc_helpers.h - remove this comment before 2026.11.0
1707
1709static constexpr size_t VALUE_ACCURACY_MAX_LEN = 64;
1710
1712size_t value_accuracy_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value, int8_t accuracy_decimals);
1714size_t value_accuracy_with_uom_to_buf(std::span<char, VALUE_ACCURACY_MAX_LEN> buf, float value,
1715 int8_t accuracy_decimals, StringRef unit_of_measurement);
1716
1718int8_t step_to_accuracy_decimals(float step);
1719
1720// base64_encode (both overloads), base64_decode (vector overload) moved to alloc_helpers.h
1721// Remove this comment before 2026.11.0
1722size_t base64_decode(std::string const &encoded_string, uint8_t *buf, size_t buf_len);
1723size_t base64_decode(const uint8_t *encoded_data, size_t encoded_len, uint8_t *buf, size_t buf_len);
1724
1729bool base64_decode_int32_vector(const std::string &base64, std::vector<int32_t> &out);
1730
1732
1735
1737void rgb_to_hsv(float red, float green, float blue, int &hue, float &saturation, float &value);
1739void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green, float &blue);
1740
1742
1745
1747constexpr float celsius_to_fahrenheit(float value) { return value * 1.8f + 32.0f; }
1749constexpr float fahrenheit_to_celsius(float value) { return (value - 32.0f) / 1.8f; }
1750
1751enum class TemperatureUnit : uint8_t {
1752 CELSIUS = 0,
1753 FAHRENHEIT = 1,
1754 KELVIN = 2,
1755};
1756
1758
1761
1767template<typename... X> struct Callback;
1768
1769template<typename... Ts> struct Callback<void(Ts...)> {
1770 // The inline storage path stores callable bytes in ctx_ via memcpy.
1771 // sizeof equality with uintptr_t ensures void* can round-trip arbitrary bit patterns,
1772 // which combined with flat address spaces on all ESPHome targets means no trap representations.
1773 static_assert(sizeof(void *) == sizeof(std::uintptr_t), "void* must be the same size as uintptr_t");
1774
1775 void (*fn_)(void *, Ts...){nullptr};
1776 void *ctx_{nullptr};
1777
1779 void call(Ts... args) const { this->fn_(this->ctx_, std::forward<Ts>(args)...); }
1780
1783 template<typename F> static Callback create(F &&callable) {
1784 using DecayF = std::decay_t<F>;
1785 if constexpr (sizeof(DecayF) <= sizeof(void *) && std::is_trivially_copyable_v<DecayF>) {
1786 // Small trivial callable (e.g. [this]() { this->method(); }) - store inline in ctx.
1787 // Safe under C++20 (P0593R6): byte copy into aligned storage implicitly
1788 // creates objects of implicit-lifetime types (trivially copyable qualifies).
1789 Callback cb; // fn and ctx are zero-initialized by default
1790 // Decay callable to a local variable first. When F is a function reference
1791 // (e.g. void(&)(int)), &callable would point at machine code, not a pointer variable.
1792 DecayF decayed = std::forward<F>(callable);
1793 __builtin_memcpy(&cb.ctx_, &decayed, sizeof(DecayF));
1794 cb.fn_ = [](void *c, Ts... args) {
1795 alignas(DecayF) char buf[sizeof(DecayF)];
1796 __builtin_memcpy(buf, &c, sizeof(DecayF));
1797 (*std::launder(reinterpret_cast<DecayF *>(buf)))(args...);
1798 };
1799 return cb;
1800 } else {
1801 // Large or non-trivial callable - heap allocate.
1802 // Intentionally never freed: callbacks in ESPHome are registered during setup()
1803 // and live for device lifetime. Same lifetime as the previous std::function approach.
1804 auto *stored = new DecayF(std::forward<F>(callable));
1805 return {[](void *c, Ts... args) { (*static_cast<DecayF *>(c))(args...); }, static_cast<void *>(stored)};
1806 }
1807 }
1808};
1809
1811void *callback_manager_grow(void *data, uint16_t size, uint16_t &capacity, size_t elem_size);
1812
1813template<typename... X> class CallbackManager;
1814
1826template<typename... Ts> class CallbackManager<void(Ts...)> {
1827 using CbType = Callback<void(Ts...)>;
1828 static_assert(std::is_trivially_copyable_v<CbType>, "Callback must be trivially copyable");
1829
1830 public:
1831 CallbackManager() = default;
1832 ~CallbackManager() { ::operator delete(this->data_); }
1833
1834 // Non-copyable (would alias data_), movable (for std::map support)
1838 : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
1839 other.data_ = nullptr;
1840 other.size_ = 0;
1841 other.capacity_ = 0;
1842 }
1844 std::swap(this->data_, other.data_);
1845 std::swap(this->size_, other.size_);
1846 std::swap(this->capacity_, other.capacity_);
1847 return *this;
1848 }
1849
1852 template<typename F> void add(F &&callback) { this->add_(CbType::create(std::forward<F>(callback))); }
1853
1855 inline void ESPHOME_ALWAYS_INLINE call(const Ts &...args) {
1856 if (this->size_ != 0) {
1857 for (auto *it = this->data_, *end = it + this->size_; it != end; ++it) {
1858 it->call(args...);
1859 }
1860 }
1861 }
1862 uint16_t size() const { return this->size_; }
1863
1865 void operator()(const Ts &...args) { this->call(args...); }
1866
1867 protected:
1868 template<typename...> friend class LazyCallbackManager;
1871 void add_(CbType cb) {
1872 if (this->size_ == this->capacity_) {
1873 this->data_ =
1874 static_cast<CbType *>(callback_manager_grow(this->data_, this->size_, this->capacity_, sizeof(CbType)));
1875 }
1876 this->data_[this->size_++] = cb;
1877 }
1878 CbType *data_{nullptr};
1879 uint16_t size_{0};
1880 uint16_t capacity_{0};
1881};
1882
1891template<size_t N, typename... X> class StaticCallbackManager;
1892
1893template<size_t N, typename... Ts> class StaticCallbackManager<N, void(Ts...)> {
1894 public:
1897 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
1898
1900 void call(Ts... args) {
1901 for (auto &cb : this->callbacks_)
1902 cb.call(args...);
1903 }
1904 size_t size() const { return this->callbacks_.size(); }
1905
1907 void operator()(Ts... args) { call(args...); }
1908
1909 protected:
1911 void add_(Callback<void(Ts...)> cb) { this->callbacks_.push_back(cb); }
1913};
1914
1915template<typename... X> class LazyCallbackManager;
1916
1932template<typename... Ts> class LazyCallbackManager<void(Ts...)> {
1933 public:
1937 ~LazyCallbackManager() { delete this->callbacks_; }
1938
1939 // Non-copyable and non-movable (entities are never copied or moved)
1944
1946 template<typename F> void add(F &&callback) { this->add_(Callback<void(Ts...)>::create(std::forward<F>(callback))); }
1947
1949 void call(Ts... args) {
1950 if (this->callbacks_) {
1951 this->callbacks_->call(args...);
1952 }
1953 }
1954
1956 size_t size() const { return this->callbacks_ ? this->callbacks_->size() : 0; }
1957
1959 bool empty() const { return !this->callbacks_ || this->callbacks_->size() == 0; }
1960
1962 void operator()(Ts... args) { this->call(args...); }
1963
1964 protected:
1966 void add_(Callback<void(Ts...)> cb) {
1967 if (!this->callbacks_) {
1968 this->callbacks_ = new CallbackManager<void(Ts...)>();
1969 }
1970 this->callbacks_->add_(cb);
1971 }
1972 CallbackManager<void(Ts...)> *callbacks_{nullptr};
1973};
1974
1976template<typename T> class Deduplicator {
1977 public:
1979 bool next(T value) {
1980 if (this->has_value_ && !this->value_unknown_ && this->last_value_ == value) {
1981 return false;
1982 }
1983 this->has_value_ = true;
1984 this->value_unknown_ = false;
1985 this->last_value_ = value;
1986 return true;
1987 }
1990 bool ret = !this->value_unknown_;
1991 this->value_unknown_ = true;
1992 return ret;
1993 }
1995 bool has_value() const { return this->has_value_; }
1996
1997 protected:
1998 bool has_value_{false};
1999 bool value_unknown_{false};
2001};
2002
2004template<typename T> class Parented {
2005 public:
2007 Parented(T *parent) : parent_(parent) {}
2008
2010 T *get_parent() const { return parent_; }
2012 void set_parent(T *parent) { parent_ = parent; }
2013
2014 protected:
2015 T *parent_{nullptr};
2016};
2017
2019
2022
2027class Mutex {
2028 public:
2029 Mutex(const Mutex &) = delete;
2030 Mutex &operator=(const Mutex &) = delete;
2031
2032#if defined(USE_ESP8266) || defined(USE_RP2)
2033 // Single-threaded platforms: inline no-ops so the compiler eliminates all call overhead.
2034 Mutex() = default;
2035 ~Mutex() = default;
2036 void lock() {}
2037 bool try_lock() { return true; }
2038 void unlock() {}
2039#elif defined(USE_ESP32) || defined(USE_LIBRETINY)
2040 // FreeRTOS platforms: inline to avoid out-of-line call overhead.
2041 Mutex() { handle_ = xSemaphoreCreateMutex(); }
2042 ~Mutex() = default;
2043 void lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); }
2044 bool try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; }
2045 void unlock() { xSemaphoreGive(this->handle_); }
2046
2047 private:
2048 SemaphoreHandle_t handle_;
2049#else
2050 Mutex();
2051 ~Mutex();
2052 void lock();
2053 bool try_lock();
2054 void unlock();
2055
2056 private:
2057 // d-pointer to store private data on new platforms
2058 void *handle_; // NOLINT(clang-diagnostic-unused-private-field)
2059#endif
2060};
2061
2067 public:
2068 LockGuard(Mutex &mutex) : mutex_(mutex) { mutex_.lock(); }
2069 ~LockGuard() { mutex_.unlock(); }
2070
2071 private:
2072 Mutex &mutex_;
2073};
2074
2096 public:
2097 InterruptLock();
2099
2100 protected:
2101#if defined(USE_ESP8266) || defined(USE_RP2) || defined(USE_ZEPHYR)
2103#endif
2104};
2105
2115 public:
2116 LwIPLock(const LwIPLock &) = delete;
2117 LwIPLock &operator=(const LwIPLock &) = delete;
2118
2119#if defined(USE_ESP32) || defined(USE_RP2)
2120 // Platforms with potential lwIP core locking — out-of-line implementations in helpers.cpp
2121 LwIPLock();
2122 ~LwIPLock();
2123#else
2124 // No lwIP core locking — inline no-ops (empty bodies instead of = default
2125 // to prevent clang-tidy unused-variable warnings at call sites)
2128#endif
2129};
2130
2131#if defined(USE_ESP8266) && F_CPU != 160000000L
2132// Forward decl from <user_interface.h>
2133// NOLINTNEXTLINE(readability-redundant-declaration)
2134extern "C" bool system_update_cpu_freq(uint8_t freq);
2135#endif
2136
2144 public:
2147#if defined(USE_ESP8266) && F_CPU != 160000000L
2150#else
2151 // Not = default, so clang-tidy does not flag unused variables at call sites
2154#endif
2155};
2156
2163 public:
2165 void start();
2167 void stop();
2168
2170 static bool is_high_frequency() { return num_requests > 0; }
2171
2172 protected:
2173 bool started_{false};
2174 static uint8_t num_requests; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
2175};
2176
2178void get_mac_address_raw(uint8_t *mac); // NOLINT(readability-non-const-parameter)
2179
2180// get_mac_address, get_mac_address_pretty moved to alloc_helpers.h - remove this comment before 2026.11.0
2181
2185void get_mac_address_into_buffer(std::span<char, MAC_ADDRESS_BUFFER_SIZE> buf);
2186
2190const char *get_mac_address_pretty_into_buffer(std::span<char, MAC_ADDRESS_PRETTY_BUFFER_SIZE> buf);
2191
2192#ifdef USE_ESP32
2194void set_mac_address(uint8_t *mac);
2195
2199bool get_custom_mac_address(uint8_t *mac);
2200#endif
2201
2205
2208bool mac_address_is_valid(const uint8_t *mac);
2209
2212
2214
2217
2218template<typename T> struct RAMDeleter;
2220template<typename T> using RAMUniquePtr = std::unique_ptr<T, RAMDeleter<T>>;
2221
2231template<class T> class RAMAllocator {
2232 public:
2233 using value_type = T;
2234
2235 enum Flags {
2236 NONE = 0, // Perform external allocation and fall back to internal memory
2237 ALLOC_EXTERNAL = 1 << 0, // Perform external allocation only.
2238 ALLOC_INTERNAL = 1 << 1, // Perform internal allocation only.
2239 ALLOW_FAILURE = 1 << 2, // Does nothing. Kept for compatibility.
2240 PREFER_INTERNAL = 1 << 3, // Perform internal allocation and fall back to external memory
2241 };
2242
2243 constexpr RAMAllocator() = default;
2244 constexpr RAMAllocator(uint8_t flags) {
2245 if (flags & PREFER_INTERNAL) {
2247 return;
2248 }
2249 const uint8_t alloc_bits = flags & (ALLOC_INTERNAL | ALLOC_EXTERNAL);
2250 if (alloc_bits != 0) {
2251 this->flags_ = alloc_bits;
2252 return;
2253 }
2254 this->flags_ = ALLOC_INTERNAL | ALLOC_EXTERNAL;
2255 }
2256 template<class U> constexpr RAMAllocator(const RAMAllocator<U> &other) : flags_{other.flags_} {}
2257
2258 T *allocate(size_t n) { return this->allocate(n, sizeof(T)); }
2259
2260 T *allocate(size_t n, size_t manual_size) {
2261 size_t size = n * manual_size;
2262 T *ptr = nullptr;
2263#ifdef USE_ESP32
2264 const auto caps = this->get_caps_();
2265 ptr = static_cast<T *>(heap_caps_malloc_prefer(size, 2, caps[0], caps[1]));
2266#else
2267 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2268 ptr = static_cast<T *>(malloc(size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2269#endif
2270 return ptr;
2271 }
2272
2273 T *reallocate(T *p, size_t n) { return this->reallocate(p, n, sizeof(T)); }
2274
2275 T *reallocate(T *p, size_t n, size_t manual_size) {
2276 size_t size = n * manual_size;
2277 T *ptr = nullptr;
2278#ifdef USE_ESP32
2279 const auto caps = this->get_caps_();
2280 ptr = static_cast<T *>(heap_caps_realloc_prefer(p, size, 2, caps[0], caps[1]));
2281#else
2282 // Ignore ALLOC_EXTERNAL/ALLOC_INTERNAL flags if external allocation is not supported
2283 ptr = static_cast<T *>(realloc(p, size)); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2284#endif
2285 return ptr;
2286 }
2287
2288 void deallocate(T *p, size_t n) {
2289 free(p); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc)
2290 }
2291
2294 template<typename... Args> RAMUniquePtr<T> make_unique(Args &&...args) {
2295 static_assert(alignof(T) <= alignof(std::max_align_t), "malloc storage cannot hold an over aligned type");
2296 T *p = this->allocate(1);
2297 if (p == nullptr)
2298 return {};
2299 // ::new so a class scoped operator new cannot hide the global placement form
2300 return RAMUniquePtr<T>(::new (p) T(std::forward<Args>(args)...));
2301 }
2302
2305 static_assert(std::is_trivially_default_constructible_v<T>, "elements are left unconstructed");
2306 static_assert(alignof(T) <= alignof(std::max_align_t), "malloc storage cannot hold an over aligned type");
2307 if (n == 0 || n > SIZE_MAX / sizeof(T))
2308 return {};
2309 return RAMUniquePtr<T[]>(this->allocate(n));
2310 }
2311
2315 size_t get_free_heap_size() const {
2316#ifdef USE_ESP8266
2317 return ESP.getFreeHeap(); // NOLINT(readability-static-accessed-through-instance)
2318#elif defined(USE_ESP32)
2319 auto max_internal =
2320 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2321 auto max_external =
2322 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_free_size(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2323 return max_internal + max_external;
2324#elif defined(USE_RP2)
2325 return ::rp2040.getFreeHeap();
2326#elif defined(USE_LIBRETINY)
2327 return lt_heap_get_free();
2328#else
2329 return 100000;
2330#endif
2331 }
2332
2337#ifdef USE_ESP8266
2338 return ESP.getMaxFreeBlockSize(); // NOLINT(readability-static-accessed-through-instance)
2339#elif defined(USE_ESP32)
2340 auto max_internal =
2341 this->flags_ & ALLOC_INTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL) : 0;
2342 auto max_external =
2343 this->flags_ & ALLOC_EXTERNAL ? heap_caps_get_largest_free_block(MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM) : 0;
2344 return std::max(max_internal, max_external);
2345#else
2346 return this->get_free_heap_size();
2347#endif
2348 }
2349
2350 private:
2351#ifdef USE_ESP32
2357 std::array<uint32_t, 2> get_caps_() const {
2358 constexpr uint32_t external_caps = MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT;
2359 constexpr uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
2360 if (this->flags_ & PREFER_INTERNAL) {
2361 return {internal_caps, external_caps};
2362 }
2363 const uint32_t primary = (this->flags_ & ALLOC_EXTERNAL) ? external_caps : internal_caps;
2364 const uint32_t fallback = (this->flags_ & ALLOC_INTERNAL) ? internal_caps : external_caps;
2365 return {primary, fallback};
2366 }
2367#endif
2368
2369 uint8_t flags_{ALLOC_INTERNAL | ALLOC_EXTERNAL};
2370};
2371
2372template<class T> using ExternalRAMAllocator = RAMAllocator<T>;
2373
2375template<typename T> struct RAMDeleter {
2376 void operator()(T *p) const {
2377 p->~T();
2379 }
2380};
2382template<typename T> struct RAMDeleter<T[]> {
2383 static_assert(std::is_trivially_destructible_v<T>, "RAMUniquePtr<T[]> is for trivially destructible elements");
2384 void operator()(T *p) const { RAMAllocator<T>().deallocate(p, 1); }
2385};
2386
2391template<typename T, typename U>
2392concept comparable_with = requires(T a, U b) {
2393 { a > b } -> std::convertible_to<bool>;
2394 { a < b } -> std::convertible_to<bool>;
2395};
2396
2397template<std::totally_ordered T, comparable_with<T> U> T clamp_at_least(T value, U min) {
2398 if (value < min)
2399 return min;
2400 return value;
2401}
2402template<std::totally_ordered T, comparable_with<T> U> T clamp_at_most(T value, U max) {
2403 if (value > max)
2404 return max;
2405 return value;
2406}
2407
2410
2415template<typename T, enable_if_t<!std::is_pointer<T>::value, int> = 0> T id(T value) { return value; }
2420template<typename T, enable_if_t<std::is_pointer<T *>::value, int> = 0> T &id(T *value) { return *value; }
2421
2423
2424} // namespace esphome
Heap-allocating helper functions.
uint8_t m
Definition bl0906.h:1
void ESPHOME_ALWAYS_INLINE call(const Ts &...args)
Call all callbacks in this manager.
Definition helpers.h:1855
CallbackManager & operator=(const CallbackManager &)=delete
void operator()(const Ts &...args)
Call all callbacks in this manager.
Definition helpers.h:1865
CallbackManager & operator=(CallbackManager &&other) noexcept
Definition helpers.h:1843
void add(F &&callback)
Add any callable.
Definition helpers.h:1852
CallbackManager(CallbackManager &&other) noexcept
Definition helpers.h:1837
void add_(CbType cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1871
CallbackManager(const CallbackManager &)=delete
const T & operator[](size_t index) const
Definition helpers.h:176
const T & at(size_t index) const
Definition helpers.h:177
constexpr ConstVector()=default
const T * begin() const
Definition helpers.h:171
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:181
constexpr ConstVector(const T *data, size_t size)
Definition helpers.h:166
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:186
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory).
Definition helpers.h:135
constexpr bool empty() const
Definition helpers.h:146
const T * begin() const
Definition helpers.h:142
constexpr ConstVector(const T *data, size_t size)
Definition helpers.h:140
const T & at(size_t i) const
Definition helpers.h:148
const constexpr T & operator[](size_t i) const
Definition helpers.h:147
const T * data() const
Definition helpers.h:144
constexpr size_t size() const
Definition helpers.h:145
constexpr ConstVector()=default
const T * end() const
Definition helpers.h:143
Runs the CPU at 160 MHz while alive.
Definition helpers.h:2143
CpuFrequencyBoost & operator=(const CpuFrequencyBoost &)=delete
CpuFrequencyBoost(const CpuFrequencyBoost &)=delete
Helper class to deduplicate items in a series of values.
Definition helpers.h:1976
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:1979
bool has_value() const
Returns true if this deduplicator has processed any items.
Definition helpers.h:1995
bool next_unknown()
Returns true if the deduplicator's value was previously known.
Definition helpers.h:1989
bool operator!=(const ConstIterator &other) const
Definition helpers.h:503
ConstIterator(const FixedRingBuffer *buf, index_type pos)
Definition helpers.h:497
bool operator!=(const Iterator &other) const
Definition helpers.h:488
Iterator(FixedRingBuffer *buf, index_type pos)
Definition helpers.h:482
Fixed-capacity circular buffer - allocates once at runtime, never reallocates.
Definition helpers.h:474
FixedRingBuffer & operator=(const FixedRingBuffer &)=delete
ConstIterator begin() const
Definition helpers.h:581
bool push(const T &value)
Push a value. Returns false if full.
Definition helpers.h:536
const T & front() const
Definition helpers.h:566
index_type capacity() const
Definition helpers.h:569
void push_overwrite(const T &value)
Push a value, overwriting the oldest if full.
Definition helpers.h:546
void init(index_type capacity)
Allocate capacity - can only be called once.
Definition helpers.h:524
void pop()
Remove the oldest element.
Definition helpers.h:558
void clear()
Clear all elements (reset to empty, keep capacity)
Definition helpers.h:573
FixedRingBuffer(const FixedRingBuffer &)=delete
index_type size() const
Definition helpers.h:567
ConstIterator end() const
Definition helpers.h:582
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Definition helpers.h:621
const T & at(size_t i) const
Definition helpers.h:810
FixedVector(FixedVector &&other) noexcept
Definition helpers.h:677
FixedVector(std::initializer_list< T > init_list)
Constructor from initializer list - allocates exact size needed This enables brace initialization: Fi...
Definition helpers.h:668
const T * begin() const
Definition helpers.h:815
bool full() const
Definition helpers.h:800
FixedVector & operator=(std::initializer_list< T > init_list)
Assignment from initializer list - avoids temporary and move overhead This enables: FixedVector<int> ...
Definition helpers.h:700
T & front()
Access first element (no bounds checking - matches std::vector behavior) Caller must ensure vector is...
Definition helpers.h:780
const T & operator[](size_t i) const
Definition helpers.h:805
T & operator[](size_t i)
Access element without bounds checking (matches std::vector behavior) Caller must ensure index is val...
Definition helpers.h:804
size_t capacity() const
Definition helpers.h:799
T & back()
Access last element (no bounds checking - matches std::vector behavior) Caller must ensure vector is ...
Definition helpers.h:785
bool empty() const
Definition helpers.h:798
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:759
void pop_back()
Remove the last element in place (no reallocation, keeps capacity) Caller must ensure vector is not e...
Definition helpers.h:790
T & emplace_back(Args &&...args)
Emplace element without bounds checking - constructs in-place with arguments Caller must ensure suffi...
Definition helpers.h:771
size_t size() const
Definition helpers.h:797
const T & front() const
Definition helpers.h:781
const T & back() const
Definition helpers.h:786
const T * end() const
Definition helpers.h:816
bool try_init(size_t n)
Definition helpers.h:717
FixedVector & operator=(FixedVector &&other) noexcept
Definition helpers.h:684
T & at(size_t i)
Access element with bounds checking (matches std::vector behavior) Note: No exception thrown on out o...
Definition helpers.h:809
void push_back(const T &value)
Add element without bounds checking Caller must ensure sufficient capacity was allocated via init() S...
Definition helpers.h:748
void init(size_t n)
Definition helpers.h:711
Helper class to request loop() to be called as fast as possible.
Definition helpers.h:2162
static bool is_high_frequency()
Check whether the loop is running continuously.
Definition helpers.h:2170
void stop()
Stop running the loop continuously.
Definition helpers.cpp:786
void start()
Start running the loop continuously.
Definition helpers.cpp:780
Helper class to disable interrupts.
Definition helpers.h:2095
LazyCallbackManager & operator=(const LazyCallbackManager &)=delete
LazyCallbackManager(const LazyCallbackManager &)=delete
size_t size() const
Return the number of registered callbacks.
Definition helpers.h:1956
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1966
void add(F &&callback)
Add any callable. Allocates the underlying CallbackManager on first use.
Definition helpers.h:1946
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1962
LazyCallbackManager & operator=(LazyCallbackManager &&)=delete
~LazyCallbackManager()
Destructor - clean up allocated CallbackManager if any.
Definition helpers.h:1937
void call(Ts... args)
Call all callbacks in this manager. No-op if no callbacks registered.
Definition helpers.h:1949
bool empty() const
Check if any callbacks are registered.
Definition helpers.h:1959
LazyCallbackManager(LazyCallbackManager &&)=delete
Helper class that wraps a mutex with a RAII-style API.
Definition helpers.h:2066
LockGuard(Mutex &mutex)
Definition helpers.h:2068
Helper class to lock the lwIP TCPIP core when making lwIP API calls from non-TCPIP threads.
Definition helpers.h:2114
LwIPLock(const LwIPLock &)=delete
LwIPLock & operator=(const LwIPLock &)=delete
Mutex implementation, with API based on the unavailable std::mutex.
Definition helpers.h:2027
~Mutex()=default
Definition helpers.cpp:36
void unlock()
Definition helpers.h:2038
Mutex()=default
Definition helpers.cpp:35
bool try_lock()
Definition helpers.h:2037
Mutex(const Mutex &)=delete
Mutex & operator=(const Mutex &)=delete
Helper class to easily give an object a parent of type T.
Definition helpers.h:2004
T * get_parent() const
Get the parent of this object.
Definition helpers.h:2010
Parented(T *parent)
Definition helpers.h:2007
void set_parent(T *parent)
Set the parent of this object.
Definition helpers.h:2012
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:2231
constexpr RAMAllocator(uint8_t flags)
Definition helpers.h:2244
T * reallocate(T *p, size_t n, size_t manual_size)
Definition helpers.h:2275
size_t get_free_heap_size() const
Return the total heap space available via this allocator.
Definition helpers.h:2315
T * reallocate(T *p, size_t n)
Definition helpers.h:2273
void deallocate(T *p, size_t n)
Definition helpers.h:2288
RAMUniquePtr< T > make_unique(Args &&...args)
Value initialize one T; empty on exhaustion.
Definition helpers.h:2294
size_t get_max_free_block_size() const
Return the maximum size block this allocator could allocate.
Definition helpers.h:2336
T * allocate(size_t n)
Definition helpers.h:2258
constexpr RAMAllocator(const RAMAllocator< U > &other)
Definition helpers.h:2256
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:2304
T * allocate(size_t n, size_t manual_size)
Definition helpers.h:2260
constexpr RAMAllocator()=default
Helper class for efficient buffer allocation - uses stack for small sizes, heap for large This is use...
Definition helpers.h:824
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:211
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:259
bool is_inline_() const
Definition helpers.h:281
void set(const uint8_t *src, size_t size)
Set buffer contents, allocating heap if needed.
Definition helpers.h:274
SmallInlineBuffer & operator=(const SmallInlineBuffer &)=delete
size_t size() const
Definition helpers.h:278
uint8_t inline_[InlineSize]
Definition helpers.h:285
SmallInlineBuffer & operator=(SmallInlineBuffer &&other) noexcept
Definition helpers.h:231
const uint8_t * data() const
Definition helpers.h:277
SmallInlineBuffer(SmallInlineBuffer &&other) noexcept
Definition helpers.h:220
void add(F &&callback)
Add any callable.
Definition helpers.h:1897
void call(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1900
void add_(Callback< void(Ts...)> cb)
Non-template core to avoid code duplication per lambda type.
Definition helpers.h:1911
StaticVector< Callback< void(Ts...)>, N > callbacks_
Definition helpers.h:1912
void operator()(Ts... args)
Call all callbacks in this manager.
Definition helpers.h:1907
CallbackManager backed by StaticVector for compile-time-known callback counts.
Definition helpers.h:1891
ConstIterator(const StaticRingBuffer *buf, index_type pos)
Definition helpers.h:416
bool operator!=(const ConstIterator &other) const
Definition helpers.h:422
bool operator!=(const Iterator &other) const
Definition helpers.h:407
Iterator(StaticRingBuffer *buf, index_type pos)
Definition helpers.h:401
Fixed-size circular buffer with FIFO semantics and iteration support.
Definition helpers.h:395
bool push(const T &value)
Definition helpers.h:429
ConstIterator begin() const
Definition helpers.h:460
ConstIterator end() const
Definition helpers.h:461
index_type size() const
Definition helpers.h:448
const T & front() const
Definition helpers.h:447
void clear()
Clear all elements (reset to empty)
Definition helpers.h:452
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:291
const_reverse_iterator rend() const
Definition helpers.h:381
size_t size() const
Definition helpers.h:360
constexpr StaticVector() noexcept
Definition helpers.h:306
reverse_iterator rbegin()
Definition helpers.h:378
const T & operator[](size_t i) const
Definition helpers.h:369
reverse_iterator rend()
Definition helpers.h:379
void push_back(const T &value)
Definition helpers.h:330
bool empty() const
Definition helpers.h:362
static constexpr size_t capacity()
Definition helpers.h:361
void assign(InputIt first, InputIt last)
Definition helpers.h:343
const_reverse_iterator rbegin() const
Definition helpers.h:380
T & operator[](size_t i)
Definition helpers.h:368
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition helpers.h:297
typename std::array< T, N >::iterator iterator
Definition helpers.h:294
typename std::array< T, N >::const_iterator const_iterator
Definition helpers.h:295
std::reverse_iterator< iterator > reverse_iterator
Definition helpers.h:296
const T * data() const
Definition helpers.h:366
const_iterator end() const
Definition helpers.h:375
StaticVector(InputIt first, InputIt last)
Definition helpers.h:309
StaticVector(const StaticVector< T, M > &other)
Definition helpers.h:325
StaticVector(std::initializer_list< T > init)
Definition helpers.h:316
void resize(size_t n)
Definition helpers.h:340
const_iterator begin() const
Definition helpers.h:374
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:2392
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]
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:1212
T clamp_at_most(T value, U max)
Definition helpers.h:2402
bool random_bytes(uint8_t *data, size_t len)
Generate len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
Definition helpers.cpp:20
size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str)
Safely append a string to buffer, returning new position (capped at size).
Definition helpers.h:1235
constexpr uint32_t fnv1a_hash_extend(uint32_t hash, const char *str)
Extend a FNV-1a hash with additional string data.
Definition helpers.h:930
float random_float()
Return a random float between 0 and 1.
Definition helpers.cpp:205
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:1379
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:94
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:279
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:1034
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:1092
bool system_update_cpu_freq(uint8_t freq)
std::unique_ptr< T, RAMDeleter< T > > RAMUniquePtr
unique_ptr over RAMAllocator storage
Definition helpers.h:2220
constexpr char to_sanitized_char(char c)
Sanitize a single char: keep alphanumerics, dashes, underscores; replace others with underscore.
Definition helpers.h:1117
bool mac_address_is_valid(const uint8_t *mac)
Check if the MAC address is not all zeros or all ones.
Definition helpers.cpp:812
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:1385
void format_mac_addr_lower_no_sep(const uint8_t *mac, char *output)
Format MAC address as xxxxxxxxxxxxxx (lowercase, no separators)
Definition helpers.h:1604
constexpr uint32_t FNV1_OFFSET_BASIS
FNV-1 32-bit offset basis.
Definition helpers.h:903
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:715
std::string format_hex(const uint8_t *data, size_t length)
Format the byte array data of length len in lowercased hex.
uint16_t uint16_t size_t elem_size
Definition helpers.cpp:34
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:566
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:1164
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:493
std::string format_bin(const uint8_t *data, size_t length)
Format the byte array data of length len in binary.
constexpr T convert_little_endian(T val)
Convert a value between host byte order and little endian (least significant byte first) order.
Definition helpers.h:1043
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:908
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:677
va_end(args)
const void size_t len
Definition hal.h:64
constexpr size_t format_hex_prefixed_size(size_t byte_count)
Calculate buffer size needed for format_hex_prefixed_to: "0xXXXXXXXX...\0" = bytes * 2 + 3.
Definition helpers.h:1509
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:982
uint32_t small_pow10(int8_t n)
Return 10^n for small non-negative n (0-3) as uint32_t, avoiding float.
Definition helpers.h:1448
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:1513
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:301
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:433
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:1459
uint16_t size
Definition helpers.cpp:33
bool str_contains_ignore_case_fallback(const char *haystack, const char *needle)
Fallback implementation for case insensitive substring comparison.
Definition helpers.cpp:231
int8_t ilog10(float value)
Compute floor(log10(fabs(value))) using iterative comparison.
Definition helpers.cpp:508
uint32_t fnv1_hash_object_id(const char *str, size_t len)
Calculate FNV-1 hash of a string while applying snake_case + sanitize transformations.
Definition helpers.h:1143
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:168
T clamp_at_least(T value, U min)
Definition helpers.h:2397
optional< T > parse_number(const char *str)
Parse an unsigned decimal number from a null-terminated string.
Definition helpers.h:1273
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:1468
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:596
uint32_t random_uint32()
Return a random 32-bit unsigned integer.
Definition helpers.cpp:12
size_t size_t pos
Definition helpers.h:1186
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:801
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:1431
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:836
std::string format_hex_pretty(const uint8_t *data, size_t length, char separator, bool show_length)
Format a byte array in pretty-printed, human-readable hex format.
constexpr size_t format_hex_size(size_t byte_count)
Calculate buffer size needed for format_hex_to: "XXXXXXXX...\0" = bytes * 2 + 1.
Definition helpers.h:1506
bool str_equals_case_insensitive(const std::string &a, const std::string &b)
Compare strings for equality in case-insensitive manner.
Definition helpers.cpp:209
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:225
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:596
char * int8_to_str(char *buf, int8_t val)
Write int8 value to buffer without modulo operations.
Definition helpers.h:1405
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:585
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:365
char * uint32_to_str_unchecked(char *buf, uint32_t val)
Write unsigned 32-bit integer to buffer (internal, no size check).
Definition helpers.cpp:347
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:1532
TemperatureUnit
Definition helpers.h:1751
constexpr uint32_t FNV1_PRIME
FNV-1 32-bit prime.
Definition helpers.h:905
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:992
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:738
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:795
uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t poly, bool refin, bool refout)
Definition helpers.cpp:134
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:986
const void * src
Definition hal.h:64
uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc, uint8_t poly, bool msb_first)
Calculate a CRC-8 checksum of data with size len.
Definition helpers.cpp:67
constexpr float celsius_to_fahrenheit(float value)
Convert degrees Celsius to degrees Fahrenheit.
Definition helpers.h:1747
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:978
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:1187
constexpr uint8_t parse_hex_char(char c)
Definition helpers.h:1368
bool str_startswith(const std::string &str, const std::string &start)
Check whether a string starts with a value.
Definition helpers.cpp:216
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:1007
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:1635
int written
Definition helpers.h:1193
To bit_cast(const From &src)
Convert data between types, without aliasing issues or undefined behaviour.
Definition helpers.h:89
constexpr char to_snake_case_char(char c)
Convert a single char to snake_case: lowercase and space to underscore.
Definition helpers.h:1113
constexpr float fahrenheit_to_celsius(float value)
Convert degrees Fahrenheit to degrees Celsius.
Definition helpers.h:1749
uint8_t reverse_bits(uint8_t x)
Reverse the order of 8 bits.
Definition helpers.h:1017
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:361
float lerp(float completion, float start, float end)=delete
constexpr size_t format_hex_pretty_uint16_size(size_t count)
Calculate buffer size needed for format_hex_pretty_to with uint16_t data: "XXXX:XXXX:....
Definition helpers.h:1567
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:884
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:217
constexpr uint32_t fnv1a_hash(const char *str)
Calculate a FNV-1a hash of str.
Definition helpers.h:953
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:247
uint16_t uint16_t & capacity
Definition helpers.cpp:33
ParseOnOffState
Return values for parse_on_off().
Definition helpers.h:1697
@ PARSE_ON
Definition helpers.h:1699
@ PARSE_TOGGLE
Definition helpers.h:1701
@ PARSE_OFF
Definition helpers.h:1700
@ PARSE_NONE
Definition helpers.h:1698
float pow10_int(int8_t exp)
Compute 10^exp using iterative multiplication/division.
Definition helpers.h:871
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:470
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:1599
static void uint32_t
static Callback create(F &&callable)
Create from any callable.
Definition helpers.h:1783
void call(Ts... args) const
Invoke the callback. Only valid on Callbacks created via create(), never on default-constructed insta...
Definition helpers.h:1779
Lightweight type-erased callback (8 bytes on 32-bit) that avoids std::function overhead.
Definition helpers.h:1767
void operator()(T *p) const
Definition helpers.h:2384
Destroys and frees RAMAllocator storage. Not convertible: free() needs the address malloc returned.
Definition helpers.h:2375
void operator()(T *p) const
Definition helpers.h:2376
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