Skip to content

Port Stream parseInt and parseFloat enhancements #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 34 additions & 32 deletions cores/arduino/Stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#include "Stream.h"

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

// private method to read stream with timeout
int Stream::timedRead()
Expand Down Expand Up @@ -54,14 +53,30 @@ int Stream::timedPeek()

// returns peek of the next digit in the stream or -1 if timeout
// discards non-numeric characters
int Stream::peekNextDigit()
int Stream::peekNextDigit(LookaheadMode lookahead, bool detectDecimal)
{
int c;
while (1) {
c = timedPeek();
if (c < 0) return c; // timeout
if (c == '-') return c;
if (c >= '0' && c <= '9') return c;

if( c < 0 ||
c == '-' ||
(c >= '0' && c <= '9') ||
(detectDecimal && c == '.')) return c;

switch( lookahead ){
case SKIP_NONE: return -1; // Fail code.
case SKIP_WHITESPACE:
switch( c ){
case ' ':
case '\t':
case '\r':
case '\n': break;
default: return -1; // Fail code.
}
case SKIP_ALL:
break;
}
read(); // discard non-numeric
}
}
Expand Down Expand Up @@ -107,68 +122,55 @@ bool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t
}
}


// returns the first valid (long) integer value from the current position.
// initial characters that are not digits (or the minus sign) are skipped
// function is terminated by the first character that is not a digit.
long Stream::parseInt()
{
return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
}

// as above but a given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
long Stream::parseInt(char skipChar)
// lookahead determines how parseInt looks ahead in the stream.
// See LookaheadMode enumeration at the top of the file.
// Lookahead is terminated by the first character that is not a valid part of an integer.
// Once parsing commences, 'ignore' will be skipped in the stream.
long Stream::parseInt(LookaheadMode lookahead, char ignore)
{
bool isNegative = false;
long value = 0;
int c;

c = peekNextDigit();
c = peekNextDigit(lookahead, false);
// ignore non numeric leading characters
if(c < 0)
return 0; // zero returned if timeout

do{
if(c == skipChar)
; // ignore this charactor
if(c == ignore)
; // ignore this character
else if(c == '-')
isNegative = true;
else if(c >= '0' && c <= '9') // is c a digit?
value = value * 10 + c - '0';
read(); // consume the character we got with peek
c = timedPeek();
}
while( (c >= '0' && c <= '9') || c == skipChar );
while( (c >= '0' && c <= '9') || c == ignore );

if(isNegative)
value = -value;
return value;
}


// as parseInt but returns a floating point value
float Stream::parseFloat()
float Stream::parseFloat(LookaheadMode lookahead, char ignore)
{
return parseFloat(NO_SKIP_CHAR);
}

// as above but the given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
float Stream::parseFloat(char skipChar){
bool isNegative = false;
bool isFraction = false;
long value = 0;
char c;
int c;
float fraction = 1.0;

c = peekNextDigit();
c = peekNextDigit(lookahead, true);
// ignore non numeric leading characters
if(c < 0)
return 0; // zero returned if timeout

do{
if(c == skipChar)
if(c == ignore)
; // ignore
else if(c == '-')
isNegative = true;
Expand All @@ -182,7 +184,7 @@ float Stream::parseFloat(char skipChar){
read(); // consume the character we got with peek
c = timedPeek();
}
while( (c >= '0' && c <= '9') || c == '.' || c == skipChar );
while( (c >= '0' && c <= '9') || (c == '.' && !isFraction) || c == ignore );

if(isNegative)
value = -value;
Expand Down
43 changes: 30 additions & 13 deletions cores/arduino/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,32 @@
// compatability macros for testing
/*
#define getInt() parseInt()
#define getInt(skipChar) parseInt(skipchar)
#define getInt(ignore) parseInt(ignore)
#define getFloat() parseFloat()
#define getFloat(skipChar) parseFloat(skipChar)
#define getFloat(ignore) parseFloat(ignore)
#define getString( pre_string, post_string, buffer, length)
readBytesBetween( pre_string, terminator, buffer, length)
*/

// This enumeration provides the lookahead options for parseInt(), parseFloat()
// The rules set out here are used until either the first valid character is found
// or a time out occurs due to lack of input.
enum LookaheadMode{
SKIP_ALL, // All invalid characters are ignored.
SKIP_NONE, // Nothing is skipped, and the stream is not touched unless the first waiting character is valid.
SKIP_WHITESPACE // Only tabs, spaces, line feeds & carriage returns are skipped.
};

#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field

class Stream : public Print
{
protected:
unsigned long _timeout; // number of milliseconds to wait for the next char before aborting timed read
unsigned long _startMillis; // used for timeout measurement
int timedRead(); // private method to read stream with timeout
int timedPeek(); // private method to peek stream with timeout
int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout
int peekNextDigit(LookaheadMode lookahead, bool detectDecimal); // returns the next numeric digit in the stream or -1 if timeout

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

bool find(char target) { return find (&target, 1); }

bool findUntil(char *target, char *terminator); // as find but search ends if the terminator string is found
bool findUntil(uint8_t *target, char *terminator) { return findUntil((char *)target, terminator); }

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

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

long parseInt(); // returns the first valid (long) integer value from the current position.
// initial characters that are not digits (or the minus sign) are skipped
// integer is terminated by the first character that is not a digit.

float parseFloat(); // float version of parseInt
float parseFloat(LookaheadMode lookahead = SKIP_ALL, char ignore = NO_IGNORE_CHAR);
// float version of parseInt

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

protected:
long parseInt(char skipChar); // as above but the given skipChar is ignored
// as above but the given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored

float parseFloat(char skipChar); // as above but the given skipChar is ignored
long parseInt(char ignore) { return parseInt(SKIP_ALL, ignore); }
float parseFloat(char ignore) { return parseFloat(SKIP_ALL, ignore); }
// These overload exists for compatibility with any class that has derived
// Stream and used parseFloat/Int with a custom ignore character. To keep
// the public API simple, these overload remains protected.

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

#undef NO_IGNORE_CHAR
#endif