Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-21 09:30:08

0001 #pragma once
0002 
0003 // Copyright (C) 2002-2007 Nikolaus Gebhardt
0004 // This file is part of the "Irrlicht Engine" and the "irrXML" project.
0005 // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
0006 
0007 // ------------------------------------------------------------------------------------
0008 // Original description: (Schrompf)
0009 // Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a
0010 // float inside a large string. Before parsing, it does a strlen on the given point.
0011 // Changes:
0012 //  22nd October 08 (Aramis_acg): Added temporary cast to double, added strtoul10_64
0013 //     to ensure long numbers are handled correctly
0014 // ------------------------------------------------------------------------------------
0015 
0016 #pragma once
0017 #ifndef FAST_A_TO_F_H_INCLUDED
0018 #define FAST_A_TO_F_H_INCLUDED
0019 
0020 #ifdef __GNUC__
0021 #   pragma GCC system_header
0022 #endif
0023 
0024 #include <cmath>
0025 #include <limits>
0026 #include <stdint.h>
0027 #include <assimp/defs.h>
0028 
0029 #include "StringComparison.h"
0030 #include <assimp/DefaultLogger.hpp>
0031 #include <assimp/Exceptional.h>
0032 #include <assimp/StringUtils.h>
0033 
0034 #ifdef _MSC_VER
0035 #  include <stdint.h>
0036 #else
0037 #  include <assimp/Compiler/pstdint.h>
0038 #endif
0039 
0040 namespace Assimp {
0041 
0042 static constexpr size_t NumItems = 16;
0043 
0044 constexpr double fast_atof_table[NumItems] =  {  // we write [16] here instead of [] to work around a swig bug
0045     0.0,
0046     0.1,
0047     0.01,
0048     0.001,
0049     0.0001,
0050     0.00001,
0051     0.000001,
0052     0.0000001,
0053     0.00000001,
0054     0.000000001,
0055     0.0000000001,
0056     0.00000000001,
0057     0.000000000001,
0058     0.0000000000001,
0059     0.00000000000001,
0060     0.000000000000001
0061 };
0062 
0063 // ------------------------------------------------------------------------------------
0064 // Convert a string in decimal format to a number
0065 // ------------------------------------------------------------------------------------
0066 inline unsigned int strtoul10( const char* in, const char** out=0) {
0067     unsigned int value = 0;
0068 
0069     for ( ;; ) {
0070         if ( *in < '0' || *in > '9' ) {
0071             break;
0072         }
0073 
0074         value = ( value * 10 ) + ( *in - '0' );
0075         ++in;
0076     }
0077     if ( out ) {
0078         *out = in;
0079     }
0080     return value;
0081 }
0082 
0083 // ------------------------------------------------------------------------------------
0084 // Convert a string in octal format to a number
0085 // ------------------------------------------------------------------------------------
0086 inline unsigned int strtoul8( const char* in, const char** out=0) {
0087     unsigned int value( 0 );
0088     for ( ;; ) {
0089         if ( *in < '0' || *in > '7' ) {
0090             break;
0091         }
0092 
0093         value = ( value << 3 ) + ( *in - '0' );
0094         ++in;
0095     }
0096     if ( out ) {
0097         *out = in;
0098     }
0099     return value;
0100 }
0101 
0102 // ------------------------------------------------------------------------------------
0103 // Convert a string in hex format to a number
0104 // ------------------------------------------------------------------------------------
0105 inline unsigned int strtoul16( const char* in, const char** out=0) {
0106     unsigned int value( 0 );
0107     for ( ;; ) {
0108         if ( *in >= '0' && *in <= '9' ) {
0109             value = ( value << 4u ) + ( *in - '0' );
0110         } else if (*in >= 'A' && *in <= 'F') {
0111             value = ( value << 4u ) + ( *in - 'A' ) + 10;
0112         } else if (*in >= 'a' && *in <= 'f') {
0113             value = ( value << 4u ) + ( *in - 'a' ) + 10;
0114         } else {
0115             break;
0116         }
0117         ++in;
0118     }
0119     if ( out ) {
0120         *out = in;
0121     }
0122     return value;
0123 }
0124 
0125 // ------------------------------------------------------------------------------------
0126 // Convert just one hex digit
0127 // Return value is UINT_MAX if the input character is not a hex digit.
0128 // ------------------------------------------------------------------------------------
0129 inline unsigned int HexDigitToDecimal(char in) {
0130     unsigned int out( UINT_MAX );
0131     if ( in >= '0' && in <= '9' ) {
0132         out = in - '0';
0133     } else if ( in >= 'a' && in <= 'f' ) {
0134         out = 10u + in - 'a';
0135     } else if ( in >= 'A' && in <= 'F' ) {
0136         out = 10u + in - 'A';
0137     }
0138 
0139     // return value is UINT_MAX if the input is not a hex digit
0140     return out;
0141 }
0142 
0143 // ------------------------------------------------------------------------------------
0144 // Convert a hex-encoded octet (2 characters, i.e. df or 1a).
0145 // ------------------------------------------------------------------------------------
0146 inline uint8_t HexOctetToDecimal(const char* in) {
0147     return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]);
0148 }
0149 
0150 // ------------------------------------------------------------------------------------
0151 // signed variant of strtoul10
0152 // ------------------------------------------------------------------------------------
0153 inline int strtol10( const char* in, const char** out = 0) {
0154     bool inv = (*in=='-');
0155     if ( inv || *in == '+' ) {
0156         ++in;
0157     }
0158 
0159     int value = strtoul10(in,out);
0160     if (inv) {
0161         if (value < INT_MAX && value > INT_MIN) {
0162             value = -value;
0163         } else {
0164             ASSIMP_LOG_WARN( "Converting the string \"", in, "\" into an inverted value resulted in overflow." );
0165         }
0166     }
0167     return value;
0168 }
0169 
0170 // ------------------------------------------------------------------------------------
0171 // Parse a C++-like integer literal - hex and oct prefixes.
0172 // 0xNNNN - hex
0173 // 0NNN   - oct
0174 // NNN    - dec
0175 // ------------------------------------------------------------------------------------
0176 inline unsigned int strtoul_cppstyle( const char* in, const char** out=0) {
0177     if ('0' == in[0]) {
0178         return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out);
0179     }
0180     return strtoul10(in, out);
0181 }
0182 
0183 // ------------------------------------------------------------------------------------
0184 // Special version of the function, providing higher accuracy and safety
0185 // It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
0186 // ------------------------------------------------------------------------------------
0187 template<typename ExceptionType = DeadlyImportError>
0188 inline uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) {
0189     unsigned int cur = 0;
0190     uint64_t value = 0;
0191 
0192     if ( *in < '0' || *in > '9' ) {
0193         // The string is known to be bad, so don't risk printing the whole thing.
0194         throw ExceptionType("The string \"", ai_str_toprintable(in, (int)strlen(in)), "\" cannot be converted into a value." );
0195     }
0196 
0197     for ( ;; ) {
0198         if ( *in < '0' || *in > '9' ) {
0199             break;
0200         }
0201 
0202         const uint64_t new_value = ( value * (uint64_t) 10 ) + ( (uint64_t) ( *in - '0' ) );
0203 
0204         // numeric overflow, we rely on you
0205         if ( new_value < value ) {
0206             ASSIMP_LOG_WARN( "Converting the string \"", in, "\" into a value resulted in overflow." );
0207             return 0;
0208         }
0209 
0210         value = new_value;
0211 
0212         ++in;
0213         ++cur;
0214 
0215         if (max_inout && *max_inout == cur) {
0216             if (out) { /* skip to end */
0217                 while ( *in >= '0' && *in <= '9' ) {
0218                     ++in;
0219                 }
0220                 *out = in;
0221             }
0222 
0223             return value;
0224         }
0225     }
0226     if ( out ) {
0227         *out = in;
0228     }
0229 
0230     if ( max_inout ) {
0231         *max_inout = cur;
0232     }
0233 
0234     return value;
0235 }
0236 
0237 // ------------------------------------------------------------------------------------
0238 // signed variant of strtoul10_64
0239 // ------------------------------------------------------------------------------------
0240 template<typename ExceptionType = DeadlyImportError>
0241 inline int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) {
0242     bool inv = (*in == '-');
0243     if ( inv || *in == '+' ) {
0244         ++in;
0245     }
0246 
0247     int64_t value = strtoul10_64<ExceptionType>(in, out, max_inout);
0248     if (inv) {
0249         value = -value;
0250     }
0251     return value;
0252 }
0253 
0254 // Number of relevant decimals for floating-point parsing.
0255 #define AI_FAST_ATOF_RELAVANT_DECIMALS 15
0256 
0257 // ------------------------------------------------------------------------------------
0258 //! Provides a fast function for converting a string into a float,
0259 //! about 6 times faster than atof in win32.
0260 // If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
0261 // ------------------------------------------------------------------------------------
0262 template<typename Real, typename ExceptionType = DeadlyImportError>
0263 inline const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) {
0264     Real f = 0;
0265 
0266     bool inv = (*c == '-');
0267     if (inv || *c == '+') {
0268         ++c;
0269     }
0270 
0271     if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) {
0272         out = std::numeric_limits<Real>::quiet_NaN();
0273         c += 3;
0274         return c;
0275     }
0276 
0277     if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) {
0278         out = std::numeric_limits<Real>::infinity();
0279         if (inv) {
0280             out = -out;
0281         }
0282         c += 3;
0283         if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) {
0284             c += 5;
0285         }
0286         return c;
0287      }
0288 
0289     if (!(c[0] >= '0' && c[0] <= '9') &&
0290             !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) {
0291         // The string is known to be bad, so don't risk printing the whole thing.
0292         throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, (int)strlen(c)),
0293                                     "\" as a real number: does not start with digit "
0294                                     "or decimal point followed by digit.");
0295     }
0296 
0297     if (*c != '.' && (! check_comma || c[0] != ',')) {
0298         f = static_cast<Real>( strtoul10_64<ExceptionType> ( c, &c) );
0299     }
0300 
0301     if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') {
0302         ++c;
0303 
0304         // NOTE: The original implementation is highly inaccurate here. The precision of a single
0305         // IEEE 754 float is not high enough, everything behind the 6th digit tends to be more
0306         // inaccurate than it would need to be. Casting to double seems to solve the problem.
0307         // strtol_64 is used to prevent integer overflow.
0308 
0309         // Another fix: this tends to become 0 for long numbers if we don't limit the maximum
0310         // number of digits to be read. AI_FAST_ATOF_RELAVANT_DECIMALS can be a value between
0311         // 1 and 15.
0312         unsigned int diff = AI_FAST_ATOF_RELAVANT_DECIMALS;
0313         double pl = static_cast<double>( strtoul10_64<ExceptionType> ( c, &c, &diff ));
0314 
0315         pl *= fast_atof_table[diff];
0316         f += static_cast<Real>( pl );
0317     }
0318     // For backwards compatibility: eat trailing dots, but not trailing commas.
0319     else if (*c == '.') {
0320         ++c;
0321     }
0322 
0323     // A major 'E' must be allowed. Necessary for proper reading of some DXF files.
0324     // Thanks to Zhao Lei to point out that this if() must be outside the if (*c == '.' ..)
0325     if (*c == 'e' || *c == 'E') {
0326         ++c;
0327         const bool einv = (*c=='-');
0328         if (einv || *c=='+') {
0329             ++c;
0330         }
0331 
0332         // The reason float constants are used here is that we've seen cases where compilers
0333         // would perform such casts on compile-time constants at runtime, which would be
0334         // bad considering how frequently fast_atoreal_move<float> is called in Assimp.
0335         Real exp = static_cast<Real>( strtoul10_64<ExceptionType>(c, &c) );
0336         if (einv) {
0337             exp = -exp;
0338         }
0339         f *= std::pow(static_cast<Real>(10.0), exp);
0340     }
0341 
0342     if (inv) {
0343         f = -f;
0344     }
0345     out = f;
0346     return c;
0347 }
0348 
0349 // ------------------------------------------------------------------------------------
0350 // The same but more human.
0351 template<typename ExceptionType = DeadlyImportError>
0352 inline ai_real fast_atof(const char* c) {
0353     ai_real ret(0.0);
0354     fast_atoreal_move<ai_real, ExceptionType>(c, ret);
0355 
0356     return ret;
0357 }
0358 
0359 template<typename ExceptionType = DeadlyImportError>
0360 inline
0361 ai_real fast_atof( const char* c, const char** cout) {
0362     ai_real ret(0.0);
0363     *cout = fast_atoreal_move<ai_real, ExceptionType>(c, ret);
0364 
0365     return ret;
0366 }
0367 
0368 template<typename ExceptionType = DeadlyImportError>
0369 inline ai_real fast_atof( const char** inout) {
0370     ai_real ret(0.0);
0371     *inout = fast_atoreal_move<ai_real, ExceptionType>(*inout, ret);
0372 
0373     return ret;
0374 }
0375 
0376 } //! namespace Assimp
0377 
0378 #endif // FAST_A_TO_F_H_INCLUDED