#pragma once #include #include #include #include #include #include "esphome/core/defines.h" #ifdef USE_JSON #include "esphome/components/json/json_util.h" #endif // USE_JSON #ifdef USE_ESP8266 #include #endif // USE_ESP8266 namespace esphome { /** * StringRef is a reference to a string owned by something else. So it behaves like simple string, but it does not own * pointer. When it is default constructed, it has empty string. You can freely copy or move around this struct, but * never free its pointer. str() function can be used to export the content as std::string. StringRef is adopted from * */ class StringRef { public: using traits_type = std::char_traits; using value_type = traits_type::char_type; using allocator_type = std::allocator; using size_type = std::allocator_traits::size_type; using difference_type = std::allocator_traits::difference_type; using const_reference = const value_type &; using const_pointer = const value_type *; using const_iterator = const_pointer; using const_reverse_iterator = std::reverse_iterator; constexpr StringRef() : base_(""), len_(0) {} explicit StringRef(const std::string &s) : base_(s.c_str()), len_(s.size()) {} explicit StringRef(const char *s) : base_(s), len_(strlen(s)) {} constexpr StringRef(const char *s, size_t n) : base_(s), len_(n) {} template constexpr StringRef(const CharT *s, size_t n) : base_(reinterpret_cast(s)), len_(n) {} template StringRef(InputIt first, InputIt last) : base_(reinterpret_cast(&*first)), len_(std::distance(first, last)) {} template StringRef(InputIt *first, InputIt *last) : base_(reinterpret_cast(first)), len_(std::distance(first, last)) {} template constexpr static StringRef from_lit(const CharT (&s)[N]) { return StringRef{s, N - 1}; } static StringRef from_maybe_nullptr(const char *s) { if (s == nullptr) { return StringRef(); } return StringRef(s); } constexpr const_iterator begin() const { return base_; }; constexpr const_iterator cbegin() const { return base_; }; constexpr const_iterator end() const { return base_ + len_; }; constexpr const_iterator cend() const { return base_ + len_; }; const_reverse_iterator rbegin() const { return const_reverse_iterator{base_ + len_}; } const_reverse_iterator crbegin() const { return const_reverse_iterator{base_ + len_}; } const_reverse_iterator rend() const { return const_reverse_iterator{base_}; } const_reverse_iterator crend() const { return const_reverse_iterator{base_}; } constexpr const char *c_str() const { return base_; } constexpr size_type size() const { return len_; } constexpr size_type length() const { return len_; } constexpr bool empty() const { return len_ == 0; } constexpr const_reference operator[](size_type pos) const { return *(base_ + pos); } std::string str() const { return std::string(base_, len_); } const uint8_t *byte() const { return reinterpret_cast(base_); } operator std::string() const { return str(); } /// Find first occurrence of substring, returns std::string::npos if not found. /// Note: Requires the underlying string to be null-terminated. size_type find(const char *s, size_type pos = 0) const { if (pos >= len_) return std::string::npos; const char *result = std::strstr(base_ + pos, s); // Verify entire match is within bounds (strstr searches to null terminator) if (result && result + std::strlen(s) <= base_ + len_) return static_cast(result - base_); return std::string::npos; } size_type find(char c, size_type pos = 0) const { if (pos >= len_) return std::string::npos; const void *result = std::memchr(base_ + pos, static_cast(c), len_ - pos); return result ? static_cast(static_cast(result) - base_) : std::string::npos; } /// Return substring as std::string std::string substr(size_type pos = 0, size_type count = std::string::npos) const { if (pos >= len_) return std::string(); size_type actual_count = (count == std::string::npos || pos + count > len_) ? len_ - pos : count; return std::string(base_ + pos, actual_count); } private: const char *base_; size_type len_; }; inline bool operator==(const StringRef &lhs, const StringRef &rhs) { return lhs.size() == rhs.size() && std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs)); } inline bool operator==(const StringRef &lhs, const std::string &rhs) { return lhs.size() == rhs.size() && std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs)); } inline bool operator==(const std::string &lhs, const StringRef &rhs) { return rhs == lhs; } inline bool operator==(const StringRef &lhs, const char *rhs) { return lhs.size() == strlen(rhs) && std::equal(std::begin(lhs), std::end(lhs), rhs); } inline bool operator==(const char *lhs, const StringRef &rhs) { return rhs == lhs; } inline bool operator!=(const StringRef &lhs, const StringRef &rhs) { return !(lhs == rhs); } inline bool operator!=(const StringRef &lhs, const std::string &rhs) { return !(lhs == rhs); } inline bool operator!=(const std::string &lhs, const StringRef &rhs) { return !(rhs == lhs); } inline bool operator!=(const StringRef &lhs, const char *rhs) { return !(lhs == rhs); } inline bool operator!=(const char *lhs, const StringRef &rhs) { return !(rhs == lhs); } #ifdef USE_ESP8266 inline bool operator==(const StringRef &lhs, const __FlashStringHelper *rhs) { PGM_P p = reinterpret_cast(rhs); size_t rhs_len = strlen_P(p); if (lhs.size() != rhs_len) { return false; } return memcmp_P(lhs.c_str(), p, rhs_len) == 0; } inline bool operator==(const __FlashStringHelper *lhs, const StringRef &rhs) { return rhs == lhs; } inline bool operator!=(const StringRef &lhs, const __FlashStringHelper *rhs) { return !(lhs == rhs); } inline bool operator!=(const __FlashStringHelper *lhs, const StringRef &rhs) { return !(rhs == lhs); } #endif // USE_ESP8266 inline bool operator<(const StringRef &lhs, const StringRef &rhs) { return std::lexicographical_compare(std::begin(lhs), std::end(lhs), std::begin(rhs), std::end(rhs)); } inline std::string &operator+=(std::string &lhs, const StringRef &rhs) { lhs.append(rhs.c_str(), rhs.size()); return lhs; } inline std::string operator+(const char *lhs, const StringRef &rhs) { auto str = std::string(lhs); str.append(rhs.c_str(), rhs.size()); return str; } inline std::string operator+(const StringRef &lhs, const char *rhs) { auto str = lhs.str(); str.append(rhs); return str; } inline std::string operator+(const StringRef &lhs, const std::string &rhs) { auto str = lhs.str(); str.append(rhs); return str; } inline std::string operator+(const std::string &lhs, const StringRef &rhs) { std::string str(lhs); str.append(rhs.c_str(), rhs.size()); return str; } // String conversion functions for ADL compatibility (allows stoi(x) where x is StringRef) // Must be in esphome namespace for ADL to find them. Uses strtol/strtod directly to avoid heap allocation. namespace internal { // NOLINTBEGIN(google-runtime-int) template inline R parse_number(const StringRef &str, size_t *pos, F conv) { char *end; R result = conv(str.c_str(), &end); // Set pos to 0 on conversion failure (when no characters consumed), otherwise index after number if (pos) *pos = (end == str.c_str()) ? 0 : static_cast(end - str.c_str()); return result; } template inline R parse_number(const StringRef &str, size_t *pos, int base, F conv) { char *end; R result = conv(str.c_str(), &end, base); // Set pos to 0 on conversion failure (when no characters consumed), otherwise index after number if (pos) *pos = (end == str.c_str()) ? 0 : static_cast(end - str.c_str()); return result; } // NOLINTEND(google-runtime-int) } // namespace internal // NOLINTBEGIN(readability-identifier-naming,google-runtime-int) inline int stoi(const StringRef &str, size_t *pos = nullptr, int base = 10) { return static_cast(internal::parse_number(str, pos, base, std::strtol)); } inline long stol(const StringRef &str, size_t *pos = nullptr, int base = 10) { return internal::parse_number(str, pos, base, std::strtol); } inline float stof(const StringRef &str, size_t *pos = nullptr) { return internal::parse_number(str, pos, std::strtof); } inline double stod(const StringRef &str, size_t *pos = nullptr) { return internal::parse_number(str, pos, std::strtod); } // NOLINTEND(readability-identifier-naming,google-runtime-int) #ifdef USE_JSON // NOLINTNEXTLINE(readability-identifier-naming) inline void convertToJson(const StringRef &src, JsonVariant dst) { dst.set(src.c_str()); } #endif // USE_JSON } // namespace esphome