Implemented a parser and updated tiny_obj_loader.cc to use it unless a define is set.
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -87,13 +88,165 @@ static inline int parseInt(const char *&token) {
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// Tries to parse a floating point number located at s.
|
||||
//
|
||||
// s_end should be a location in the string where reading should absolutely
|
||||
// stop. For example at the end of the string, to prevent buffer overflows.
|
||||
//
|
||||
// Parses the following EBNF grammar:
|
||||
// sign = "+" | "-" ;
|
||||
// END = ? anything not in digit ?
|
||||
// digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
|
||||
// integer = [sign] , digit , {digit} ;
|
||||
// decimal = integer , ["." , integer] ;
|
||||
// float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ;
|
||||
//
|
||||
// Valid strings are for example:
|
||||
// -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2
|
||||
//
|
||||
// If the parsing is a success, result is set to the parsed value and true
|
||||
// is returned.
|
||||
//
|
||||
// The function is greedy and will parse until any of the following happens:
|
||||
// - a non-conforming character is encountered.
|
||||
// - s_end is reached.
|
||||
//
|
||||
// The following situations triggers a failure:
|
||||
// - s >= s_end.
|
||||
// - parse failure.
|
||||
//
|
||||
static bool tryParseDouble(const char *s, const char *s_end, double *result)
|
||||
{
|
||||
if (s >= s_end)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double mantissa = 0.0;
|
||||
// This exponent is base 2 rather than 10.
|
||||
// However the exponent we parse is supposed to be one of ten,
|
||||
// thus we must take care to convert the exponent/and or the
|
||||
// mantissa to a * 2^E, where a is the mantissa and E is the
|
||||
// exponent.
|
||||
// To get the final double we will use ldexp, it requires the
|
||||
// exponent to be in base 2.
|
||||
int exponent = 0;
|
||||
|
||||
// NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED
|
||||
// TO JUMP OVER DEFINITIONS.
|
||||
char sign = '+';
|
||||
char exp_sign = '+';
|
||||
char const *curr = s;
|
||||
|
||||
// How many characters were read in a loop.
|
||||
int read = 0;
|
||||
// Tells whether a loop terminated due to reaching s_end.
|
||||
bool end_not_reached = false;
|
||||
|
||||
/*
|
||||
BEGIN PARSING.
|
||||
*/
|
||||
|
||||
// Find out what sign we've got.
|
||||
if (*curr == '+' || *curr == '-')
|
||||
{
|
||||
sign = *curr;
|
||||
curr++;
|
||||
}
|
||||
else if (isdigit(*curr)) { /* Pass through. */ }
|
||||
else
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Read the integer part.
|
||||
while ((end_not_reached = (curr != s_end)) && isdigit(*curr))
|
||||
{
|
||||
mantissa *= 10;
|
||||
mantissa += static_cast<int>(*curr - 0x30);
|
||||
curr++; read++;
|
||||
}
|
||||
|
||||
// We must make sure we actually got something.
|
||||
if (read == 0)
|
||||
goto fail;
|
||||
// We allow numbers of form "#", "###" etc.
|
||||
if (!end_not_reached)
|
||||
goto assemble;
|
||||
|
||||
// Read the decimal part.
|
||||
if (*curr == '.')
|
||||
{
|
||||
curr++;
|
||||
read = 1;
|
||||
while ((end_not_reached = (curr != s_end)) && isdigit(*curr))
|
||||
{
|
||||
// NOTE: Don't use powf here, it will absolutely murder precision.
|
||||
mantissa += static_cast<int>(*curr - 0x30) * pow(10, -read);
|
||||
read++; curr++;
|
||||
}
|
||||
}
|
||||
else if (*curr == 'e' || *curr == 'E') {}
|
||||
else
|
||||
{
|
||||
goto assemble;
|
||||
}
|
||||
|
||||
if (!end_not_reached)
|
||||
goto assemble;
|
||||
|
||||
// Read the exponent part.
|
||||
if (*curr == 'e' || *curr == 'E')
|
||||
{
|
||||
curr++;
|
||||
// Figure out if a sign is present and if it is.
|
||||
if ((end_not_reached = (curr != s_end)) && (*curr == '+' || *curr == '-'))
|
||||
{
|
||||
exp_sign = *curr;
|
||||
curr++;
|
||||
}
|
||||
else if (isdigit(*curr)) { /* Pass through. */ }
|
||||
else
|
||||
{
|
||||
// Empty E is not allowed.
|
||||
goto fail;
|
||||
}
|
||||
|
||||
read = 0;
|
||||
while ((end_not_reached = (curr != s_end)) && isdigit(*curr))
|
||||
{
|
||||
exponent *= 10;
|
||||
exponent += static_cast<int>(*curr - 0x30);
|
||||
curr++; read++;
|
||||
}
|
||||
exponent *= (exp_sign == '+'? 1 : -1);
|
||||
if (read == 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assemble:
|
||||
*result = (sign == '+'? 1 : -1) * ldexp(mantissa * pow(5, exponent), exponent);
|
||||
return true;
|
||||
fail:
|
||||
return false;
|
||||
}
|
||||
static inline float parseFloat(const char *&token) {
|
||||
token += strspn(token, " \t");
|
||||
#ifdef TINY_OBJ_LOADER_OLD_FLOAT_PARSER
|
||||
float f = (float)atof(token);
|
||||
token += strcspn(token, " \t\r");
|
||||
#else
|
||||
const char *end = token + strcspn(token, " \t\r");
|
||||
double val = 0.0;
|
||||
tryParseDouble(token, end, &val);
|
||||
float f = static_cast<float>(val);
|
||||
token = end;
|
||||
#endif
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
static inline void parseFloat2(float &x, float &y, const char *&token) {
|
||||
x = parseFloat(token);
|
||||
y = parseFloat(token);
|
||||
|
||||
Reference in New Issue
Block a user