From 78875abee4a396b73b36557cbfa58ff5b51b3c1c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 21 Apr 2026 04:40:40 +0200 Subject: [PATCH] [core] Make buf_append_str PROGMEM-aware on ESP8266 (#15738) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- esphome/core/helpers.h | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index bb164f503..78476bf59 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -1079,7 +1079,33 @@ __attribute__((format(printf, 4, 5))) inline size_t buf_append_printf(char *buf, } #endif -/// Safely append a string to buffer without format parsing, returning new position (capped at size). +#ifdef USE_ESP8266 +/// Safely append a PROGMEM string to buffer, returning new position (capped at size). +/// ESP8266 internal implementation — prefer the `buf_append_str` macro which wraps +/// literals with `PSTR()` automatically so they stay in flash instead of eating RAM. +/// @param buf Output buffer +/// @param size Total buffer size +/// @param pos Current position in buffer +/// @param str PROGMEM-resident string to append (must not be null) +/// @return New position after appending; returns `size` if `pos >= size`, otherwise +/// returns at most `size - 1` because one byte is reserved for the null terminator +inline size_t buf_append_str_p(char *buf, size_t size, size_t pos, PGM_P str) { + if (pos >= size) { + return size; + } + size_t remaining = size - pos - 1; // reserve space for null terminator + size_t len = strnlen_P(str, remaining); + memcpy_P(buf + pos, str, len); + pos += len; + buf[pos] = '\0'; + return pos; +} +/// Safely append a string to buffer, returning new position (capped at size). +/// More efficient than buf_append_printf for plain string literals. +/// On ESP8266 the literal is wrapped with PSTR() so it stays in flash. +#define buf_append_str(buf, size, pos, str) buf_append_str_p(buf, size, pos, PSTR(str)) +#else +/// Safely append a string to buffer, returning new position (capped at size). /// More efficient than buf_append_printf for plain string literals. /// @param buf Output buffer /// @param size Total buffer size @@ -1091,15 +1117,13 @@ inline size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str return size; } size_t remaining = size - pos - 1; // reserve space for null terminator - size_t len = strlen(str); - if (len > remaining) { - len = remaining; - } + size_t len = strnlen(str, remaining); memcpy(buf + pos, str, len); pos += len; buf[pos] = '\0'; return pos; } +#endif /// Concatenate a name with a separator and suffix using an efficient stack-based approach. /// This avoids multiple heap allocations during string construction.