Skip to content

Commit ae2fa13

Browse files
committed
Fix findUntil and general timeouts in Stream library
1 parent 7211eb0 commit ae2fa13

File tree

4 files changed

+302
-100
lines changed

4 files changed

+302
-100
lines changed

hardware/arduino/avr/cores/arduino/Stream.cpp

Lines changed: 130 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
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"
@@ -27,43 +29,73 @@
2729
#define NO_SKIP_CHAR 1 // a magic char not found in a valid ASCII numeric field
2830

2931
// private method to read stream with timeout
30-
int Stream::timedRead()
32+
33+
void Stream::startClock()
3134
{
32-
int c;
3335
_startMillis = millis();
36+
}
37+
38+
bool Stream::checkClock()
39+
{
40+
if (millis() - _startMillis < _timeout)
41+
return true;
42+
return false;
43+
}
44+
45+
int Stream::_timedRead()
46+
{
3447
do {
35-
c = read();
48+
int c = read();
3649
if (c >= 0) return c;
37-
} while(millis() - _startMillis < _timeout);
38-
return -1; // -1 indicates timeout
50+
} while(checkClock());
51+
return - 1; // -1 is timeout
52+
}
53+
54+
int Stream::timedRead()
55+
{
56+
startClock();
57+
return _timedRead();
3958
}
4059

4160
// private method to peek stream with timeout
42-
int Stream::timedPeek()
61+
// callers must call startClock() first
62+
int Stream::_timedPeek()
4363
{
4464
int c;
45-
_startMillis = millis();
4665
do {
4766
c = peek();
4867
if (c >= 0) return c;
49-
} while(millis() - _startMillis < _timeout);
68+
} while(checkClock());
5069
return -1; // -1 indicates timeout
5170
}
5271

53-
// returns peek of the next digit in the stream or -1 if timeout
54-
// discards non-numeric characters
55-
int Stream::peekNextDigit()
72+
int Stream::timedPeek()
73+
{
74+
startClock();
75+
return _timedPeek();
76+
}
77+
78+
// private method that assumes startClock() has already been called
79+
int Stream::_peekNextDigit()
5680
{
5781
int c;
5882
while (1) {
59-
c = timedPeek();
83+
c = _timedPeek();
6084
if (c < 0) return c; // timeout
6185
if (c == '-') return c;
6286
if (c >= '0' && c <= '9') return c;
6387
read(); // discard non-numeric
6488
}
6589
}
6690

91+
// returns peek of the next digit in the stream or -1 if timeout
92+
// discards non-numeric characters
93+
int Stream::peekNextDigit()
94+
{
95+
startClock();
96+
return _peekNextDigit();
97+
}
98+
6799
// Public Methods
68100
//////////////////////////////////////////////////////////////
69101

@@ -75,7 +107,7 @@ void Stream::setTimeout(unsigned long timeout) // sets the maximum number of mi
75107
// find returns true if the target string is found
76108
bool Stream::find(char *target)
77109
{
78-
return findUntil(target, (char*)"");
110+
return findUntil(target, strlen(target), NULL, 0);
79111
}
80112

81113
// reads data from the stream until the target string of given length is found
@@ -96,32 +128,13 @@ bool Stream::findUntil(char *target, char *terminator)
96128
// returns true if target string is found, false if terminated or timed out
97129
bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen)
98130
{
99-
size_t index = 0; // maximum target string length is 64k bytes!
100-
size_t termIndex = 0;
101-
int c;
102-
103-
if( *target == 0)
104-
return true; // return true if target is a null string
105-
while( (c = timedRead()) > 0){
106-
107-
if(c != target[index])
108-
index = 0; // reset index if any char does not match
109-
110-
if( c == target[index]){
111-
//////Serial.print("found "); Serial.write(c); Serial.print("index now"); Serial.println(index+1);
112-
if(++index >= targetLen){ // return true if all chars in the target match
113-
return true;
114-
}
115-
}
116-
117-
if(termLen > 0 && c == terminator[termIndex]){
118-
if(++termIndex >= termLen)
119-
return false; // return false if terminate string found before target string
120-
}
121-
else
122-
termIndex = 0;
131+
if (terminator == NULL) {
132+
MultiTarget t[1] = {{target, targetLen, 0}};
133+
return findMulti(t, 1) == 0 ? true : false;
134+
} else {
135+
MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}};
136+
return findMulti(t, 2) == 0 ? true : false;
123137
}
124-
return false;
125138
}
126139

127140

