Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(api): Update Arduino Stream class #10328

Merged
merged 3 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 117 additions & 126 deletions cores/esp32/Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@

Created July 2011
parsing functions based on TextFinder library by Michael Margolis

findMulti/findUntil routines written by Jim Leonard/Xuth
*/

#include "Arduino.h"
#include "Stream.h"
#include "esp32-hal.h"

#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

// private method to read stream with timeout
int Stream::timedRead() {
Expand Down Expand Up @@ -55,18 +55,26 @@ int Stream::timedPeek() {

// returns peek of the next digit in the stream or -1 if timeout
// discards non-numeric characters
int Stream::peekNextDigit() {
int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal) {
int c;
while (1) {
c = timedPeek();
if (c < 0) {
return c; // timeout
}
if (c == '-') {

if (c < 0 || c == '-' || (c >= '0' && c <= '9') || (detectDecimal && c == '.')) {
return c;
}
if (c >= '0' && c <= '9') {
return c;

switch (lookahead) {
case SKIP_NONE: return -1; // Fail code.
case SKIP_WHITESPACE:
switch (c) {
case ' ':
case '\t':
case '\r':
case '\n': break;
default: return -1; // Fail code.
}
case SKIP_ALL: break;
}
read(); // discard non-numeric
}
Expand All @@ -79,9 +87,6 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi
{
_timeout = timeout;
}
unsigned long Stream::getTimeout(void) {
return _timeout;
}

// find returns true if the target string is found
bool Stream::find(const char *target) {
Expand All @@ -105,115 +110,40 @@ bool Stream::findUntil(const char *target, const char *terminator) {
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) {
if (terminator == NULL) {
MultiTarget t[1] = {{target, targetLen, 0}};
return findMulti(t, 1) == 0 ? true : false;
return findMulti(t, 1) == 0;
} else {
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
return findMulti(t, 2) == 0 ? true : false;
return findMulti(t, 2) == 0;
}
}

int Stream::findMulti(struct Stream::MultiTarget *targets, int tCount) {
// any zero length target string automatically matches and would make
// a mess of the rest of the algorithm.
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
if (t->len <= 0) {
return t - targets;
}
}

while (1) {
int c = timedRead();
if (c < 0) {
return -1;
}

for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
// the simple case is if we match, deal with that first.
if (c == t->str[t->index]) {
if (++t->index == t->len) {
return t - targets;
} else {
continue;
}
}

// if not we need to walk back and see if we could have matched further
// down the stream (ie '1112' doesn't match the first position in '11112'
// but it will match the second position so we can't just reset the current
// index to 0 when we find a mismatch.
if (t->index == 0) {
continue;
}

int origIndex = t->index;
do {
--t->index;
// first check if current char works against the new current index
if (c != t->str[t->index]) {
continue;
}

// if it's the only char then we're good, nothing more to check
if (t->index == 0) {
t->index++;
break;
}

// otherwise we need to check the rest of the found string
int diff = origIndex - t->index;
size_t i;
for (i = 0; i < t->index; ++i) {
if (t->str[i] != t->str[i + diff]) {
break;
}
}

// if we successfully got through the previous loop then our current
// index is good.
if (i == t->index) {
t->index++;
break;
}

// otherwise we just try the next index
} while (t->index);
}
}
// unreachable
return -1;
}

// returns the first valid (long) integer value from the current position.
// initial characters that are not digits (or the minus sign) are skipped
// function is terminated by the first character that is not a digit.
long Stream::parseInt() {
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
}

// as above but a given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
long Stream::parseInt(char skipChar) {
boolean isNegative = false;
// lookahead determines how parseInt looks ahead in the stream.
// See LookaheadMode enumeration at the top of the file.
// Lookahead is terminated by the first character that is not a valid part of an integer.
// Once parsing commences, 'ignore' will be skipped in the stream.
long Stream::parseInt(LookaheadMode lookahead, char ignore) {
bool isNegative = false;
long value = 0;
int c;

c = peekNextDigit();
c = peekNextDigit(lookahead, false);
// ignore non numeric leading characters
if (c < 0) {
return 0; // zero returned if timeout
}

do {
if (c == skipChar) {
} // ignore this character
if ((char)c == ignore)
; // ignore this character
else if (c == '-') {
isNegative = true;
} else if (c >= '0' && c <= '9') { // is c a digit?
value = value * 10 + c - '0';
}
read(); // consume the character we got with peek
c = timedPeek();
} while ((c >= '0' && c <= '9') || c == skipChar);
} while ((c >= '0' && c <= '9') || (char)c == ignore);

if (isNegative) {
value = -value;
Expand All @@ -222,50 +152,43 @@ long Stream::parseInt(char skipChar) {
}

// as parseInt but returns a floating point value
float Stream::parseFloat() {
return parseFloat(NO_SKIP_CHAR);
}

// as above but the given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
float Stream::parseFloat(char skipChar) {
boolean isNegative = false;
boolean isFraction = false;
long value = 0;
float Stream::parseFloat(LookaheadMode lookahead, char ignore) {
bool isNegative = false;
bool isFraction = false;
double value = 0.0;
int c;
float fraction = 1.0;
double fraction = 1.0;

c = peekNextDigit();
c = peekNextDigit(lookahead, true);
// ignore non numeric leading characters
if (c < 0) {
return 0; // zero returned if timeout
}

do {
if (c == skipChar) {
} // ignore
if ((char)c == ignore)
; // ignore
else if (c == '-') {
isNegative = true;
} else if (c == '.') {
isFraction = true;
} else if (c >= '0' && c <= '9') { // is c a digit?
value = value * 10 + c - '0';
if (isFraction) {
fraction *= 0.1f;
fraction *= 0.1;
value = value + fraction * (c - '0');
} else {
value = value * 10 + c - '0';
}
}
read(); // consume the character we got with peek
c = timedPeek();
} while ((c >= '0' && c <= '9') || c == '.' || c == skipChar);
} while ((c >= '0' && c <= '9') || (c == '.' && !isFraction) || (char)c == ignore);

if (isNegative) {
value = -value;
}
if (isFraction) {
return value * fraction;
} else {
return value;
}

return value;
}

// read characters from stream into buffer
Expand All @@ -291,13 +214,10 @@ size_t Stream::readBytes(char *buffer, size_t length) {
// returns the number of characters placed in the buffer (0 means no valid data found)

size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) {
if (length < 1) {
return 0;
}
size_t index = 0;
while (index < length) {
int c = timedRead();
if (c < 0 || c == terminator) {
if (c < 0 || (char)c == terminator) {
break;
}
*buffer++ = (char)c;
Expand All @@ -319,9 +239,80 @@ String Stream::readString() {
String Stream::readStringUntil(char terminator) {
String ret;
int c = timedRead();
while (c >= 0 && c != terminator) {
while (c >= 0 && (char)c != terminator) {
ret += (char)c;
c = timedRead();
}
return ret;
}

int Stream::findMulti(struct Stream::MultiTarget *targets, int tCount) {
// any zero length target string automatically matches and would make
// a mess of the rest of the algorithm.
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
if (t->len <= 0) {
return t - targets;
}
}

while (1) {
int c = timedRead();
if (c < 0) {
return -1;
}

for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
// the simple case is if we match, deal with that first.
if ((char)c == t->str[t->index]) {
if (++t->index == t->len) {
return t - targets;
} else {
continue;
}
}

// if not we need to walk back and see if we could have matched further
// down the stream (ie '1112' doesn't match the first position in '11112'
// but it will match the second position so we can't just reset the current
// index to 0 when we find a mismatch.
if (t->index == 0) {
continue;
}

int origIndex = t->index;
do {
--t->index;
// first check if current char works against the new current index
if ((char)c != t->str[t->index]) {
continue;
}

// if it's the only char then we're good, nothing more to check
if (t->index == 0) {
t->index++;
break;
}

// otherwise we need to check the rest of the found string
int diff = origIndex - t->index;
size_t i;
for (i = 0; i < t->index; ++i) {
if (t->str[i] != t->str[i + diff]) {
break;
}
}

// if we successfully got through the previous loop then our current
// index is good.
if (i == t->index) {
t->index++;
break;
}

// otherwise we just try the next index
} while (t->index);
}
}
// unreachable
return -1;
}
Loading
Loading