ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
progmem.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cstddef>
6#include <cstdint>
7#include <new>
8#include <string>
9
10#include "esphome/core/hal.h" // For PROGMEM definition
11
12// Platform-agnostic macros for PROGMEM string handling
13// On ESP8266/Arduino: Use Arduino's F() macro for PROGMEM strings
14// On other platforms: Use plain strings (no PROGMEM)
15
16#ifdef USE_ESP8266
17// ESP8266 uses Arduino macros
18#define ESPHOME_F(string_literal) F(string_literal)
19#define ESPHOME_PGM_P PGM_P
20#define ESPHOME_PSTR(s) PSTR(s)
21#define ESPHOME_strncpy_P strncpy_P
22#define ESPHOME_strncat_P strncat_P
23#define ESPHOME_snprintf_P snprintf_P
24#define ESPHOME_strcmp_P strcmp_P
25#define ESPHOME_strcasecmp_P strcasecmp_P
26#define ESPHOME_strncmp_P strncmp_P
27#define ESPHOME_strncasecmp_P strncasecmp_P
28#define ESPHOME_strlen_P strlen_P
29#define ESPHOME_strstr_P strstr_P
30// Type for pointers to PROGMEM strings (for use with ESPHOME_F return values)
31using ProgmemStr = const __FlashStringHelper *;
32// Storage class for PROGMEM_STRING_TABLE data; LOG_STR_ARG treats a LogString as PGM_P
33// on ESP8266, so the table data must be in flash to match.
34#define ESPHOME_PROGMEM_STRING_TABLE_STORAGE PROGMEM
35#else
36#define ESPHOME_F(string_literal) (string_literal)
37#define ESPHOME_PGM_P const char *
38#define ESPHOME_PSTR(s) (s)
39#define ESPHOME_strncpy_P strncpy
40#define ESPHOME_strncat_P strncat
41#define ESPHOME_snprintf_P snprintf
42#define ESPHOME_strcmp_P strcmp
43#define ESPHOME_strcasecmp_P strcasecmp
44#define ESPHOME_strncmp_P strncmp
45#define ESPHOME_strncasecmp_P strncasecmp
46#define ESPHOME_strlen_P strlen
47#define ESPHOME_strstr_P strstr
48// Type for pointers to strings (no PROGMEM on non-ESP8266 platforms)
49using ProgmemStr = const char *;
50// No-op on non-ESP8266 platforms where PROGMEM itself is a no-op.
51#define ESPHOME_PROGMEM_STRING_TABLE_STORAGE
52#endif
53
54namespace esphome {
55
57#ifdef USE_ESP8266
58std::string progmem_string(ProgmemStr str);
59#else
60inline std::string progmem_string(ProgmemStr str) { return std::string(str); }
61#endif
62
64template<size_t N> struct FixedString {
65 char data[N]{};
66 constexpr FixedString(const char (&str)[N]) {
67 for (size_t i = 0; i < N; ++i)
68 data[i] = str[i];
69 }
70 constexpr size_t size() const { return N - 1; } // exclude null terminator
71};
72
82template<FixedString... Strs> struct ProgmemStringTable {
83 static constexpr size_t COUNT = sizeof...(Strs);
84 static constexpr size_t BLOB_SIZE = (0 + ... + (Strs.size() + 1));
86 static constexpr size_t MAX_LENGTH = std::max({Strs.size()...});
89 static constexpr bool INDEXED = BLOB_SIZE <= UINT8_MAX;
90
92 static constexpr auto make_blob() {
93 std::array<char, BLOB_SIZE> result{};
94 size_t pos = 0;
95 auto copy = [&](const auto &str) {
96 for (size_t i = 0; i <= str.size(); ++i)
97 result[pos++] = str.data[i];
98 };
99 (copy(Strs), ...);
100 return result;
101 }
102
104 static constexpr auto make_offsets() {
105 static_assert(COUNT > 0, "PROGMEM_STRING_TABLE must contain at least one string");
106 static_assert(COUNT <= 255, "PROGMEM_STRING_TABLE supports at most 255 strings with uint8_t indices");
107 if constexpr (INDEXED) {
108 std::array<uint8_t, COUNT> result{};
109 size_t pos = 0, idx = 0;
110 ((result[idx++] = static_cast<uint8_t>(pos), pos += Strs.size() + 1), ...);
111 return result;
112 } else {
113 return std::array<uint8_t, 0>{};
114 }
115 }
116};
117
119inline const char *progmem_skip_strings(const char *blob, uint8_t idx) {
120 while (idx-- > 0)
121 blob += ESPHOME_strlen_P(blob) + 1;
122 return blob;
123}
124
125// Forward declaration for LogString (defined in log.h)
126struct LogString;
127
131#define PROGMEM_STRING_TABLE(Name, ...) \
132 struct Name { \
133 using Table = ::esphome::ProgmemStringTable<__VA_ARGS__>; \
134 static constexpr size_t COUNT = Table::COUNT; \
135 static constexpr uint8_t LAST_INDEX = COUNT - 1; \
136 static constexpr size_t BLOB_SIZE = Table::BLOB_SIZE; \
137 static constexpr size_t MAX_LENGTH = Table::MAX_LENGTH; \
138 static constexpr auto BLOB ESPHOME_PROGMEM_STRING_TABLE_STORAGE = Table::make_blob(); \
139 static constexpr auto OFFSETS ESPHOME_PROGMEM_STRING_TABLE_STORAGE = Table::make_offsets(); \
140 static const char *get_(uint8_t idx, uint8_t fallback) { \
141 if (idx >= COUNT) \
142 idx = fallback; \
143 /* std::launder is used here to prevent the inter-procedural analysis that */ \
144 /* causes the false positive that the string is not null terminated */ \
145 if constexpr (Table::INDEXED) \
146 return std::launder(&BLOB[::esphome::progmem_read_byte(&OFFSETS[idx])]); \
147 else \
148 return std::launder(::esphome::progmem_skip_strings(BLOB.data(), idx)); \
149 } \
150 static ::ProgmemStr get_progmem_str(uint8_t idx, uint8_t fallback) { \
151 return reinterpret_cast<::ProgmemStr>(get_(idx, fallback)); \
152 } \
153 static const ::esphome::LogString *get_log_str(uint8_t idx, uint8_t fallback) { \
154 return reinterpret_cast<const ::esphome::LogString *>(get_(idx, fallback)); \
155 } \
156 }
157
158} // namespace esphome
std::string progmem_string(ProgmemStr str)
Copies a string stored with ESPHOME_F into a std::string.
Definition helpers.cpp:24
const char * progmem_skip_strings(const char *blob, uint8_t idx)
Skip idx strings in a packed NUL-separated blob that may live in flash.
Definition progmem.h:119
size_t size_t pos
Definition helpers.h:1179
const __FlashStringHelper * ProgmemStr
Definition progmem.h:31
Helper for C++20 string literal template arguments.
Definition progmem.h:64
constexpr size_t size() const
Definition progmem.h:70
constexpr FixedString(const char(&str)[N])
Definition progmem.h:66
Compile-time string table that packs strings into a single blob with offset lookup.
Definition progmem.h:82
static constexpr size_t COUNT
Definition progmem.h:83
static constexpr size_t MAX_LENGTH
Length of the longest string, for sizing a RAM copy on ESP8266.
Definition progmem.h:86
static constexpr size_t BLOB_SIZE
Definition progmem.h:84
static constexpr auto make_blob()
Generate packed string blob at compile time.
Definition progmem.h:92
static constexpr bool INDEXED
One-byte offsets address up to 255 bytes; a larger blob has no offset table and is scanned instead,...
Definition progmem.h:89
static constexpr auto make_offsets()
Generate offset table at compile time.
Definition progmem.h:104