From 57aaf276dc7e3fc9dbe5e9ba31ca3a274847d370 Mon Sep 17 00:00:00 2001 From: Ahmed El-Sharnoby Date: Sat, 18 Nov 2023 05:27:40 +0000 Subject: [PATCH] replace Strings with with StreamStrings to avoid String Reallocations. --- cores/esp8266/Stream.cpp | 54 +++++++++++++++++++ cores/esp8266/Stream.h | 4 ++ libraries/ESP8266WebServer/src/Parsing-impl.h | 27 ++++------ 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/cores/esp8266/Stream.cpp b/cores/esp8266/Stream.cpp index b9b5b95f65..e852a1d91b 100644 --- a/cores/esp8266/Stream.cpp +++ b/cores/esp8266/Stream.cpp @@ -22,6 +22,7 @@ #include #include +#include #define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait #define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field @@ -288,6 +289,59 @@ String Stream::readStringUntil(const char* terminator, uint32_t untilTotalNumber return ret; } +String Stream::readStreamString(const ssize_t maxLen ,const oneShotMs::timeType timeoutMs) { + String ret; + S2Stream stream(ret); + sendGeneric(&stream, maxLen, -1, timeoutMs); + return ret; +} + +String Stream::readStreamStringUntil(const int readUntilChar, const oneShotMs::timeType timeoutMs) { + String ret; + S2Stream stream(ret); + sendGeneric(&stream, -1, readUntilChar, timeoutMs); + return ret; +} + +String Stream::readStreamStringUntil (const char* terminatorString, uint32_t untilTotalNumberOfOccurrences, const oneShotMs::timeType timeoutMs) { + String ret; + S2Stream stream(ret); + uint32_t occurrences = 0; + size_t termLen = strlen(terminatorString); + size_t termIndex = 0; + // Serial.printf("S %s\n",terminatorString); + while(1){ + size_t read = sendGeneric(&stream, -1, terminatorString[termIndex], timeoutMs); + // Serial.printf("r %d, l %d, ti %d\n", read, termLen, termIndex); + if(getLastSendReport() != Report::Success) { + Serial.printf("Error %d\n", (int) getLastSendReport()); + break; + } + if(termIndex == termLen - 1){ + // Serial.printf("m %d\n", occurrences); + if(++occurrences == untilTotalNumberOfOccurrences){ + break; + }else{ + ret += terminatorString; + termIndex = 0; + continue; + } + } + int c = timedPeek(); + // Serial.printf("c %c %02X\n", c, c); + if( c >= 0 && c != terminatorString[++termIndex]){ + ret += String(terminatorString).substring(0, termIndex); + termIndex = 0; + continue; + }; + if(c < 0 || (read == 0 && termIndex == 0)) break; + } + + return ret; +} + + + // read what can be read, immediate exit on unavailable data // prototype similar to Arduino's `int Client::read(buf, len)` int Stream::read (uint8_t* buffer, size_t maxLen) diff --git a/cores/esp8266/Stream.h b/cores/esp8266/Stream.h index 0706bec001..a752112a02 100644 --- a/cores/esp8266/Stream.h +++ b/cores/esp8266/Stream.h @@ -216,6 +216,10 @@ class Stream: public Print { size_t sendSize (Stream& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } size_t sendSize (Stream&& to, const ssize_t maxLen, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires) { return sendSize(&to, maxLen, timeoutMs); } + String readStreamString (const ssize_t maxLen = -1 ,const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires); + String readStreamStringUntil (const int readUntilChar, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires); + String readStreamStringUntil (const char* terminatorString, uint32_t untilTotalNumberOfOccurrences = 1, const oneShotMs::timeType timeoutMs = oneShotMs::neverExpires); + // remaining size (-1 by default = unknown) virtual ssize_t streamRemaining () { return -1; } diff --git a/libraries/ESP8266WebServer/src/Parsing-impl.h b/libraries/ESP8266WebServer/src/Parsing-impl.h index 0d432aa69f..0e1e4a4604 100644 --- a/libraries/ESP8266WebServer/src/Parsing-impl.h +++ b/libraries/ESP8266WebServer/src/Parsing-impl.h @@ -44,9 +44,8 @@ static bool readBytesWithTimeout(typename ServerType::ClientType& client, size_t template typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemplate::_parseRequest(ClientType& client) { // Read the first line of HTTP request - String req = client.readStringUntil('\r'); + String req = client.readStreamStringUntil("\r\n"); DBGWS("request: %s\n", req.c_str()); - client.readStringUntil('\n'); //reset header value for (int i = 0; i < _headerKeysCount; ++i) { _currentHeaders[i].value.clear(); @@ -122,8 +121,7 @@ typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemp uint32_t contentLength = 0; //parse headers while(1){ - req = client.readStringUntil('\r'); - client.readStringUntil('\n'); + req = client.readStreamStringUntil("\r\n"); if (req.isEmpty()) break; //no more headers int headerDiv = req.indexOf(':'); if (headerDiv == -1){ @@ -198,8 +196,7 @@ typename ESP8266WebServerTemplate::ClientFuture ESP8266WebServerTemp String headerValue; //parse headers while(1){ - req = client.readStringUntil('\r'); - client.readStringUntil('\n'); + req = client.readStreamStringUntil("\r\n"); if (req.isEmpty()) break;//no moar headers int headerDiv = req.indexOf(':'); if (headerDiv == -1){ @@ -351,11 +348,10 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const String line; int retry = 0; do { - line = client.readStringUntil('\r'); + line = client.readStreamStringUntil("\r\n"); ++retry; } while (line.length() == 0 && retry < 3); - client.readStringUntil('\n'); //start reading the form if (line == ("--"+boundary)){ std::unique_ptr postArgs(new RequestArgument[WEBSERVER_MAX_POST_ARGS]); @@ -367,8 +363,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const String argFilename; bool argIsFile = false; - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); + line = client.readStreamStringUntil("\r\n"); if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))){ int nameStart = line.indexOf('='); if (nameStart != -1){ @@ -388,19 +383,16 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const DBGWS("PostArg Name: %s\n", argName.c_str()); using namespace mime; argType = FPSTR(mimeTable[txt].mimeType); - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); + line = client.readStreamStringUntil("\r\n"); if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))){ argType = line.substring(line.indexOf(':')+2); //skip next line - client.readStringUntil('\r'); - client.readStringUntil('\n'); + client.readStringUntil("\r\n"); } DBGWS("PostArg Type: %s\n", argType.c_str()); if (!argIsFile){ while(1){ - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); + line = client.readStreamStringUntil("\r\n"); if (line.startsWith("--"+boundary)) break; if (argValue.length() > 0) argValue += '\n'; argValue += line; @@ -474,8 +466,7 @@ bool ESP8266WebServerTemplate::_parseForm(ClientType& client, const _currentUpload->type.c_str(), (int)_currentUpload->totalSize); if (!client.connected()) return _parseFormUploadAborted(); - line = client.readStringUntil('\r'); - client.readStringUntil('\n'); + line = client.readStreamStringUntil("\r\n"); if (line == "--") { // extra two dashes mean we reached the end of all form fields DBGWS("Done Parsing POST\n"); break;