Skip to content

Commit 0f84e93

Browse files
committed
Added Stream class
1 parent ea00d59 commit 0f84e93

File tree

3 files changed

+448
-0
lines changed

3 files changed

+448
-0
lines changed

Diff for: api/ArduinoAPI.h

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#ifdef __cplusplus
2929
#include "Print.h"
3030
#include "String.h"
31+
#include "Stream.h"
3132
#endif
3233

3334
#endif

Diff for: api/Stream.cpp

+319
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
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+
// private 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+
// private 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+
}

0 commit comments

Comments
 (0)