Skip to content

Commit eb397e2

Browse files
committed
Port Stream parseInt and parseFloat enhancements to SAMD
Also add Stream::find(char) API. Port of: arduino/Arduino#3337
1 parent 4b41eec commit eb397e2

File tree

2 files changed

+64
-45
lines changed

2 files changed

+64
-45
lines changed

cores/arduino/Stream.cpp

+34-32
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
#include "Stream.h"
2727

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

3130
// private method to read stream with timeout
3231
int Stream::timedRead()
@@ -54,14 +53,30 @@ int Stream::timedPeek()
5453

5554
// returns peek of the next digit in the stream or -1 if timeout
5655
// discards non-numeric characters
57-
int Stream::peekNextDigit()
56+
int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal)
5857
{
5958
int c;
6059
while (1) {
6160
c = timedPeek();
62-
if (c < 0) return c; // timeout
63-
if (c == '-') return c;
64-
if (c >= '0' && c <= '9') return c;
61+
62+
if( c < 0 ||
63+
c == '-' ||
64+
(c >= '0' && c <= '9') ||
65+
(detectDecimal && c == '.')) 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:
78+
break;
79+
}
6580
read(); // discard non-numeric
6681
}
6782
}
@@ -107,68 +122,55 @@ bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t
107122
}
108123
}
109124

110-
111125
// returns the first valid (long) integer value from the current position.
112-
// initial characters that are not digits (or the minus sign) are skipped
113-
// function is terminated by the first character that is not a digit.
114-
long Stream::parseInt()
115-
{
116-
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
117-
}
118-
119-
// as above but a given skipChar is ignored
120-
// this allows format characters (typically commas) in values to be ignored
121-
long Stream::parseInt(char skipChar)
126+
// lookahead determines how parseInt looks ahead in the stream.
127+
// See LookaheadMode enumeration at the top of the file.
128+
// Lookahead is terminated by the first character that is not a valid part of an integer.
129+
// Once parsing commences, 'ignore' will be skipped in the stream.
130+
long Stream::parseInt(LookaheadMode lookahead, char ignore)
122131
{
123132
bool isNegative = false;
124133
long value = 0;
125134
int c;
126135

127-
c = peekNextDigit();
136+
c = peekNextDigit(lookahead, false);
128137
// ignore non numeric leading characters
129138
if(c < 0)
130139
return 0; // zero returned if timeout
131140

132141
do{
133-
if(c == skipChar)
134-
; // ignore this charactor
142+
if(c == ignore)
143+
; // ignore this character
135144
else if(c == '-')
136145
isNegative = true;
137146
else if(c >= '0' && c <= '9') // is c a digit?
138147
value = value * 10 + c - '0';
139148
read(); // consume the character we got with peek
140149
c = timedPeek();
141150
}
142-
while( (c >= '0' && c <= '9') || c == skipChar );
151+
while( (c >= '0' && c <= '9') || c == ignore );
143152

144153
if(isNegative)
145154
value = -value;
146155
return value;
147156
}
148157

