Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 648094c

Browse files
me-no-devpre-commit-ci-lite[bot]
andauthoredSep 13, 2024··
fix(api): Update Arduino Stream class (#10328)
* fix(api): Update Arduino Stream class Upstream code contains some fixes * Update Stream.h * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent eda6d21 commit 648094c

File tree

2 files changed

+186
-176
lines changed

2 files changed

+186
-176
lines changed
 

‎cores/esp32/Stream.cpp

Lines changed: 117 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@
1818
1919
Created July 2011
2020
parsing functions based on TextFinder library by Michael Margolis
21+
22+
findMulti/findUntil routines written by Jim Leonard/Xuth
2123
*/
2224

2325
#include "Arduino.h"
2426
#include "Stream.h"
25-
#include "esp32-hal.h"
2627

2728
#define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait
28-
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
2929

3030
// private method to read stream with timeout
3131
int Stream::timedRead() {
@@ -55,18 +55,26 @@ int Stream::timedPeek() {
5555

5656
// returns peek of the next digit in the stream or -1 if timeout
5757
// discards non-numeric characters
58-
int Stream::peekNextDigit() {
58+
int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal) {
5959
int c;
6060
while (1) {
6161
c = timedPeek();
62-
if (c < 0) {
63-
return c; // timeout
64-
}
65-
if (c == '-') {
62+
63+
if (c < 0 || c == '-' || (c >= '0' && c <= '9') || (detectDecimal && c == '.')) {
6664
return c;
6765
}
68-
if (c >= '0' && c <= '9') {
69-
return c;
66+
67+
switch (lookahead) {
68+
case SKIP_NONE: return -1; // Fail code.
69+
case SKIP_WHITESPACE:
70+
switch (c) {
71+
case ' ':
72+
case '\t':
73+
case '\r':
74+
case '\n': break;
75+
default: return -1; // Fail code.
76+
}
77+
case SKIP_ALL: break;
7078
}
7179
read(); // discard non-numeric
7280
}
@@ -79,9 +87,6 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi
7987
{
8088
_timeout = timeout;
8189
}
82-
unsigned long Stream::getTimeout(void) {
83-
return _timeout;
84-
}
8590

8691
// find returns true if the target string is found
8792
bool Stream::find(const char *target) {
@@ -105,115 +110,40 @@ bool Stream::findUntil(const char *target, const char *terminator) {
105110
bool Stream::findUntil(const char *target, size_t targetLen, const char *terminator, size_t termLen) {
106111
if (terminator == NULL) {
107112
MultiTarget t[1] = {{target, targetLen, 0}};
108-
return findMulti(t, 1) == 0 ? true : false;
113+
return findMulti(t, 1) == 0;
109114
} else {
110115
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
111-
return findMulti(t, 2) == 0 ? true : false;
116+
return findMulti(t, 2) == 0;
112117
}
113118
}
114119

115-
int Stream::findMulti(struct Stream::MultiTarget *targets, int tCount) {
116-
// any zero length target string automatically matches and would make
117-
// a mess of the rest of the algorithm.
118-
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
119-
if (t->len <= 0) {
120-
return t - targets;
121-
}
122-
}
123-
124-
while (1) {
125-
int c = timedRead();
126-
if (c < 0) {
127-
return -1;
128-
}
129-
130-
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
131-
// the simple case is if we match, deal with that first.
132-
if (c == t->str[t->index]) {
133-
if (++t->index == t->len) {
134-
return t - targets;
135-
} else {
136-
continue;
137-
}
138-
}
139-
140-
// if not we need to walk back and see if we could have matched further
141-
// down the stream (ie '1112' doesn't match the first position in '11112'
142-
// but it will match the second position so we can't just reset the current
143-
// index to 0 when we find a mismatch.
144-
if (t->index == 0) {
145-
continue;
146-
}
147-
148-
int origIndex = t->index;
149-
do {
150-
--t->index;
151-
// first check if current char works against the new current index
152-
if (c != t->str[t->index]) {
153-
continue;
154-
}
155-
156-
// if it's the only char then we're good, nothing more to check
157-
if (t->index == 0) {
158-
t->index++;
159-
break;
160-
}
161-
162-
// otherwise we need to check the rest of the found string
163-
int diff = origIndex - t->index;
164-
size_t i;
165-
for (i = 0; i < t->index; ++i) {
166-
if (t->str[i] != t->str[i + diff]) {
167-
break;
168-
}
169-
}
170-
171-
// if we successfully got through the previous loop then our current
172-
// index is good.
173-
if (i == t->index) {
174-
t->index++;
175-
break;
176-
}
177-
178-
// otherwise we just try the next index
179-
} while (t->index);
180-
}
181-
}
182-
// unreachable
183-
return -1;
184-
}
185-
186120
// returns the first valid (long) integer value from the current position.
187-
// initial characters that are not digits (or the minus sign) are skipped
188-
// function is terminated by the first character that is not a digit.
189-
long Stream::parseInt() {
190-
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
191-
}
192-
193-
// as above but a given skipChar is ignored
194-
// this allows format characters (typically commas) in values to be ignored
195-
long Stream::parseInt(char skipChar) {
196-
boolean isNegative = false;
121+
// lookahead determines how parseInt looks ahead in the stream.
122+
// See LookaheadMode enumeration at the top of the file.
123+
// Lookahead is terminated by the first character that is not a valid part of an integer.
124+
// Once parsing commences, 'ignore' will be skipped in the stream.
125+
long Stream::parseInt(LookaheadMode lookahead, char ignore) {
126+
bool isNegative = false;
197127
long value = 0;
198128
int c;
199129

200-
c = peekNextDigit();
130+
c = peekNextDigit(lookahead, false);
201131
// ignore non numeric leading characters
202132
if (c < 0) {
203133
return 0; // zero returned if timeout
204134
}
205135

206136
do {
207-
if (c == skipChar) {
208-
} // ignore this character
137+
if ((char)c == ignore)
138+
; // ignore this character
209139
else if (c == '-') {
210140
isNegative = true;
211141
} else if (c >= '0' && c <= '9') { // is c a digit?
212142
value = value * 10 + c - '0';
213143
}
214144
read(); // consume the character we got with peek
215145
c = timedPeek();
216-
} while ((c >= '0' && c <= '9') || c == skipChar);
146+
} while ((c >= '0' && c <= '9') || (char)c == ignore);
217147

218148
if (isNegative) {
219149
value = -value;
@@ -222,50 +152,43 @@ long Stream::parseInt(char skipChar) {
222152
}
223153

224154
// as parseInt but returns a floating point value
225-
float Stream::parseFloat() {
226-
return parseFloat(NO_SKIP_CHAR);
227-
}
228-
229-
// as above but the given skipChar is ignored
230-
// this allows format characters (typically commas) in values to be ignored
231-
float Stream::parseFloat(char skipChar) {
232-
boolean isNegative = false;
233-
boolean isFraction = false;
234-
long value = 0;
155+
float Stream::parseFloat(LookaheadMode lookahead, char ignore) {
156+
bool isNegative = false;
157+
bool isFraction = false;
158+
double value = 0.0;
235159
int c;
236-
float fraction = 1.0;
160+
double fraction = 1.0;
237161

238-
c = peekNextDigit();
162+
c = peekNextDigit(lookahead, true);
239163
// ignore non numeric leading characters
240164
if (c < 0) {
241165
return 0; // zero returned if timeout
242166
}
243167

244168
do {
245-
if (c == skipChar) {
246-
} // ignore
169+
if ((char)c == ignore)
170+
; // ignore
247171
else if (c == '-') {
248172
isNegative = true;
249173
} else if (c == '.') {
250174
isFraction = true;
251175
} else if (c >= '0' && c <= '9') { // is c a digit?
252-
value = value * 10 + c - '0';
253176
if (isFraction) {
254-
fraction *= 0.1f;
177+
fraction *= 0.1;
178+
value = value + fraction * (c - '0');
179+
} else {
180+
value = value * 10 + c - '0';
255181
}
256182
}
257183
read(); // consume the character we got with peek
258184
c = timedPeek();
259-
} while ((c >= '0' && c <= '9') || c == '.' || c == skipChar);
185+
} while ((c >= '0' && c <= '9') || (c == '.' && !isFraction) || (char)c == ignore);
260186

261187
if (isNegative) {
262188
value = -value;
263189
}
264-
if (isFraction) {
265-
return value * fraction;
266-
} else {
267-
return value;
268-
}
190+
191+
return value;
269192
}
270193

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

293216
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) {
294-
if (length < 1) {
295-
return 0;
296-
}
297217
size_t index = 0;
298218
while (index < length) {
299219
int c = timedRead();
300-
if (c < 0 || c == terminator) {
220+
if (c < 0 || (char)c == terminator) {
301221
break;
302222
}
303223
*buffer++ = (char)c;
@@ -319,9 +239,80 @@ String Stream::readString() {
319239
String Stream::readStringUntil(char terminator) {
320240
String ret;
321241
int c = timedRead();
322-
while (c >= 0 && c != terminator) {
242+
while (c >= 0 && (char)c != terminator) {
323243
ret += (char)c;
324244
c = timedRead();
325245
}
326246
return ret;
327247
}
248+
249+
int Stream::findMulti(struct Stream::MultiTarget *targets, int tCount) {
250+
// any zero length target string automatically matches and would make
251+
// a mess of the rest of the algorithm.
252+
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
253+
if (t->len <= 0) {
254+
return t - targets;
255+
}
256+
}
257+
258+
while (1) {
259+
int c = timedRead();
260+
if (c < 0) {
261+
return -1;
262+
}
263+
264+
for (struct MultiTarget *t = targets; t < targets + tCount; ++t) {
265+
// the simple case is if we match, deal with that first.
266+
if ((char)c == t->str[t->index]) {
267+
if (++t->index == t->len) {
268+
return t - targets;
269+
} else {
270+
continue;
271+
}
272+
}
273+
274+
// if not we need to walk back and see if we could have matched further
275+
// down the stream (ie '1112' doesn't match the first position in '11112'
276+
// but it will match the second position so we can't just reset the current
277+
// index to 0 when we find a mismatch.
278+
if (t->index == 0) {
279+
continue;
280+
}
281+
282+
int origIndex = t->index;
283+
do {
284+
--t->index;
285+
// first check if current char works against the new current index
286+
if ((char)c != t->str[t->index]) {
287+
continue;
288+
}
289+
290+
// if it's the only char then we're good, nothing more to check
291+
if (t->index == 0) {
292+
t->index++;
293+
break;
294+
}
295+
296+
// otherwise we need to check the rest of the found string
297+
int diff = origIndex - t->index;
298+
size_t i;
299+
for (i = 0; i < t->index; ++i) {
300+
if (t->str[i] != t->str[i + diff]) {
301+
break;
302+
}
303+
}
304+
305+
// if we successfully got through the previous loop then our current
306+
// index is good.
307+
if (i == t->index) {
308+
t->index++;
309+
break;
310+
}
311+
312+
// otherwise we just try the next index
313+
} while (t->index);
314+
}
315+
}
316+
// unreachable
317+
return -1;
318+
}

‎cores/esp32/Stream.h

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,83 @@
11
/*
2-
Stream.h - base class for character-based streams.
3-
Copyright (c) 2010 David A. Mellis. All right reserved.
2+
Stream.h - base class for character-based streams.
3+
Copyright (c) 2010 David A. Mellis. All right reserved.
44
5-
This library is free software; you can redistribute it and/or
6-
modify it under the terms of the GNU Lesser General Public
7-
License as published by the Free Software Foundation; either
8-
version 2.1 of the License, or (at your option) any later version.
5+
This library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
99
10-
This library is distributed in the hope that it will be useful,
11-
but WITHOUT ANY WARRANTY; without even the implied warranty of
12-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13-
Lesser General Public License for more details.
10+
This library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
1414
15-
You should have received a copy of the GNU Lesser General Public
16-
License along with this library; if not, write to the Free Software
17-
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with this library; if not, write to the Free Software
17+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
1818
19-
parsing functions based on TextFinder library by Michael Margolis
20-
*/
19+
parsing functions based on TextFinder library by Michael Margolis
20+
*/
2121

22-
#ifndef Stream_h
23-
#define Stream_h
22+
#pragma once
2423

2524
#include <inttypes.h>
2625
#include "Print.h"
2726

2827
// compatibility macros for testing
2928
/*
30-
#define getInt() parseInt()
31-
#define getInt(skipChar) parseInt(skipchar)
32-
#define getFloat() parseFloat()
33-
#define getFloat(skipChar) parseFloat(skipChar)
34-
#define getString( pre_string, post_string, buffer, length)
35-
readBytesBetween( pre_string, terminator, buffer, length)
36-
*/
29+
#define getInt() parseInt()
30+
#define getInt(ignore) parseInt(ignore)
31+
#define getFloat() parseFloat()
32+
#define getFloat(ignore) parseFloat(ignore)
33+
#define getString( pre_string, post_string, buffer, length)
34+
readBytesBetween( pre_string, terminator, buffer, length)
35+
*/
36+
37+
// This enumeration provides the lookahead options for parseInt(), parseFloat()
38+
// The rules set out here are used until either the first valid character is found
39+
// or a time out occurs due to lack of input.
40+
enum LookaheadMode {
41+
SKIP_ALL, // All invalid characters are ignored.
42+
SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid.
43+
SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped.
44+
};
45+
46+
#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field
3747

3848
class Stream : public Print {
3949
protected:
40-
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
41-
unsigned long _startMillis; // used for timeout measurement
42-
int timedRead(); // private method to read stream with timeout
43-
int timedPeek(); // private method to peek stream with timeout
44-
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
50+
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
51+
unsigned long _startMillis; // used for timeout measurement
52+
int timedRead(); // private method to read stream with timeout
53+
int timedPeek(); // private method to peek stream with timeout
54+
int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout
4555

4656
public:
4757
virtual int available() = 0;
4858
virtual int read() = 0;
4959
virtual int peek() = 0;
5060

51-
Stream() : _startMillis(0) {
61+
Stream() {
5262
_timeout = 1000;
5363
}
54-
virtual ~Stream() {}
5564

5665
// parsing methods
5766

5867
void setTimeout(unsigned long timeout); // sets maximum milliseconds to wait for stream data, default is 1 second
59-
unsigned long getTimeout(void);
68+
unsigned long getTimeout(void) {
69+
return _timeout;
70+
}
6071

6172
bool find(const char *target); // reads data from the stream until the target string is found
62-
bool find(uint8_t *target) {
63-
return find((char *)target);
73+
bool find(const uint8_t *target) {
74+
return find((const char *)target);
6475
}
6576
// returns true if target string is found, false if timed out (see setTimeout)
6677

6778
bool find(const char *target, size_t length); // reads data from the stream until the target string of given length is found
6879
bool find(const uint8_t *target, size_t length) {
69-
return find((char *)target, length);
80+
return find((const char *)target, length);
7081
}
7182
// returns true if target string is found, false if timed out
7283

@@ -76,22 +87,26 @@ class Stream : public Print {
7687

7788
bool findUntil(const char *target, const char *terminator); // as find but search ends if the terminator string is found
7889
bool findUntil(const uint8_t *target, const char *terminator) {
79-
return findUntil((char *)target, terminator);
90+
return findUntil((const char *)target, terminator);
8091
}
8192

8293
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
8394
bool findUntil(const uint8_t *target, size_t targetLen, const char *terminate, size_t termLen) {
84-
return findUntil((char *)target, targetLen, terminate, termLen);
95+
return findUntil((const char *)target, targetLen, terminate, termLen);
8596
}
8697

87-
long parseInt(); // returns the first valid (long) integer value from the current position.
88-
// initial characters that are not digits (or the minus sign) are skipped
89-
// integer is terminated by the first character that is not a digit.
98+
long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
99+
// returns the first valid (long) integer value from the current position.
100+
// lookahead determines how parseInt looks ahead in the stream.
101+
// See LookaheadMode enumeration at the top of the file.
102+
// Lookahead is terminated by the first character that is not a valid part of an integer.
103+
// Once parsing commences, 'ignore' will be skipped in the stream.
90104

91-
float parseFloat(); // float version of parseInt
105+
float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
106+
// float version of parseInt
92107

93-
virtual size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
94-
virtual size_t readBytes(uint8_t *buffer, size_t length) {
108+
size_t readBytes(char *buffer, size_t length); // read chars from stream into buffer
109+
size_t readBytes(uint8_t *buffer, size_t length) {
95110
return readBytes((char *)buffer, length);
96111
}
97112
// terminates if length characters have been read or timeout (see setTimeout)
@@ -105,15 +120,19 @@ class Stream : public Print {
105120
// returns the number of characters placed in the buffer (0 means no valid data found)
106121

107122
// Arduino String functions to be added here
108-
virtual String readString();
123+
String readString();
109124
String readStringUntil(char terminator);
110125

111126
protected:
112-
long parseInt(char skipChar); // as above but the given skipChar is ignored
113-
// as above but the given skipChar is ignored
114-
// this allows format characters (typically commas) in values to be ignored
115-
116-
float parseFloat(char skipChar); // as above but the given skipChar is ignored
127+
long parseInt(char ignore) {
128+
return parseInt(SKIP_ALL, ignore);
129+
}
130+
float parseFloat(char ignore) {
131+
return parseFloat(SKIP_ALL, ignore);
132+
}
133+
// These overload exists for compatibility with any class that has derived
134+
// Stream and used parseFloat/Int with a custom ignore character. To keep
135+
// the public API simple, these overload remains protected.
117136

118137
struct MultiTarget {
119138
const char *str; // string you're searching for
@@ -126,4 +145,4 @@ class Stream : public Print {
126145
int findMulti(struct MultiTarget *targets, int tCount);
127146
};
128147

129-
#endif
148+
#undef NO_IGNORE_CHAR

0 commit comments

Comments
 (0)
Please sign in to comment.