Skip to content

Commit 5497f7e

Browse files
Copilotsofthack007
andauthored
Stream /json/effects via respondModeData(namesOnly=true), eliminating JSON buffer lock
Agent-Logs-Url: https://github.com/wled/WLED/sessions/88471964-aa31-4148-9f41-fa2fbdc8da34 Co-authored-by: softhack007 <91616163+softhack007@users.noreply.github.com>
1 parent 3af2ae5 commit 5497f7e

1 file changed

Lines changed: 19 additions & 14 deletions

File tree

wled00/json.cpp

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,23 +1213,30 @@ static size_t writeJSONStringElement(uint8_t* dest, size_t maxLen, const char* s
12131213
return 1 + n;
12141214
}
12151215

1216-
// Generate a streamed JSON response for the mode data
1217-
// This uses sendChunked to send the reply in blocks based on how much fit in the outbound
1218-
// packet buffer, minimizing the required state (ie. just the next index to send). This
1219-
// allows us to send an arbitrarily large response without using any significant amount of
1220-
// memory (so no worries about buffer limits).
1221-
void respondModeData(AsyncWebServerRequest* request) {
1216+
// Generate a streamed JSON response for the mode data (namesOnly=false) or mode names
1217+
// (namesOnly=true). This uses sendChunked to send the reply in blocks based on how much
1218+
// fit in the outbound packet buffer, minimizing the required state (ie. just the next index
1219+
// to send). This allows us to send an arbitrarily large response without using any
1220+
// significant amount of memory (so no worries about buffer limits).
1221+
void respondModeData(AsyncWebServerRequest* request, bool namesOnly = false) {
12221222
size_t fx_index = 0;
12231223
request->sendChunked(FPSTR(CONTENT_TYPE_JSON),
1224-
[fx_index](uint8_t* data, size_t len, size_t) mutable {
1224+
[fx_index, namesOnly](uint8_t* data, size_t len, size_t) mutable {
12251225
size_t bytes_written = 0;
12261226
char lineBuffer[256];
12271227
while (fx_index < strip.getModeCount()) {
12281228
strncpy_P(lineBuffer, strip.getModeData(fx_index), sizeof(lineBuffer)-1); // Copy to stack buffer for strchr
12291229
if (lineBuffer[0] != 0) {
12301230
lineBuffer[sizeof(lineBuffer)-1] = '\0'; // terminate string (only needed if strncpy filled the buffer)
1231-
const char* dataPtr = strchr(lineBuffer,'@'); // Find '@', if there is one
1232-
size_t mode_bytes = writeJSONStringElement(data, len, dataPtr ? dataPtr + 1 : "");
1231+
char* dataPtr = strchr(lineBuffer,'@'); // Find '@', if there is one
1232+
const char* value;
1233+
if (namesOnly) {
1234+
if (dataPtr) *dataPtr = '\0'; // truncate at '@' to get name only
1235+
value = lineBuffer;
1236+
} else {
1237+
value = dataPtr ? dataPtr + 1 : ""; // everything after '@' is the fx data
1238+
}
1239+
size_t mode_bytes = writeJSONStringElement(data, len, value);
12331240
if (mode_bytes == 0) break; // didn't fit; break loop and try again next packet
12341241
if (fx_index == 0) *data = '[';
12351242
data += mode_bytes;
@@ -1276,7 +1283,7 @@ class LockedJsonResponse: public AsyncJsonResponse {
12761283
void serveJson(AsyncWebServerRequest* request)
12771284
{
12781285
enum class json_target {
1279-
all, state, info, state_info, nodes, effects, palettes, networks, config, pins
1286+
all, state, info, state_info, nodes, palettes, networks, config, pins
12801287
};
12811288
json_target subJson = json_target::all;
12821289

@@ -1285,7 +1292,7 @@ void serveJson(AsyncWebServerRequest* request)
12851292
else if (url.indexOf("info") > 0) subJson = json_target::info;
12861293
else if (url.indexOf("si") > 0) subJson = json_target::state_info;
12871294
else if (url.indexOf(F("nodes")) > 0) subJson = json_target::nodes;
1288-
else if (url.indexOf(F("eff")) > 0) subJson = json_target::effects;
1295+
else if (url.indexOf(F("eff")) > 0) { respondModeData(request, true); return; }
12891296
else if (url.indexOf(F("palx")) > 0) subJson = json_target::palettes;
12901297
else if (url.indexOf(F("fxda")) > 0) { respondModeData(request); return; }
12911298
else if (url.indexOf(F("net")) > 0) subJson = json_target::networks;
@@ -1312,7 +1319,7 @@ void serveJson(AsyncWebServerRequest* request)
13121319
}
13131320
// releaseJSONBufferLock() will be called when "response" is destroyed (from AsyncWebServer)
13141321
// make sure you delete "response" if no "request->send(response);" is made
1315-
LockedJsonResponse *response = new LockedJsonResponse(pDoc, subJson==json_target::effects); // will clear and convert JsonDocument into JsonArray if necessary
1322+
LockedJsonResponse *response = new LockedJsonResponse(pDoc, false); // will clear JsonDocument
13161323

13171324
JsonVariant lDoc = response->getRoot();
13181325

@@ -1326,8 +1333,6 @@ void serveJson(AsyncWebServerRequest* request)
13261333
serializeNodes(lDoc); break;
13271334
case json_target::palettes:
13281335
serializePalettes(lDoc, request->hasParam(F("page")) ? request->getParam(F("page"))->value().toInt() : 0); break;
1329-
case json_target::effects:
1330-
serializeModeNames(lDoc); break;
13311336
case json_target::networks:
13321337
serializeNetworks(lDoc); break;
13331338
case json_target::config:

0 commit comments

Comments
 (0)