149-
150158
// as parseInt but returns a floating point value
151-
float Stream::parseFloat()
159+
float Stream::parseFloat(LookaheadMode lookahead, char ignore)
152160
{
153-
return parseFloat(NO_SKIP_CHAR);
154-
}
155-
156-
// as above but the given skipChar is ignored
157-
// this allows format characters (typically commas) in values to be ignored
158-
float Stream::parseFloat(char skipChar){
159161
bool isNegative = false;
160162
bool isFraction = false;
161163
long value = 0;
162-
char c;
164+
int c;
163165
float fraction = 1.0;
164166

165-
c = peekNextDigit();
167+
c = peekNextDigit(lookahead, true);
166168
// ignore non numeric leading characters
167169
if(c < 0)
168170
return 0; // zero returned if timeout
169171

170172
do{
171-
if(c == skipChar)
173+
if(c == ignore)
172174
; // ignore
173175
else if(c == '-')
174176
isNegative = true;
@@ -182,7 +184,7 @@ float Stream::parseFloat(char skipChar){
182184
read(); // consume the character we got with peek
183185
c = timedPeek();
184186
}
185-
while( (c >= '0' && c <= '9') || c == '.' || c == skipChar );
187+
while( (c >= '0' && c <= '9') || (c == '.' && !isFraction) || c == ignore );
186188

187189
if(isNegative)
188190
value = -value;

cores/arduino/Stream.h

+30-13
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,32 @@
2828
// compatability macros for testing
2929
/*
3030
#define getInt() parseInt()
31-
#define getInt(skipChar) parseInt(skipchar)
31+
#define getInt(ignore) parseInt(ignore)
3232
#define getFloat() parseFloat()
33-
#define getFloat(skipChar) parseFloat(skipChar)
33+
#define getFloat(ignore) parseFloat(ignore)
3434
#define getString( pre_string, post_string, buffer, length)
3535
readBytesBetween( pre_string, terminator, buffer, length)
3636
*/
3737

38+
// This enumeration provides the lookahead options for parseInt(), parseFloat()
39+
// The rules set out here are used until either the first valid character is found
40+
// or a time out occurs due to lack of input.
41+
enum LookaheadMode{
42+
SKIP_ALL, // All invalid characters are ignored.
43+
SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid.
44+
SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped.
45+
};
46+
47+
#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field
48+
3849
class Stream : public Print
3950
{
4051
protected:
4152
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
4253
unsigned long _startMillis; // used for timeout measurement
4354
int timedRead(); // private method to read stream with timeout
4455
int timedPeek(); // private method to peek stream with timeout
45-
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
56+
int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout
4657

4758
public:
4859
virtual int available() = 0;
@@ -64,18 +75,23 @@ class Stream : public Print
6475
bool find(uint8_t *target, size_t length) { return find ((char *)target, length); }
6576
// returns true if target string is found, false if timed out
6677

78+
bool find(char target) { return find (&target, 1); }
79+
6780
bool findUntil(char *target, char *terminator); // as find but search ends if the terminator string is found
6881
bool findUntil(uint8_t *target, char *terminator) { return findUntil((char *)target, terminator); }
6982

7083
bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen); // as above but search ends if the terminate string is found
7184
bool findUntil(uint8_t *target, size_t targetLen, char *terminate, size_t termLen) {return findUntil((char *)target, targetLen, terminate, termLen); }
7285

86+
long parseInt(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
87+
// returns the first valid (long) integer value from the current position.
88+
// lookahead determines how parseInt looks ahead in the stream.
89+
// See LookaheadMode enumeration at the top of the file.
90+
// Lookahead is terminated by the first character that is not a valid part of an integer.
91+
// Once parsing commences, 'ignore' will be skipped in the stream.
7392

74-
long parseInt(); // returns the first valid (long) integer value from the current position.
75-
// initial characters that are not digits (or the minus sign) are skipped
76-
// integer is terminated by the first character that is not a digit.
77-
78-
float parseFloat(); // float version of parseInt
93+
float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
94+
// float version of parseInt
7995

8096
size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer
8197
size_t readBytes( uint8_t *buffer, size_t length) { return readBytes((char *)buffer, length); }
@@ -92,11 +108,11 @@ class Stream : public Print
92108
String readStringUntil(char terminator);
93109

94110
protected:
95-
long parseInt(char skipChar); // as above but the given skipChar is ignored
96-
// as above but the given skipChar is ignored
97-
// this allows format characters (typically commas) in values to be ignored
98-
99-
float parseFloat(char skipChar); // as above but the given skipChar is ignored
111+
long parseInt(char ignore) { return parseInt(SKIP_ALL, ignore); }
112+
float parseFloat(char ignore) { return parseFloat(SKIP_ALL, ignore); }
113+
// These overload exists for compatibility with any class that has derived
114+
// Stream and used parseFloat/Int with a custom ignore character. To keep
115+
// the public API simple, these overload remains protected.
100116

101117
struct MultiTarget {
102118
const char *str; // string you're searching for
@@ -109,4 +125,5 @@ class Stream : public Print
109125
int findMulti(struct MultiTarget *targets, int tCount);
110126
};
111127

128+
#undef NO_IGNORE_CHAR
112129
#endif

0 commit comments

Comments
 (0)