1 #pragma once
2 
3 // Copyright (C) 2002-2007 Nikolaus Gebhardt
4 // This file is part of the "Irrlicht Engine" and the "irrXML" project.
5 // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
6 
7 // ------------------------------------------------------------------------------------
8 // Original description: (Schrompf)
9 // Adapted to the ASSIMP library because the builtin atof indeed takes AGES to parse a
10 // float inside a large string. Before parsing, it does a strlen on the given point.
11 // Changes:
12 //  22nd October 08 (Aramis_acg): Added temporary cast to double, added strtoul10_64
13 //     to ensure long numbers are handled correctly
14 // ------------------------------------------------------------------------------------
15 
16 #pragma once
17 #ifndef FAST_A_TO_F_H_INCLUDED
18 #define FAST_A_TO_F_H_INCLUDED
19 
20 #ifdef __GNUC__
21 #   pragma GCC system_header
22 #endif
23 
24 #include <cmath>
25 #include <limits>
26 #include <stdint.h>
27 #include <stdexcept>
28 #include <assimp/defs.h>
29 
30 #include "StringComparison.h"
31 #include <assimp/DefaultLogger.hpp>
32 
33 #ifdef _MSC_VER
34 #  include <stdint.h>
35 #else
36 #  include <assimp/Compiler/pstdint.h>
37 #endif
38 
39 namespace Assimp {
40 
41 const double fast_atof_table[16] =  {  // we write [16] here instead of [] to work around a swig bug
42     0.0,
43     0.1,
44     0.01,
45     0.001,
46     0.0001,
47     0.00001,
48     0.000001,
49     0.0000001,
50     0.00000001,
51     0.000000001,
52     0.0000000001,
53     0.00000000001,
54     0.000000000001,
55     0.0000000000001,
56     0.00000000000001,
57     0.000000000000001
58 };
59 
60 
61 // ------------------------------------------------------------------------------------
62 // Convert a string in decimal format to a number
63 // ------------------------------------------------------------------------------------
64 inline
65 unsigned int strtoul10( const char* in, const char** out=0) {
66     unsigned int value = 0;
67 
68     for ( ;; ) {
69         if ( *in < '0' || *in > '9' ) {
70             break;
71         }
72 
73         value = ( value * 10 ) + ( *in - '0' );
74         ++in;
75     }
76     if ( out ) {
77         *out = in;
78     }
79     return value;
80 }
81 
82 // ------------------------------------------------------------------------------------
83 // Convert a string in octal format to a number
84 // ------------------------------------------------------------------------------------
85 inline
86 unsigned int strtoul8( const char* in, const char** out=0) {
87     unsigned int value( 0 );
88     for ( ;; ) {
89         if ( *in < '0' || *in > '7' ) {
90             break;
91         }
92 
93         value = ( value << 3 ) + ( *in - '0' );
94         ++in;
95     }
96     if ( out ) {
97         *out = in;
98     }
99     return value;
100 }
101 
102 // ------------------------------------------------------------------------------------
103 // Convert a string in hex format to a number
104 // ------------------------------------------------------------------------------------
105 inline
106 unsigned int strtoul16( const char* in, const char** out=0) {
107     unsigned int value( 0 );
108     for ( ;; ) {
109         if ( *in >= '0' && *in <= '9' ) {
110             value = ( value << 4u ) + ( *in - '0' );
111         } else if (*in >= 'A' && *in <= 'F') {
112             value = ( value << 4u ) + ( *in - 'A' ) + 10;
113         } else if (*in >= 'a' && *in <= 'f') {
114             value = ( value << 4u ) + ( *in - 'a' ) + 10;
115         } else {
116             break;
117         }
118         ++in;
119     }
120     if ( out ) {
121         *out = in;
122     }
123     return value;
124 }
125 
126 // ------------------------------------------------------------------------------------
127 // Convert just one hex digit
128 // Return value is UINT_MAX if the input character is not a hex digit.
129 // ------------------------------------------------------------------------------------
130 inline
HexDigitToDecimal(char in)131 unsigned int HexDigitToDecimal(char in) {
132     unsigned int out( UINT_MAX );
133     if ( in >= '0' && in <= '9' ) {
134         out = in - '0';
135     } else if ( in >= 'a' && in <= 'f' ) {
136         out = 10u + in - 'a';
137     } else if ( in >= 'A' && in <= 'F' ) {
138         out = 10u + in - 'A';
139     }
140 
141     // return value is UINT_MAX if the input is not a hex digit
142     return out;
143 }
144 
145 // ------------------------------------------------------------------------------------
146 // Convert a hex-encoded octet (2 characters, i.e. df or 1a).
147 // ------------------------------------------------------------------------------------
148 inline
HexOctetToDecimal(const char * in)149 uint8_t HexOctetToDecimal(const char* in) {
150     return ((uint8_t)HexDigitToDecimal(in[0])<<4)+(uint8_t)HexDigitToDecimal(in[1]);
151 }
152 
153 // ------------------------------------------------------------------------------------
154 // signed variant of strtoul10
155 // ------------------------------------------------------------------------------------
156 inline
157 int strtol10( const char* in, const char** out=0) {
158     bool inv = (*in=='-');
159     if ( inv || *in == '+' ) {
160         ++in;
161     }
162 
163     int value = strtoul10(in,out);
164     if (inv) {
165         value = -value;
166     }
167     return value;
168 }
169 
170 // ------------------------------------------------------------------------------------
171 // Parse a C++-like integer literal - hex and oct prefixes.
172 // 0xNNNN - hex
173 // 0NNN   - oct
174 // NNN    - dec
175 // ------------------------------------------------------------------------------------
176 inline
177 unsigned int strtoul_cppstyle( const char* in, const char** out=0) {
178     if ('0' == in[0]) {
179         return 'x' == in[1] ? strtoul16(in+2,out) : strtoul8(in+1,out);
180     }
181     return strtoul10(in, out);
182 }
183 
184 // ------------------------------------------------------------------------------------
185 // Special version of the function, providing higher accuracy and safety
186 // It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
187 // ------------------------------------------------------------------------------------
188 inline
189 uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_inout=0) {
190     unsigned int cur = 0;
191     uint64_t value = 0;
192 
193     if ( *in < '0' || *in > '9' ) {
194         throw std::invalid_argument( std::string( "The string \"" ) + in + "\" cannot be converted into a value." );
195     }
196 
197     for ( ;; ) {
198         if ( *in < '0' || *in > '9' ) {
199             break;
200         }
201 
202         const uint64_t new_value = ( value * (uint64_t) 10 ) + ( (uint64_t) ( *in - '0' ) );
203 
204         // numeric overflow, we rely on you
205         if ( new_value < value ) {
206             ASSIMP_LOG_WARN_F( "Converting the string \"", in, "\" into a value resulted in overflow." );
207             return 0;
208         }
209 
210         value = new_value;
211 
212         ++in;
213         ++cur;
214 
215         if (max_inout && *max_inout == cur) {
216             if (out) { /* skip to end */
217                 while ( *in >= '0' && *in <= '9' ) {
218                     ++in;
219                 }
220                 *out = in;
221             }
222 
223             return value;
224         }
225     }
226     if ( out ) {
227         *out = in;
228     }
229 
230     if ( max_inout ) {
231         *max_inout = cur;
232     }
233 
234     return value;
235 }
236 
237 // ------------------------------------------------------------------------------------
238 // signed variant of strtoul10_64
239 // ------------------------------------------------------------------------------------
240 inline
241 int64_t strtol10_64(const char* in, const char** out = 0, unsigned int* max_inout = 0) {
242     bool inv = (*in == '-');
243     if ( inv || *in == '+' ) {
244         ++in;
245     }
246 
247     int64_t value = strtoul10_64(in, out, max_inout);
248     if (inv) {
249         value = -value;
250     }
251     return value;
252 }
253 
254 // Number of relevant decimals for floating-point parsing.
255 #define AI_FAST_ATOF_RELAVANT_DECIMALS 15
256 
257 // ------------------------------------------------------------------------------------
258 //! Provides a fast function for converting a string into a float,
259 //! about 6 times faster than atof in win32.
260 // If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
261 // ------------------------------------------------------------------------------------
262 template<typename Real>
263 inline
264 const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) {
265     Real f = 0;
266 
267     bool inv = (*c == '-');
268     if (inv || *c == '+') {
269         ++c;
270     }
271 
272     if ((c[0] == 'N' || c[0] == 'n') && ASSIMP_strincmp(c, "nan", 3) == 0) {
273         out = std::numeric_limits<Real>::quiet_NaN();
274         c += 3;
275         return c;
276     }
277 
278     if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inf", 3) == 0) {
279         out = std::numeric_limits<Real>::infinity();
280         if (inv) {
281             out = -out;
282         }
283         c += 3;
284         if ((c[0] == 'I' || c[0] == 'i') && ASSIMP_strincmp(c, "inity", 5) == 0) {
285             c += 5;
286         }
287         return c;
288      }
289 
290     if (!(c[0] >= '0' && c[0] <= '9') &&
291             !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) {
292         throw std::invalid_argument("Cannot parse string "
293                                     "as real number: does not start with digit "
294                                     "or decimal point followed by digit.");
295     }
296 
297     if (*c != '.' && (! check_comma || c[0] != ',')) {
298         f = static_cast<Real>( strtoul10_64 ( c, &c) );
299     }
300 
301     if ((*c == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9') {
302         ++c;
303 
304         // NOTE: The original implementation is highly inaccurate here. The precision of a single
305         // IEEE 754 float is not high enough, everything behind the 6th digit tends to be more
306         // inaccurate than it would need to be. Casting to double seems to solve the problem.
307         // strtol_64 is used to prevent integer overflow.
308 
309         // Another fix: this tends to become 0 for long numbers if we don't limit the maximum
310         // number of digits to be read. AI_FAST_ATOF_RELAVANT_DECIMALS can be a value between
311         // 1 and 15.
312         unsigned int diff = AI_FAST_ATOF_RELAVANT_DECIMALS;
313         double pl = static_cast<double>( strtoul10_64 ( c, &c, &diff ));
314 
315         pl *= fast_atof_table[diff];
316         f += static_cast<Real>( pl );
317     }
318     // For backwards compatibility: eat trailing dots, but not trailing commas.
319     else if (*c == '.') {
320         ++c;
321     }
322 
323     // A major 'E' must be allowed. Necessary for proper reading of some DXF files.
324     // Thanks to Zhao Lei to point out that this if() must be outside the if (*c == '.' ..)
325     if (*c == 'e' || *c == 'E') {
326         ++c;
327         const bool einv = (*c=='-');
328         if (einv || *c=='+') {
329             ++c;
330         }
331 
332         // The reason float constants are used here is that we've seen cases where compilers
333         // would perform such casts on compile-time constants at runtime, which would be
334         // bad considering how frequently fast_atoreal_move<float> is called in Assimp.
335         Real exp = static_cast<Real>( strtoul10_64(c, &c) );
336         if (einv) {
337             exp = -exp;
338         }
339         f *= std::pow(static_cast<Real>(10.0), exp);
340     }
341 
342     if (inv) {
343         f = -f;
344     }
345     out = f;
346     return c;
347 }
348 
349 // ------------------------------------------------------------------------------------
350 // The same but more human.
351 inline
fast_atof(const char * c)352 ai_real fast_atof(const char* c) {
353     ai_real ret(0.0);
354     fast_atoreal_move<ai_real>(c, ret);
355 
356     return ret;
357 }
358 
359 inline
fast_atof(const char * c,const char ** cout)360 ai_real fast_atof( const char* c, const char** cout) {
361     ai_real ret(0.0);
362     *cout = fast_atoreal_move<ai_real>(c, ret);
363 
364     return ret;
365 }
366 
367 inline
fast_atof(const char ** inout)368 ai_real fast_atof( const char** inout) {
369     ai_real ret(0.0);
370     *inout = fast_atoreal_move<ai_real>(*inout, ret);
371 
372     return ret;
373 }
374 
375 } //! namespace Assimp
376 
377 #endif // FAST_A_TO_F_H_INCLUDED
378