@@ -137,11 +150,12 @@ long Stream::parseInt()
137150
// this allows format characters (typically commas) in values to be ignored
138151
long Stream::parseInt(char skipChar)
139152
{
140-
boolean isNegative = false;
153+
bool isNegative = false;
141154
long value = 0;
142155
int c;
143156

144-
c = peekNextDigit();
157+
startClock();
158+
c = _peekNextDigit();
145159
// ignore non numeric leading characters
146160
if(c < 0)
147161
return 0; // zero returned if timeout
@@ -173,13 +187,14 @@ float Stream::parseFloat()
173187
// as above but the given skipChar is ignored
174188
// this allows format characters (typically commas) in values to be ignored
175189
float Stream::parseFloat(char skipChar){
176-
boolean isNegative = false;
177-
boolean isFraction = false;
190+
bool isNegative = false;
191+
bool isFraction = false;
178192
long value = 0;
179-
int c;
193+
char c;
180194
float fraction = 1.0;
181195

182-
c = peekNextDigit();
196+
startClock();
197+
c = _peekNextDigit();
183198
// ignore non numeric leading characters
184199
if(c < 0)
185200
return 0; // zero returned if timeout
@@ -216,9 +231,10 @@ float Stream::parseFloat(char skipChar){
216231
//
217232
size_t Stream::readBytes(char *buffer, size_t length)
218233
{
234+
startClock();
219235
size_t count = 0;
220236
while (count < length) {
221-
int c = timedRead();
237+
int c = _timedRead();
222238
if (c < 0) break;
223239
*buffer++ = (char)c;
224240
count++;
@@ -233,10 +249,11 @@ size_t Stream::readBytes(char *buffer, size_t length)
233249

234250
size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length)
235251
{
252+
startClock();
236253
if (length < 1) return 0;
237254
size_t index = 0;
238255
while (index < length) {
239-
int c = timedRead();
256+
int c = _timedRead();
240257
if (c < 0 || c == terminator) break;
241258
*buffer++ = (char)c;
242259
index++;
@@ -246,25 +263,88 @@ size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length)
246263

247264
String Stream::readString()
248265
{
266+
startClock();
249267
String ret;
250-
int c = timedRead();
268+
int c = _timedRead();
251269
while (c >= 0)
252270
{
253271
ret += (char)c;
254-
c = timedRead();
272+
c = _timedRead();
255273
}
256274
return ret;
257275
}
258276

259277
String Stream::readStringUntil(char terminator)
260278
{
279+
startClock();
261280
String ret;
262-
int c = timedRead();
281+
int c = _timedRead();
263282
while (c >= 0 && c != terminator)
264283
{
265284
ret += (char)c;
266-
c = timedRead();
285+
c = _timedRead();
267286
}
268287
return ret;
269288
}
270289

290+
int Stream::findMulti( struct Stream::MultiTarget *targets, int tCount) {
291+
// any zero length target string automatically matches and would make
292+
// a mess of the rest of the algorithm.
293+
for (struct MultiTarget *t = targets; t < targets+tCount; ++t)
294+
if (t->len <= 0)
295+
return t - targets;
296+
297+
startClock();
298+
while(1) {
299+
int c = _timedRead();
300+
if (c < 0)
301+
return -1;
302+
303+
for (struct MultiTarget *t = targets; t < targets+tCount; ++t) {
304+
// the simple case is if we match, deal with that first.
305+
if (c == t->str[t->index])
306+
if (++t->index == t->len)
307+
return t - targets;
308+
else
309+
continue;
310+
311+
// if not we need to walk back and see if we could have matched further
312+
// down the stream (ie '1112' doesn't match the first position in '11112'
313+
// but it will match the second position so we can't just reset the current
314+
// index to 0 when we find a mismatch.
315+
if (t->index == 0)
316+
continue;
317+
318+
int origIndex = t->index;
319+
do {
320+
--t->index;
321+
// first check if current char works against the new current index
322+
if (c != t->str[t->index])
323+
continue;
324+
325+
// if it's the only char then we're good, nothing more to check
326+
if (t->index == 0) {
327+
t->index++;
328+
break;
329+
}
330+
331+
// otherwise we need to check the rest of the found string
332+
int diff = origIndex - t->index;
333+
int i;
334+
for (i = 0; i < t->index; ++i)
335+
if (t->str[i] != t->str[i + diff])
336+
break;
337+
// if we successfully got through the previous loop then our current
338+
// index is good.
339+
if (i == t->index) {
340+
t->index++;
341+
break;
342+
}
343+
// otherwise we just try the next index
344+
} while (t->index);
345+
}
346+
}
347+
// unreachable
348+
return -1;
349+
}
350+

hardware/arduino/avr/cores/arduino/Stream.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,14 @@ class Stream : public Print
4141
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
4242
unsigned long _startMillis; // used for timeout measurement
4343
int timedRead(); // private method to read stream with timeout
44+
int _timedRead(); // timedRead() but assumes clock already started
4445
int timedPeek(); // private method to peek stream with timeout
46+
int _timedPeek(); // timedPeak() but assumes clock already started
4547
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
48+
int _peekNextDigit(); // peekNextDigit() but assumes clock already started
49+
void startClock(); // helper, used for all of the timed read routines
50+
bool checkClock(); // helper, consistent check if we've timed out
51+
4652

4753
public:
4854
virtual int available() = 0;
@@ -88,6 +94,7 @@ class Stream : public Print
8894
// returns the number of characters placed in the buffer (0 means no valid data found)
8995

9096
// Arduino String functions to be added here
97+
9198
String readString();
9299
String readStringUntil(char terminator);
93100

@@ -97,6 +104,20 @@ class Stream : public Print
97104
// this allows format characters (typically commas) in values to be ignored
98105

99106
float parseFloat(char skipChar); // as above but the given skipChar is ignored
107+
108+
public:
109+
struct MultiTarget {
110+
const char *str; // string you're searching for
111+
size_t len; // length of string you're searching for
112+
size_t index; // index used by the search routine.
113+
};
114+
115+
// This allows you to search for an arbitrary number of strings.
116+
// Returns index of the target that is found first or -1 if timeout occurs.
117+
int findMulti(struct MultiTarget *targets, int tCount);
118+
119+
100120
};
101121

122+
102123
#endif

0 commit comments

Comments
 (0)