#ifndef NUMBERTOSTRING_H #define NUMBERTOSTRING_H #include #include "JSONDebug.h" #ifdef JSON_LESS_MEMORY #include "JSONMemory.h" #endif #include "JSONSharedString.h" #include #ifdef JSON_STRICT #include #endif template struct getLenSize{ char tmp[GETLENSIZE == 16]; // compile time assertion enum {GETLEN = 41}; }; template<> struct getLenSize<1>{ enum {GETLEN = 5}; }; template <> struct getLenSize<2>{ enum {GETLEN = 7}; }; template <> struct getLenSize<4>{ enum {GETLEN = 12}; }; template <> struct getLenSize<8>{ enum {GETLEN = 22}; }; static inline bool _floatsAreEqual(const json_number & one, const json_number & two) json_pure; static inline bool _floatsAreEqual(const json_number & one, const json_number & two) json_nothrow { return (one > two) ? (one - two) < JSON_FLOAT_THRESHHOLD : (one - two) > -JSON_FLOAT_THRESHHOLD; } #ifdef JSON_LESS_MEMORY #define num_str_result s.ptr #endif class NumberToString { public: template static json_string _itoa(T val) json_nothrow { #ifdef JSON_LESS_MEMORY json_auto s(getLenSize::GETLEN); #else json_char num_str_result[getLenSize::GETLEN]; #endif num_str_result[getLenSize::GETLEN - 1] = JSON_TEXT('\0'); //null terminator json_char * runner = &num_str_result[getLenSize::GETLEN - 2]; bool negative; START_MEM_SCOPE long value = (long)val; //first thing, check if it's negative, if so, make it positive if (value < 0){ value = -value; negative = true; } else { negative = false; } //create the string do { *runner-- = (json_char)(value % 10) + JSON_TEXT('0'); } while(value /= 10); END_MEM_SCOPE //if it's negative, add the negation if (negative){ *runner = JSON_TEXT('-'); return json_string(runner); } return json_string(runner + 1); } #ifndef JSON_LIBRARY template static json_string _uitoa(T val) json_nothrow { #ifdef JSON_LESS_MEMORY json_auto s(getLenSize::GETLEN); #else json_char num_str_result[getLenSize::GETLEN]; #endif num_str_result[getLenSize::GETLEN - 1] = JSON_TEXT('\0'); //null terminator json_char * runner = &num_str_result[getLenSize::GETLEN - 2]; //create the string START_MEM_SCOPE unsigned long value = (unsigned long)val; do { *runner-- = (json_char)(value % 10) + JSON_TEXT('0'); } while(value /= 10); END_MEM_SCOPE return json_string(runner + 1); } #endif #ifdef JSON_ISO_STRICT #define EXTRA_LONG #define FLOAT_STRING "%f" #define LFLOAT_STRING L"%f" #else #define EXTRA_LONG long #define FLOAT_STRING "%Lf" #define LFLOAT_STRING L"%Lf" #endif static json_string _ftoa(json_number value) json_nothrow { #ifndef JSON_LIBRARY //ScopeCoverage(_ftoa_coverage, 6); if (json_unlikely(value >= 0.0 && _floatsAreEqual(value, (json_number)((unsigned EXTRA_LONG long)value)))){ return _uitoa((unsigned EXTRA_LONG long)value); } else #else //ScopeCoverage(_ftoa_coverage, 5); #endif if (json_unlikely(_floatsAreEqual(value, (json_number)((long EXTRA_LONG)value)))){ return _itoa((long EXTRA_LONG)value); } #ifdef JSON_LESS_MEMORY json_auto s(64); #else json_char num_str_result[64]; #endif #ifdef JSON_UNICODE std::swprintf(num_str_result, 63, LFLOAT_STRING, (EXTRA_LONG double)value); #else //Thanks to Salvor Hardin for this Visual C++ fix #ifdef _MSC_VER _snprintf_s(num_str_result, 63, 63, FLOAT_STRING, (EXTRA_LONG double)value); //yes, 63 appears twice using _snprintf_s() #else snprintf(num_str_result, 63, FLOAT_STRING, (EXTRA_LONG double)value); #endif #endif //strip the trailing zeros for(json_char * pos = &num_str_result[0]; *pos; ++pos){ if (json_unlikely(*pos == '.')){ //only care about after the decimal for(json_char * runner = pos + 1; *runner; ++runner){ if (json_likely(*runner != JSON_TEXT('0'))){ pos = runner + 1; //have to go to the end 1.0001 } } *pos = JSON_TEXT('\0'); break; } } return json_string(num_str_result); } #if defined(JSON_SAFE) || defined(JSON_DEBUG) static bool isNumeric(const json_string & str) json_nothrow { const json_char * p = str.c_str(); bool decimal = false; bool scientific = false; #ifdef JSON_STRICT bool leadingzero = false; #endif //first letter is weird switch(*p){ case JSON_TEXT('\0'): return false; #ifndef JSON_STRICT case JSON_TEXT('.'): decimal = true; break; case JSON_TEXT('+'): #endif case JSON_TEXT('-'): switch (*(p + 1)){ case JSON_TEXT('.'): case JSON_TEXT('e'): case JSON_TEXT('E'): case JSON_TEXT('\0'): return false; case JSON_TEXT('0'): #ifdef JSON_STRICT switch(*(p + 2)){ case JSON_TEXT('.'): case JSON_TEXT('e'): case JSON_TEXT('E'): leadingzero = false; break; case JSON_TEXT('\0'): return true; default: leadingzero = true; break; } #endif ++p; break; default: break; } break; case JSON_TEXT('1'): case JSON_TEXT('2'): case JSON_TEXT('3'): case JSON_TEXT('4'): case JSON_TEXT('5'): case JSON_TEXT('6'): case JSON_TEXT('7'): case JSON_TEXT('8'): case JSON_TEXT('9'): break; case JSON_TEXT('0'): ++p; #ifdef JSON_STRICT leadingzero = true; #endif switch(*p){ case JSON_TEXT('.'): decimal = true; break; case JSON_TEXT('e'): case JSON_TEXT('E'): #ifdef JSON_STRICT leadingzero = false; //not leading, just a zero #endif scientific = true; ++p; switch(*p){ case JSON_TEXT('\0'): return false; case JSON_TEXT('-'): case JSON_TEXT('+'): #ifndef JSON_STRICT case JSON_TEXT('0'): //cant have a leading zero in scrict #endif case JSON_TEXT('1'): case JSON_TEXT('2'): case JSON_TEXT('3'): case JSON_TEXT('4'): case JSON_TEXT('5'): case JSON_TEXT('6'): case JSON_TEXT('7'): case JSON_TEXT('8'): case JSON_TEXT('9'): break; default: return false; } break; #ifndef JSON_STRICT case JSON_TEXT('x'): return (str.find_first_not_of(JSON_TEXT("0123456789ABCDEFabcdef"), 2) == json_string::npos); case JSON_TEXT('1'): case JSON_TEXT('2'): case JSON_TEXT('3'): case JSON_TEXT('4'): case JSON_TEXT('5'): case JSON_TEXT('6'): case JSON_TEXT('7'): return (str.find_first_not_of(JSON_TEXT("01234567"), 1) == json_string::npos); #endif case JSON_TEXT('\0'): //just 0 return true; default: return false; } break; default: return false; } ++p; //next digits while (*p){ switch(*p){ case JSON_TEXT('.'): if (json_unlikely(decimal)){ return false; //multiple decimals } if (json_unlikely(scientific)){ return false; } decimal = true; break; case JSON_TEXT('e'): case JSON_TEXT('E'): if (json_unlikely(scientific)){ return false; } scientific = true; ++p; switch(*p){ case JSON_TEXT('\0'): return false; case JSON_TEXT('-'): case JSON_TEXT('+'): if (!isdigit(*(p + 1))){ return false; } #ifdef JSON_STRICT if (*(p + 1) == JSON_TEXT('0')){ //no leading zeros on scientific notations return false; } #endif break; #ifndef JSON_STRICT case JSON_TEXT('0'): //cant have a leading zero in scrict #endif case JSON_TEXT('1'): case JSON_TEXT('2'): case JSON_TEXT('3'): case JSON_TEXT('4'): case JSON_TEXT('5'): case JSON_TEXT('6'): case JSON_TEXT('7'): case JSON_TEXT('8'): case JSON_TEXT('9'): break; default: return false; } break; case JSON_TEXT('0'): case JSON_TEXT('1'): case JSON_TEXT('2'): case JSON_TEXT('3'): case JSON_TEXT('4'): case JSON_TEXT('5'): case JSON_TEXT('6'): case JSON_TEXT('7'): case JSON_TEXT('8'): case JSON_TEXT('9'): break; default: return false; } ++p; } #ifdef JSON_STRICT if (leadingzero && !decimal){ return false; } #endif return true; } #endif #ifdef JSON_STRICT //much faster because no octal or hex support static json_number _atof (const json_char * num){ json_number sign = (json_number)1.0; //sign if (*num==JSON_TEXT('-')){ sign = -1.0; ++num; } else { } //skip leading zero if one #if defined(JSON_SAFE) || defined(JSON_DEBUG) bool _leadingzeros = *num == JSON_TEXT('0'); bool _leadingdigits = false; #endif if (*num == JSON_TEXT('0')){ ++num; } #ifdef JSON_STRICT else if (json_likely(*num < JSON_TEXT('1') || *num > JSON_TEXT('9'))){ return std::numeric_limits::signaling_NaN(); } #endif JSON_ASSERT_SAFE(*num != JSON_TEXT('0'), JSON_TEXT("multiple leading zeros"), return std::numeric_limits::signaling_NaN(); ); // Number json_number n = (json_number)0.0; if (json_likely(*num >= JSON_TEXT('1') && *num <= JSON_TEXT('9'))){ #if defined(JSON_SAFE) || defined(JSON_DEBUG) _leadingdigits = true; #endif do { n = (n * 10.0) + (*num++ - JSON_TEXT('0')); } while (*num >= JSON_TEXT('0') && *num <= JSON_TEXT('9')); } else { JSON_ASSERT_SAFE( (*num) == JSON_TEXT('.') || //.xxx (*num) == JSON_TEXT('e') || //0Exxx (*num) == JSON_TEXT('E') || //0exxx (*num) == JSON_TEXT('\0') //end of the number, just zero , JSON_TEXT("first digit not a number, e, period, or terminator"), return std::numeric_limits::signaling_NaN(); ); } // Fractional part json_number scale = (json_number)0.0; if (*num == JSON_TEXT('.')) { JSON_ASSERT_SAFE(_leadingzeros || _leadingdigits, JSON_TEXT("period without leading anything"), return std::numeric_limits::signaling_NaN(); ); ++num; for(; *num >= JSON_TEXT('0') && *num <= JSON_TEXT('9');){ n = (n * 10.0) + (*num++ - JSON_TEXT('0')); --scale; }; } else { JSON_ASSERT_SAFE(!_leadingzeros || n == 0, JSON_TEXT("leading zero on an int"), return std::numeric_limits::signaling_NaN(); ); JSON_ASSERT_SAFE( (*num) == JSON_TEXT('e') || //0Exxx (*num) == JSON_TEXT('E') || //0exxx (*num) == JSON_TEXT('\0') //end of the number, just zero , JSON_TEXT("next char not an e or terminator"), return std::numeric_limits::signaling_NaN(); ); } // Exponent int subscale = 0, signsubscale = 1; if (json_unlikely(*num == JSON_TEXT('e') || *num == JSON_TEXT('E'))){ ++num; switch(*num){ case JSON_TEXT('+'): ++num; break; case JSON_TEXT('-'): signsubscale = -1; ++num; JSON_ASSERT_SAFE(*num != JSON_TEXT('0'), JSON_TEXT("negative cant be followed by leading zero even after E"), return std::numeric_limits::signaling_NaN(); ); break; default: break; } JSON_ASSERT_SAFE(*num != JSON_TEXT('\0'), JSON_TEXT("no exponent for scientific notation"), return std::numeric_limits::signaling_NaN(); ); while (*num >= JSON_TEXT('0') && *num <= JSON_TEXT('9')){ subscale=(subscale * 10) + (*num++ - JSON_TEXT('0')); } } JSON_ASSERT_SAFE(*num == JSON_TEXT('\0'), JSON_TEXT("done with number, not at terminator"), return std::numeric_limits::signaling_NaN(); ); return sign * n * pow((json_number)10.0, scale + subscale * signsubscale); // number = +/- number.fraction * 10^+/- exponent } #endif }; #endif