Compare commits

...

3 Commits

Author SHA1 Message Date
J. Nick Koston
038a939f96 Merge branch 'dev' into http_request_reduce_alloc 2026-01-28 10:57:51 -10:00
J. Nick Koston
3f9d6e39a9 add comment here as well 2026-01-28 10:51:29 -10:00
J. Nick Koston
ba6e050c91 [http_request] Reduce heap allocations in update check by parsing JSON directly from buffer 2026-01-28 00:02:32 -10:00
3 changed files with 17 additions and 16 deletions

View File

@@ -90,16 +90,14 @@ void HttpRequestUpdate::update_task(void *params) {
UPDATE_RETURN;
}
size_t read_index = container->get_bytes_read();
size_t content_length = container->content_length;
container->end();
container.reset(); // Release ownership of the container's shared_ptr
bool valid = false;
{ // Ensures the response string falls out of scope and deallocates before the task ends
std::string response((char *) data, read_index);
allocator.deallocate(data, container->content_length);
container->end();
container.reset(); // Release ownership of the container's shared_ptr
valid = json::parse_json(response, [this_update](JsonObject root) -> bool {
{ // Scope to ensure JsonDocument is destroyed before deallocating buffer
valid = json::parse_json(data, read_index, [this_update](JsonObject root) -> bool {
if (!root[ESPHOME_F("name")].is<const char *>() || !root[ESPHOME_F("version")].is<const char *>() ||
!root[ESPHOME_F("builds")].is<JsonArray>()) {
ESP_LOGE(TAG, "Manifest does not contain required fields");
@@ -137,6 +135,7 @@ void HttpRequestUpdate::update_task(void *params) {
return false;
});
}
allocator.deallocate(data, content_length);
if (!valid) {
ESP_LOGE(TAG, "Failed to parse JSON from %s", this_update->source_url_.c_str());
@@ -157,17 +156,12 @@ void HttpRequestUpdate::update_task(void *params) {
}
}
{ // Ensures the current version string falls out of scope and deallocates before the task ends
std::string current_version;
#ifdef ESPHOME_PROJECT_VERSION
current_version = ESPHOME_PROJECT_VERSION;
this_update->update_info_.current_version = ESPHOME_PROJECT_VERSION;
#else
current_version = ESPHOME_VERSION;
this_update->update_info_.current_version = ESPHOME_VERSION;
#endif
this_update->update_info_.current_version = current_version;
}
bool trigger_update_available = false;
if (this_update->update_info_.latest_version.empty() ||

View File

@@ -25,8 +25,13 @@ std::string build_json(const json_build_t &f) {
}
bool parse_json(const std::string &data, const json_parse_t &f) {
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
return parse_json(reinterpret_cast<const uint8_t *>(data.c_str()), data.size(), f);
}
bool parse_json(const uint8_t *data, size_t len, const json_parse_t &f) {
// NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
JsonDocument doc = parse_json(reinterpret_cast<const uint8_t *>(data.c_str()), data.size());
JsonDocument doc = parse_json(data, len);
if (doc.overflowed() || doc.isNull())
return false;
return f(doc.as<JsonObject>());

View File

@@ -50,6 +50,8 @@ std::string build_json(const json_build_t &f);
/// Parse a JSON string and run the provided json parse function if it's valid.
bool parse_json(const std::string &data, const json_parse_t &f);
/// Parse JSON from raw bytes and run the provided json parse function if it's valid.
bool parse_json(const uint8_t *data, size_t len, const json_parse_t &f);
/// Parse a JSON string and return the root JsonDocument (or an unbound object on error)
JsonDocument parse_json(const uint8_t *data, size_t len);