ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
string_ref.h
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cstdint>
5#include <cstring>
6#include <iterator>
7#include <memory>
8#include <string>
10
11#ifdef USE_JSON
13#endif // USE_JSON
14
15#ifdef USE_ESP8266
16#include <pgmspace.h>
17#endif // USE_ESP8266
18
19namespace esphome {
20
31class StringRef {
32 public:
33 using traits_type = std::char_traits<char>;
34 using value_type = traits_type::char_type;
35 using allocator_type = std::allocator<char>;
36 using size_type = std::allocator_traits<allocator_type>::size_type;
37 using difference_type = std::allocator_traits<allocator_type>::difference_type;
38 using const_reference = const value_type &;
39 using const_pointer = const value_type *;
41 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
42
43 constexpr StringRef() : base_(""), len_(0) {}
44 explicit StringRef(const std::string &s) : base_(s.c_str()), len_(s.size()) {}
45 explicit StringRef(const char *s) : base_(s), len_(strlen(s)) {}
46 constexpr StringRef(const char *s, size_t n) : base_(s), len_(n) {}
47 template<typename CharT>
48 constexpr StringRef(const CharT *s, size_t n) : base_(reinterpret_cast<const char *>(s)), len_(n) {}
49 template<typename InputIt>
50 StringRef(InputIt first, InputIt last)
51 : base_(reinterpret_cast<const char *>(&*first)), len_(std::distance(first, last)) {}
52 template<typename InputIt>
53 StringRef(InputIt *first, InputIt *last)
54 : base_(reinterpret_cast<const char *>(first)), len_(std::distance(first, last)) {}
55 template<typename CharT, size_t N> constexpr static StringRef from_lit(const CharT (&s)[N]) {
56 return StringRef{s, N - 1};
57 }
58 static StringRef from_maybe_nullptr(const char *s) {
59 if (s == nullptr) {
60 return StringRef();
61 }
62
63 return StringRef(s);
64 }
65
66 constexpr const_iterator begin() const { return base_; };
67 constexpr const_iterator cbegin() const { return base_; };
68
69 constexpr const_iterator end() const { return base_ + len_; };
70 constexpr const_iterator cend() const { return base_ + len_; };
71
72 const_reverse_iterator rbegin() const { return const_reverse_iterator{base_ + len_}; }
73 const_reverse_iterator crbegin() const { return const_reverse_iterator{base_ + len_}; }
74
77
78 constexpr const char *c_str() const { return base_; }
79 constexpr size_type size() const { return len_; }
80 constexpr size_type length() const { return len_; }
81 constexpr bool empty() const { return len_ == 0; }
82 constexpr const_reference operator[](size_type pos) const { return *(base_ + pos); }
83
85 bool starts_with(const StringRef &prefix) const {
86 return len_ >= prefix.len_ && (prefix.len_ == 0 || std::memcmp(base_, prefix.base_, prefix.len_) == 0);
87 }
88 bool starts_with(const char *prefix) const { return this->starts_with(StringRef(prefix)); }
89 bool starts_with(const std::string &prefix) const { return this->starts_with(StringRef(prefix)); }
90
92 size_type copy(char *dest, size_type count, size_type pos = 0) const {
93 if (pos >= len_)
94 return 0;
95 size_type actual = (count > len_ - pos) ? len_ - pos : count;
96 std::memcpy(dest, base_ + pos, actual);
97 return actual;
98 }
99
100 std::string str() const { return std::string(base_, len_); } // fine for {nullptr, 0}: nothing is read
101 const uint8_t *byte() const { return reinterpret_cast<const uint8_t *>(base_); }
102
103 operator std::string() const { return str(); }
104
106 int compare(const StringRef &other) const {
107 size_type common = std::min(len_, other.len_);
108 int result = common == 0 ? 0 : std::memcmp(base_, other.base_, common);
109 if (result != 0)
110 return result;
111 if (len_ < other.len_)
112 return -1;
113 if (len_ > other.len_)
114 return 1;
115 return 0;
116 }
117 int compare(const char *s) const { return compare(StringRef(s)); }
118 int compare(const std::string &s) const { return compare(StringRef(s)); }
119
122 size_type find(const char *s, size_type pos = 0) const {
123 if (pos >= len_)
124 return std::string::npos;
125 const char *result = std::strstr(base_ + pos, s);
126 // Verify entire match is within bounds (strstr searches to null terminator)
127 if (result && result + std::strlen(s) <= base_ + len_)
128 return static_cast<size_type>(result - base_);
129 return std::string::npos;
130 }
131 size_type find(char c, size_type pos = 0) const {
132 if (pos >= len_)
133 return std::string::npos;
134 const void *result = std::memchr(base_ + pos, static_cast<unsigned char>(c), len_ - pos);
135 return result ? static_cast<size_type>(static_cast<const char *>(result) - base_) : std::string::npos;
136 }
137
139 std::string substr(size_type pos = 0, size_type count = std::string::npos) const {
140 if (pos >= len_)
141 return std::string();
142 size_type actual_count = (count == std::string::npos || pos + count > len_) ? len_ - pos : count;
143 return std::string(base_ + pos, actual_count);
144 }
145
146 private:
147 const char *base_;
148 size_type len_;
149};
150
151inline bool operator==(const StringRef &lhs, const StringRef &rhs) {
152 return lhs.size() == rhs.size() && std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
153}
154
155inline bool operator==(const StringRef &lhs, const std::string &rhs) {
156 return lhs.size() == rhs.size() && std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs));
157}
158
159inline bool operator==(const std::string &lhs, const StringRef &rhs) { return rhs == lhs; }
160
161inline bool operator==(const StringRef &lhs, const char *rhs) {
162 return lhs.size() == strlen(rhs) && std::equal(std::begin(lhs), std::end(lhs), rhs);
163}
164
165inline bool operator==(const char *lhs, const StringRef &rhs) { return rhs == lhs; }
166
167inline bool operator!=(const StringRef &lhs, const StringRef &rhs) { return !(lhs == rhs); }
168
169inline bool operator!=(const StringRef &lhs, const std::string &rhs) { return !(lhs == rhs); }
170
171inline bool operator!=(const std::string &lhs, const StringRef &rhs) { return !(rhs == lhs); }
172
173inline bool operator!=(const StringRef &lhs, const char *rhs) { return !(lhs == rhs); }
174
175inline bool operator!=(const char *lhs, const StringRef &rhs) { return !(rhs == lhs); }
176
177#ifdef USE_ESP8266
178inline bool operator==(const StringRef &lhs, const __FlashStringHelper *rhs) {
179 PGM_P p = reinterpret_cast<PGM_P>(rhs);
180 size_t rhs_len = strlen_P(p);
181 if (lhs.size() != rhs_len) {
182 return false;
183 }
184 return memcmp_P(lhs.c_str(), p, rhs_len) == 0;
185}
186
187inline bool operator==(const __FlashStringHelper *lhs, const StringRef &rhs) { return rhs == lhs; }
188
189inline bool operator!=(const StringRef &lhs, const __FlashStringHelper *rhs) { return !(lhs == rhs); }
190
191inline bool operator!=(const __FlashStringHelper *lhs, const StringRef &rhs) { return !(rhs == lhs); }
192
193#endif // USE_ESP8266
194
195inline bool operator<(const StringRef &lhs, const StringRef &rhs) {
196 return std::lexicographical_compare(std::begin(lhs), std::end(lhs), std::begin(rhs), std::end(rhs));
197}
198
199inline std::string &operator+=(std::string &lhs, const StringRef &rhs) {
200 lhs.append(rhs.c_str(), rhs.size());
201 return lhs;
202}
203
204inline std::string operator+(const char *lhs, const StringRef &rhs) {
205 auto str = std::string(lhs);
206 str.append(rhs.c_str(), rhs.size());
207 return str;
208}
209
210inline std::string operator+(const StringRef &lhs, const char *rhs) {
211 auto str = lhs.str();
212 str.append(rhs);
213 return str;
214}
215
216inline std::string operator+(const StringRef &lhs, const std::string &rhs) {
217 auto str = lhs.str();
218 str.append(rhs);
219 return str;
220}
221
222inline std::string operator+(const std::string &lhs, const StringRef &rhs) {
223 std::string str(lhs);
224 str.append(rhs.c_str(), rhs.size());
225 return str;
226}
227
231 public:
232 constexpr ProgmemStringRef() = default;
233 constexpr ProgmemStringRef(const char *s, size_t len) : ref_(s, len) {}
234 constexpr explicit ProgmemStringRef(const StringRef &s) : ref_(s) {}
236 template<size_t N> static constexpr ProgmemStringRef from_lit(const char (&s)[N]) { return {s, N - 1}; }
237
238 constexpr const char *progmem_ptr() const { return this->ref_.c_str(); }
239 constexpr size_t size() const { return this->ref_.size(); }
240 constexpr bool empty() const { return this->ref_.empty(); }
241
243 size_t write_to(char *buf, size_t buf_size) const {
244 if (buf_size == 0)
245 return 0;
246 size_t len = std::min(this->ref_.size(), buf_size - 1);
247#ifdef USE_ESP8266
248 memcpy_P(buf, this->ref_.c_str(), len);
249#else
250 std::memcpy(buf, this->ref_.c_str(), len);
251#endif
252 buf[len] = '\0';
253 return len;
254 }
255
256 bool equals(const StringRef &other) const {
257#ifdef USE_ESP8266
258 // A StringRef may be {nullptr, 0}, and memcmp_P with a null pointer is undefined even for 0 bytes
259 return other.size() == this->ref_.size() &&
260 (other.empty() || memcmp_P(other.c_str(), this->ref_.c_str(), other.size()) == 0);
261#else
262 return other == this->ref_;
263#endif
264 }
265
266#ifndef USE_ESP8266
268 constexpr const StringRef &ram_ref() const { return this->ref_; }
269#endif
270
271 // Remove before 2027.5.0
272 [[deprecated(
273 "May point to flash; use progmem_ptr() or the owner's accessors. Removed in 2027.5.0")]] constexpr const char *
274 c_str() const {
275 return this->ref_.c_str();
276 }
277
278 private:
279 StringRef ref_;
280};
281
282#ifdef USE_ESP8266
283inline bool operator==(const StringRef &lhs, const ProgmemStringRef &rhs) { return rhs.equals(lhs); }
284inline bool operator!=(const StringRef &lhs, const ProgmemStringRef &rhs) { return !rhs.equals(lhs); }
285inline bool operator==(const std::string &lhs, const ProgmemStringRef &rhs) { return rhs.equals(StringRef(lhs)); }
286inline bool operator!=(const std::string &lhs, const ProgmemStringRef &rhs) { return !rhs.equals(StringRef(lhs)); }
287#endif
288
289// String conversion functions for ADL compatibility (allows stoi(x) where x is StringRef)
290// Must be in esphome namespace for ADL to find them. Uses strtol/strtod directly to avoid heap allocation.
291namespace internal {
292// NOLINTBEGIN(google-runtime-int)
293template<typename R, typename F> inline R parse_number(const StringRef &str, size_t *pos, F conv) {
294 if (str.empty()) { // nothing to parse, and a null view must not reach the C library
295 if (pos)
296 *pos = 0;
297 return R{};
298 }
299 char *end;
300 R result = conv(str.c_str(), &end);
301 // Set pos to 0 on conversion failure (when no characters consumed), otherwise index after number
302 if (pos)
303 *pos = (end == str.c_str()) ? 0 : static_cast<size_t>(end - str.c_str());
304 return result;
305}
306template<typename R, typename F> inline R parse_number(const StringRef &str, size_t *pos, int base, F conv) {
307 if (str.empty()) { // nothing to parse, and a null view must not reach the C library
308 if (pos)
309 *pos = 0;
310 return R{};
311 }
312 char *end;
313 R result = conv(str.c_str(), &end, base);
314 // Set pos to 0 on conversion failure (when no characters consumed), otherwise index after number
315 if (pos)
316 *pos = (end == str.c_str()) ? 0 : static_cast<size_t>(end - str.c_str());
317 return result;
318}
319// NOLINTEND(google-runtime-int)
320} // namespace internal
321// readability-non-const-parameter: `pos` is written through by internal::parse_number, one call
322// frame away; the check only inspects these bodies, so it wrongly proposes `const size_t *`.
323// NOLINTBEGIN(readability-identifier-naming,google-runtime-int,readability-non-const-parameter)
324inline int stoi(const StringRef &str, size_t *pos = nullptr, int base = 10) {
325 return static_cast<int>(internal::parse_number<long>(str, pos, base, std::strtol));
326}
327inline long stol(const StringRef &str, size_t *pos = nullptr, int base = 10) {
328 return internal::parse_number<long>(str, pos, base, std::strtol);
329}
330inline float stof(const StringRef &str, size_t *pos = nullptr) {
331 return internal::parse_number<float>(str, pos, std::strtof);
332}
333inline double stod(const StringRef &str, size_t *pos = nullptr) {
334 return internal::parse_number<double>(str, pos, std::strtod);
335}
336// NOLINTEND(readability-identifier-naming,google-runtime-int,readability-non-const-parameter)
337
338#ifdef USE_JSON
339// A StringRef may view a scratch buffer (mqtt topics do), so the document copies it
340// NOLINTNEXTLINE(readability-identifier-naming)
341inline void convertToJson(const StringRef &src, JsonVariant dst) {
342 // Bounded by the view length; a null, empty view becomes "" rather than JSON null
343 if (src.empty()) {
344 dst.set("");
345 return;
346 }
347 // The explicit isStatic is load bearing: without it clang-tidy reads size() as the bool of the two argument
348 // constructor and asks for empty() instead
349 dst.set(JsonString(src.c_str(), src.size(), /* isStatic= */ false));
350}
351
352namespace json {
353// For a view that outlives the document, see json_util.h. ArduinoJson keeps a linked string as a bare
354// pointer and reads it to its terminator on every platform, so the view must be a whole string, as the
355// entity getters' views are. A null, empty view links "" too
356inline JsonString linked(const StringRef &s) { return JsonString(s.empty() ? "" : s.c_str(), s.size(), true); }
357#ifdef USE_ESP8266
358// ArduinoJson copies a flash string into the document
359inline ProgmemStr linked(const ProgmemStringRef &s) { return reinterpret_cast<ProgmemStr>(s.progmem_ptr()); }
360#else
361inline JsonString linked(const ProgmemStringRef &s) { return JsonString(s.progmem_ptr(), s.size(), true); }
362#endif
363} // namespace json
364#endif // USE_JSON
365
366} // namespace esphome
A string that may live in flash (on ESP8266): pointer and length, with no byte-reading operations.
Definition string_ref.h:230
constexpr const char * progmem_ptr() const
Definition string_ref.h:238
size_t write_to(char *buf, size_t buf_size) const
Copy into buf (null terminated, truncated to fit), returns the length copied.
Definition string_ref.h:243
static constexpr ProgmemStringRef from_lit(const char(&s)[N])
A PROGMEM char array, with its length known at compile time.
Definition string_ref.h:236
constexpr ProgmemStringRef()=default
constexpr ProgmemStringRef(const char *s, size_t len)
Definition string_ref.h:233
constexpr ProgmemStringRef(const StringRef &s)
Definition string_ref.h:234
constexpr const StringRef & ram_ref() const
The string is in RAM here, so it can be viewed directly.
Definition string_ref.h:268
bool equals(const StringRef &other) const
Definition string_ref.h:256
constexpr size_t size() const
Definition string_ref.h:239
constexpr const char * c_str() const
Definition string_ref.h:274
constexpr bool empty() const
Definition string_ref.h:240
StringRef is a reference to a string owned by something else.
Definition string_ref.h:31
bool starts_with(const char *prefix) const
Definition string_ref.h:88
StringRef(const std::string &s)
Definition string_ref.h:44
std::allocator< char > allocator_type
Definition string_ref.h:35
std::reverse_iterator< const_iterator > const_reverse_iterator
Definition string_ref.h:41
size_type find(char c, size_type pos=0) const
Definition string_ref.h:131
const_reverse_iterator crend() const
Definition string_ref.h:76
constexpr const char * c_str() const
Definition string_ref.h:78
bool starts_with(const std::string &prefix) const
Definition string_ref.h:89
constexpr StringRef(const CharT *s, size_t n)
Definition string_ref.h:48
std::char_traits< char > traits_type
Definition string_ref.h:33
const value_type * const_pointer
Definition string_ref.h:39
constexpr size_type length() const
Definition string_ref.h:80
int compare(const StringRef &other) const
Compare (compatible with std::string::compare)
Definition string_ref.h:106
size_type find(const char *s, size_type pos=0) const
Find first occurrence of substring, returns std::string::npos if not found.
Definition string_ref.h:122
traits_type::char_type value_type
Definition string_ref.h:34
constexpr const_iterator cend() const
Definition string_ref.h:70
constexpr const_reference operator[](size_type pos) const
Definition string_ref.h:82
size_type copy(char *dest, size_type count, size_type pos=0) const
Copy characters to destination buffer (std::string::copy-like, but returns 0 instead of throwing on o...
Definition string_ref.h:92
constexpr const_iterator begin() const
Definition string_ref.h:66
int compare(const char *s) const
Definition string_ref.h:117
constexpr StringRef(const char *s, size_t n)
Definition string_ref.h:46
bool starts_with(const StringRef &prefix) const
True if the view begins with the given prefix (std::string::starts_with-like)
Definition string_ref.h:85
StringRef(const char *s)
Definition string_ref.h:45
constexpr bool empty() const
Definition string_ref.h:81
const_reverse_iterator rbegin() const
Definition string_ref.h:72
constexpr size_type size() const
Definition string_ref.h:79
static constexpr StringRef from_lit(const CharT(&s)[N])
Definition string_ref.h:55
constexpr const_iterator end() const
Definition string_ref.h:69
std::string str() const
Definition string_ref.h:100
std::allocator_traits< allocator_type >::size_type size_type
Definition string_ref.h:36
std::string substr(size_type pos=0, size_type count=std::string::npos) const
Return substring as std::string.
Definition string_ref.h:139
constexpr StringRef()
Definition string_ref.h:43
std::allocator_traits< allocator_type >::difference_type difference_type
Definition string_ref.h:37
StringRef(InputIt first, InputIt last)
Definition string_ref.h:50
static StringRef from_maybe_nullptr(const char *s)
Definition string_ref.h:58
int compare(const std::string &s) const
Definition string_ref.h:118
const value_type & const_reference
Definition string_ref.h:38
const_reverse_iterator rend() const
Definition string_ref.h:75
constexpr const_iterator cbegin() const
Definition string_ref.h:67
const_reverse_iterator crbegin() const
Definition string_ref.h:73
const_pointer const_iterator
Definition string_ref.h:40
const uint8_t * byte() const
Definition string_ref.h:101
StringRef(InputIt *first, InputIt *last)
Definition string_ref.h:53
unsigned count
Definition gif.cpp:51
R parse_number(const StringRef &str, size_t *pos, F conv)
Definition string_ref.h:293
JsonString linked(const char *s)
Definition json_util.h:24
std::string operator+(const char *lhs, const StringRef &rhs)
Definition string_ref.h:204
bool operator!=(const StringRef &lhs, const StringRef &rhs)
Definition string_ref.h:167
const void size_t len
Definition hal.h:68
double stod(const StringRef &str, size_t *pos=nullptr)
Definition string_ref.h:333
void convertToJson(const StringRef &src, JsonVariant dst)
Definition string_ref.h:341
bool operator<(const StringRef &lhs, const StringRef &rhs)
Definition string_ref.h:195
std::string & operator+=(std::string &lhs, const StringRef &rhs)
Definition string_ref.h:199
size_t size_t pos
Definition helpers.h:1255
float stof(const StringRef &str, size_t *pos=nullptr)
Definition string_ref.h:330
const void * src
Definition hal.h:68
bool operator==(const StringRef &lhs, const StringRef &rhs)
Definition string_ref.h:151
int stoi(const StringRef &str, size_t *pos=nullptr, int base=10)
Definition string_ref.h:324
long stol(const StringRef &str, size_t *pos=nullptr, int base=10)
Definition string_ref.h:327
STL namespace.
const __FlashStringHelper * ProgmemStr
Definition progmem.h:31
uint8_t end[39]
Definition sun_gtil2.cpp:17