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