diff --git a/cores/esp32/Stream.cpp b/cores/esp32/Stream.cpp index 1eb8e512a32..5c2060eaa35 100644 --- a/cores/esp32/Stream.cpp +++ b/cores/esp32/Stream.cpp @@ -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() { @@ -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 } @@ -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) { @@ -105,107 +110,32 @@ 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? @@ -213,7 +143,7 @@ long Stream::parseInt(char skipChar) { } 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; @@ -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 @@ -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; @@ -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; +} diff --git a/cores/esp32/Stream.h b/cores/esp32/Stream.h index 5a83747a55f..123694f6fc6 100644 --- a/cores/esp32/Stream.h +++ b/cores/esp32/Stream.h @@ -1,72 +1,83 @@ /* - Stream.h - base class for character-based streams. - Copyright (c) 2010 David A. Mellis. All right reserved. + Stream.h - base class for character-based streams. + Copyright (c) 2010 David A. Mellis. All right reserved. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - parsing functions based on TextFinder library by Michael Margolis - */ + parsing functions based on TextFinder library by Michael Margolis +*/ -#ifndef Stream_h -#define Stream_h +#pragma once #include #include "Print.h" // compatibility macros for testing /* - #define getInt() parseInt() - #define getInt(skipChar) parseInt(skipchar) - #define getFloat() parseFloat() - #define getFloat(skipChar) parseFloat(skipChar) - #define getString( pre_string, post_string, buffer, length) - readBytesBetween( pre_string, terminator, buffer, length) - */ +#define getInt() parseInt() +#define getInt(ignore) parseInt(ignore) +#define getFloat() parseFloat() +#define getFloat(ignore) parseFloat(ignore) +#define getString( pre_string, post_string, buffer, length) +readBytesBetween( pre_string, terminator, buffer, length) +*/ + +// This enumeration provides the lookahead options for parseInt(), parseFloat() +// The rules set out here are used until either the first valid character is found +// or a time out occurs due to lack of input. +enum LookaheadMode { + SKIP_ALL, // All invalid characters are ignored. + SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid. + SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped. +}; + +#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field class Stream : public Print { protected: - unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read - unsigned long _startMillis; // used for timeout measurement - int timedRead(); // private method to read stream with timeout - int timedPeek(); // private method to peek stream with timeout - int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout + unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read + unsigned long _startMillis; // used for timeout measurement + int timedRead(); // private method to read stream with timeout + int timedPeek(); // private method to peek stream with timeout + int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout public: virtual int available() = 0; virtual int read() = 0; virtual int peek() = 0; - Stream() : _startMillis(0) { + Stream() { _timeout = 1000; } - virtual ~Stream() {} // parsing methods void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second - unsigned long getTimeout(void); + unsigned long getTimeout(void) { + return _timeout; + } bool find(const char *target); // reads data from the stream until the target string is found - bool find(uint8_t *target) { - return find((char *)target); + bool find(const uint8_t *target) { + return find((const char *)target); } // returns true if target string is found, false if timed out (see setTimeout) bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found bool find(const uint8_t *target, size_t length) { - return find((char *)target, length); + return find((const char *)target, length); } // returns true if target string is found, false if timed out @@ -76,22 +87,26 @@ class Stream : public Print { bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found bool findUntil(const uint8_t *target, const char *terminator) { - return findUntil((char *)target, terminator); + return findUntil((const char *)target, terminator); } bool findUntil(const char *target, size_t targetLen, const char *terminate, size_t termLen); // as above but search ends if the terminate string is found bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) { - return findUntil((char *)target, targetLen, terminate, termLen); + return findUntil((const char *)target, targetLen, terminate, termLen); } - long parseInt(); // returns the first valid (long) integer value from the current position. - // initial characters that are not digits (or the minus sign) are skipped - // integer is terminated by the first character that is not a digit. + long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR); + // returns the first valid (long) integer value from the current position. + // 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. - float parseFloat(); // float version of parseInt + float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR); + // float version of parseInt - virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer - virtual size_t readBytes(uint8_t *buffer, size_t length) { + size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer + size_t readBytes(uint8_t *buffer, size_t length) { return readBytes((char *)buffer, length); } // terminates if length characters have been read or timeout (see setTimeout) @@ -105,15 +120,19 @@ class Stream : public Print { // returns the number of characters placed in the buffer (0 means no valid data found) // Arduino String functions to be added here - virtual String readString(); + String readString(); String readStringUntil(char terminator); protected: - long parseInt(char skipChar); // as above but the given skipChar is ignored - // as above but the given skipChar is ignored - // this allows format characters (typically commas) in values to be ignored - - float parseFloat(char skipChar); // as above but the given skipChar is ignored + long parseInt(char ignore) { + return parseInt(SKIP_ALL, ignore); + } + float parseFloat(char ignore) { + return parseFloat(SKIP_ALL, ignore); + } + // These overload exists for compatibility with any class that has derived + // Stream and used parseFloat/Int with a custom ignore character. To keep + // the public API simple, these overload remains protected. struct MultiTarget { const char *str; // string you're searching for @@ -126,4 +145,4 @@ class Stream : public Print { int findMulti(struct MultiTarget *targets, int tCount); }; -#endif +#undef NO_IGNORE_CHAR