1 | /* |
2 | Stream.cpp - adds parsing methods to Stream class |
3 | Copyright (c) 2008 David A. Mellis. All right reserved. |
4 | |
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. |
9 | |
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. |
14 | |
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 |
18 | |
19 | Created July 2011 |
20 | parsing functions based on TextFinder library by Michael Margolis |
21 | |
22 | findMulti/findUntil routines written by Jim Leonard/Xuth |
23 | */ |
24 |
|
25 | #include "Arduino.h" |
26 | #include "Stream.h" |
27 |
|
28 | #define PARSE_TIMEOUT 1000 // default number of milli-seconds to wait |
29 | |
30 | // protected method to read stream with timeout |
31 | int Stream::timedRead() |
32 | { |
33 | int c; |
34 | _startMillis = millis(); |
35 | do { |
36 | c = read(); |
37 | if (c >= 0) return c; |
38 | } while(millis() - _startMillis < _timeout); |
39 | return -1; // -1 indicates timeout |
40 | } |
41 |
|
42 | // protected method to peek stream with timeout |
43 | int Stream::timedPeek() |
44 | { |
45 | int c; |
46 | _startMillis = millis(); |
47 | do { |
48 | c = peek(); |
49 | if (c >= 0) return c; |
50 | } while(millis() - _startMillis < _timeout); |
51 | return -1; // -1 indicates timeout |
52 | } |
53 |
|
54 | // returns peek of the next digit in the stream or -1 if timeout |
55 | // discards non-numeric characters |
56 | int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal) |
57 | { |
58 | int c; |
59 | while (1) { |
60 | c = timedPeek(); |
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 | } |
80 | read(); // discard non-numeric |
81 | } |
82 | } |
83 |
|
84 | // Public Methods |
85 | ////////////////////////////////////////////////////////////// |
86 |
|
87 | void Stream::setTimeout(unsigned long timeout) // sets the maximum number of milliseconds to wait |
88 | { |
89 | _timeout = timeout; |
90 | } |
91 |
|
92 | // find returns true if the target string is found |
93 | bool Stream::find(char *target) |
94 | { |
95 | return findUntil(target, strlen(target), NULL, 0); |
96 | } |
97 |
|
98 | // reads data from the stream until the target string of given length is found |
99 | // returns true if target string is found, false if timed out |
100 | bool Stream::find(char *target, size_t length) |
101 | { |
102 | return findUntil(target, length, NULL, 0); |
103 | } |
104 |
|
105 | // as find but search ends if the terminator string is found |
106 | bool Stream::findUntil(char *target, char *terminator) |
107 | { |
108 | return findUntil(target, strlen(target), terminator, strlen(terminator)); |
109 | } |
110 |
|
111 | // reads data from the stream until the target string of the given length is found |
112 | // search terminated if the terminator string is found |
113 | // returns true if target string is found, false if terminated or timed out |
114 | bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen) |
115 | { |
116 | if (terminator == NULL) { |
117 | MultiTarget t[1] = {{target, targetLen, 0}}; |
118 | return findMulti(t, 1) == 0 ? true : false; |
119 | } else { |
120 | MultiTarget t[2] = {{target, targetLen, 0}, {terminator, termLen, 0}}; |
121 | return findMulti(t, 2) == 0 ? true : false; |
122 | } |
123 | } |
124 |
|
125 | // returns the first valid (long) integer value from the current position. |
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) |
131 | { |
132 | bool isNegative = false; |
133 | long value = 0; |
134 | int c; |
135 |
|
136 | c = peekNextDigit(lookahead, false); |
137 | // ignore non numeric leading characters |
138 | if(c < 0) |
139 | return 0; // zero returned if timeout |
140 |
|
141 | do{ |
142 | if(c == ignore) |
143 | ; // ignore this character |
144 | else if(c == '-') |
145 | isNegative = true; |
146 | else if(c >= '0' && c <= '9') // is c a digit? |
147 | value = value * 10 + c - '0'; |
148 | read(); // consume the character we got with peek |
149 | c = timedPeek(); |
150 | } |
151 | while( (c >= '0' && c <= '9') || c == ignore ); |
152 |
|
153 | if(isNegative) |
154 | value = -value; |
155 | return value; |
156 | } |
157 |
|
158 | // as parseInt but returns a floating point value |
159 | float Stream::parseFloat(LookaheadMode lookahead, char ignore) |
160 | { |
161 | bool isNegative = false; |
162 | bool isFraction = false; |
163 | long value = 0; |
164 | int c; |
165 | float fraction = 1.0; |
166 |
|
167 | c = peekNextDigit(lookahead, true); |
168 | // ignore non numeric leading characters |
169 | if(c < 0) |
170 | return 0; // zero returned if timeout |
171 |
|
172 | do{ |
173 | if(c == ignore) |
174 | ; // ignore |
175 | else if(c == '-') |
176 | isNegative = true; |
177 | else if (c == '.') |
178 | isFraction = true; |
179 | else if(c >= '0' && c <= '9') { // is c a digit? |
180 | value = value * 10 + c - '0'; |
181 | if(isFraction) |
182 | fraction *= 0.1; |
183 | } |
184 | read(); // consume the character we got with peek |
185 | c = timedPeek(); |
186 | } |
187 | while( (c >= '0' && c <= '9') || (c == '.' && !isFraction) || c == ignore ); |
188 |
|
189 | if(isNegative) |
190 | value = -value; |
191 | if(isFraction) |
192 | return value * fraction; |
193 | else |
194 | return value; |
195 | } |
196 |
|
197 | // read characters from stream into buffer |
198 | // terminates if length characters have been read, or timeout (see setTimeout) |
199 | // returns the number of characters placed in the buffer |
200 | // the buffer is NOT null terminated. |
201 | // |
202 | size_t Stream::readBytes(char *buffer, size_t length) |
203 | { |
204 | size_t count = 0; |
205 | while (count < length) { |
206 | int c = timedRead(); |
207 | if (c < 0) break; |
208 | *buffer++ = (char)c; |
209 | count++; |
210 | } |
211 | return count; |
212 | } |
213 |
|
214 |
|
215 | // as readBytes with terminator character |
216 | // terminates if length characters have been read, timeout, or if the terminator character detected |
217 | // returns the number of characters placed in the buffer (0 means no valid data found) |
218 |
|
219 | size_t Stream::readBytesUntil(char terminator, char *buffer, size_t length) |
220 | { |
221 | if (length < 1) return 0; |
222 | size_t index = 0; |
223 | while (index < length) { |
224 | int c = timedRead(); |
225 | if (c < 0 || c == terminator) break; |
226 | *buffer++ = (char)c; |
227 | index++; |
228 | } |
229 | return index; // return number of characters, not including null terminator |
230 | } |
231 |
|
232 | String Stream::readString() |
233 | { |
234 | String ret; |
235 | int c = timedRead(); |
236 | while (c >= 0) |
237 | { |
238 | ret += (char)c; |
239 | c = timedRead(); |
240 | } |
241 | return ret; |
242 | } |
243 |
|
244 | String Stream::readStringUntil(char terminator) |
245 | { |
246 | String ret; |
247 | int c = timedRead(); |
248 | while (c >= 0 && c != terminator) |
249 | { |
250 | ret += (char)c; |
251 | c = timedRead(); |
252 | } |
253 | return ret; |
254 | } |
255 |
|
256 | int Stream::findMulti( struct Stream::MultiTarget *targets, int tCount) { |
257 | // any zero length target string automatically matches and would make |
258 | // a mess of the rest of the algorithm. |
259 | for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { |
260 | if (t->len <= 0) |
261 | return t - targets; |
262 | } |
263 |
|
264 | while (1) { |
265 | int c = timedRead(); |
266 | if (c < 0) |
267 | return -1; |
268 |
|
269 | for (struct MultiTarget *t = targets; t < targets+tCount; ++t) { |
270 | // the simple case is if we match, deal with that first. |
271 | if (c == t->str[t->index]) { |
272 | if (++t->index == t->len) |
273 | return t - targets; |
274 | else |
275 | continue; |
276 | } |
277 |
|
278 | // if not we need to walk back and see if we could have matched further |
279 | // down the stream (ie '1112' doesn't match the first position in '11112' |
280 | // but it will match the second position so we can't just reset the current |
281 | // index to 0 when we find a mismatch. |
282 | if (t->index == 0) |
283 | continue; |
284 |
|
285 | int origIndex = t->index; |
286 | do { |
287 | --t->index; |
288 | // first check if current char works against the new current index |
289 | if (c != t->str[t->index]) |
290 | continue; |
291 |
|
292 | // if it's the only char then we're good, nothing more to check |
293 | if (t->index == 0) { |
294 | t->index++; |
295 | break; |
296 | } |
297 |
|
298 | // otherwise we need to check the rest of the found string |
299 | int diff = origIndex - t->index; |
300 | size_t i; |
301 | for (i = 0; i < t->index; ++i) { |
302 | if (t->str[i] != t->str[i + diff]) |
303 | break; |
304 | } |
305 |
|
306 | // if we successfully got through the previous loop then our current |
307 | // index is good. |
308 | if (i == t->index) { |
309 | t->index++; |
310 | break; |
311 | } |
312 |
|
313 | // otherwise we just try the next index |
314 | } while (t->index); |
315 | } |
316 | } |
317 | // unreachable |
318 | return -1; |
319 | } |
320 |
|