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